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/.
5 # Integrates the web-platform-tests test runner with mach.
7 from __future__
import absolute_import
, unicode_literals
, print_function
12 from six
import iteritems
14 from mozbuild
.base
import (
16 MachCommandConditions
as conditions
,
20 from mach
.decorators
import (
25 from mach_commands_base
import WebPlatformTestsRunner
, create_parser_wpt
28 class WebPlatformTestsRunnerSetup(MozbuildObject
):
29 default_log_type
= "mach"
31 def __init__(self
, *args
, **kwargs
):
32 super(WebPlatformTestsRunnerSetup
, self
).__init
__(*args
, **kwargs
)
33 self
._here
= os
.path
.join(self
.topsrcdir
, "testing", "web-platform")
34 kwargs
["tests_root"] = os
.path
.join(self
._here
, "tests")
35 sys
.path
.insert(0, kwargs
["tests_root"])
36 build_path
= os
.path
.join(self
.topobjdir
, "build")
37 if build_path
not in sys
.path
:
38 sys
.path
.append(build_path
)
40 def kwargs_common(self
, kwargs
):
41 """Setup kwargs relevant for all browser products"""
43 tests_src_path
= os
.path
.join(self
._here
, "tests")
46 kwargs
["product"] in {"firefox", "firefox_android"}
47 and kwargs
["specialpowers_path"] is None
49 kwargs
["specialpowers_path"] = os
.path
.join(
50 self
.distdir
, "xpi-stage", "specialpowers@mozilla.org.xpi"
53 if kwargs
["product"] == "firefox_android":
54 # package_name may be different in the future
55 package_name
= kwargs
["package_name"]
57 kwargs
["package_name"] = package_name
= "org.mozilla.geckoview.test"
59 # Note that this import may fail in non-firefox-for-android trees
60 from mozrunner
.devices
.android_device
import (
62 verify_android_device
,
66 kwargs
["adb_binary"] = get_adb_path(self
)
68 InstallIntent
.NO
if kwargs
.pop("no_install") else InstallIntent
.YES
70 verify_android_device(
71 self
, install
=install
, verbose
=False, xre
=True, app
=package_name
74 if kwargs
["certutil_binary"] is None:
75 kwargs
["certutil_binary"] = os
.path
.join(
76 os
.environ
.get("MOZ_HOST_BIN"), "certutil"
79 if kwargs
["install_fonts"] is None:
80 kwargs
["install_fonts"] = True
82 if kwargs
["config"] is None:
83 kwargs
["config"] = os
.path
.join(
84 self
.topobjdir
, "_tests", "web-platform", "wptrunner.local.ini"
88 kwargs
["exclude"] is None
89 and kwargs
["include"] is None
90 and not sys
.platform
.startswith("linux")
92 kwargs
["exclude"] = ["css"]
94 if kwargs
["ssl_type"] in (None, "pregenerated"):
95 cert_root
= os
.path
.join(tests_src_path
, "tools", "certs")
96 if kwargs
["ca_cert_path"] is None:
97 kwargs
["ca_cert_path"] = os
.path
.join(cert_root
, "cacert.pem")
99 if kwargs
["host_key_path"] is None:
100 kwargs
["host_key_path"] = os
.path
.join(
101 cert_root
, "web-platform.test.key"
104 if kwargs
["host_cert_path"] is None:
105 kwargs
["host_cert_path"] = os
.path
.join(
106 cert_root
, "web-platform.test.pem"
109 if kwargs
["reftest_screenshot"] is None:
110 kwargs
["reftest_screenshot"] = "fail"
112 kwargs
["capture_stdio"] = True
116 def kwargs_firefox(self
, kwargs
):
117 """Setup kwargs specific to running Firefox and other gecko browsers"""
119 from wptrunner
import wptcommandline
121 kwargs
= self
.kwargs_common(kwargs
)
123 if kwargs
["binary"] is None:
124 kwargs
["binary"] = self
.get_binary_path()
126 if kwargs
["certutil_binary"] is None:
127 kwargs
["certutil_binary"] = self
.get_binary_path("certutil")
129 if kwargs
["webdriver_binary"] is None:
130 kwargs
["webdriver_binary"] = self
.get_binary_path(
131 "geckodriver", validate_exists
=False
134 if kwargs
["install_fonts"] is None:
135 kwargs
["install_fonts"] = True
138 kwargs
["install_fonts"]
139 and mozinfo
.info
["os"] == "win"
140 and mozinfo
.info
["os_version"] == "6.1"
142 # On Windows 7 --install-fonts fails, so fall back to a Firefox-specific codepath
143 self
.setup_fonts_firefox()
144 kwargs
["install_fonts"] = False
146 if kwargs
["preload_browser"] is None:
147 kwargs
["preload_browser"] = False
149 if kwargs
["prefs_root"] is None:
150 kwargs
["prefs_root"] = os
.path
.join(self
.topsrcdir
, "testing", "profiles")
152 if kwargs
["stackfix_dir"] is None:
153 kwargs
["stackfix_dir"] = self
.bindir
155 kwargs
= wptcommandline
.check_args(kwargs
)
159 def kwargs_wptrun(self
, kwargs
):
160 """Setup kwargs for wpt-run which is only used for non-gecko browser products"""
161 from tools
.wpt
import run
163 kwargs
= self
.kwargs_common(kwargs
)
165 # Add additional kwargs consumed by the run frontend. Currently we don't
166 # have a way to set these through mach
167 kwargs
["channel"] = None
168 kwargs
["prompt"] = True
169 kwargs
["install_browser"] = False
170 kwargs
["install_webdriver"] = None
171 kwargs
["affected"] = None
174 # We do this explicitly to avoid calling pip with options that aren't
175 # supported in the in-tree version
176 wptrunner_path
= os
.path
.join(self
._here
, "tests", "tools", "wptrunner")
177 browser_cls
= run
.product_setup
[kwargs
["product"]].browser_cls
178 requirements
= ["requirements.txt"]
179 if hasattr(browser_cls
, "requirements"):
180 requirements
.append(browser_cls
.requirements
)
182 for filename
in requirements
:
183 path
= os
.path
.join(wptrunner_path
, filename
)
184 if os
.path
.exists(path
):
185 self
.virtualenv_manager
.install_pip_requirements(
186 path
, require_hashes
=False
189 venv
= run
.virtualenv
.Virtualenv(
190 self
.virtualenv_manager
.virtualenv_root
, skip_virtualenv_setup
=True
193 kwargs
= run
.setup_wptrunner(venv
, **kwargs
)
194 except run
.WptrunError
as e
:
195 print(e
.message
, file=sys
.stderr
)
198 # This is kind of a hack; override the metadata paths so we don't use
199 # gecko metadata for non-gecko products
200 for key
, value
in list(iteritems(kwargs
["test_paths"])):
201 meta_suffix
= key
.strip("/")
202 meta_dir
= os
.path
.join(
203 self
._here
, "products", kwargs
["product"], meta_suffix
205 value
["metadata_path"] = meta_dir
206 if not os
.path
.exists(meta_dir
):
207 os
.makedirs(meta_dir
)
210 def setup_fonts_firefox(self
):
211 # Ensure the Ahem font is available
212 if not sys
.platform
.startswith("darwin"):
213 font_path
= os
.path
.join(os
.path
.dirname(self
.get_binary_path()), "fonts")
215 font_path
= os
.path
.join(
216 os
.path
.dirname(self
.get_binary_path()),
222 ahem_src
= os
.path
.join(
223 self
.topsrcdir
, "testing", "web-platform", "tests", "fonts", "Ahem.ttf"
225 ahem_dest
= os
.path
.join(font_path
, "Ahem.ttf")
226 if not os
.path
.exists(ahem_dest
) and os
.path
.exists(ahem_src
):
227 with
open(ahem_src
, "rb") as src
, open(ahem_dest
, "wb") as dest
:
228 dest
.write(src
.read())
231 class WebPlatformTestsServeRunner(MozbuildObject
):
232 def run(self
, **kwargs
):
235 os
.path
.abspath(os
.path
.join(os
.path
.dirname(__file__
), "tests", "tools")),
237 from serve
import serve
238 from wptrunner
import wptcommandline
239 import manifestupdate
243 logger
= logging
.getLogger("web-platform-tests")
245 src_root
= self
.topsrcdir
246 obj_root
= self
.topobjdir
247 src_wpt_dir
= os
.path
.join(src_root
, "testing", "web-platform")
249 config_path
= manifestupdate
.generate_config(
253 os
.path
.join(obj_root
, "_tests", "web-platform"),
257 test_paths
= wptcommandline
.get_test_paths(
258 wptcommandline
.config
.read(config_path
)
261 def get_route_builder(*args
, **kwargs
):
262 route_builder
= serve
.get_route_builder(*args
, **kwargs
)
264 for url_base
, paths
in iteritems(test_paths
):
266 route_builder
.add_mount_point(url_base
, paths
["tests_path"])
270 return 0 if serve
.run(route_builder
=get_route_builder
, **kwargs
) else 1
273 class WebPlatformTestsUpdater(MozbuildObject
):
274 """Update web platform tests."""
276 def setup_logging(self
, **kwargs
):
279 return update
.setup_logging(kwargs
, {"mach": sys
.stdout
})
281 def update_manifest(self
, logger
, **kwargs
):
282 import manifestupdate
284 return manifestupdate
.run(
285 logger
=logger
, src_root
=self
.topsrcdir
, obj_root
=self
.topobjdir
, **kwargs
288 def run_update(self
, logger
, **kwargs
):
290 from update
import updatecommandline
292 self
.update_manifest(logger
, **kwargs
)
294 if kwargs
["config"] is None:
295 kwargs
["config"] = os
.path
.join(
296 self
.topobjdir
, "_tests", "web-platform", "wptrunner.local.ini"
298 if kwargs
["product"] is None:
299 kwargs
["product"] = "firefox"
301 kwargs
["store_state"] = False
303 kwargs
= updatecommandline
.check_args(kwargs
)
306 update
.run_update(logger
, **kwargs
)
310 traceback
.print_exc()
313 class WebPlatformTestsUnittestRunner(MozbuildObject
):
314 def run(self
, **kwargs
):
315 import unittestrunner
317 return unittestrunner
.run(self
.topsrcdir
, **kwargs
)
320 class WebPlatformTestsTestPathsRunner(MozbuildObject
):
321 """Update web platform tests."""
323 def run(self
, **kwargs
):
326 os
.path
.abspath(os
.path
.join(os
.path
.dirname(__file__
), "tests", "tools")),
328 from wptrunner
import wptcommandline
329 from manifest
import testpaths
330 import manifestupdate
334 logger
= logging
.getLogger("web-platform-tests")
336 src_root
= self
.topsrcdir
337 obj_root
= self
.topobjdir
338 src_wpt_dir
= os
.path
.join(src_root
, "testing", "web-platform")
340 config_path
= manifestupdate
.generate_config(
344 os
.path
.join(obj_root
, "_tests", "web-platform"),
348 test_paths
= wptcommandline
.get_test_paths(
349 wptcommandline
.config
.read(config_path
)
352 for url_base
, paths
in iteritems(test_paths
):
353 if "manifest_path" not in paths
:
354 paths
["manifest_path"] = os
.path
.join(
355 paths
["metadata_path"], "MANIFEST.json"
359 path
=paths
["manifest_path"],
361 tests_root
=paths
["tests_path"],
362 update
=kwargs
["update"],
363 rebuild
=kwargs
["rebuild"],
365 cache_root
=kwargs
["cache_root"],
366 test_ids
=kwargs
["test_ids"],
369 testpaths
.write_output(results
, kwargs
["json"])
373 class WebPlatformTestsFissionRegressionsRunner(MozbuildObject
):
374 def run(self
, **kwargs
):
376 import fissionregressions
378 src_root
= self
.topsrcdir
379 obj_root
= self
.topobjdir
380 logger
= mozlog
.structuredlog
.StructuredLogger("web-platform-tests")
383 return fissionregressions
.run(logger
, src_root
, obj_root
, **kwargs
)
388 traceback
.print_exc()
392 def create_parser_update():
393 from update
import updatecommandline
395 return updatecommandline
.create_parser()
398 def create_parser_manifest_update():
399 import manifestupdate
401 return manifestupdate
.create_parser()
404 def create_parser_metadata_summary():
407 return metasummary
.create_parser()
410 def create_parser_metadata_merge():
413 return metamerge
.get_parser()
416 def create_parser_serve():
418 0, os
.path
.abspath(os
.path
.join(os
.path
.dirname(__file__
), "tests", "tools"))
422 return serve
.serve
.get_parser()
425 def create_parser_unittest():
426 import unittestrunner
428 return unittestrunner
.get_parser()
431 def create_parser_fission_regressions():
432 import fissionregressions
434 return fissionregressions
.get_parser()
437 def create_parser_testpaths():
439 from mozboot
.util
import get_state_dir
441 parser
= argparse
.ArgumentParser()
445 action
="store_false",
447 help="Don't update manifest before continuing",
454 help="Force a full rebuild of the manifest.",
459 default
=os
.path
.join(get_state_dir(), "cache", "wpt"),
460 help="Path in which to store any caches (default <tests_root>/.wptcache/)",
463 "test_ids", action
="store", nargs
="+", help="Test ids for which to get paths"
466 "--json", action
="store_true", default
=False, help="Output as JSON"
472 class MachCommands(MachCommandBase
):
474 self
.activate_virtualenv()
477 "web-platform-tests",
479 conditions
=[conditions
.is_firefox_or_android
],
480 description
="Run web-platform-tests.",
481 parser
=create_parser_wpt
,
483 def run_web_platform_tests(self
, **params
):
485 if params
["product"] is None:
486 if conditions
.is_android(self
):
487 params
["product"] = "firefox_android"
489 params
["product"] = "firefox"
490 if "test_objects" in params
:
493 for item
in params
["test_objects"]:
494 include
.append(item
["name"])
495 test_types
.add(item
.get("subsuite"))
496 if None not in test_types
:
497 params
["test_types"] = list(test_types
)
498 params
["include"] = include
499 del params
["test_objects"]
500 if params
.get("debugger", None):
503 if not mozdebug
.get_debugger_info(params
.get("debugger")):
506 wpt_setup
= self
._spawn
(WebPlatformTestsRunnerSetup
)
507 wpt_setup
._mach
_context
= self
._mach
_context
508 wpt_runner
= WebPlatformTestsRunner(wpt_setup
)
510 logger
= wpt_runner
.setup_logging(**params
)
512 if conditions
.is_android(self
) and params
["product"] != "firefox_android":
514 "Must specify --product=firefox_android in Android environment."
517 return wpt_runner
.run(logger
, **params
)
522 conditions
=[conditions
.is_firefox_or_android
],
523 description
="Run web-platform-tests.",
524 parser
=create_parser_wpt
,
526 def run_wpt(self
, **params
):
527 return self
.run_web_platform_tests(**params
)
530 "web-platform-tests-update",
532 description
="Update web-platform-test metadata.",
533 parser
=create_parser_update
,
535 def update_web_platform_tests(self
, **params
):
537 self
.virtualenv_manager
.install_pip_package("html5lib==1.0.1")
538 self
.virtualenv_manager
.install_pip_package("ujson")
539 self
.virtualenv_manager
.install_pip_package("requests")
541 wpt_updater
= self
._spawn
(WebPlatformTestsUpdater
)
542 logger
= wpt_updater
.setup_logging(**params
)
543 return wpt_updater
.run_update(logger
, **params
)
548 description
="Update web-platform-test metadata.",
549 parser
=create_parser_update
,
551 def update_wpt(self
, **params
):
552 return self
.update_web_platform_tests(**params
)
555 "wpt-manifest-update",
557 description
="Update web-platform-test manifests.",
558 parser
=create_parser_manifest_update
,
560 def wpt_manifest_update(self
, **params
):
562 wpt_setup
= self
._spawn
(WebPlatformTestsRunnerSetup
)
563 wpt_runner
= WebPlatformTestsRunner(wpt_setup
)
564 logger
= wpt_runner
.setup_logging(**params
)
566 "The wpt manifest is now automatically updated, "
567 "so running this command is usually unnecessary"
569 return 0 if wpt_runner
.update_manifest(logger
, **params
) else 1
574 description
="Run the wpt server",
575 parser
=create_parser_serve
,
577 def wpt_serve(self
, **params
):
581 logger
= logging
.getLogger("web-platform-tests")
582 logger
.addHandler(logging
.StreamHandler(sys
.stdout
))
583 wpt_serve
= self
._spawn
(WebPlatformTestsServeRunner
)
584 return wpt_serve
.run(**params
)
587 "wpt-metadata-summary",
589 description
="Create a json summary of the wpt metadata",
590 parser
=create_parser_metadata_summary
,
592 def wpt_summary(self
, **params
):
595 wpt_setup
= self
._spawn
(WebPlatformTestsRunnerSetup
)
596 return metasummary
.run(wpt_setup
.topsrcdir
, wpt_setup
.topobjdir
, **params
)
599 "wpt-metadata-merge", category
="testing", parser
=create_parser_metadata_merge
601 def wpt_meta_merge(self
, **params
):
604 if params
["dest"] is None:
605 params
["dest"] = params
["current"]
606 return metamerge
.run(**params
)
611 description
="Run the wpt tools and wptrunner unit tests",
612 parser
=create_parser_unittest
,
614 def wpt_unittest(self
, **params
):
616 self
.virtualenv_manager
.install_pip_package("tox")
617 runner
= self
._spawn
(WebPlatformTestsUnittestRunner
)
618 return 0 if runner
.run(**params
) else 1
623 description
="Get a mapping from test ids to files",
624 parser
=create_parser_testpaths
,
626 def wpt_test_paths(self
, **params
):
627 runner
= self
._spawn
(WebPlatformTestsTestPathsRunner
)
632 "wpt-fission-regressions",
634 description
="Dump a list of fission-specific regressions",
635 parser
=create_parser_fission_regressions
,
637 def wpt_fission_regressions(self
, **params
):
638 runner
= self
._spawn
(WebPlatformTestsFissionRegressionsRunner
)