Bug 1628277 - Created the android layer r=acreskey

also, isolate the profile handling in its layer,
added the --proxy and --profile options

Differential Revision: https://phabricator.services.mozilla.com/D71044
This commit is contained in:
Tarek Ziadé 2020-04-20 14:06:10 +00:00
Родитель ea156f7bf4
Коммит 43fca3ecc1
9 изменённых файлов: 206 добавлений и 32 удалений

Просмотреть файл

@ -2,13 +2,15 @@
# 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 mozperftest.browser.browsertime import BrowsertimeRunner
from mozperftest.browser.profile import Profile
from mozperftest.layers import Layers
def get_layers():
return (BrowsertimeRunner,)
return (Profile, BrowsertimeRunner)
def pick_browser(env, flavor, mach_cmd):
if flavor == "script":
return BrowsertimeRunner(env, mach_cmd)
return Layers(env, mach_cmd, get_layers())
raise NotImplementedError(flavor)

Просмотреть файл

@ -67,7 +67,16 @@ class BrowsertimeRunner(NodeRunner):
name = "browsertime (%s)" % NodeRunner.name
arguments = {
"--cycles": {"type": int, "default": 1, "help": "Number of full cycles"}
"--browser-cycles": {
"type": int,
"default": 1,
"help": "Number of full cycles",
},
"--browser-binary": {
"type": str,
"default": None,
"help": "Path to the desktop browser, or Android app name.",
},
}
def __init__(self, env, mach_cmd):
@ -75,7 +84,6 @@ class BrowsertimeRunner(NodeRunner):
self.topsrcdir = mach_cmd.topsrcdir
self._mach_context = mach_cmd._mach_context
self.virtualenv_manager = mach_cmd.virtualenv_manager
self.proxy = None
self._created_dirs = []
@property
@ -409,19 +417,27 @@ class BrowsertimeRunner(NodeRunner):
return extra_args
def get_profile(self, metadata):
# XXX we'll use conditioned profiles
from mozprofile import create_profile
def browsertime_android(self, metadata):
app_name = self.get_arg("android-app-name")
profile = create_profile(app="firefox")
prefs = metadata.get_browser_prefs()
profile.set_preferences(prefs)
self.info("Created profile at %s" % profile.profile)
self._created_dirs.append(profile.profile)
return profile
args_list = [
# "--browser", "firefox",
"--android",
# Work around a `selenium-webdriver` issue where Browsertime
# fails to find a Firefox binary even though we're going to
# actually do things on an Android device.
# "--firefox.binaryPath", self.mach_cmd.get_binary_path(),
"--firefox.android.package",
app_name,
]
activity = self.get_arg("android-activity")
if activity is not None:
args_list += ["--firefox.android.activity", activity]
return args_list
def __call__(self, metadata):
cycles = self.get_arg("cycles", 1)
cycles = self.get_arg("browser-cycles", 1)
for cycle in range(1, cycles + 1):
metadata.run_hook("before_cycle", cycle=cycle)
try:
@ -431,9 +447,7 @@ class BrowsertimeRunner(NodeRunner):
return metadata
def _one_cycle(self, metadata):
# keep the object around
# see https://bugzilla.mozilla.org/show_bug.cgi?id=1625118
profile = self.get_profile(metadata)
profile = self.get_arg("profile-directory")
test_script = self.get_arg("tests")[0]
output = self.get_arg("output")
if output is not None:
@ -451,7 +465,7 @@ class BrowsertimeRunner(NodeRunner):
"--resultDir",
result_dir,
"--firefox.profileTemplate",
profile.profile,
profile,
"--iterations",
"1",
test_script,
@ -473,14 +487,12 @@ class BrowsertimeRunner(NodeRunner):
args += ["--" + name, value]
firefox_args = ["--firefox.developer"]
if self.get_arg("android"):
args.extend(self.browsertime_android(metadata))
extra = self.extra_default_args(args=firefox_args)
command = [self.browsertime_js] + extra + args
self.info("Running browsertime with this command %s" % " ".join(command))
self.node(command)
metadata.set_result(result_dir)
return metadata
def teardown(self):
for dir in self._created_dirs:
if os.path.exists(dir):
shutil.rmtree(dir)

Просмотреть файл

@ -0,0 +1,59 @@
# 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/.
import os
import shutil
from mozperftest.layers import Layer
from mozprofile import create_profile
from mozprofile.prefs import Preferences
HERE = os.path.dirname(__file__)
class Profile(Layer):
name = "profile"
arguments = {
"--profile-directory": {"type": str, "default": None, "help": "Profile to use"},
"--profile-user-js": {"type": str, "default": None, "help": "Custom user.js"},
}
def __init__(self, env, mach_cmd):
super(Profile, self).__init__(env, mach_cmd)
self._created_dirs = []
def setup(self):
pass
def __call__(self, metadata):
if self.get_arg("profile-directory") is not None:
# no need to create one or load a conditioned one
return
# XXX we'll use conditioned profiles later
#
# XXX keeping a reference on self, otherwise mozprofile
# silently deletes the dir in a __del__ call
self.profile = profile = create_profile(app="firefox")
prefs = metadata.get_browser_prefs()
if prefs == {}:
prefs["mozperftest"] = "true"
# apply custom user prefs if any
user_js = self.get_arg("profile-user-js")
if user_js is not None:
self.info("Applying use prefs from %s" % user_js)
default_prefs = dict(Preferences.read_prefs(user_js))
prefs.update(default_prefs)
profile.set_preferences(prefs)
self.info("Created profile at %s" % profile.profile)
self._created_dirs.append(profile.profile)
self.set_arg("profile-directory", profile.profile)
return metadata
def teardown(self):
for dir in self._created_dirs:
if os.path.exists(dir):
shutil.rmtree(dir)

Просмотреть файл

@ -35,13 +35,18 @@ class MachEnvironment:
finally:
self.unfreeze()
def _normalize(self, name):
if name.startswith("--"):
name = name[2:]
return name.replace("-", "_")
def set_arg(self, name, value):
"""Sets the argument"""
# see if we want to restrict to existing keys
self._mach_args[name] = value
self._mach_args[self._normalize(name)] = value
def get_arg(self, name, default=None):
return self._mach_args.get(name, default)
return self._mach_args.get(self._normalize(name), default)
def get_layer(self, name):
for layer in self.layers:

Просмотреть файл

@ -26,7 +26,7 @@ class ConsoleOutput(Layer):
metadata.get_result(),
self.warning,
output=self.get_arg("output"),
prefix=self.get_arg("prefix"),
prefix=self.get_arg("perfherder-prefix"),
)
res = cm.get_standardized_data(
group_name="firefox", transformer="SingleJsonRetriever"
@ -34,7 +34,7 @@ class ConsoleOutput(Layer):
_, results = res["file-output"], res["data"]
# Filter out unwanted metrics
results = filter_metrics(results, self.get_arg("metrics"))
results = filter_metrics(results, self.get_arg("perfherder-metrics"))
if not results:
self.warning("No results left after filtering")
return metadata

Просмотреть файл

@ -19,12 +19,12 @@ class Perfherder(Layer):
"default": False,
"help": "Output data in the perfherder format.",
},
"--prefix": {
"--perfherder-prefix": {
"type": str,
"default": "",
"help": "Prefix the output files with this string.",
},
"--metrics": {
"--perfherder-metrics": {
"nargs": "*",
"default": [],
"help": "The metrics that should be retrieved from the data.",
@ -56,7 +56,7 @@ class Perfherder(Layer):
metadata.get_result(),
self.warning,
output=self.get_arg("output"),
prefix=self.get_arg("prefix"),
prefix=self.get_arg("perfherder-prefix"),
)
res = cm.get_standardized_data(
group_name="firefox", transformer="SingleJsonRetriever"
@ -64,7 +64,7 @@ class Perfherder(Layer):
_, results = res["file-output"], res["data"]
# Filter out unwanted metrics
results = filter_metrics(results, self.get_arg("metrics"))
results = filter_metrics(results, self.get_arg("perfherder-metrics"))
if not results:
self.warning("No results left after filtering")
return metadata

Просмотреть файл

@ -3,10 +3,11 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from mozperftest.layers import Layers
from mozperftest.system.proxy import ProxyRunner
from mozperftest.system.android import AndroidDevice
def get_layers():
return (ProxyRunner,)
return (ProxyRunner, AndroidDevice)
def pick_system(env, flavor, mach_cmd):

Просмотреть файл

@ -0,0 +1,92 @@
# 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/.
import os
from mozdevice import ADBDevice
from mozperftest.layers import Layer
HERE = os.path.dirname(__file__)
class DeviceError(Exception):
pass
class AndroidDevice(Layer):
name = "android"
arguments = {
"--android": {
"action": "store_true",
"default": False,
"help": "Use an android device via ADB",
},
"--android-intent": {"type": str, "default": None, "help": "Intent to use"},
"--android-app-name": {"type": str, "default": None, "help": "App name"},
"--android-activity": {"type": str, "default": None, "help": "Activity to use"},
"--android-install-apk": {
"nargs": "*",
"default": [],
"help": "APK to install to the device",
},
}
def __init__(self, env, mach_cmd):
super(AndroidDevice, self).__init__(env, mach_cmd)
self.android_activity = self.app_name = self.device = None
def setup(self):
pass
def _setup_options(self, app_name="org.mozilla.firefox"):
self.app_name = app_name
try:
self.device = ADBDevice(verbose=True)
except AttributeError as e:
self.error("Could not connect to the phone. Is it connected?")
raise DeviceError(str(e))
# install APKs
for apk in self.get_arg("android-install-apk"):
self.info("Installing %s" % apk)
self.device.install_app(apk, replace=True)
self.info("Done.")
if self.android_activity is None:
# guess the activity, given the app
if "fenix" in app_name:
self.android_activity = "org.mozilla.fenix.IntentReceiverActivity"
elif "geckoview_example" in app_name:
self.android_activity = (
"org.mozilla.geckoview_example.GeckoViewActivity"
)
self.set_arg("android_activity", self.android_activity)
# checking that the app is installed
if not self.device.is_app_installed(self.app_name):
raise Exception("%s is not installed" % self.app_name)
self.info("Android environment:")
self.info("\t- application name: %s" % self.app_name)
self.info("\t- activity: %s" % self.android_activity)
self.info("\t- intent: %s" % self.get_arg("android_intent"))
def teardown(self):
pass
def __call__(self, metadata):
android = self.get_arg("android")
app_name = self.get_arg("android-app-name")
if app_name is None:
app_name = self.get_arg("browser-binary")
if app_name is not None and app_name.startswith("org.mozilla.") and not android:
self.set_arg("android", True)
android = True
if not android:
return metadata
if app_name is None:
app_name = "org.mozilla.firefox"
self.set_arg("android-app-name", app_name)
self.metadata = metadata
self._setup_options(app_name)
return metadata

Просмотреть файл

@ -25,6 +25,9 @@ class ProxyRunner(Layer):
pass
def __call__(self, metadata):
if not self.get_arg("proxy"):
return metadata
self.metadata = metadata
if not self.get_arg("proxy"):
return metadata