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 from __future__
import absolute_import
, print_function
7 from abc
import ABCMeta
, abstractmethod
, abstractproperty
8 from argparse
import ArgumentParser
, SUPPRESS
9 from distutils
.util
import strtobool
10 from distutils
import spawn
11 from itertools
import chain
12 from six
.moves
.urllib
.parse
import urlparse
18 from mozprofile
import DEFAULT_PORTS
24 here
= os
.path
.abspath(os
.path
.dirname(__file__
))
27 from mozbuild
.base
import (
29 MachCommandConditions
as conditions
,
32 build_obj
= MozbuildObject
.from_environment(cwd
=here
)
38 # Maps test flavors to data needed to run them
42 "aliases": ("plain", "mochitest"),
43 "enabled_apps": ("firefox", "android"),
47 "install_subdir": "tests",
51 "aliases": ("chrome", "mochitest-chrome"),
52 "enabled_apps": ("firefox"),
59 "aliases": ("browser", "browser-chrome", "mochitest-browser-chrome", "bc"),
60 "enabled_apps": ("firefox", "thunderbird"),
67 "aliases": ("a11y", "mochitest-a11y", "accessibility"),
68 "enabled_apps": ("firefox",),
74 SUPPORTED_FLAVORS
= list(
75 chain
.from_iterable([f
["aliases"] for f
in ALL_FLAVORS
.values()])
77 CANONICAL_FLAVORS
= sorted([f
["aliases"][0] for f
in ALL_FLAVORS
.values()])
80 def get_default_valgrind_suppression_files():
81 # We are trying to locate files in the source tree. So if we
82 # don't know where the source tree is, we must give up.
84 # When this is being run by |mach mochitest --valgrind ...|, it is
85 # expected that |build_obj| is not None, and so the logic below will
86 # select the correct suppression files.
88 # When this is run from mozharness, |build_obj| is None, and we expect
89 # that testing/mozharness/configs/unittests/linux_unittests.py will
90 # select the correct suppression files (and paths to them) and
91 # will specify them using the --valgrind-supp-files= flag. Hence this
92 # function will not get called when running from mozharness.
94 # Note: keep these Valgrind .sup file names consistent with those
95 # in testing/mozharness/configs/unittests/linux_unittest.py.
96 if build_obj
is None or build_obj
.topsrcdir
is None:
99 supps_path
= os
.path
.join(build_obj
.topsrcdir
, "build", "valgrind")
102 if mozinfo
.os
== "linux":
103 if mozinfo
.processor
== "x86_64":
104 rv
.append(os
.path
.join(supps_path
, "x86_64-pc-linux-gnu.sup"))
105 rv
.append(os
.path
.join(supps_path
, "cross-architecture.sup"))
106 elif mozinfo
.processor
== "x86":
107 rv
.append(os
.path
.join(supps_path
, "i386-pc-linux-gnu.sup"))
108 rv
.append(os
.path
.join(supps_path
, "cross-architecture.sup"))
113 @six.add_metaclass(ABCMeta
)
114 class ArgumentContainer
:
124 def validate(self
, parser
, args
, context
):
127 def get_full_path(self
, path
, cwd
):
128 """Get an absolute path relative to cwd."""
129 return os
.path
.normpath(os
.path
.join(cwd
, os
.path
.expanduser(path
)))
132 class MochitestArguments(ArgumentContainer
):
133 """General mochitest arguments."""
135 LOG_LEVELS
= ("DEBUG", "INFO", "WARNING", "ERROR", "FATAL")
144 "help": "Test to run. Can be a single test file or a directory of tests "
145 "(to run recursively). If omitted, the entire suite is run.",
151 "choices": SUPPORTED_FLAVORS
,
152 "metavar": "{{{}}}".format(", ".join(CANONICAL_FLAVORS
)),
154 "help": "Only run tests of this flavor.",
164 "help": "Always keep the browser open after tests complete. Or always close the "
165 "browser with --keep-open=false",
174 "Override the default binary used to run tests with the path provided, e.g "
175 "/usr/bin/firefox. If you have run ./mach package beforehand, you can "
176 "specify 'dist' to run tests against the distribution bundle's binary."
183 "dest": "utilityPath",
184 "default": build_obj
.bindir
if build_obj
is not None else None,
185 "help": "absolute path to directory containing utility programs "
186 "(xpcshell, ssltunnel, certutil)",
191 ["--certificate-path"],
195 "help": "absolute path to directory containing certificate store to use testing profile", # NOQA: E501
202 "action": "store_false",
205 "help": "Do not start running tests automatically.",
213 "help": "The per-test timeout in seconds (default: 60 seconds).",
220 "dest": "maxTimeouts",
222 "help": "The maximum number of timeouts permitted before halting testing.",
229 "dest": "totalChunks",
230 "help": "Total number of chunks to split tests into.",
239 "help": "If running tests by chunks, the chunk number to run.",
244 ["--chunk-by-runtime"],
246 "action": "store_true",
247 "dest": "chunkByRuntime",
248 "help": "Group tests such that each chunk has roughly the same runtime.",
256 "dest": "chunkByDir",
257 "help": "Group tests together in the same chunk that are in the same top "
258 "chunkByDir directories.",
263 ["--run-by-manifest"],
265 "action": "store_true",
266 "dest": "runByManifest",
267 "help": "Run each manifest in a single browser instance with a fresh profile.",
275 "action": "store_true",
276 "help": "Shuffle execution order of tests.",
283 "dest": "consoleLevel",
284 "choices": LOG_LEVELS
,
286 "help": "One of {} to determine the level of console logging.".format(
287 ", ".join(LOG_LEVELS
)
295 "dest": "bisectChunk",
297 "help": "Specify the failing test name to find the previous tests that may be "
298 "causing the failure.",
306 "help": "Start running the test sequence at this test.",
314 "help": "Stop running the test sequence at this test.",
321 "help": "Subsuite of tests to run. Unlike tags, subsuites also remove tests from "
322 "the default set. Only one can be specified at once.",
329 "dest": "environment",
330 "metavar": "NAME=VALUE",
332 "help": "Sets the given variable in the application's environment.",
336 ["--exclude-extension"],
339 "dest": "extensionsToExclude",
341 "help": "Excludes the given extension from being installed in the test profile.",
349 "dest": "browserArgs",
351 "help": "Provides an argument to the test application (e.g Firefox).",
356 ["--leak-threshold"],
359 "dest": "defaultLeakThreshold",
361 "help": "Fail if the number of bytes leaked in default processes through "
362 "refcounted objects (or bytes in classes with MOZ_COUNT_CTOR and "
363 "MOZ_COUNT_DTOR) is greater than the given number.",
368 ["--fatal-assertions"],
370 "action": "store_true",
371 "dest": "fatalAssertions",
373 "help": "Abort testing whenever an assertion is hit (requires a debug build to "
379 ["--extra-profile-file"],
382 "dest": "extraProfileFiles",
384 "help": "Copy specified files/dirs to testing profile. Can be specified more "
390 ["--install-extension"],
393 "dest": "extensionsToInstall",
395 "help": "Install the specified extension in the testing profile. Can be a path "
402 "dest": "profilePath",
404 "help": "Directory where the profile will be stored. This directory will be "
405 "deleted after the tests are finished.",
410 ["--testing-modules-dir"],
412 "dest": "testingModulesDir",
414 "help": "Directory where testing-only JS modules are located.",
423 "help": "Repeat the tests the given number of times.",
427 ["--run-until-failure"],
429 "action": "store_true",
430 "dest": "runUntilFailure",
432 "help": "Run tests repeatedly but stop the first time a test fails. Default cap "
433 "is 30 runs, which can be overridden with the --repeat parameter.",
439 "dest": "manifestFile",
441 "help": "Path to a manifestparser (.ini formatted) manifest of tests to run.",
446 ["--extra-mozinfo-json"],
448 "dest": "extra_mozinfo_json",
450 "help": "Filter tests based on a given mozinfo file.",
455 ["--testrun-manifest-file"],
457 "dest": "testRunManifestFile",
458 "default": "tests.json",
459 "help": "Overrides the default filename of the tests.json manifest file that is "
460 "generated by the harness and used by SimpleTest. Only useful when running "
461 "multiple test runs simulatenously on the same machine.",
468 "dest": "dump_tests",
470 "help": "Specify path to a filename to dump all the tests that will be run",
477 "dest": "failureFile",
479 "help": "Filename of the output file where we can store a .json list of failures "
480 "to be run in the future with --run-only-tests.",
487 "action": "store_true",
490 "help": "Delay execution between tests.",
498 "help": "Path to the httpd.js file.",
506 "metavar": "PREF=VALUE",
508 "dest": "extraPrefs",
509 "help": "Defines an extra user preference.",
515 "action": "store_true",
517 "help": "Open the Browser Console.",
523 "action": "store_true",
525 "help": "Start the browser JS debugger before running the test. Implies --no-autorun.", # NOQA: E501
529 ["--jsdebugger-path"],
532 "dest": "jsdebuggerPath",
533 "help": "Path to a Firefox binary that will be used to run the toolbox. Should "
534 "be used together with --jsdebugger.",
538 ["--debug-on-failure"],
540 "action": "store_true",
542 "dest": "debugOnFailure",
543 "help": "Breaks execution and enters the JS debugger on a test failure. Should "
544 "be used together with --jsdebugger.",
550 "action": "store_false",
553 "help": "Run tests with electrolysis preferences and test filtering disabled.",
557 ["--enable-a11y-checks"],
559 "action": "store_true",
561 "dest": "a11y_checks",
562 "help": "Run tests with accessibility checks disabled.",
566 ["--enable-fission"],
568 "action": "store_true",
570 "help": "Run tests with fission (site isolation) enabled.",
574 ["--enable-xorigin-tests"],
576 "action": "store_true",
578 "dest": "xOriginTests",
579 "help": "Run tests in a cross origin iframe.",
583 ["--store-chrome-manifest"],
586 "help": "Destination path to write a copy of any chrome manifest "
587 "written by the harness.",
593 ["--jscov-dir-prefix"],
596 "help": "Directory to store per-test line coverage data as json "
597 "(browser-chrome only). To emit lcov formatted data, set "
598 "JS_CODE_COVERAGE_OUTPUT_DIR in the environment.",
606 "action": "store_true",
608 "help": "Run tests with DMD active.",
612 ["--dump-output-directory"],
615 "dest": "dumpOutputDirectory",
616 "help": "Specifies the directory in which to place dumped memory reports.",
620 ["--dump-about-memory-after-test"],
622 "action": "store_true",
624 "dest": "dumpAboutMemoryAfterTest",
625 "help": "Dump an about:memory log after each test in the directory specified "
626 "by --dump-output-directory.",
630 ["--dump-dmd-after-test"],
632 "action": "store_true",
634 "dest": "dumpDMDAfterTest",
635 "help": "Dump a DMD log (and an accompanying about:memory log) after each test. "
636 "These will be dumped into your default temp directory, NOT the directory "
637 "specified by --dump-output-directory. The logs are numbered by test, and "
638 "each test will include output that indicates the DMD output filename.",
642 ["--screenshot-on-fail"],
644 "action": "store_true",
646 "dest": "screenshotOnFail",
647 "help": "Take screenshots on all test failures. Set $MOZ_UPLOAD_DIR to a directory " # NOQA: E501
648 "for storing the screenshots.",
654 "action": "store_true",
657 "help": "Do not print test log lines unless a failure occurs.",
663 "action": "store_true",
666 "help": "Run tests in headless mode.",
674 "help": "Name of the pidfile to generate.",
679 ["--use-test-media-devices"],
681 "action": "store_true",
683 "dest": "useTestMediaDevices",
684 "help": "Use test media device drivers for media testing.",
691 "help": "Path to fake GMP plugin. Will be deduced from the binary if not passed.",
699 "default": None, # individual scripts will set a sane default
700 "help": "Absolute path to directory containing XRE (probably xulrunner).",
707 "dest": "symbolsPath",
709 "help": "Absolute path to directory containing breakpad symbols, or the URL of a "
710 "zip file containing symbols",
718 "help": "Debugger binary to run tests in. Program name or path.",
724 "dest": "debuggerArgs",
726 "help": "Arguments to pass to the debugger.",
733 "help": "Valgrind binary to run tests with. Program name or path.",
739 "dest": "valgrindArgs",
741 "help": "Comma-separated list of extra arguments to pass to Valgrind.",
745 ["--valgrind-supp-files"],
747 "dest": "valgrindSuppFiles",
749 "help": "Comma-separated list of suppression files to pass to Valgrind.",
753 ["--debugger-interactive"],
755 "action": "store_true",
756 "dest": "debuggerInteractive",
758 "help": "Prevents the test harness from redirecting stdout and stderr for "
759 "interactive debuggers.",
769 "help": "Filter out tests that don't have the given tag. Can be used multiple "
770 "times in which case the test must contain at least one of the given tags.",
777 "help": "host:port to use when connecting to Marionette",
781 ["--marionette-socket-timeout"],
784 "help": "Timeout while waiting to receive a message from the marionette server.",
789 ["--marionette-startup-timeout"],
792 "help": "Timeout while waiting for marionette server startup.",
797 ["--cleanup-crashes"],
799 "action": "store_true",
800 "dest": "cleanupCrashes",
802 "help": "Delete pending crash reports before running tests.",
807 ["--websocket-process-bridge-port"],
810 "dest": "websocket_process_bridge_port",
811 "help": "Port for websocket/process bridge. Default 8191.",
815 ["--failure-pattern-file"],
818 "dest": "failure_pattern_file",
819 "help": "File describes all failure patterns of the tests.",
824 ["--sandbox-read-whitelist"],
827 "dest": "sandboxReadWhitelist",
829 "help": "Path to add to the sandbox whitelist.",
836 "action": "store_true",
838 "help": "Run tests in verification mode: Run many times in different "
839 "ways, to see if there are intermittent failures.",
843 ["--verify-fission"],
845 "action": "store_true",
847 "help": "Run tests once without Fission, once with Fission",
851 ["--verify-max-time"],
855 "help": "Maximum time, in seconds, to run in --verify mode.",
859 ["--enable-webrender"],
861 "action": "store_true",
862 "dest": "enable_webrender",
864 "help": "Enable the WebRender compositor in Gecko.",
870 "action": "store_true",
873 "help": "Run the Firefox Profiler and get a performance profile of the "
874 "mochitest. This is useful to find performance issues, and also "
875 "to see what exactly the test is doing. To get profiler options run: "
876 "`MOZ_PROFILER_HELP=1 ./mach run`",
880 ["--profiler-save-only"],
882 "action": "store_true",
883 "dest": "profilerSaveOnly",
885 "help": "Run the Firefox Profiler and save it to the path specified by the "
886 "MOZ_UPLOAD_DIR environment variable.",
893 "dest": "runFailures",
895 "help": "Run fail-if/skip-if tests that match a keyword given.",
899 ["--timeout-as-pass"],
901 "action": "store_true",
902 "dest": "timeoutAsPass",
904 "help": "treat harness level timeouts as passing (used for quarantine jobs).",
910 "action": "store_true",
911 "dest": "crashAsPass",
913 "help": "treat harness level crashes as passing (used for quarantine jobs).",
919 # Bug 1065098 - The gmplugin process fails to produce a leak
920 # log for some reason.
921 "ignoreMissingLeaks": ["gmplugin"],
922 "extensionsToExclude": ["specialpowers"],
923 # Set server information on the args object
924 "webServer": "127.0.0.1",
925 "httpPort": DEFAULT_PORTS
["http"],
926 "sslPort": DEFAULT_PORTS
["https"],
927 "webSocketPort": "9988",
928 # The default websocket port is incorrect in mozprofile; it is
929 # set to the SSL proxy setting. See:
930 # see https://bugzilla.mozilla.org/show_bug.cgi?id=916517
931 # args.webSocketPort = DEFAULT_PORTS['ws']
934 def validate(self
, parser
, options
, context
):
935 """Validate generic options."""
937 # and android doesn't use 'app' the same way, so skip validation
938 if parser
.app
!= "android":
939 if options
.app
is None:
941 from mozbuild
.base
import BinaryNotFoundException
944 options
.app
= build_obj
.get_binary_path()
945 except BinaryNotFoundException
as e
:
946 print("{}\n\n{}\n".format(e
, e
.help()))
950 "could not find the application path, --appname must be specified"
952 elif options
.app
== "dist" and build_obj
:
953 options
.app
= build_obj
.get_binary_path(where
="staged-package")
955 options
.app
= self
.get_full_path(options
.app
, parser
.oldcwd
)
956 if not os
.path
.exists(options
.app
):
958 "Error: Path {} doesn't exist. Are you executing "
959 "$objdir/_tests/testing/mochitest/runtests.py?".format(options
.app
)
962 if options
.flavor
is None:
963 options
.flavor
= "plain"
965 for value
in ALL_FLAVORS
.values():
966 if options
.flavor
in value
["aliases"]:
967 options
.flavor
= value
["suite"]
970 if options
.gmp_path
is None and options
.app
and build_obj
:
971 # Need to fix the location of gmp_fake which might not be shipped in the binary
974 ("gmp-clearkey", "0.1"),
975 ("gmp-fakeopenh264", "1.0"),
977 options
.gmp_path
= os
.pathsep
.join(
978 os
.path
.join(build_obj
.bindir
, *p
) for p
in gmp_modules
981 if options
.totalChunks
is not None and options
.thisChunk
is None:
982 parser
.error("thisChunk must be specified when totalChunks is specified")
984 if options
.extra_mozinfo_json
:
985 if not os
.path
.isfile(options
.extra_mozinfo_json
):
987 "Error: couldn't find mozinfo.json at '%s'."
988 % options
.extra_mozinfo_json
991 options
.extra_mozinfo_json
= json
.load(open(options
.extra_mozinfo_json
))
993 if options
.totalChunks
:
994 if not 1 <= options
.thisChunk
<= options
.totalChunks
:
995 parser
.error("thisChunk must be between 1 and totalChunks")
997 if options
.chunkByDir
and options
.chunkByRuntime
:
998 parser
.error("can only use one of --chunk-by-dir or --chunk-by-runtime")
1000 if options
.xrePath
is None:
1001 # default xrePath to the app path if not provided
1002 # but only if an app path was explicitly provided
1003 if options
.app
!= parser
.get_default("app"):
1004 options
.xrePath
= os
.path
.dirname(options
.app
)
1006 options
.xrePath
= os
.path
.join(
1007 os
.path
.dirname(options
.xrePath
), "Resources"
1009 elif build_obj
is not None:
1010 # otherwise default to dist/bin
1011 options
.xrePath
= build_obj
.bindir
1014 "could not find xre directory, --xre-path must be specified"
1017 # allow relative paths
1019 options
.xrePath
= self
.get_full_path(options
.xrePath
, parser
.oldcwd
)
1021 if options
.profilePath
:
1022 options
.profilePath
= self
.get_full_path(options
.profilePath
, parser
.oldcwd
)
1024 if options
.utilityPath
:
1025 options
.utilityPath
= self
.get_full_path(options
.utilityPath
, parser
.oldcwd
)
1027 if options
.certPath
:
1028 options
.certPath
= self
.get_full_path(options
.certPath
, parser
.oldcwd
)
1030 options
.certPath
= os
.path
.join(
1031 build_obj
.topsrcdir
, "build", "pgo", "certs"
1034 if options
.symbolsPath
and len(urlparse(options
.symbolsPath
).scheme
) < 2:
1035 options
.symbolsPath
= self
.get_full_path(options
.symbolsPath
, parser
.oldcwd
)
1036 elif not options
.symbolsPath
and build_obj
:
1037 options
.symbolsPath
= os
.path
.join(
1038 build_obj
.distdir
, "crashreporter-symbols"
1041 if options
.debugOnFailure
and not options
.jsdebugger
:
1042 parser
.error("--debug-on-failure requires --jsdebugger.")
1044 if options
.jsdebuggerPath
and not options
.jsdebugger
:
1045 parser
.error("--jsdebugger-path requires --jsdebugger.")
1047 if options
.debuggerArgs
and not options
.debugger
:
1048 parser
.error("--debugger-args requires --debugger.")
1050 if options
.valgrind
or options
.debugger
:
1051 # valgrind and some debuggers may cause Gecko to start slowly. Make sure
1052 # marionette waits long enough to connect.
1053 options
.marionette_startup_timeout
= 900
1054 options
.marionette_socket_timeout
= 540
1056 if options
.store_chrome_manifest
:
1057 options
.store_chrome_manifest
= os
.path
.abspath(
1058 options
.store_chrome_manifest
1060 if not os
.path
.isdir(os
.path
.dirname(options
.store_chrome_manifest
)):
1062 "directory for %s does not exist as a destination to copy a "
1063 "chrome manifest." % options
.store_chrome_manifest
1066 if options
.jscov_dir_prefix
:
1067 options
.jscov_dir_prefix
= os
.path
.abspath(options
.jscov_dir_prefix
)
1068 if not os
.path
.isdir(options
.jscov_dir_prefix
):
1070 "directory %s does not exist as a destination for coverage "
1071 "data." % options
.jscov_dir_prefix
1074 if options
.testingModulesDir
is None:
1075 # Try to guess the testing modules directory.
1076 possible
= [os
.path
.join(here
, os
.path
.pardir
, "modules")]
1079 0, os
.path
.join(build_obj
.topobjdir
, "_tests", "modules")
1083 if os
.path
.isdir(p
):
1084 options
.testingModulesDir
= p
1087 # Paths to specialpowers and mochijar from the tests archive.
1088 options
.stagedAddons
= [
1089 os
.path
.join(here
, "extensions", "specialpowers"),
1090 os
.path
.join(here
, "mochijar"),
1093 objdir_xpi_stage
= os
.path
.join(build_obj
.distdir
, "xpi-stage")
1094 if os
.path
.isdir(objdir_xpi_stage
):
1095 options
.stagedAddons
= [
1096 os
.path
.join(objdir_xpi_stage
, "specialpowers"),
1097 os
.path
.join(objdir_xpi_stage
, "mochijar"),
1099 plugins_dir
= os
.path
.join(build_obj
.distdir
, "plugins")
1101 os
.path
.isdir(plugins_dir
)
1102 and plugins_dir
not in options
.extraProfileFiles
1104 options
.extraProfileFiles
.append(plugins_dir
)
1106 # Even if buildbot is updated, we still want this, as the path we pass in
1107 # to the app must be absolute and have proper slashes.
1108 if options
.testingModulesDir
is not None:
1109 options
.testingModulesDir
= os
.path
.normpath(options
.testingModulesDir
)
1111 if not os
.path
.isabs(options
.testingModulesDir
):
1112 options
.testingModulesDir
= os
.path
.abspath(options
.testingModulesDir
)
1114 if not os
.path
.isdir(options
.testingModulesDir
):
1116 "--testing-modules-dir not a directory: %s"
1117 % options
.testingModulesDir
1120 options
.testingModulesDir
= options
.testingModulesDir
.replace("\\", "/")
1121 if options
.testingModulesDir
[-1] != "/":
1122 options
.testingModulesDir
+= "/"
1124 if options
.runUntilFailure
:
1125 if not options
.repeat
:
1128 if options
.dumpOutputDirectory
is None:
1129 options
.dumpOutputDirectory
= tempfile
.gettempdir()
1131 if options
.dumpAboutMemoryAfterTest
or options
.dumpDMDAfterTest
:
1132 if not os
.path
.isdir(options
.dumpOutputDirectory
):
1134 "--dump-output-directory not a directory: %s"
1135 % options
.dumpOutputDirectory
1138 if options
.useTestMediaDevices
:
1139 if not mozinfo
.isLinux
:
1141 "--use-test-media-devices is only supported on Linux currently"
1144 gst01
= spawn
.find_executable("gst-launch-0.1")
1145 gst010
= spawn
.find_executable("gst-launch-0.10")
1146 gst10
= spawn
.find_executable("gst-launch-1.0")
1147 pactl
= spawn
.find_executable("pactl")
1149 if not (gst01
or gst10
or gst010
):
1151 "Missing gst-launch-{0.1,0.10,1.0}, required for "
1152 "--use-test-media-devices"
1157 "Missing binary pactl required for " "--use-test-media-devices"
1160 # The a11y and chrome flavors can't run with e10s.
1161 if options
.flavor
in ("a11y", "chrome") and options
.e10s
:
1163 "mochitest-{} does not support e10s, try again with "
1164 "--disable-e10s.".format(options
.flavor
)
1167 if options
.enable_fission
:
1168 options
.extraPrefs
.append("fission.autostart=true")
1170 options
.leakThresholds
= {
1171 "default": options
.defaultLeakThreshold
,
1172 "tab": options
.defaultLeakThreshold
,
1173 "forkserver": options
.defaultLeakThreshold
,
1174 # GMP rarely gets a log, but when it does, it leaks a little.
1178 # See the dependencies of bug 1401764.
1180 options
.leakThresholds
["tab"] = 1000
1182 # XXX We can't normalize test_paths in the non build_obj case here,
1183 # because testRoot depends on the flavor, which is determined by the
1184 # mach command and therefore not finalized yet. Conversely, test paths
1185 # need to be normalized here for the mach case.
1186 if options
.test_paths
and build_obj
:
1187 # Normalize test paths so they are relative to test root
1188 options
.test_paths
= [
1189 build_obj
._wrap
_path
_argument
(p
).relpath() for p
in options
.test_paths
1195 class AndroidArguments(ArgumentContainer
):
1196 """Android specific arguments."""
1202 "action": "store_true",
1204 "help": "Skip the installation of the APK.",
1210 "dest": "deviceSerial",
1211 "help": "adb serial number of remote device. This is required "
1212 "when more than one device is connected to the host. "
1213 "Use 'adb devices' to see connected devices.",
1222 "help": "Path to adb binary.",
1227 ["--remote-webserver"],
1229 "dest": "remoteWebServer",
1231 "help": "IP address of the remote web server.",
1238 "default": DEFAULT_PORTS
["http"],
1239 "help": "http port of the remote web server.",
1247 "default": DEFAULT_PORTS
["https"],
1248 "help": "ssl port of the remote web server.",
1253 ["--remoteTestRoot"],
1255 "dest": "remoteTestRoot",
1257 "help": "Remote directory to use as test root "
1258 "(eg. /data/local/tmp/test_root).",
1263 ["--enable-coverage"],
1265 "action": "store_true",
1267 "help": "Enable collecting code coverage information when running "
1272 ["--coverage-output-dir"],
1276 "help": "When using --enable-java-coverage, save the code coverage report "
1277 "files to this directory.",
1283 # we don't want to exclude specialpowers on android just yet
1284 "extensionsToExclude": [],
1285 # mochijar doesn't get installed via marionette on android
1286 "extensionsToInstall": [os
.path
.join(here
, "mochijar")],
1287 "logFile": "mochitest.log",
1288 "utilityPath": None,
1291 def validate(self
, parser
, options
, context
):
1292 """Validate android options."""
1295 options
.log_mach
= "-"
1297 objdir_xpi_stage
= os
.path
.join(build_obj
.distdir
, "xpi-stage")
1298 if os
.path
.isdir(objdir_xpi_stage
):
1299 options
.extensionsToInstall
= [
1300 os
.path
.join(objdir_xpi_stage
, "mochijar"),
1301 os
.path
.join(objdir_xpi_stage
, "specialpowers"),
1304 if options
.remoteWebServer
is None:
1305 options
.remoteWebServer
= moznetwork
.get_ip()
1307 options
.webServer
= options
.remoteWebServer
1309 if options
.app
is None:
1310 options
.app
= "org.mozilla.geckoview.test"
1312 if build_obj
and "MOZ_HOST_BIN" in os
.environ
:
1313 options
.xrePath
= os
.environ
["MOZ_HOST_BIN"]
1315 # Only reset the xrePath if it wasn't provided
1316 if options
.xrePath
is None:
1317 options
.xrePath
= options
.utilityPath
1320 options
.topsrcdir
= build_obj
.topsrcdir
1322 if options
.pidFile
!= "":
1323 f
= open(options
.pidFile
, "w")
1324 f
.write("%s" % os
.getpid())
1327 if options
.coverage_output_dir
and not options
.enable_coverage
:
1328 parser
.error("--coverage-output-dir must be used with --enable-coverage")
1329 if options
.enable_coverage
:
1330 if not options
.autorun
:
1331 parser
.error("--enable-coverage cannot be used with --no-autorun")
1332 if not options
.coverage_output_dir
:
1334 "--coverage-output-dir must be specified when using --enable-coverage"
1336 parent_dir
= os
.path
.dirname(options
.coverage_output_dir
)
1337 if not os
.path
.isdir(options
.coverage_output_dir
):
1339 "The directory for the coverage output does not exist: %s"
1343 # allow us to keep original application around for cleanup while
1345 options
.remoteappname
= options
.app
1350 "generic": [MochitestArguments
],
1351 "android": [MochitestArguments
, AndroidArguments
],
1355 class MochitestArgumentParser(ArgumentParser
):
1356 """%(prog)s [options] [test paths]"""
1361 def __init__(self
, app
=None, **kwargs
):
1362 ArgumentParser
.__init
__(
1363 self
, usage
=self
.__doc
__, conflict_handler
="resolve", **kwargs
1366 self
.oldcwd
= os
.getcwd()
1368 if not self
.app
and build_obj
:
1369 if conditions
.is_android(build_obj
):
1370 self
.app
= "android"
1372 # platform can't be determined and app wasn't specified explicitly,
1373 # so just use generic arguments and hope for the best
1374 self
.app
= "generic"
1376 if self
.app
not in container_map
:
1378 "Unrecognized app '{}'! Must be one of: {}".format(
1379 self
.app
, ", ".join(container_map
.keys())
1384 for container
in self
.containers
:
1385 defaults
.update(container
.defaults
)
1386 group
= self
.add_argument_group(
1387 container
.__class
__.__name
__, container
.__doc
__
1390 for cli
, kwargs
in container
.args
:
1391 # Allocate new lists so references to original don't get mutated.
1392 # allowing multiple uses within a single process.
1393 if "default" in kwargs
and isinstance(kwargs
["default"], list):
1394 kwargs
["default"] = []
1396 if "suppress" in kwargs
:
1397 if kwargs
["suppress"]:
1398 kwargs
["help"] = SUPPRESS
1399 del kwargs
["suppress"]
1401 group
.add_argument(*cli
, **kwargs
)
1403 self
.set_defaults(**defaults
)
1404 mozlog
.commandline
.add_logging_group(self
)
1407 def containers(self
):
1408 if self
._containers
:
1409 return self
._containers
1411 containers
= container_map
[self
.app
]
1412 self
._containers
= [c() for c
in containers
]
1413 return self
._containers
1415 def validate(self
, args
):
1416 for container
in self
.containers
:
1417 args
= container
.validate(self
, args
, self
.context
)