1#!/usr/bin/env python
2
3# This Source Code Form is subject to the terms of the Mozilla Public
4# License, v. 2.0. If a copy of the MPL was not distributed with this
5# file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7from __future__ import absolute_import
8
9import os
10
11from mozdevice import ADBDevice
12
13from logger.logger import RaptorLogger
14from performance_tuning import tune_performance
15from perftest import PerftestAndroid
16
17from .base import Browsertime
18
19LOG = RaptorLogger(component="raptor-browsertime-android")
20
21
22class BrowsertimeAndroid(PerftestAndroid, Browsertime):
23    """Android setup and configuration for browsertime
24
25    When running raptor-browsertime tests on android, we create the profile (and set the proxy
26    prefs in the profile that is using playback) but we don't need to copy it onto the device
27    because geckodriver takes care of that.
28    We tell browsertime to use our profile (we pass it in with the firefox.profileTemplate arg);
29    browsertime creates a copy of that and passes that into geckodriver. Geckodriver then takes
30    the profile and copies it onto the mobile device's test root for us; and then it even writes
31    the geckoview app config.yaml file onto the device, which points the app to the profile on
32    the device's test root.
33    Therefore, raptor doesn't have to copy the profile onto the scard (and create the config.yaml)
34    file ourselves. Also note when using playback, the nss certificate db is created as usual when
35    mitmproxy is started (and saved in the profile) so it is already included in the profile that
36    browsertime/geckodriver copies onto the device.
37    """
38
39    def __init__(self, app, binary, activity=None, intent=None, **kwargs):
40        super(BrowsertimeAndroid, self).__init__(
41            app, binary, profile_class="firefox", **kwargs
42        )
43
44        self.config.update({"activity": activity, "intent": intent})
45        self.remote_test_root = "/data/local/tmp/tests/raptor"
46        self.remote_profile = os.path.join(self.remote_test_root, "profile")
47
48    @property
49    def browsertime_args(self):
50        if self.config['app'] == 'chrome-m':
51            args_list = [
52                '--browser', 'chrome',
53                '--android',
54            ]
55        else:
56            args_list = [
57                "--browser", "firefox",
58                "--android",
59                # Work around a `selenium-webdriver` issue where Browsertime
60                # fails to find a Firefox binary even though we're going to
61                # actually do things on an Android device.
62                "--firefox.binaryPath", self.browsertime_node,
63                "--firefox.android.package", self.config["binary"],
64                "--firefox.android.activity", self.config["activity"],
65            ]
66
67        # if running on Fenix we must add the intent as we use a special non-default one there
68        if self.config["app"] == "fenix" and self.config.get("intent") is not None:
69            args_list.extend(["--firefox.android.intentArgument=-a"])
70            args_list.extend(
71                ["--firefox.android.intentArgument", self.config["intent"]]
72            )
73            args_list.extend(["--firefox.android.intentArgument=-d"])
74            args_list.extend(["--firefox.android.intentArgument", str("about:blank")])
75
76        return args_list
77
78    def setup_chrome_args(self, test):
79        chrome_args = ["--use-mock-keychain", "--no-default-browser-check", "--no-first-run"]
80
81        if test.get("playback", False):
82            pb_args = [
83                "--proxy-server=%s:%d" % (self.playback.host, self.playback.port),
84                "--proxy-bypass-list=localhost;127.0.0.1",
85                "--ignore-certificate-errors",
86            ]
87
88            if not self.is_localhost:
89                pb_args[0] = pb_args[0].replace("127.0.0.1", self.config["host"])
90
91            chrome_args.extend(pb_args)
92
93        if self.debug_mode:
94            chrome_args.extend(["--auto-open-devtools-for-tabs"])
95
96        args_list = []
97        for arg in chrome_args:
98            args_list.extend(["--chrome.args=" + str(arg.replace("'", '"'))])
99
100        return args_list
101
102    def build_browser_profile(self):
103        super(BrowsertimeAndroid, self).build_browser_profile()
104
105        # Merge in the Android profile.
106        path = os.path.join(self.profile_data_dir, "raptor-android")
107        LOG.info("Merging profile: {}".format(path))
108        self.profile.merge(path)
109        self.profile.set_preferences(
110            {"browser.tabs.remote.autostart": self.config["e10s"]}
111        )
112
113        # There's no great way to have "after" advice in Python, so we do this
114        # in super and then again here since the profile merging re-introduces
115        # the "#MozRunner" delimiters.
116        self.remove_mozprofile_delimiters_from_profile()
117
118    def setup_adb_device(self):
119        if self.device is None:
120            self.device = ADBDevice(verbose=True)
121            if not self.config.get("disable_perf_tuning", False):
122                tune_performance(self.device, log=LOG)
123
124        self.clear_app_data()
125        self.set_debug_app_flag()
126
127    def run_test_setup(self, test):
128        super(BrowsertimeAndroid, self).run_test_setup(test)
129
130        self.set_reverse_ports()
131
132        if self.playback:
133            self.turn_on_android_app_proxy()
134        self.remove_mozprofile_delimiters_from_profile()
135
136    def run_tests(self, tests, test_names):
137        self.setup_adb_device()
138
139        if self.config['app'] == "chrome-m":
140            # Make sure that chrome is enabled on the device
141            self.device.shell_output("pm enable com.android.chrome", root=True)
142
143        return super(BrowsertimeAndroid, self).run_tests(tests, test_names)
144
145    def run_test_teardown(self, test):
146        LOG.info("removing reverse socket connections")
147        self.device.remove_socket_connections("reverse")
148
149        super(BrowsertimeAndroid, self).run_test_teardown(test)
150