blob: 25b3ba44643e4799ce946273f9d9a5f2abf3037b [file] [log] [blame]
#!/usr/bin/env python
import json
import logging
import shutil
import signal
import subprocess
import sys
import tempfile
import time
import types
import os
import urlparse
from benchmark_builder import BenchmarkBuilder
from benchmark_results import BenchmarkResults
from browser_driver.browser_driver_factory import BrowserDriverFactory
from http_server_driver.http_server_driver_factory import HTTPServerDriverFactory
from utils import timeout
_log = logging.getLogger(__name__)
class BenchmarkRunner(object):
def __init__(self, plan_file, local_copy, count_override, build_dir, output_file, platform, browser, scale_unit=True, device_id=None):
try:
plan_file = self._find_plan_file(plan_file)
with open(plan_file, 'r') as fp:
self._plan_name = os.path.split(os.path.splitext(plan_file)[0])[1]
self._plan = json.load(fp)
if not 'options' in self._plan:
self._plan['options'] = {}
if local_copy:
self._plan['local_copy'] = local_copy
if count_override:
self._plan['count'] = count_override
self._browser_driver = BrowserDriverFactory.create(platform, browser)
self._http_server_driver = HTTPServerDriverFactory.create(platform)
self._http_server_driver.set_device_id(device_id)
self._build_dir = os.path.abspath(build_dir) if build_dir else None
self._output_file = output_file
self._scale_unit = scale_unit
self._device_id = device_id
except IOError as error:
_log.error('Can not open plan file: {plan_file} - Error {error}'.format(plan_file=plan_file, error=error))
raise error
except ValueError as error:
_log.error('Plan file: {plan_file} may not follow JSON format - Error {error}'.format(plan_file=plan_file, error=error))
raise error
def _find_plan_file(self, plan_file):
if not os.path.exists(plan_file):
absPath = os.path.join(os.path.dirname(__file__), 'data/plans', plan_file)
if os.path.exists(absPath):
return absPath
if not absPath.endswith('.plan'):
absPath += '.plan'
if os.path.exists(absPath):
return absPath
return plan_file
def _get_result(self, test_url):
result = self._browser_driver.add_additional_results(test_url, self._http_server_driver.fetch_result())
assert(not self._http_server_driver.get_return_code())
return result
def _run_one_test(self, web_root, test_file):
result = None
try:
self._http_server_driver.serve(web_root)
url = urlparse.urljoin(self._http_server_driver.base_url(), self._plan_name + '/' + test_file)
self._browser_driver.launch_url(url, self._plan['options'], self._build_dir)
with timeout(self._plan['timeout']):
result = self._get_result(url)
finally:
self._browser_driver.close_browsers()
self._http_server_driver.kill_server()
return json.loads(result)
def _run_benchmark(self, count, web_root):
results = []
debug_outputs = []
for iteration in xrange(1, count + 1):
_log.info('Start the iteration {current_iteration} of {iterations} for current benchmark'.format(current_iteration=iteration, iterations=count))
try:
self._browser_driver.prepare_env(self._device_id)
if 'entry_point' in self._plan:
result = self._run_one_test(web_root, self._plan['entry_point'])
debug_outputs.append(result.pop('debugOutput', None))
assert(result)
results.append(result)
elif 'test_files' in self._plan:
run_result = {}
for test in self._plan['test_files']:
result = self._run_one_test(web_root, test)
assert(result)
run_result = self._merge(run_result, result)
debug_outputs.append(result.pop('debugOutput', None))
results.append(run_result)
else:
raise Exception('Plan does not contain entry_point or test_files')
finally:
self._browser_driver.restore_env()
self._browser_driver.close_browsers()
_log.info('End the iteration {current_iteration} of {iterations} for current benchmark'.format(current_iteration=iteration, iterations=count))
results = self._wrap(results)
output_file = self._output_file if self._output_file else self._plan['output_file']
self._dump(self._merge({'debugOutput': debug_outputs}, results), output_file)
self.show_results(results, self._scale_unit)
def execute(self):
with BenchmarkBuilder(self._plan_name, self._plan) as web_root:
self._run_benchmark(int(self._plan['count']), web_root)
@classmethod
def _dump(cls, results, output_file):
_log.info('Dumping the results to file {output_file}'.format(output_file=output_file))
try:
with open(output_file, 'w') as fp:
json.dump(results, fp)
except IOError as error:
_log.error('Cannot open output file: {output_file} - Error: {error}'.format(output_file=output_file, error=error))
_log.error('Results are:\n {result}'.format(result=json.dumps(results)))
@classmethod
def _wrap(cls, dicts):
_log.debug('Merging following results:\n{results}'.format(results=json.dumps(dicts)))
if not dicts:
return None
ret = {}
for dic in dicts:
ret = cls._merge(ret, dic)
_log.debug('Results after merging:\n{result}'.format(result=json.dumps(ret)))
return ret
@classmethod
def _merge(cls, a, b):
assert(isinstance(a, type(b)))
arg_type = type(a)
# special handle for list type, and should be handle before equal check
if arg_type == types.ListType and len(a) and (type(a[0]) == types.StringType or type(a[0]) == types.UnicodeType):
return a
if arg_type == types.DictType:
result = {}
for key, value in a.items():
if key in b:
result[key] = cls._merge(value, b[key])
else:
result[key] = value
for key, value in b.items():
if key not in result:
result[key] = value
return result
# for other types
return a + b
@classmethod
def show_results(cls, results, scale_unit=True):
results = BenchmarkResults(results)
print results.format(scale_unit)