blob: 5d9f87f2fc8553cb6f991215286005f5333d3237 [file] [log] [blame]
"""Provides constants and functions needed by tv_testcase classes."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import importlib
import json
import sys
# pylint: disable=C6204
from urllib import urlencode
import urlparse
import container_util
try:
import custom_query_param_constants as query_param_constants
except ImportError:
import default_query_param_constants as query_param_constants
# These are watched for in webdriver_benchmark_test.py
TEST_RESULT = "webdriver_benchmark TEST RESULT"
TEST_COMPLETE = "webdriver_benchmark TEST COMPLETE"
# These are event types that can be injected
EVENT_TYPE_KEY_DOWN = "KeyDown"
EVENT_TYPE_KEY_UP = "KeyUp"
def import_selenium_module(submodule=None):
"""Dynamically imports a selenium.webdriver submodule.
This is done because selenium 3.0 is not commonly pre-installed
on workstations, and we want to have a friendly error message for that
case.
Args:
submodule: module subpath underneath "selenium.webdriver"
Returns:
appropriate module
"""
if submodule:
module_path = ".".join(("selenium", submodule))
else:
module_path = "selenium"
# As of this writing, Google uses selenium 3.0.0b2 internally, so
# thats what we will target here as well.
try:
module = importlib.import_module(module_path)
if submodule is None:
# Only the top-level module has __version__
if not module.__version__.startswith("3.0"):
raise ImportError("Not version 3.0.x")
except ImportError:
sys.stderr.write("Could not import {}\n"
"Please install selenium >= 3.0.0b2.\n"
"Commonly: \"sudo pip install 'selenium>=3.0.0b2'\"\n"
.format(module_path))
sys.exit(1)
return module
def generate_url(default_url, query_params_override=None):
"""Returns the URL indicated by the path and query parameters.
Args:
default_url: the default url to use; its query params may be overridden
query_params_override: optional query params that override the ones
contained within the default URL
Returns:
the url generated from the parameters
"""
parsed_url = list(urlparse.urlparse(default_url))
query_params = query_param_constants.BASE_QUERY_PARAMS
if query_params_override:
query_params.update(query_params_override)
else:
query_params.update(urlparse.parse_qsl(parsed_url[4]))
parsed_url[4] = urlencode(query_params, doseq=True)
return urlparse.urlunparse(parsed_url)
def record_test_result(name, result):
"""Records an individual scalar result of a benchmark test.
Args:
name: string name of test case
result: Test result. Must be JSON encodable scalar.
"""
value_to_record = result
string_value_to_record = json.JSONEncoder().encode(value_to_record)
print("{}: {} {}\n".format(TEST_RESULT, name, string_value_to_record))
sys.stdout.flush()
class RecordStrategyMax(object):
""""Records the max of an array of values."""
def run(self, name, values):
"""Records the max of an array of values.
Args:
name: string name of test case
values: must be array of JSON encodable scalar
"""
record_test_result("{}Max".format(name), max(values))
class RecordStrategyMin(object):
""""Records the min of an array of values."""
def run(self, name, values):
"""Records the min of an array of values.
Args:
name: string name of test case
values: must be array of JSON encodable scalar
"""
record_test_result("{}Min".format(name), min(values))
class RecordStrategyMean(object):
""""Records the mean of an array of values."""
def run(self, name, values):
"""Records the mean of an array of values.
Args:
name: string name of test case
values: must be array of JSON encodable scalar
"""
record_test_result("{}Mean".format(name), container_util.mean(values))
class RecordStrategyMedian(object):
""""Records the median of an array of test results."""
def run(self, name, values):
"""Records the median of an array of values.
Args:
name: string name of test case
values: must be array of JSON encodable scalar
"""
record_test_result("{}Median".format(name),
container_util.percentile(values, 50))
class RecordStrategyPercentile(object):
""""Records the specified percentile of an array of test results."""
def __init__(self, percentile):
"""Initializes the record strategy.
Args:
percentile: the percentile to record
"""
self.percentile = percentile
def run(self, name, values):
"""Records the percentile of an array of values.
Args:
name: string name of test case
values: must be array of JSON encodable scalar
Raises:
RuntimeError: Raised on invalid args.
"""
record_test_result("{}Pct{}".format(name, self.percentile),
container_util.percentile(values, self.percentile))
class RecordStrategyStandardDeviation(object):
"""Records the standard deviation of an array of values."""
def run(self, name, values):
"""Records the standard deviation of an array of values.
Args:
name: string name of test case
values: must be array of JSON encodable scalar
Raises:
RuntimeError: Raised on invalid args.
"""
record_test_result("{}StdDev".format(name),
container_util.sample_standard_deviation(values))
class ResultsRecorder(object):
""""Collects values and records results after a benchmark test ends."""
def __init__(self, name, record_strategies):
"""Initializes the value recorder.
Args:
name: the name to use when recording test results
record_strategies: the strategies to use when the test ends
"""
self.name = name
self.record_strategies = record_strategies
self.values = []
def collect_value(self, value):
self.values.append(value)
def on_end_test(self):
"""Handles logic related to the end of the benchmark test."""
# Only run the strategies if values have been collected.
if self.values:
for record_strategy in self.record_strategies:
record_strategy.run(self.name, self.values)