mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 02:09:05 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			338 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			338 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			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/.
 | 
						|
 | 
						|
import argparse
 | 
						|
import io
 | 
						|
import os
 | 
						|
import platform
 | 
						|
import sys
 | 
						|
 | 
						|
from mach.decorators import Command
 | 
						|
from mozbuild.base import MozbuildObject
 | 
						|
 | 
						|
here = os.path.abspath(os.path.dirname(__file__))
 | 
						|
 | 
						|
GVE = "org.mozilla.geckoview_example"
 | 
						|
 | 
						|
 | 
						|
def get(url):
 | 
						|
    import requests
 | 
						|
 | 
						|
    resp = requests.get(url)
 | 
						|
    resp.raise_for_status()
 | 
						|
    return resp
 | 
						|
 | 
						|
 | 
						|
def untar(fileobj, dest):
 | 
						|
    import tarfile
 | 
						|
 | 
						|
    with tarfile.open(fileobj=fileobj, mode="r") as tar_data:
 | 
						|
        tar_data.extractall(path=dest)
 | 
						|
 | 
						|
 | 
						|
def unzip(fileobj, dest):
 | 
						|
    import zipfile
 | 
						|
 | 
						|
    with zipfile.ZipFile(fileobj) as zip_data:
 | 
						|
        zip_data.extractall(path=dest)
 | 
						|
 | 
						|
 | 
						|
def create_parser_interventions():
 | 
						|
    from mozlog import commandline
 | 
						|
 | 
						|
    parser = argparse.ArgumentParser()
 | 
						|
    parser.add_argument("--webdriver-binary", help="Path to webdriver binary")
 | 
						|
    parser.add_argument(
 | 
						|
        "--webdriver-port",
 | 
						|
        action="store",
 | 
						|
        default="4444",
 | 
						|
        help="Port on which to run WebDriver",
 | 
						|
    )
 | 
						|
    parser.add_argument(
 | 
						|
        "--webdriver-ws-port",
 | 
						|
        action="store",
 | 
						|
        default="9222",
 | 
						|
        help="Port on which to run WebDriver BiDi websocket",
 | 
						|
    )
 | 
						|
    parser.add_argument("--bug", help="Bug to run tests for")
 | 
						|
    parser.add_argument(
 | 
						|
        "--do2fa",
 | 
						|
        action="store_true",
 | 
						|
        default=False,
 | 
						|
        help="Do two-factor auth live in supporting tests",
 | 
						|
    )
 | 
						|
    parser.add_argument(
 | 
						|
        "--config", help="Path to JSON file containing logins and other settings"
 | 
						|
    )
 | 
						|
    parser.add_argument(
 | 
						|
        "--debug", action="store_true", default=False, help="Debug failing tests"
 | 
						|
    )
 | 
						|
    parser.add_argument(
 | 
						|
        "--headless",
 | 
						|
        action="store_true",
 | 
						|
        default=False,
 | 
						|
        help="Run firefox in headless mode",
 | 
						|
    )
 | 
						|
    parser.add_argument(
 | 
						|
        "--interventions",
 | 
						|
        action="store",
 | 
						|
        default="both",
 | 
						|
        choices=["enabled", "disabled", "both", "none"],
 | 
						|
        help="Enable webcompat interventions",
 | 
						|
    )
 | 
						|
    parser.add_argument(
 | 
						|
        "--shims",
 | 
						|
        action="store",
 | 
						|
        default="none",
 | 
						|
        choices=["enabled", "disabled", "both", "none"],
 | 
						|
        help="Enable SmartBlock shims",
 | 
						|
    )
 | 
						|
    parser.add_argument(
 | 
						|
        "--platform",
 | 
						|
        action="store",
 | 
						|
        choices=["android", "desktop"],
 | 
						|
        help="Platform to target",
 | 
						|
    )
 | 
						|
 | 
						|
    desktop_group = parser.add_argument_group("Desktop-specific arguments")
 | 
						|
    desktop_group.add_argument("--binary", help="Path to browser binary")
 | 
						|
 | 
						|
    android_group = parser.add_argument_group("Android-specific arguments")
 | 
						|
    android_group.add_argument(
 | 
						|
        "--device-serial",
 | 
						|
        action="store",
 | 
						|
        help="Running Android instances to connect to, if not emulator-5554",
 | 
						|
    )
 | 
						|
    android_group.add_argument(
 | 
						|
        "--package-name",
 | 
						|
        action="store",
 | 
						|
        default=GVE,
 | 
						|
        help="Android package name to use",
 | 
						|
    )
 | 
						|
 | 
						|
    commandline.add_logging_group(parser)
 | 
						|
    return parser
 | 
						|
 | 
						|
 | 
						|
class InterventionTest(MozbuildObject):
 | 
						|
    def set_default_kwargs(self, logger, command_context, kwargs):
 | 
						|
        platform = kwargs["platform"]
 | 
						|
        binary = kwargs["binary"]
 | 
						|
        device_serial = kwargs["device_serial"]
 | 
						|
        is_gve_build = command_context.substs.get("MOZ_APP_NAME") == "fennec"
 | 
						|
 | 
						|
        if platform == "android" or (
 | 
						|
            platform is None and binary is None and (device_serial or is_gve_build)
 | 
						|
        ):
 | 
						|
            kwargs["platform"] = "android"
 | 
						|
        else:
 | 
						|
            kwargs["platform"] = "desktop"
 | 
						|
 | 
						|
        if kwargs["platform"] == "desktop" and kwargs["binary"] is None:
 | 
						|
            kwargs["binary"] = self.get_binary_path()
 | 
						|
 | 
						|
        if kwargs["webdriver_binary"] is None:
 | 
						|
            webdriver_binary = self.get_binary_path(
 | 
						|
                "geckodriver", validate_exists=False
 | 
						|
            )
 | 
						|
 | 
						|
            if not os.path.exists(webdriver_binary):
 | 
						|
                webdriver_binary = self.install_geckodriver(
 | 
						|
                    logger, dest=os.path.dirname(webdriver_binary)
 | 
						|
                )
 | 
						|
 | 
						|
            if not os.path.exists(webdriver_binary):
 | 
						|
                logger.error("Can't find geckodriver")
 | 
						|
                sys.exit(1)
 | 
						|
            kwargs["webdriver_binary"] = webdriver_binary
 | 
						|
 | 
						|
    def platform_string_geckodriver(self):
 | 
						|
        uname = platform.uname()
 | 
						|
        platform_name = {"Linux": "linux", "Windows": "win", "Darwin": "macos"}.get(
 | 
						|
            uname[0]
 | 
						|
        )
 | 
						|
 | 
						|
        if platform_name in ("linux", "win"):
 | 
						|
            bits = "64" if uname[4] == "x86_64" else "32"
 | 
						|
        elif platform_name == "macos":
 | 
						|
            bits = ""
 | 
						|
        else:
 | 
						|
            raise ValueError(f"No precompiled geckodriver for platform {uname}")
 | 
						|
 | 
						|
        return f"{platform_name}{bits}"
 | 
						|
 | 
						|
    def install_geckodriver(self, logger, dest):
 | 
						|
        """Install latest Geckodriver."""
 | 
						|
        if dest is None:
 | 
						|
            dest = os.path.join(self.distdir, "dist", "bin")
 | 
						|
 | 
						|
        is_windows = platform.uname()[0] == "Windows"
 | 
						|
 | 
						|
        release = get(
 | 
						|
            "https://api.github.com/repos/mozilla/geckodriver/releases/latest"
 | 
						|
        ).json()
 | 
						|
        ext = "zip" if is_windows else "tar.gz"
 | 
						|
        platform_name = self.platform_string_geckodriver()
 | 
						|
        name_suffix = f"-{platform_name}.{ext}"
 | 
						|
        for item in release["assets"]:
 | 
						|
            if item["name"].endswith(name_suffix):
 | 
						|
                url = item["browser_download_url"]
 | 
						|
                break
 | 
						|
        else:
 | 
						|
            raise ValueError(f"Failed to find geckodriver for platform {platform_name}")
 | 
						|
 | 
						|
        logger.info(f"Installing geckodriver from {url}")
 | 
						|
 | 
						|
        data = io.BytesIO(get(url).content)
 | 
						|
        data.seek(0)
 | 
						|
        decompress = unzip if ext == "zip" else untar
 | 
						|
        decompress(data, dest=dest)
 | 
						|
 | 
						|
        exe_ext = ".exe" if is_windows else ""
 | 
						|
        path = os.path.join(dest, f"geckodriver{exe_ext}")
 | 
						|
 | 
						|
        return path
 | 
						|
 | 
						|
    def setup_device(self, command_context, kwargs):
 | 
						|
        if kwargs["platform"] != "android":
 | 
						|
            return
 | 
						|
 | 
						|
        app = kwargs["package_name"]
 | 
						|
        device_serial = kwargs["device_serial"]
 | 
						|
 | 
						|
        if not device_serial:
 | 
						|
            from mozrunner.devices.android_device import (
 | 
						|
                InstallIntent,
 | 
						|
                verify_android_device,
 | 
						|
            )
 | 
						|
 | 
						|
            verify_android_device(
 | 
						|
                command_context, app=app, network=True, install=InstallIntent.YES
 | 
						|
            )
 | 
						|
 | 
						|
            kwargs["device_serial"] = os.environ.get("DEVICE_SERIAL")
 | 
						|
 | 
						|
        # GVE does not have the webcompat addon by default. Add it.
 | 
						|
        if app == GVE:
 | 
						|
            kwargs["addon"] = "/data/local/tmp/webcompat.xpi"
 | 
						|
            push_to_device(
 | 
						|
                command_context.substs["ADB"],
 | 
						|
                device_serial,
 | 
						|
                webcompat_addon(command_context),
 | 
						|
                kwargs["addon"],
 | 
						|
            )
 | 
						|
 | 
						|
    def run(self, command_context, **kwargs):
 | 
						|
        import mozlog
 | 
						|
        import runner
 | 
						|
 | 
						|
        mozlog.commandline.setup_logging(
 | 
						|
            "test-interventions", kwargs, {"mach": sys.stdout}
 | 
						|
        )
 | 
						|
        logger = mozlog.get_default_logger("test-interventions")
 | 
						|
        status_handler = mozlog.handlers.StatusHandler()
 | 
						|
        logger.add_handler(status_handler)
 | 
						|
 | 
						|
        self.set_default_kwargs(logger, command_context, kwargs)
 | 
						|
 | 
						|
        self.setup_device(command_context, kwargs)
 | 
						|
 | 
						|
        if kwargs["interventions"] != "none":
 | 
						|
            interventions = (
 | 
						|
                ["enabled", "disabled"]
 | 
						|
                if kwargs["interventions"] == "both"
 | 
						|
                else [kwargs["interventions"]]
 | 
						|
            )
 | 
						|
 | 
						|
            for interventions_setting in interventions:
 | 
						|
                runner.run(
 | 
						|
                    logger,
 | 
						|
                    os.path.join(here, "interventions"),
 | 
						|
                    kwargs["webdriver_binary"],
 | 
						|
                    kwargs["webdriver_port"],
 | 
						|
                    kwargs["webdriver_ws_port"],
 | 
						|
                    browser_binary=kwargs.get("binary"),
 | 
						|
                    device_serial=kwargs.get("device_serial"),
 | 
						|
                    package_name=kwargs.get("package_name"),
 | 
						|
                    addon=kwargs.get("addon"),
 | 
						|
                    bug=kwargs["bug"],
 | 
						|
                    debug=kwargs["debug"],
 | 
						|
                    interventions=interventions_setting,
 | 
						|
                    config=kwargs["config"],
 | 
						|
                    headless=kwargs["headless"],
 | 
						|
                    do2fa=kwargs["do2fa"],
 | 
						|
                )
 | 
						|
 | 
						|
        if kwargs["shims"] != "none":
 | 
						|
            shims = (
 | 
						|
                ["enabled", "disabled"]
 | 
						|
                if kwargs["shims"] == "both"
 | 
						|
                else [kwargs["shims"]]
 | 
						|
            )
 | 
						|
 | 
						|
            for shims_setting in shims:
 | 
						|
                runner.run(
 | 
						|
                    logger,
 | 
						|
                    os.path.join(here, "shims"),
 | 
						|
                    kwargs["webdriver_binary"],
 | 
						|
                    kwargs["webdriver_port"],
 | 
						|
                    kwargs["webdriver_ws_port"],
 | 
						|
                    browser_binary=kwargs.get("binary"),
 | 
						|
                    device_serial=kwargs.get("device_serial"),
 | 
						|
                    package_name=kwargs.get("package_name"),
 | 
						|
                    addon=kwargs.get("addon"),
 | 
						|
                    bug=kwargs["bug"],
 | 
						|
                    debug=kwargs["debug"],
 | 
						|
                    shims=shims_setting,
 | 
						|
                    config=kwargs["config"],
 | 
						|
                    headless=kwargs["headless"],
 | 
						|
                    do2fa=kwargs["do2fa"],
 | 
						|
                )
 | 
						|
 | 
						|
        summary = status_handler.summarize()
 | 
						|
        passed = (
 | 
						|
            summary.unexpected_statuses == 0
 | 
						|
            and summary.log_level_counts.get("ERROR", 0) == 0
 | 
						|
            and summary.log_level_counts.get("CRITICAL", 0) == 0
 | 
						|
        )
 | 
						|
        return passed
 | 
						|
 | 
						|
 | 
						|
def webcompat_addon(command_context):
 | 
						|
    import shutil
 | 
						|
 | 
						|
    src = os.path.join(command_context.topsrcdir, "browser", "extensions", "webcompat")
 | 
						|
    dst = os.path.join(
 | 
						|
        command_context.virtualenv_manager.virtualenv_root, "webcompat.xpi"
 | 
						|
    )
 | 
						|
    shutil.make_archive(dst, "zip", src)
 | 
						|
    shutil.move(f"{dst}.zip", dst)
 | 
						|
    return dst
 | 
						|
 | 
						|
 | 
						|
def push_to_device(adb_path, device_serial, local_path, remote_path):
 | 
						|
    from mozdevice import ADBDeviceFactory
 | 
						|
 | 
						|
    device = ADBDeviceFactory(adb=adb_path, device=device_serial)
 | 
						|
    device.push(local_path, remote_path)
 | 
						|
    device.chmod(remote_path)
 | 
						|
 | 
						|
 | 
						|
@Command(
 | 
						|
    "test-interventions",
 | 
						|
    category="testing",
 | 
						|
    description="Test the webcompat interventions",
 | 
						|
    parser=create_parser_interventions,
 | 
						|
    virtualenv_name="webcompat",
 | 
						|
)
 | 
						|
def test_interventions(command_context, **params):
 | 
						|
    here = os.path.abspath(os.path.dirname(__file__))
 | 
						|
    command_context.virtualenv_manager.activate()
 | 
						|
    command_context.virtualenv_manager.install_pip_requirements(
 | 
						|
        os.path.join(here, "requirements.txt"),
 | 
						|
        require_hashes=False,
 | 
						|
    )
 | 
						|
 | 
						|
    intervention_test = command_context._spawn(InterventionTest)
 | 
						|
    return 0 if intervention_test.run(command_context, **params) else 1
 |