Bug 1599925 [wpt PR 20491] - Add experimental support for weblayer in wpt., a=testonly
authorAndrew Luo <aluo@chromium.org>
Tue, 10 Dec 2019 16:40:17 +0000
changeset 506427 f54cc9052639862043614712b6cf7e03350bffca
parent 506426 a6d6c36aeac9bccedb0f7bca8d7b96fa1a8ccb1a
child 506428 c59980aa889bb8af036bf10435c99891a503d571
push id36904
push userncsoregi@mozilla.com
push dateWed, 11 Dec 2019 17:42:45 +0000
treeherdermozilla-central@b823b005f00e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstestonly
bugs1599925, 20491, 1019521, 1941072, 722639
milestone73.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1599925 [wpt PR 20491] - Add experimental support for weblayer in wpt., a=testonly Automatic update from web-platform-tests Add experimental support for weblayer in wpt. Bug: 1019521 Change-Id: I3a4e75f5d640760268435766bdd7efc2fa1c36f2 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1941072 Reviewed-by: Luke Z <lpz@chromium.org> Reviewed-by: Changwan Ryu <changwan@chromium.org> Commit-Queue: Andrew Luo <aluo@chromium.org> Cr-Commit-Position: refs/heads/master@{#722639} -- wpt-commits: b83efc6b6ca6a650b33653a6cc3da1be2a519e3e wpt-pr: 20491
testing/web-platform/tests/tools/wpt/browser.py
testing/web-platform/tests/tools/wpt/run.py
testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/__init__.py
testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/android_weblayer.py
--- a/testing/web-platform/tests/tools/wpt/browser.py
+++ b/testing/web-platform/tests/tools/wpt/browser.py
@@ -707,16 +707,30 @@ class ChromeAndroid(ChromeAndroidBase):
     requirements = "requirements_chrome_android.txt"
 
     def find_binary(self, venv_path=None, channel=None):
         if channel in ("beta", "dev", "canary"):
             return "com.chrome." + channel
         return "com.android.chrome"
 
 
+#TODO(aluo): This is largely copied from the AndroidWebView implementation.
+#            Tests are not running for weblayer yet (crbug/1019521), this
+#            initial implementation will help to reproduce and debug any issues.
+class AndroidWeblayer(ChromeAndroidBase):
+    """Weblayer-specific interface for Android."""
+
+    product = "android_weblayer"
+    # TODO(aluo): replace this with weblayer version after tests are working.
+    requirements = "requirements_android_webview.txt"
+
+    def find_binary(self, venv_path=None, channel=None):
+        return "org.chromium.weblayer.shell"
+
+
 class AndroidWebview(ChromeAndroidBase):
     """Webview-specific interface for Android.
 
     Design doc:
     https://docs.google.com/document/d/19cGz31lzCBdpbtSC92svXlhlhn68hrsVwSB7cfZt54o/view
     """
 
     product = "android_webview"
--- a/testing/web-platform/tests/tools/wpt/run.py
+++ b/testing/web-platform/tests/tools/wpt/run.py
@@ -101,17 +101,17 @@ your PATH.
 
 Otherwise run with --ssl-type=none""")
             else:
                 raise WptrunError("""OpenSSL not found. If you don't need HTTPS support run with --ssl-type=none,
 otherwise install OpenSSL and ensure that it's on your $PATH.""")
 
 
 def check_environ(product):
-    if product not in ("android_webview", "chrome", "chrome_android", "firefox", "firefox_android", "servo"):
+    if product not in ("android_weblayer", "android_webview", "chrome", "chrome_android", "firefox", "firefox_android", "servo"):
         config_builder = serve.build_config(os.path.join(wpt_root, "config.json"))
         # Override the ports to avoid looking for free ports
         config_builder.ssl = {"type": "none"}
         config_builder.ports = {"http": [8000]}
 
         is_windows = platform.uname()[0] == "Windows"
 
         with config_builder as config:
@@ -394,16 +394,41 @@ class ChromeiOS(BrowserSetup):
     name = "chrome_ios"
     browser_cls = browser.ChromeiOS
 
     def setup_kwargs(self, kwargs):
         if kwargs["webdriver_binary"] is None:
             raise WptrunError("Unable to locate or install chromedriver binary")
 
 
+class AndroidWeblayer(BrowserSetup):
+    name = "android_weblayer"
+    browser_cls = browser.AndroidWeblayer
+
+    def setup_kwargs(self, kwargs):
+        if kwargs.get("device_serial"):
+            self.browser.device_serial = kwargs["device_serial"]
+        if kwargs["webdriver_binary"] is None:
+            webdriver_binary = self.browser.find_webdriver()
+
+            if webdriver_binary is None:
+                install = self.prompt_install("chromedriver")
+
+                if install:
+                    logger.info("Downloading chromedriver")
+                    webdriver_binary = self.browser.install_webdriver(dest=self.venv.bin_path)
+            else:
+                logger.info("Using webdriver binary %s" % webdriver_binary)
+
+            if webdriver_binary:
+                kwargs["webdriver_binary"] = webdriver_binary
+            else:
+                raise WptrunError("Unable to locate or install chromedriver binary")
+
+
 class AndroidWebview(BrowserSetup):
     name = "android_webview"
     browser_cls = browser.AndroidWebview
 
     def setup_kwargs(self, kwargs):
         if kwargs.get("device_serial"):
             self.browser.device_serial = kwargs["device_serial"]
         if kwargs["webdriver_binary"] is None:
@@ -633,16 +658,17 @@ class Epiphany(BrowserSetup):
             webdriver_binary = self.browser.find_webdriver()
 
             if webdriver_binary is None:
                 raise WptrunError("Unable to find WebKitWebDriver in PATH")
             kwargs["webdriver_binary"] = webdriver_binary
 
 
 product_setup = {
+    "android_weblayer": AndroidWeblayer,
     "android_webview": AndroidWebview,
     "firefox": Firefox,
     "firefox_android": FirefoxAndroid,
     "chrome": Chrome,
     "chrome_android": ChromeAndroid,
     "chrome_ios": ChromeiOS,
     "edgechromium": EdgeChromium,
     "edge": Edge,
--- a/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/__init__.py
+++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/__init__.py
@@ -17,17 +17,18 @@ a dictionary with the fields
                    the executor class.
 "env_options": String naming a function of no arguments that returns the
                arguments passed to the TestEnvironment.
 
 All classes and functions named in the above dict must be imported into the
 module global scope.
 """
 
-product_list = ["android_webview",
+product_list = ["android_weblayer",
+                "android_webview",
                 "chrome",
                 "chrome_android",
                 "chrome_ios",
                 "edgechromium",
                 "edge",
                 "edge_webdriver",
                 "firefox",
                 "firefox_android",
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/android_weblayer.py
@@ -0,0 +1,135 @@
+import subprocess
+
+from .base import Browser, ExecutorBrowser, require_arg
+from .base import get_timeout_multiplier   # noqa: F401
+from .chrome import executor_kwargs as chrome_executor_kwargs
+from ..webdriver_server import ChromeDriverServer
+from ..executors.executorwebdriver import (WebDriverTestharnessExecutor,  # noqa: F401
+                                           WebDriverRefTestExecutor)  # noqa: F401
+from ..executors.executorchrome import ChromeDriverWdspecExecutor  # noqa: F401
+
+
+__wptrunner__ = {"product": "android_weblayer",
+                 "check_args": "check_args",
+                 "browser": "WeblayerShell",
+                 "executor": {"testharness": "WebDriverTestharnessExecutor",
+                              "reftest": "WebDriverRefTestExecutor",
+                              "wdspec": "ChromeDriverWdspecExecutor"},
+                 "browser_kwargs": "browser_kwargs",
+                 "executor_kwargs": "executor_kwargs",
+                 "env_extras": "env_extras",
+                 "env_options": "env_options",
+                 "timeout_multiplier": "get_timeout_multiplier"}
+
+_wptserve_ports = set()
+
+
+def check_args(**kwargs):
+    require_arg(kwargs, "webdriver_binary")
+
+
+def browser_kwargs(test_type, run_info_data, config, **kwargs):
+    return {"binary": kwargs["binary"],
+            "device_serial": kwargs["device_serial"],
+            "webdriver_binary": kwargs["webdriver_binary"],
+            "webdriver_args": kwargs.get("webdriver_args")}
+
+
+def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
+                    **kwargs):
+    # Use update() to modify the global list in place.
+    _wptserve_ports.update(set(
+        server_config['ports']['http'] + server_config['ports']['https'] +
+        server_config['ports']['ws'] + server_config['ports']['wss']
+    ))
+
+    executor_kwargs = chrome_executor_kwargs(test_type, server_config,
+                                             cache_manager, run_info_data,
+                                             **kwargs)
+    del executor_kwargs["capabilities"]["goog:chromeOptions"]["prefs"]
+    del executor_kwargs["capabilities"]["goog:chromeOptions"]["useAutomationExtension"]
+    capabilities = executor_kwargs["capabilities"]
+    # Note that for WebLayer, we launch a test shell and have the test shell use
+    # WebLayer.
+    # https://cs.chromium.org/chromium/src/weblayer/shell/android/shell_apk/
+    capabilities["goog:chromeOptions"]["androidPackage"] = \
+        "org.chromium.weblayer.shell"
+    capabilities["goog:chromeOptions"]["androidActivity"] = ".WebLayerShellActivity"
+    if kwargs.get('device_serial'):
+        capabilities["goog:chromeOptions"]["androidDeviceSerial"] = kwargs['device_serial']
+
+    # Workaround: driver.quit() cannot quit WeblayerShell.
+    executor_kwargs["pause_after_test"] = False
+    # Workaround: driver.close() is not supported.
+    executor_kwargs["restart_after_test"] = True
+    executor_kwargs["close_after_done"] = False
+    return executor_kwargs
+
+
+def env_extras(**kwargs):
+    return []
+
+
+def env_options():
+    # allow the use of host-resolver-rules in lieu of modifying /etc/hosts file
+    return {"server_host": "127.0.0.1"}
+
+
+#TODO: refactor common elements of WeblayerShell and ChromeAndroidBrowser
+class WeblayerShell(Browser):
+    """Chrome is backed by chromedriver, which is supplied through
+    ``wptrunner.webdriver.ChromeDriverServer``.
+    """
+
+    def __init__(self, logger, binary, webdriver_binary="chromedriver",
+                 device_serial=None,
+                 webdriver_args=None):
+        """Creates a new representation of Chrome.  The `binary` argument gives
+        the browser binary to use for testing."""
+        Browser.__init__(self, logger)
+        self.binary = binary
+        self.device_serial = device_serial
+        self.server = ChromeDriverServer(self.logger,
+                                         binary=webdriver_binary,
+                                         args=webdriver_args)
+        self.setup_adb_reverse()
+
+    def _adb_run(self, args):
+        cmd = ['adb']
+        if self.device_serial:
+            cmd.extend(['-s', self.device_serial])
+        cmd.extend(args)
+        self.logger.info(' '.join(cmd))
+        subprocess.check_call(cmd)
+
+    def setup_adb_reverse(self):
+        self._adb_run(['wait-for-device'])
+        self._adb_run(['forward', '--remove-all'])
+        self._adb_run(['reverse', '--remove-all'])
+        # "adb reverse" basically forwards network connection from device to
+        # host.
+        for port in _wptserve_ports:
+            self._adb_run(['reverse', 'tcp:%d' % port, 'tcp:%d' % port])
+
+    def start(self, **kwargs):
+        self.server.start(block=False)
+
+    def stop(self, force=False):
+        self.server.stop(force=force)
+
+    def pid(self):
+        return self.server.pid
+
+    def is_alive(self):
+        # TODO(ato): This only indicates the driver is alive,
+        # and doesn't say anything about whether a browser session
+        # is active.
+        return self.server.is_alive
+
+    def cleanup(self):
+        self.stop()
+        self._adb_run(['forward', '--remove-all'])
+        self._adb_run(['reverse', '--remove-all'])
+
+    def executor_browser(self):
+        return ExecutorBrowser, {"webdriver_url": self.server.url}