зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1608374 - Split perftest from raptor.py r=AlexandruIonescu,rwood
Differential Revision: https://phabricator.services.mozilla.com/D59489 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
fd9fdcfe67
Коммит
46a00cab18
|
@ -0,0 +1,610 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
import mozinfo
|
||||
import mozprocess
|
||||
import mozversion
|
||||
from cmdline import FIREFOX_ANDROID_APPS
|
||||
from condprof.client import get_profile, ProfileNotFoundError
|
||||
from condprof.util import get_current_platform
|
||||
from logger.logger import RaptorLogger
|
||||
from mozprofile import create_profile
|
||||
from mozproxy import get_playback
|
||||
|
||||
|
||||
from gecko_profile import GeckoProfile
|
||||
from results import RaptorResultsHandler
|
||||
|
||||
LOG = RaptorLogger(component='raptor-perftest')
|
||||
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
try:
|
||||
from mozbuild.base import MozbuildObject
|
||||
|
||||
build = MozbuildObject.from_environment(cwd=here)
|
||||
except ImportError:
|
||||
build = None
|
||||
|
||||
|
||||
class Perftest(object):
|
||||
"""Abstract base class for perftests that execute via a subharness,
|
||||
either Raptor or browsertime."""
|
||||
|
||||
__metaclass__ = ABCMeta
|
||||
|
||||
def __init__(self, app, binary, run_local=False, noinstall=False,
|
||||
obj_path=None, profile_class=None, installerpath=None,
|
||||
gecko_profile=False, gecko_profile_interval=None, gecko_profile_entries=None,
|
||||
symbols_path=None, host=None, power_test=False, cpu_test=False, memory_test=False,
|
||||
is_release_build=False, debug_mode=False, post_startup_delay=None,
|
||||
interrupt_handler=None, e10s=True, enable_webrender=False,
|
||||
results_handler_class=RaptorResultsHandler, no_conditioned_profile=False,
|
||||
device_name=None, extra_prefs={}, **kwargs):
|
||||
|
||||
# Override the magic --host HOST_IP with the value of the environment variable.
|
||||
if host == 'HOST_IP':
|
||||
host = os.environ['HOST_IP']
|
||||
|
||||
self.config = {
|
||||
'app': app,
|
||||
'binary': binary,
|
||||
'platform': mozinfo.os,
|
||||
'processor': mozinfo.processor,
|
||||
'run_local': run_local,
|
||||
'obj_path': obj_path,
|
||||
'gecko_profile': gecko_profile,
|
||||
'gecko_profile_interval': gecko_profile_interval,
|
||||
'gecko_profile_entries': gecko_profile_entries,
|
||||
'symbols_path': symbols_path,
|
||||
'host': host,
|
||||
'power_test': power_test,
|
||||
'memory_test': memory_test,
|
||||
'cpu_test': cpu_test,
|
||||
'is_release_build': is_release_build,
|
||||
'enable_control_server_wait': memory_test or cpu_test,
|
||||
'e10s': e10s,
|
||||
'enable_webrender': enable_webrender,
|
||||
'no_conditioned_profile': no_conditioned_profile,
|
||||
'device_name': device_name,
|
||||
'enable_fission': extra_prefs.get('fission.autostart', False),
|
||||
'extra_prefs': extra_prefs
|
||||
}
|
||||
|
||||
self.firefox_android_apps = FIREFOX_ANDROID_APPS
|
||||
# See bugs 1582757, 1606199, and 1606767; until we support win10-aarch64,
|
||||
# fennec_aurora, and reference browser conditioned-profile builds,
|
||||
# fall back to mozrunner-created profiles
|
||||
self.no_condprof = ((self.config['platform'] == 'win'
|
||||
and self.config['processor'] == 'aarch64') or
|
||||
self.config['binary'] == 'org.mozilla.fennec_aurora' or
|
||||
self.config['binary'] == 'org.mozilla.reference.browser.raptor' or
|
||||
self.config['no_conditioned_profile'])
|
||||
LOG.info("self.no_condprof is: {}".format(self.no_condprof))
|
||||
|
||||
# We can never use e10s on fennec
|
||||
if self.config['app'] == 'fennec':
|
||||
self.config['e10s'] = False
|
||||
|
||||
self.browser_name = None
|
||||
self.browser_version = None
|
||||
|
||||
self.raptor_venv = os.path.join(os.getcwd(), 'raptor-venv')
|
||||
self.installerpath = installerpath
|
||||
self.playback = None
|
||||
self.benchmark = None
|
||||
self.benchmark_port = 0
|
||||
self.gecko_profiler = None
|
||||
self.post_startup_delay = post_startup_delay
|
||||
self.device = None
|
||||
self.profile_class = profile_class or app
|
||||
self.conditioned_profile_dir = None
|
||||
self.interrupt_handler = interrupt_handler
|
||||
self.results_handler = results_handler_class(**self.config)
|
||||
|
||||
self.browser_name, self.browser_version = self.get_browser_meta()
|
||||
|
||||
browser_name, browser_version = self.get_browser_meta()
|
||||
self.results_handler.add_browser_meta(self.config['app'], browser_version)
|
||||
|
||||
# debug mode is currently only supported when running locally
|
||||
self.debug_mode = debug_mode if self.config['run_local'] else False
|
||||
|
||||
# if running debug-mode reduce the pause after browser startup
|
||||
if self.debug_mode:
|
||||
self.post_startup_delay = min(self.post_startup_delay, 3000)
|
||||
LOG.info("debug-mode enabled, reducing post-browser startup pause to %d ms"
|
||||
% self.post_startup_delay)
|
||||
LOG.info("main raptor init, config is: %s" % str(self.config))
|
||||
|
||||
self.build_browser_profile()
|
||||
|
||||
@property
|
||||
def is_localhost(self):
|
||||
return self.config.get('host') in ('localhost', '127.0.0.1')
|
||||
|
||||
def get_conditioned_profile(self):
|
||||
"""Downloads a platform-specific conditioned profile, using the
|
||||
condprofile client API; returns a self.conditioned_profile_dir"""
|
||||
|
||||
# create a temp file to help ensure uniqueness
|
||||
temp_download_dir = tempfile.mkdtemp()
|
||||
LOG.info("Making temp_download_dir from inside get_conditioned_profile {}"
|
||||
.format(temp_download_dir))
|
||||
# call condprof's client API to yield our platform-specific
|
||||
# conditioned-profile binary
|
||||
if isinstance(self, PerftestAndroid):
|
||||
android_app = self.config["binary"].split("org.mozilla.")[-1]
|
||||
device_name = self.config.get("device_name")
|
||||
if device_name is None:
|
||||
device_name = "g5"
|
||||
platform = "%s-%s" % (device_name, android_app)
|
||||
else:
|
||||
platform = get_current_platform()
|
||||
try:
|
||||
cond_prof_target_dir = get_profile(temp_download_dir, platform, "settled")
|
||||
except ProfileNotFoundError:
|
||||
# If we can't find the profile on mozilla-central, we look on try
|
||||
cond_prof_target_dir = get_profile(temp_download_dir, platform,
|
||||
"settled", repo="try")
|
||||
# now get the full directory path to our fetched conditioned profile
|
||||
self.conditioned_profile_dir = os.path.join(temp_download_dir, cond_prof_target_dir)
|
||||
if not os.path.exists(cond_prof_target_dir):
|
||||
LOG.critical("Can't find target_dir {}, from get_profile()"
|
||||
"temp_download_dir {}, platform {}, settled"
|
||||
.format(cond_prof_target_dir, temp_download_dir, platform))
|
||||
raise OSError
|
||||
|
||||
LOG.info("self.conditioned_profile_dir is now set: {}"
|
||||
.format(self.conditioned_profile_dir))
|
||||
shutil.rmtree(temp_download_dir)
|
||||
|
||||
return self.conditioned_profile_dir
|
||||
|
||||
def build_browser_profile(self):
|
||||
if self.no_condprof:
|
||||
self.profile = create_profile(self.profile_class)
|
||||
else:
|
||||
self.get_conditioned_profile()
|
||||
# use mozprofile to create a profile for us, from our conditioned profile's path
|
||||
self.profile = create_profile(self.profile_class, profile=self.conditioned_profile_dir)
|
||||
# Merge extra profile data from testing/profiles
|
||||
with open(os.path.join(self.profile_data_dir, 'profiles.json'), 'r') as fh:
|
||||
base_profiles = json.load(fh)['raptor']
|
||||
|
||||
for profile in base_profiles:
|
||||
path = os.path.join(self.profile_data_dir, profile)
|
||||
LOG.info("Merging profile: {}".format(path))
|
||||
self.profile.merge(path)
|
||||
|
||||
if self.config['extra_prefs'].get('fission.autostart', False):
|
||||
LOG.info('Enabling fission via browser preferences')
|
||||
LOG.info('Browser preferences: {}'.format(self.config['extra_prefs']))
|
||||
self.profile.set_preferences(self.config['extra_prefs'])
|
||||
|
||||
# share the profile dir with the config and the control server
|
||||
self.config['local_profile_dir'] = self.profile.profile
|
||||
LOG.info('Local browser profile: {}'.format(self.profile.profile))
|
||||
|
||||
@property
|
||||
def profile_data_dir(self):
|
||||
if 'MOZ_DEVELOPER_REPO_DIR' in os.environ:
|
||||
return os.path.join(os.environ['MOZ_DEVELOPER_REPO_DIR'], 'testing', 'profiles')
|
||||
if build:
|
||||
return os.path.join(build.topsrcdir, 'testing', 'profiles')
|
||||
return os.path.join(here, 'profile_data')
|
||||
|
||||
@property
|
||||
def artifact_dir(self):
|
||||
artifact_dir = os.getcwd()
|
||||
if self.config.get('run_local', False):
|
||||
if 'MOZ_DEVELOPER_REPO_DIR' in os.environ:
|
||||
artifact_dir = os.path.join(os.environ['MOZ_DEVELOPER_REPO_DIR'],
|
||||
'testing', 'mozharness', 'build')
|
||||
else:
|
||||
artifact_dir = here
|
||||
elif os.getenv('MOZ_UPLOAD_DIR'):
|
||||
artifact_dir = os.getenv('MOZ_UPLOAD_DIR')
|
||||
return artifact_dir
|
||||
|
||||
@abstractmethod
|
||||
def run_test_setup(self, test):
|
||||
LOG.info("starting test: %s" % test['name'])
|
||||
|
||||
# if 'alert_on' was provided in the test INI, add to our config for results/output
|
||||
self.config['subtest_alert_on'] = test.get('alert_on')
|
||||
|
||||
if test.get('playback') is not None and self.playback is None:
|
||||
self.start_playback(test)
|
||||
|
||||
if test.get("preferences") is not None:
|
||||
self.set_browser_test_prefs(test['preferences'])
|
||||
|
||||
@abstractmethod
|
||||
def setup_chrome_args(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_browser_meta(self):
|
||||
pass
|
||||
|
||||
def run_tests(self, tests, test_names):
|
||||
try:
|
||||
for test in tests:
|
||||
try:
|
||||
self.run_test(test, timeout=int(test.get('page_timeout')))
|
||||
except RuntimeError as e:
|
||||
LOG.critical("Tests failed to finish! Application timed out.")
|
||||
LOG.error(e)
|
||||
finally:
|
||||
self.run_test_teardown(test)
|
||||
return self.process_results(tests, test_names)
|
||||
finally:
|
||||
self.clean_up()
|
||||
|
||||
@abstractmethod
|
||||
def run_test(self, test, timeout):
|
||||
raise NotImplementedError()
|
||||
|
||||
@abstractmethod
|
||||
def run_test_teardown(self, test):
|
||||
self.check_for_crashes()
|
||||
|
||||
# gecko profiling symbolication
|
||||
if self.config['gecko_profile']:
|
||||
self.gecko_profiler.symbolicate()
|
||||
# clean up the temp gecko profiling folders
|
||||
LOG.info("cleaning up after gecko profiling")
|
||||
self.gecko_profiler.clean()
|
||||
|
||||
def process_results(self, tests, test_names):
|
||||
# when running locally output results in build/raptor.json; when running
|
||||
# in production output to a local.json to be turned into tc job artifact
|
||||
raptor_json_path = os.path.join(self.artifact_dir, 'raptor.json')
|
||||
if not self.config.get('run_local', False):
|
||||
raptor_json_path = os.path.join(os.getcwd(), 'local.json')
|
||||
|
||||
self.config['raptor_json_path'] = raptor_json_path
|
||||
self.config['artifact_dir'] = self.artifact_dir
|
||||
return self.results_handler.summarize_and_output(self.config, tests, test_names)
|
||||
|
||||
@abstractmethod
|
||||
def set_browser_test_prefs(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def check_for_crashes(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def clean_up(self):
|
||||
pass
|
||||
|
||||
def get_page_timeout_list(self):
|
||||
return self.results_handler.page_timeout_list
|
||||
|
||||
def get_recording_paths(self, test):
|
||||
recordings = test.get("playback_recordings")
|
||||
|
||||
if recordings:
|
||||
recording_paths = []
|
||||
proxy_dir = self.playback.mozproxy_dir
|
||||
|
||||
for recording in recordings.split():
|
||||
if not recording:
|
||||
continue
|
||||
recording_paths.append(os.path.join(proxy_dir, recording))
|
||||
|
||||
return recording_paths
|
||||
|
||||
def log_recording_dates(self, test):
|
||||
_recording_paths = self.get_recording_paths(test)
|
||||
if _recording_paths is None:
|
||||
LOG.info("No playback recordings specified in the test; so not getting recording info")
|
||||
return
|
||||
|
||||
for r in _recording_paths:
|
||||
json_path = '{}.json'.format(r.split('.')[0])
|
||||
|
||||
if os.path.exists(json_path):
|
||||
with open(json_path) as f:
|
||||
recording_date = json.loads(f.read()).get('recording_date')
|
||||
|
||||
if recording_date is not None:
|
||||
LOG.info('Playback recording date: {} '.
|
||||
format(recording_date.split(' ')[0]))
|
||||
else:
|
||||
LOG.info('Playback recording date not available')
|
||||
else:
|
||||
LOG.info('Playback recording information not available')
|
||||
|
||||
def get_playback_config(self, test):
|
||||
platform = self.config['platform']
|
||||
playback_dir = os.path.join(here, 'playback')
|
||||
|
||||
self.config.update({
|
||||
'playback_tool': test.get('playback'),
|
||||
'playback_version': test.get('playback_version', "4.0.4"),
|
||||
'playback_binary_zip': test.get('playback_binary_zip_%s' % platform),
|
||||
'playback_pageset_zip': test.get('playback_pageset_zip_%s' % platform),
|
||||
'playback_binary_manifest': test.get('playback_binary_manifest'),
|
||||
'playback_pageset_manifest': test.get('playback_pageset_manifest'),
|
||||
})
|
||||
|
||||
for key in ('playback_pageset_manifest', 'playback_pageset_zip'):
|
||||
if self.config.get(key) is None:
|
||||
continue
|
||||
self.config[key] = os.path.join(playback_dir, self.config[key])
|
||||
|
||||
LOG.info("test uses playback tool: %s " % self.config['playback_tool'])
|
||||
|
||||
def delete_proxy_settings_from_profile(self):
|
||||
# Must delete the proxy settings from the profile if running
|
||||
# the test with a host different from localhost.
|
||||
userjspath = os.path.join(self.profile.profile, 'user.js')
|
||||
with open(userjspath) as userjsfile:
|
||||
prefs = userjsfile.readlines()
|
||||
prefs = [pref for pref in prefs if 'network.proxy' not in pref]
|
||||
with open(userjspath, 'w') as userjsfile:
|
||||
userjsfile.writelines(prefs)
|
||||
|
||||
def start_playback(self, test):
|
||||
# creating the playback tool
|
||||
self.get_playback_config(test)
|
||||
self.playback = get_playback(self.config)
|
||||
|
||||
self.playback.config['playback_files'] = self.get_recording_paths(test)
|
||||
|
||||
# let's start it!
|
||||
self.playback.start()
|
||||
|
||||
self.log_recording_dates(test)
|
||||
|
||||
def _init_gecko_profiling(self, test):
|
||||
LOG.info("initializing gecko profiler")
|
||||
upload_dir = os.getenv('MOZ_UPLOAD_DIR')
|
||||
if not upload_dir:
|
||||
LOG.critical("Profiling ignored because MOZ_UPLOAD_DIR was not set")
|
||||
else:
|
||||
self.gecko_profiler = GeckoProfile(upload_dir,
|
||||
self.config,
|
||||
test)
|
||||
|
||||
|
||||
class PerftestAndroid(Perftest):
|
||||
"""Mixin class for Android-specific Perftest subclasses."""
|
||||
|
||||
def setup_chrome_args(self, test):
|
||||
'''Sets up chrome/chromium cmd-line arguments.
|
||||
|
||||
Needs to be "implemented" here to deal with Python 2
|
||||
unittest failures.
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
def get_browser_meta(self):
|
||||
'''Returns the browser name and version in a tuple (name, version).
|
||||
|
||||
Uses mozversion as the primary method to get this meta data and for
|
||||
android this is the only method which exists to get this data. With android,
|
||||
we use the installerpath attribute to determine this and this only works
|
||||
with Firefox browsers.
|
||||
'''
|
||||
browser_name = None
|
||||
browser_version = None
|
||||
|
||||
if self.config['app'] in self.firefox_android_apps:
|
||||
try:
|
||||
meta = mozversion.get_version(binary=self.installerpath)
|
||||
browser_name = meta.get('application_name')
|
||||
browser_version = meta.get('application_version')
|
||||
except Exception as e:
|
||||
LOG.warning(
|
||||
"Failed to get android browser meta data through mozversion: %s-%s" %
|
||||
(e.__class__.__name__, e)
|
||||
)
|
||||
|
||||
if not browser_name:
|
||||
LOG.warning("Could not find a browser name")
|
||||
else:
|
||||
LOG.info("Browser name: %s" % browser_name)
|
||||
|
||||
if not browser_version:
|
||||
LOG.warning("Could not find a browser version")
|
||||
else:
|
||||
LOG.info("Browser version: %s" % browser_version)
|
||||
|
||||
return (browser_name, browser_version)
|
||||
|
||||
def set_reverse_port(self, port):
|
||||
tcp_port = "tcp:{}".format(port)
|
||||
self.device.create_socket_connection('reverse', tcp_port, tcp_port)
|
||||
|
||||
def set_reverse_ports(self):
|
||||
if self.is_localhost:
|
||||
|
||||
# only raptor-webext uses the control server
|
||||
if self.config.get('browsertime', False) is False:
|
||||
LOG.info("making the raptor control server port available to device")
|
||||
self.set_reverse_port(self.control_server.port)
|
||||
|
||||
if self.playback:
|
||||
LOG.info("making the raptor playback server port available to device")
|
||||
self.set_reverse_port(self.playback.port)
|
||||
|
||||
if self.benchmark:
|
||||
LOG.info("making the raptor benchmarks server port available to device")
|
||||
self.set_reverse_port(self.benchmark_port)
|
||||
else:
|
||||
LOG.info("Reverse port forwarding is uded only on local devices")
|
||||
|
||||
def build_browser_profile(self):
|
||||
super(PerftestAndroid, self).build_browser_profile()
|
||||
|
||||
# Merge in the Android profile.
|
||||
path = os.path.join(self.profile_data_dir, 'raptor-android')
|
||||
LOG.info("Merging profile: {}".format(path))
|
||||
self.profile.merge(path)
|
||||
self.profile.set_preferences({'browser.tabs.remote.autostart': self.config['e10s']})
|
||||
|
||||
def clear_app_data(self):
|
||||
LOG.info("clearing %s app data" % self.config['binary'])
|
||||
self.device.shell("pm clear %s" % self.config['binary'])
|
||||
|
||||
def set_debug_app_flag(self):
|
||||
# required so release apks will read the android config.yml file
|
||||
LOG.info("setting debug-app flag for %s" % self.config['binary'])
|
||||
self.device.shell("am set-debug-app --persistent %s" % self.config['binary'])
|
||||
|
||||
def copy_profile_to_device(self):
|
||||
"""Copy the profile to the device, and update permissions of all files."""
|
||||
if not self.device.is_app_installed(self.config['binary']):
|
||||
raise Exception('%s is not installed' % self.config['binary'])
|
||||
|
||||
try:
|
||||
LOG.info("copying profile to device: %s" % self.remote_profile)
|
||||
self.device.rm(self.remote_profile, force=True, recursive=True)
|
||||
# self.device.mkdir(self.remote_profile)
|
||||
self.device.push(self.profile.profile, self.remote_profile)
|
||||
self.device.chmod(self.remote_profile, recursive=True, root=True)
|
||||
|
||||
except Exception:
|
||||
LOG.error("Unable to copy profile to device.")
|
||||
raise
|
||||
|
||||
def turn_on_android_app_proxy(self):
|
||||
# for geckoview/android pageload playback we can't use a policy to turn on the
|
||||
# proxy; we need to set prefs instead; note that the 'host' may be different
|
||||
# than '127.0.0.1' so we must set the prefs accordingly
|
||||
proxy_prefs = {}
|
||||
proxy_prefs["network.proxy.type"] = 1
|
||||
proxy_prefs["network.proxy.http"] = self.playback.host
|
||||
proxy_prefs["network.proxy.http_port"] = self.playback.port
|
||||
proxy_prefs["network.proxy.ssl"] = self.playback.host
|
||||
proxy_prefs["network.proxy.ssl_port"] = self.playback.port
|
||||
proxy_prefs["network.proxy.no_proxies_on"] = self.config['host']
|
||||
|
||||
LOG.info("setting profile prefs to turn on the android app proxy: {}".format(proxy_prefs))
|
||||
self.profile.set_preferences(proxy_prefs)
|
||||
|
||||
|
||||
class PerftestDesktop(Perftest):
|
||||
"""Mixin class for Desktop-specific Perftest subclasses"""
|
||||
|
||||
def setup_chrome_args(self, test):
|
||||
'''Sets up chrome/chromium cmd-line arguments.
|
||||
|
||||
Needs to be "implemented" here to deal with Python 2
|
||||
unittest failures.
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
def desktop_chrome_args(self, test):
|
||||
'''Returns cmd line options required to run pageload tests on Desktop Chrome
|
||||
and Chromium. Also add the cmd line options to turn on the proxy and
|
||||
ignore security certificate errors if using host localhost, 127.0.0.1.
|
||||
'''
|
||||
chrome_args = [
|
||||
'--use-mock-keychain',
|
||||
'--no-default-browser-check',
|
||||
]
|
||||
|
||||
if test.get('playback', False):
|
||||
pb_args = [
|
||||
'--proxy-server=%s:%d' % (self.playback.host, self.playback.port),
|
||||
'--proxy-bypass-list=localhost;127.0.0.1',
|
||||
'--ignore-certificate-errors',
|
||||
]
|
||||
|
||||
if not self.is_localhost:
|
||||
pb_args[0] = pb_args[0].replace('127.0.0.1', self.config['host'])
|
||||
|
||||
chrome_args.extend(pb_args)
|
||||
|
||||
if self.debug_mode:
|
||||
chrome_args.extend(['--auto-open-devtools-for-tabs'])
|
||||
|
||||
return chrome_args
|
||||
|
||||
def get_browser_meta(self):
|
||||
'''Returns the browser name and version in a tuple (name, version).
|
||||
|
||||
On desktop, we use mozversion but a fallback method also exists
|
||||
for non-firefox browsers, where mozversion is known to fail. The
|
||||
methods are OS-specific, with windows being the outlier.
|
||||
'''
|
||||
browser_name = None
|
||||
browser_version = None
|
||||
|
||||
try:
|
||||
meta = mozversion.get_version(binary=self.config['binary'])
|
||||
browser_name = meta.get('application_name')
|
||||
browser_version = meta.get('application_version')
|
||||
except Exception as e:
|
||||
LOG.warning(
|
||||
"Failed to get browser meta data through mozversion: %s-%s" %
|
||||
(e.__class__.__name__, e)
|
||||
)
|
||||
LOG.info("Attempting to get version through fallback method...")
|
||||
|
||||
# Fall-back method to get browser version on desktop
|
||||
try:
|
||||
if 'linux' in self.config['platform'] or 'mac' in self.config['platform']:
|
||||
command = [self.config['binary'], '--version']
|
||||
proc = mozprocess.ProcessHandler(command)
|
||||
proc.run(timeout=10, outputTimeout=10)
|
||||
proc.wait()
|
||||
|
||||
bmeta = proc.output
|
||||
meta_re = re.compile(r"([A-z\s]+)\s+([\w.]*)")
|
||||
if len(bmeta) != 0:
|
||||
match = meta_re.match(bmeta[0])
|
||||
if match:
|
||||
browser_name = self.config['app']
|
||||
browser_version = match.group(2)
|
||||
else:
|
||||
LOG.info("Couldn't get browser version and name")
|
||||
else:
|
||||
# On windows we need to use wimc to get the version
|
||||
command = r'wmic datafile where name="{0}"'.format(
|
||||
self.config['binary'].replace('\\', r"\\")
|
||||
)
|
||||
bmeta = subprocess.check_output(command)
|
||||
|
||||
meta_re = re.compile(r"\s+([\d.a-z]+)\s+")
|
||||
match = meta_re.findall(bmeta)
|
||||
if len(match) > 0:
|
||||
browser_name = self.config['app']
|
||||
browser_version = match[-1]
|
||||
else:
|
||||
LOG.info("Couldn't get browser version and name")
|
||||
except Exception as e:
|
||||
LOG.warning(
|
||||
"Failed to get browser meta data through fallback method: %s-%s" %
|
||||
(e.__class__.__name__, e)
|
||||
)
|
||||
|
||||
if not browser_name:
|
||||
LOG.warning("Could not find a browser name")
|
||||
else:
|
||||
LOG.info("Browser name: %s" % browser_name)
|
||||
|
||||
if not browser_version:
|
||||
LOG.warning("Could not find a browser version")
|
||||
else:
|
||||
LOG.info("Browser version: %s" % browser_version)
|
||||
|
||||
return (browser_name, browser_version)
|
|
@ -10,31 +10,23 @@ from abc import ABCMeta, abstractmethod
|
|||
import json
|
||||
import os
|
||||
import posixpath
|
||||
import re
|
||||
import shutil
|
||||
import six
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
import tarfile
|
||||
|
||||
import requests
|
||||
|
||||
import mozcrash
|
||||
import mozinfo
|
||||
import mozprocess
|
||||
import mozproxy.utils as mpu
|
||||
import mozversion
|
||||
from condprof.client import get_profile, ProfileNotFoundError
|
||||
from condprof.util import get_current_platform
|
||||
from logger.logger import RaptorLogger
|
||||
from mozdevice import ADBDevice
|
||||
from mozlog import commandline
|
||||
from mozpower import MozPower
|
||||
from mozprofile import create_profile
|
||||
from mozprofile.cli import parse_preferences
|
||||
from mozproxy import get_playback
|
||||
from mozrunner import runners
|
||||
|
||||
# need this so raptor imports work both from /raptor and via mach
|
||||
|
@ -58,10 +50,8 @@ except ImportError:
|
|||
|
||||
from benchmark import Benchmark
|
||||
from cmdline import (parse_args,
|
||||
FIREFOX_ANDROID_APPS,
|
||||
CHROMIUM_DISTROS)
|
||||
from control_server import RaptorControlServer
|
||||
from gecko_profile import GeckoProfile
|
||||
from gen_test_config import gen_test_config
|
||||
from outputhandler import OutputHandler
|
||||
from manifest import get_raptor_test_list
|
||||
|
@ -73,6 +63,7 @@ from utils import view_gecko_profile, write_yml_file
|
|||
from cpu import start_android_cpu_profiler
|
||||
|
||||
from signal_handler import SignalHandler, SignalHandlerException
|
||||
from perftest import Perftest, PerftestDesktop, PerftestAndroid
|
||||
|
||||
LOG = RaptorLogger(component='raptor-main')
|
||||
# Bug 1547943 - Intermittent mozrunner.errors.RunnerNotStartedError
|
||||
|
@ -82,576 +73,6 @@ mpu.LOG = RaptorLogger(component='raptor-mitmproxy')
|
|||
DEFAULT_CHROMEVERSION = '77'
|
||||
|
||||
|
||||
class Perftest(object):
|
||||
"""Abstract base class for perftests that execute via a subharness,
|
||||
either Raptor or browsertime."""
|
||||
|
||||
__metaclass__ = ABCMeta
|
||||
|
||||
def __init__(self, app, binary, run_local=False, noinstall=False,
|
||||
obj_path=None, profile_class=None, installerpath=None,
|
||||
gecko_profile=False, gecko_profile_interval=None, gecko_profile_entries=None,
|
||||
symbols_path=None, host=None, power_test=False, cpu_test=False, memory_test=False,
|
||||
is_release_build=False, debug_mode=False, post_startup_delay=None,
|
||||
interrupt_handler=None, e10s=True, enable_webrender=False,
|
||||
results_handler_class=RaptorResultsHandler, no_conditioned_profile=False,
|
||||
device_name=None, extra_prefs={}, **kwargs):
|
||||
|
||||
# Override the magic --host HOST_IP with the value of the environment variable.
|
||||
if host == 'HOST_IP':
|
||||
host = os.environ['HOST_IP']
|
||||
|
||||
self.config = {
|
||||
'app': app,
|
||||
'binary': binary,
|
||||
'platform': mozinfo.os,
|
||||
'processor': mozinfo.processor,
|
||||
'run_local': run_local,
|
||||
'obj_path': obj_path,
|
||||
'gecko_profile': gecko_profile,
|
||||
'gecko_profile_interval': gecko_profile_interval,
|
||||
'gecko_profile_entries': gecko_profile_entries,
|
||||
'symbols_path': symbols_path,
|
||||
'host': host,
|
||||
'power_test': power_test,
|
||||
'memory_test': memory_test,
|
||||
'cpu_test': cpu_test,
|
||||
'is_release_build': is_release_build,
|
||||
'enable_control_server_wait': memory_test or cpu_test,
|
||||
'e10s': e10s,
|
||||
'enable_webrender': enable_webrender,
|
||||
'no_conditioned_profile': no_conditioned_profile,
|
||||
'device_name': device_name,
|
||||
'enable_fission': extra_prefs.get('fission.autostart', False),
|
||||
'extra_prefs': extra_prefs
|
||||
}
|
||||
|
||||
self.firefox_android_apps = FIREFOX_ANDROID_APPS
|
||||
# See bugs 1582757, 1606199, and 1606767; until we support win10-aarch64,
|
||||
# fennec_aurora, and reference browser conditioned-profile builds,
|
||||
# fall back to mozrunner-created profiles
|
||||
self.no_condprof = ((self.config['platform'] == 'win'
|
||||
and self.config['processor'] == 'aarch64') or
|
||||
self.config['binary'] == 'org.mozilla.fennec_aurora' or
|
||||
self.config['binary'] == 'org.mozilla.reference.browser.raptor' or
|
||||
self.config['no_conditioned_profile'])
|
||||
LOG.info("self.no_condprof is: {}".format(self.no_condprof))
|
||||
|
||||
# We can never use e10s on fennec
|
||||
if self.config['app'] == 'fennec':
|
||||
self.config['e10s'] = False
|
||||
|
||||
self.browser_name = None
|
||||
self.browser_version = None
|
||||
|
||||
self.raptor_venv = os.path.join(os.getcwd(), 'raptor-venv')
|
||||
self.installerpath = installerpath
|
||||
self.playback = None
|
||||
self.benchmark = None
|
||||
self.benchmark_port = 0
|
||||
self.gecko_profiler = None
|
||||
self.post_startup_delay = post_startup_delay
|
||||
self.device = None
|
||||
self.profile_class = profile_class or app
|
||||
self.conditioned_profile_dir = None
|
||||
self.interrupt_handler = interrupt_handler
|
||||
self.results_handler = results_handler_class(**self.config)
|
||||
|
||||
self.browser_name, self.browser_version = self.get_browser_meta()
|
||||
|
||||
browser_name, browser_version = self.get_browser_meta()
|
||||
self.results_handler.add_browser_meta(self.config['app'], browser_version)
|
||||
|
||||
# debug mode is currently only supported when running locally
|
||||
self.debug_mode = debug_mode if self.config['run_local'] else False
|
||||
|
||||
# if running debug-mode reduce the pause after browser startup
|
||||
if self.debug_mode:
|
||||
self.post_startup_delay = min(self.post_startup_delay, 3000)
|
||||
LOG.info("debug-mode enabled, reducing post-browser startup pause to %d ms"
|
||||
% self.post_startup_delay)
|
||||
LOG.info("main raptor init, config is: %s" % str(self.config))
|
||||
|
||||
self.build_browser_profile()
|
||||
|
||||
@property
|
||||
def is_localhost(self):
|
||||
return self.config.get('host') in ('localhost', '127.0.0.1')
|
||||
|
||||
def get_conditioned_profile(self):
|
||||
"""Downloads a platform-specific conditioned profile, using the
|
||||
condprofile client API; returns a self.conditioned_profile_dir"""
|
||||
|
||||
# create a temp file to help ensure uniqueness
|
||||
temp_download_dir = tempfile.mkdtemp()
|
||||
LOG.info("Making temp_download_dir from inside get_conditioned_profile {}"
|
||||
.format(temp_download_dir))
|
||||
# call condprof's client API to yield our platform-specific
|
||||
# conditioned-profile binary
|
||||
if isinstance(self, PerftestAndroid):
|
||||
android_app = self.config["binary"].split("org.mozilla.")[-1]
|
||||
device_name = self.config.get("device_name")
|
||||
if device_name is None:
|
||||
device_name = "g5"
|
||||
platform = "%s-%s" % (device_name, android_app)
|
||||
else:
|
||||
platform = get_current_platform()
|
||||
try:
|
||||
cond_prof_target_dir = get_profile(temp_download_dir, platform, "settled")
|
||||
except ProfileNotFoundError:
|
||||
# If we can't find the profile on mozilla-central, we look on try
|
||||
cond_prof_target_dir = get_profile(temp_download_dir, platform,
|
||||
"settled", repo="try")
|
||||
# now get the full directory path to our fetched conditioned profile
|
||||
self.conditioned_profile_dir = os.path.join(temp_download_dir, cond_prof_target_dir)
|
||||
if not os.path.exists(cond_prof_target_dir):
|
||||
LOG.critical("Can't find target_dir {}, from get_profile()"
|
||||
"temp_download_dir {}, platform {}, settled"
|
||||
.format(cond_prof_target_dir, temp_download_dir, platform))
|
||||
raise OSError
|
||||
|
||||
LOG.info("self.conditioned_profile_dir is now set: {}"
|
||||
.format(self.conditioned_profile_dir))
|
||||
shutil.rmtree(temp_download_dir)
|
||||
|
||||
return self.conditioned_profile_dir
|
||||
|
||||
def build_browser_profile(self):
|
||||
if self.no_condprof:
|
||||
self.profile = create_profile(self.profile_class)
|
||||
else:
|
||||
self.get_conditioned_profile()
|
||||
# use mozprofile to create a profile for us, from our conditioned profile's path
|
||||
self.profile = create_profile(self.profile_class, profile=self.conditioned_profile_dir)
|
||||
# Merge extra profile data from testing/profiles
|
||||
with open(os.path.join(self.profile_data_dir, 'profiles.json'), 'r') as fh:
|
||||
base_profiles = json.load(fh)['raptor']
|
||||
|
||||
for profile in base_profiles:
|
||||
path = os.path.join(self.profile_data_dir, profile)
|
||||
LOG.info("Merging profile: {}".format(path))
|
||||
self.profile.merge(path)
|
||||
|
||||
if self.config['extra_prefs'].get('fission.autostart', False):
|
||||
LOG.info('Enabling fission via browser preferences')
|
||||
LOG.info('Browser preferences: {}'.format(self.config['extra_prefs']))
|
||||
self.profile.set_preferences(self.config['extra_prefs'])
|
||||
|
||||
# share the profile dir with the config and the control server
|
||||
self.config['local_profile_dir'] = self.profile.profile
|
||||
LOG.info('Local browser profile: {}'.format(self.profile.profile))
|
||||
|
||||
@property
|
||||
def profile_data_dir(self):
|
||||
if 'MOZ_DEVELOPER_REPO_DIR' in os.environ:
|
||||
return os.path.join(os.environ['MOZ_DEVELOPER_REPO_DIR'], 'testing', 'profiles')
|
||||
if build:
|
||||
return os.path.join(build.topsrcdir, 'testing', 'profiles')
|
||||
return os.path.join(here, 'profile_data')
|
||||
|
||||
@property
|
||||
def artifact_dir(self):
|
||||
artifact_dir = os.getcwd()
|
||||
if self.config.get('run_local', False):
|
||||
if 'MOZ_DEVELOPER_REPO_DIR' in os.environ:
|
||||
artifact_dir = os.path.join(os.environ['MOZ_DEVELOPER_REPO_DIR'],
|
||||
'testing', 'mozharness', 'build')
|
||||
else:
|
||||
artifact_dir = here
|
||||
elif os.getenv('MOZ_UPLOAD_DIR'):
|
||||
artifact_dir = os.getenv('MOZ_UPLOAD_DIR')
|
||||
return artifact_dir
|
||||
|
||||
@abstractmethod
|
||||
def run_test_setup(self, test):
|
||||
LOG.info("starting test: %s" % test['name'])
|
||||
|
||||
# if 'alert_on' was provided in the test INI, add to our config for results/output
|
||||
self.config['subtest_alert_on'] = test.get('alert_on')
|
||||
|
||||
if test.get('playback') is not None and self.playback is None:
|
||||
self.start_playback(test)
|
||||
|
||||
if test.get("preferences") is not None:
|
||||
self.set_browser_test_prefs(test['preferences'])
|
||||
|
||||
@abstractmethod
|
||||
def setup_chrome_args(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_browser_meta(self):
|
||||
pass
|
||||
|
||||
def run_tests(self, tests, test_names):
|
||||
try:
|
||||
for test in tests:
|
||||
try:
|
||||
self.run_test(test, timeout=int(test.get('page_timeout')))
|
||||
except RuntimeError as e:
|
||||
LOG.critical("Tests failed to finish! Application timed out.")
|
||||
LOG.error(e)
|
||||
finally:
|
||||
self.run_test_teardown(test)
|
||||
return self.process_results(tests, test_names)
|
||||
finally:
|
||||
self.clean_up()
|
||||
|
||||
@abstractmethod
|
||||
def run_test(self, test, timeout):
|
||||
raise NotImplementedError()
|
||||
|
||||
@abstractmethod
|
||||
def run_test_teardown(self, test):
|
||||
self.check_for_crashes()
|
||||
|
||||
# gecko profiling symbolication
|
||||
if self.config['gecko_profile']:
|
||||
self.gecko_profiler.symbolicate()
|
||||
# clean up the temp gecko profiling folders
|
||||
LOG.info("cleaning up after gecko profiling")
|
||||
self.gecko_profiler.clean()
|
||||
|
||||
def process_results(self, tests, test_names):
|
||||
# when running locally output results in build/raptor.json; when running
|
||||
# in production output to a local.json to be turned into tc job artifact
|
||||
raptor_json_path = os.path.join(self.artifact_dir, 'raptor.json')
|
||||
if not self.config.get('run_local', False):
|
||||
raptor_json_path = os.path.join(os.getcwd(), 'local.json')
|
||||
|
||||
self.config['raptor_json_path'] = raptor_json_path
|
||||
self.config['artifact_dir'] = self.artifact_dir
|
||||
return self.results_handler.summarize_and_output(self.config, tests, test_names)
|
||||
|
||||
@abstractmethod
|
||||
def set_browser_test_prefs(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def check_for_crashes(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def clean_up(self):
|
||||
pass
|
||||
|
||||
def get_page_timeout_list(self):
|
||||
return self.results_handler.page_timeout_list
|
||||
|
||||
def get_recording_paths(self, test):
|
||||
recordings = test.get("playback_recordings")
|
||||
|
||||
if recordings:
|
||||
recording_paths = []
|
||||
proxy_dir = self.playback.mozproxy_dir
|
||||
|
||||
for recording in recordings.split():
|
||||
if not recording:
|
||||
continue
|
||||
recording_paths.append(os.path.join(proxy_dir, recording))
|
||||
|
||||
return recording_paths
|
||||
|
||||
def log_recording_dates(self, test):
|
||||
_recording_paths = self.get_recording_paths(test)
|
||||
if _recording_paths is None:
|
||||
LOG.info("No playback recordings specified in the test; so not getting recording info")
|
||||
return
|
||||
|
||||
for r in _recording_paths:
|
||||
json_path = '{}.json'.format(r.split('.')[0])
|
||||
|
||||
if os.path.exists(json_path):
|
||||
with open(json_path) as f:
|
||||
recording_date = json.loads(f.read()).get('recording_date')
|
||||
|
||||
if recording_date is not None:
|
||||
LOG.info('Playback recording date: {} '.
|
||||
format(recording_date.split(' ')[0]))
|
||||
else:
|
||||
LOG.info('Playback recording date not available')
|
||||
else:
|
||||
LOG.info('Playback recording information not available')
|
||||
|
||||
def get_playback_config(self, test):
|
||||
platform = self.config['platform']
|
||||
playback_dir = os.path.join(here, 'playback')
|
||||
|
||||
self.config.update({
|
||||
'playback_tool': test.get('playback'),
|
||||
'playback_version': test.get('playback_version', "4.0.4"),
|
||||
'playback_binary_zip': test.get('playback_binary_zip_%s' % platform),
|
||||
'playback_pageset_zip': test.get('playback_pageset_zip_%s' % platform),
|
||||
'playback_binary_manifest': test.get('playback_binary_manifest'),
|
||||
'playback_pageset_manifest': test.get('playback_pageset_manifest'),
|
||||
})
|
||||
|
||||
for key in ('playback_pageset_manifest', 'playback_pageset_zip'):
|
||||
if self.config.get(key) is None:
|
||||
continue
|
||||
self.config[key] = os.path.join(playback_dir, self.config[key])
|
||||
|
||||
LOG.info("test uses playback tool: %s " % self.config['playback_tool'])
|
||||
|
||||
def delete_proxy_settings_from_profile(self):
|
||||
# Must delete the proxy settings from the profile if running
|
||||
# the test with a host different from localhost.
|
||||
userjspath = os.path.join(self.profile.profile, 'user.js')
|
||||
with open(userjspath) as userjsfile:
|
||||
prefs = userjsfile.readlines()
|
||||
prefs = [pref for pref in prefs if 'network.proxy' not in pref]
|
||||
with open(userjspath, 'w') as userjsfile:
|
||||
userjsfile.writelines(prefs)
|
||||
|
||||
def start_playback(self, test):
|
||||
# creating the playback tool
|
||||
self.get_playback_config(test)
|
||||
self.playback = get_playback(self.config)
|
||||
|
||||
self.playback.config['playback_files'] = self.get_recording_paths(test)
|
||||
|
||||
# let's start it!
|
||||
self.playback.start()
|
||||
|
||||
self.log_recording_dates(test)
|
||||
|
||||
def _init_gecko_profiling(self, test):
|
||||
LOG.info("initializing gecko profiler")
|
||||
upload_dir = os.getenv('MOZ_UPLOAD_DIR')
|
||||
if not upload_dir:
|
||||
LOG.critical("Profiling ignored because MOZ_UPLOAD_DIR was not set")
|
||||
else:
|
||||
self.gecko_profiler = GeckoProfile(upload_dir,
|
||||
self.config,
|
||||
test)
|
||||
|
||||
|
||||
class PerftestDesktop(Perftest):
|
||||
"""Mixin class for Desktop-specific Perftest subclasses"""
|
||||
|
||||
def setup_chrome_args(self, test):
|
||||
'''Sets up chrome/chromium cmd-line arguments.
|
||||
|
||||
Needs to be "implemented" here to deal with Python 2
|
||||
unittest failures.
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
def desktop_chrome_args(self, test):
|
||||
'''Returns cmd line options required to run pageload tests on Desktop Chrome
|
||||
and Chromium. Also add the cmd line options to turn on the proxy and
|
||||
ignore security certificate errors if using host localhost, 127.0.0.1.
|
||||
'''
|
||||
chrome_args = [
|
||||
'--use-mock-keychain',
|
||||
'--no-default-browser-check',
|
||||
]
|
||||
|
||||
if test.get('playback', False):
|
||||
pb_args = [
|
||||
'--proxy-server=%s:%d' % (self.playback.host, self.playback.port),
|
||||
'--proxy-bypass-list=localhost;127.0.0.1',
|
||||
'--ignore-certificate-errors',
|
||||
]
|
||||
|
||||
if not self.is_localhost:
|
||||
pb_args[0] = pb_args[0].replace('127.0.0.1', self.config['host'])
|
||||
|
||||
chrome_args.extend(pb_args)
|
||||
|
||||
if self.debug_mode:
|
||||
chrome_args.extend(['--auto-open-devtools-for-tabs'])
|
||||
|
||||
return chrome_args
|
||||
|
||||
def get_browser_meta(self):
|
||||
'''Returns the browser name and version in a tuple (name, version).
|
||||
|
||||
On desktop, we use mozversion but a fallback method also exists
|
||||
for non-firefox browsers, where mozversion is known to fail. The
|
||||
methods are OS-specific, with windows being the outlier.
|
||||
'''
|
||||
browser_name = None
|
||||
browser_version = None
|
||||
|
||||
try:
|
||||
meta = mozversion.get_version(binary=self.config['binary'])
|
||||
browser_name = meta.get('application_name')
|
||||
browser_version = meta.get('application_version')
|
||||
except Exception as e:
|
||||
LOG.warning(
|
||||
"Failed to get browser meta data through mozversion: %s-%s" %
|
||||
(e.__class__.__name__, e)
|
||||
)
|
||||
LOG.info("Attempting to get version through fallback method...")
|
||||
|
||||
# Fall-back method to get browser version on desktop
|
||||
try:
|
||||
if 'linux' in self.config['platform'] or 'mac' in self.config['platform']:
|
||||
command = [self.config['binary'], '--version']
|
||||
proc = mozprocess.ProcessHandler(command)
|
||||
proc.run(timeout=10, outputTimeout=10)
|
||||
proc.wait()
|
||||
|
||||
bmeta = proc.output
|
||||
meta_re = re.compile(r"([A-z\s]+)\s+([\w.]*)")
|
||||
if len(bmeta) != 0:
|
||||
match = meta_re.match(bmeta[0])
|
||||
if match:
|
||||
browser_name = self.config['app']
|
||||
browser_version = match.group(2)
|
||||
else:
|
||||
LOG.info("Couldn't get browser version and name")
|
||||
else:
|
||||
# On windows we need to use wimc to get the version
|
||||
command = r'wmic datafile where name="{0}"'.format(
|
||||
self.config['binary'].replace('\\', r"\\")
|
||||
)
|
||||
bmeta = subprocess.check_output(command)
|
||||
|
||||
meta_re = re.compile(r"\s+([\d.a-z]+)\s+")
|
||||
match = meta_re.findall(bmeta)
|
||||
if len(match) > 0:
|
||||
browser_name = self.config['app']
|
||||
browser_version = match[-1]
|
||||
else:
|
||||
LOG.info("Couldn't get browser version and name")
|
||||
except Exception as e:
|
||||
LOG.warning(
|
||||
"Failed to get browser meta data through fallback method: %s-%s" %
|
||||
(e.__class__.__name__, e)
|
||||
)
|
||||
|
||||
if not browser_name:
|
||||
LOG.warning("Could not find a browser name")
|
||||
else:
|
||||
LOG.info("Browser name: %s" % browser_name)
|
||||
|
||||
if not browser_version:
|
||||
LOG.warning("Could not find a browser version")
|
||||
else:
|
||||
LOG.info("Browser version: %s" % browser_version)
|
||||
|
||||
return (browser_name, browser_version)
|
||||
|
||||
|
||||
class PerftestAndroid(Perftest):
|
||||
"""Mixin class for Android-specific Perftest subclasses."""
|
||||
|
||||
def setup_chrome_args(self, test):
|
||||
'''Sets up chrome/chromium cmd-line arguments.
|
||||
|
||||
Needs to be "implemented" here to deal with Python 2
|
||||
unittest failures.
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
def get_browser_meta(self):
|
||||
'''Returns the browser name and version in a tuple (name, version).
|
||||
|
||||
Uses mozversion as the primary method to get this meta data and for
|
||||
android this is the only method which exists to get this data. With android,
|
||||
we use the installerpath attribute to determine this and this only works
|
||||
with Firefox browsers.
|
||||
'''
|
||||
browser_name = None
|
||||
browser_version = None
|
||||
|
||||
if self.config['app'] in self.firefox_android_apps:
|
||||
try:
|
||||
meta = mozversion.get_version(binary=self.installerpath)
|
||||
browser_name = meta.get('application_name')
|
||||
browser_version = meta.get('application_version')
|
||||
except Exception as e:
|
||||
LOG.warning(
|
||||
"Failed to get android browser meta data through mozversion: %s-%s" %
|
||||
(e.__class__.__name__, e)
|
||||
)
|
||||
|
||||
if not browser_name:
|
||||
LOG.warning("Could not find a browser name")
|
||||
else:
|
||||
LOG.info("Browser name: %s" % browser_name)
|
||||
|
||||
if not browser_version:
|
||||
LOG.warning("Could not find a browser version")
|
||||
else:
|
||||
LOG.info("Browser version: %s" % browser_version)
|
||||
|
||||
return (browser_name, browser_version)
|
||||
|
||||
def set_reverse_port(self, port):
|
||||
tcp_port = "tcp:{}".format(port)
|
||||
self.device.create_socket_connection('reverse', tcp_port, tcp_port)
|
||||
|
||||
def set_reverse_ports(self):
|
||||
if self.is_localhost:
|
||||
|
||||
# only raptor-webext uses the control server
|
||||
if self.config.get('browsertime', False) is False:
|
||||
LOG.info("making the raptor control server port available to device")
|
||||
self.set_reverse_port(self.control_server.port)
|
||||
|
||||
if self.playback:
|
||||
LOG.info("making the raptor playback server port available to device")
|
||||
self.set_reverse_port(self.playback.port)
|
||||
|
||||
if self.benchmark:
|
||||
LOG.info("making the raptor benchmarks server port available to device")
|
||||
self.set_reverse_port(self.benchmark_port)
|
||||
else:
|
||||
LOG.info("Reverse port forwarding is uded only on local devices")
|
||||
|
||||
def build_browser_profile(self):
|
||||
super(PerftestAndroid, self).build_browser_profile()
|
||||
|
||||
# Merge in the Android profile.
|
||||
path = os.path.join(self.profile_data_dir, 'raptor-android')
|
||||
LOG.info("Merging profile: {}".format(path))
|
||||
self.profile.merge(path)
|
||||
self.profile.set_preferences({'browser.tabs.remote.autostart': self.config['e10s']})
|
||||
|
||||
def clear_app_data(self):
|
||||
LOG.info("clearing %s app data" % self.config['binary'])
|
||||
self.device.shell("pm clear %s" % self.config['binary'])
|
||||
|
||||
def set_debug_app_flag(self):
|
||||
# required so release apks will read the android config.yml file
|
||||
LOG.info("setting debug-app flag for %s" % self.config['binary'])
|
||||
self.device.shell("am set-debug-app --persistent %s" % self.config['binary'])
|
||||
|
||||
def copy_profile_to_device(self):
|
||||
"""Copy the profile to the device, and update permissions of all files."""
|
||||
if not self.device.is_app_installed(self.config['binary']):
|
||||
raise Exception('%s is not installed' % self.config['binary'])
|
||||
|
||||
try:
|
||||
LOG.info("copying profile to device: %s" % self.remote_profile)
|
||||
self.device.rm(self.remote_profile, force=True, recursive=True)
|
||||
# self.device.mkdir(self.remote_profile)
|
||||
self.device.push(self.profile.profile, self.remote_profile)
|
||||
self.device.chmod(self.remote_profile, recursive=True, root=True)
|
||||
|
||||
except Exception:
|
||||
LOG.error("Unable to copy profile to device.")
|
||||
raise
|
||||
|
||||
def turn_on_android_app_proxy(self):
|
||||
# for geckoview/android pageload playback we can't use a policy to turn on the
|
||||
# proxy; we need to set prefs instead; note that the 'host' may be different
|
||||
# than '127.0.0.1' so we must set the prefs accordingly
|
||||
proxy_prefs = {}
|
||||
proxy_prefs["network.proxy.type"] = 1
|
||||
proxy_prefs["network.proxy.http"] = self.playback.host
|
||||
proxy_prefs["network.proxy.http_port"] = self.playback.port
|
||||
proxy_prefs["network.proxy.ssl"] = self.playback.host
|
||||
proxy_prefs["network.proxy.ssl_port"] = self.playback.port
|
||||
proxy_prefs["network.proxy.no_proxies_on"] = self.config['host']
|
||||
|
||||
LOG.info("setting profile prefs to turn on the android app proxy: {}".format(proxy_prefs))
|
||||
self.profile.set_preferences(proxy_prefs)
|
||||
|
||||
|
||||
class Browsertime(Perftest):
|
||||
"""Abstract base class for Browsertime"""
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче