1 # This Source Code Form is subject to the terms of the Mozilla Public
2 # License, v. 2.0. If a copy of the MPL was not distributed with this
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
11 from mach
.decorators
import Command
12 from mozbuild
.base
import MozbuildObject
14 here
= os
.path
.abspath(os
.path
.dirname(__file__
))
16 GVE
= "org.mozilla.geckoview_example"
22 resp
= requests
.get(url
)
23 resp
.raise_for_status()
27 def untar(fileobj
, dest
):
30 with tarfile
.open(fileobj
=fileobj
, mode
="r") as tar_data
:
31 tar_data
.extractall(path
=dest
)
34 def unzip(fileobj
, dest
):
37 with zipfile
.ZipFile(fileobj
) as zip_data
:
38 zip_data
.extractall(path
=dest
)
41 def create_parser_interventions():
42 from mozlog
import commandline
44 parser
= argparse
.ArgumentParser()
45 parser
.add_argument("--webdriver-binary", help="Path to webdriver binary")
50 help="Port on which to run WebDriver",
53 "--webdriver-ws-port",
56 help="Port on which to run WebDriver BiDi websocket",
58 parser
.add_argument("--bug", help="Bug to run tests for")
63 help="Do two-factor auth live in supporting tests",
66 "--config", help="Path to JSON file containing logins and other settings"
69 "--debug", action
="store_true", default
=False, help="Debug failing tests"
75 help="Run firefox in headless mode",
81 choices
=["enabled", "disabled", "both", "none"],
82 help="Enable webcompat interventions",
88 choices
=["enabled", "disabled", "both", "none"],
89 help="Enable SmartBlock shims",
94 choices
=["android", "desktop"],
95 help="Platform to target",
98 desktop_group
= parser
.add_argument_group("Desktop-specific arguments")
99 desktop_group
.add_argument("--binary", help="Path to browser binary")
101 android_group
= parser
.add_argument_group("Android-specific arguments")
102 android_group
.add_argument(
105 help="Running Android instances to connect to, if not emulator-5554",
107 android_group
.add_argument(
111 help="Android package name to use",
114 commandline
.add_logging_group(parser
)
118 class InterventionTest(MozbuildObject
):
119 def set_default_kwargs(self
, logger
, command_context
, kwargs
):
120 platform
= kwargs
["platform"]
121 binary
= kwargs
["binary"]
122 device_serial
= kwargs
["device_serial"]
123 is_gve_build
= command_context
.substs
.get("MOZ_APP_NAME") == "fennec"
125 if platform
== "android" or (
126 platform
is None and binary
is None and (device_serial
or is_gve_build
)
128 kwargs
["platform"] = "android"
130 kwargs
["platform"] = "desktop"
132 if kwargs
["platform"] == "desktop" and kwargs
["binary"] is None:
133 kwargs
["binary"] = self
.get_binary_path()
135 if kwargs
["webdriver_binary"] is None:
136 webdriver_binary
= self
.get_binary_path(
137 "geckodriver", validate_exists
=False
140 if not os
.path
.exists(webdriver_binary
):
141 webdriver_binary
= self
.install_geckodriver(
142 logger
, dest
=os
.path
.dirname(webdriver_binary
)
145 if not os
.path
.exists(webdriver_binary
):
146 logger
.error("Can't find geckodriver")
148 kwargs
["webdriver_binary"] = webdriver_binary
150 def platform_string_geckodriver(self
):
151 uname
= platform
.uname()
152 platform_name
= {"Linux": "linux", "Windows": "win", "Darwin": "macos"}.get(
156 if platform_name
in ("linux", "win"):
157 bits
= "64" if uname
[4] == "x86_64" else "32"
158 elif platform_name
== "macos":
161 raise ValueError(f
"No precompiled geckodriver for platform {uname}")
163 return f
"{platform_name}{bits}"
165 def install_geckodriver(self
, logger
, dest
):
166 """Install latest Geckodriver."""
168 dest
= os
.path
.join(self
.distdir
, "dist", "bin")
170 is_windows
= platform
.uname()[0] == "Windows"
173 "https://api.github.com/repos/mozilla/geckodriver/releases/latest"
175 ext
= "zip" if is_windows
else "tar.gz"
176 platform_name
= self
.platform_string_geckodriver()
177 name_suffix
= f
"-{platform_name}.{ext}"
178 for item
in release
["assets"]:
179 if item
["name"].endswith(name_suffix
):
180 url
= item
["browser_download_url"]
183 raise ValueError(f
"Failed to find geckodriver for platform {platform_name}")
185 logger
.info(f
"Installing geckodriver from {url}")
187 data
= io
.BytesIO(get(url
).content
)
189 decompress
= unzip
if ext
== "zip" else untar
190 decompress(data
, dest
=dest
)
192 exe_ext
= ".exe" if is_windows
else ""
193 path
= os
.path
.join(dest
, f
"geckodriver{exe_ext}")
197 def setup_device(self
, command_context
, kwargs
):
198 if kwargs
["platform"] != "android":
201 app
= kwargs
["package_name"]
202 device_serial
= kwargs
["device_serial"]
204 if not device_serial
:
205 from mozrunner
.devices
.android_device
import (
207 verify_android_device
,
210 verify_android_device(
211 command_context
, app
=app
, network
=True, install
=InstallIntent
.YES
214 kwargs
["device_serial"] = os
.environ
.get("DEVICE_SERIAL")
216 # GVE does not have the webcompat addon by default. Add it.
218 kwargs
["addon"] = "/data/local/tmp/webcompat.xpi"
220 command_context
.substs
["ADB"],
222 webcompat_addon(command_context
),
226 def run(self
, command_context
, **kwargs
):
230 mozlog
.commandline
.setup_logging(
231 "test-interventions", kwargs
, {"mach": sys
.stdout
}
233 logger
= mozlog
.get_default_logger("test-interventions")
234 status_handler
= mozlog
.handlers
.StatusHandler()
235 logger
.add_handler(status_handler
)
237 self
.set_default_kwargs(logger
, command_context
, kwargs
)
239 self
.setup_device(command_context
, kwargs
)
241 if kwargs
["interventions"] != "none":
243 ["enabled", "disabled"]
244 if kwargs
["interventions"] == "both"
245 else [kwargs
["interventions"]]
248 for interventions_setting
in interventions
:
251 os
.path
.join(here
, "interventions"),
252 kwargs
["webdriver_binary"],
253 kwargs
["webdriver_port"],
254 kwargs
["webdriver_ws_port"],
255 browser_binary
=kwargs
.get("binary"),
256 device_serial
=kwargs
.get("device_serial"),
257 package_name
=kwargs
.get("package_name"),
258 addon
=kwargs
.get("addon"),
260 debug
=kwargs
["debug"],
261 interventions
=interventions_setting
,
262 config
=kwargs
["config"],
263 headless
=kwargs
["headless"],
264 do2fa
=kwargs
["do2fa"],
267 if kwargs
["shims"] != "none":
269 ["enabled", "disabled"]
270 if kwargs
["shims"] == "both"
271 else [kwargs
["shims"]]
274 for shims_setting
in shims
:
277 os
.path
.join(here
, "shims"),
278 kwargs
["webdriver_binary"],
279 kwargs
["webdriver_port"],
280 kwargs
["webdriver_ws_port"],
281 browser_binary
=kwargs
.get("binary"),
282 device_serial
=kwargs
.get("device_serial"),
283 package_name
=kwargs
.get("package_name"),
284 addon
=kwargs
.get("addon"),
286 debug
=kwargs
["debug"],
288 config
=kwargs
["config"],
289 headless
=kwargs
["headless"],
290 do2fa
=kwargs
["do2fa"],
293 summary
= status_handler
.summarize()
295 summary
.unexpected_statuses
== 0
296 and summary
.log_level_counts
.get("ERROR", 0) == 0
297 and summary
.log_level_counts
.get("CRITICAL", 0) == 0
302 def webcompat_addon(command_context
):
305 src
= os
.path
.join(command_context
.topsrcdir
, "browser", "extensions", "webcompat")
307 command_context
.virtualenv_manager
.virtualenv_root
, "webcompat.xpi"
309 shutil
.make_archive(dst
, "zip", src
)
310 shutil
.move(f
"{dst}.zip", dst
)
314 def push_to_device(adb_path
, device_serial
, local_path
, remote_path
):
315 from mozdevice
import ADBDeviceFactory
317 device
= ADBDeviceFactory(adb
=adb_path
, device
=device_serial
)
318 device
.push(local_path
, remote_path
)
319 device
.chmod(remote_path
)
323 "test-interventions",
325 description
="Test the webcompat interventions",
326 parser
=create_parser_interventions
,
327 virtualenv_name
="webcompat",
329 def test_interventions(command_context
, **params
):
330 here
= os
.path
.abspath(os
.path
.dirname(__file__
))
331 command_context
.virtualenv_manager
.activate()
332 command_context
.virtualenv_manager
.install_pip_requirements(
333 os
.path
.join(here
, "requirements.txt"),
334 require_hashes
=False,
337 intervention_test
= command_context
._spawn
(InterventionTest
)
338 return 0 if intervention_test
.run(command_context
, **params
) else 1