1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/testing/mochitest/mach_commands.py Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,706 @@ 1.4 +# This Source Code Form is subject to the terms of the Mozilla Public 1.5 +# License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 +# file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.7 + 1.8 +from __future__ import unicode_literals 1.9 + 1.10 +import logging 1.11 +import mozpack.path 1.12 +import os 1.13 +import platform 1.14 +import sys 1.15 +import warnings 1.16 +import which 1.17 + 1.18 +from mozbuild.base import ( 1.19 + MachCommandBase, 1.20 + MachCommandConditions as conditions, 1.21 + MozbuildObject, 1.22 +) 1.23 + 1.24 +from mach.decorators import ( 1.25 + CommandArgument, 1.26 + CommandProvider, 1.27 + Command, 1.28 +) 1.29 + 1.30 +from mach.logging import StructuredHumanFormatter 1.31 + 1.32 +ADB_NOT_FOUND = ''' 1.33 +The %s command requires the adb binary to be on your path. 1.34 + 1.35 +If you have a B2G build, this can be found in 1.36 +'%s/out/host/<platform>/bin'. 1.37 +'''.lstrip() 1.38 + 1.39 +GAIA_PROFILE_NOT_FOUND = ''' 1.40 +The %s command requires a non-debug gaia profile. Either pass in --profile, 1.41 +or set the GAIA_PROFILE environment variable. 1.42 + 1.43 +If you do not have a non-debug gaia profile, you can build one: 1.44 + $ git clone https://github.com/mozilla-b2g/gaia 1.45 + $ cd gaia 1.46 + $ make 1.47 + 1.48 +The profile should be generated in a directory called 'profile'. 1.49 +'''.lstrip() 1.50 + 1.51 +GAIA_PROFILE_IS_DEBUG = ''' 1.52 +The %s command requires a non-debug gaia profile. The specified profile, 1.53 +%s, is a debug profile. 1.54 + 1.55 +If you do not have a non-debug gaia profile, you can build one: 1.56 + $ git clone https://github.com/mozilla-b2g/gaia 1.57 + $ cd gaia 1.58 + $ make 1.59 + 1.60 +The profile should be generated in a directory called 'profile'. 1.61 +'''.lstrip() 1.62 + 1.63 + 1.64 +class UnexpectedFilter(logging.Filter): 1.65 + def filter(self, record): 1.66 + msg = getattr(record, 'params', {}).get('msg', '') 1.67 + return 'TEST-UNEXPECTED-' in msg 1.68 + 1.69 + 1.70 +class MochitestRunner(MozbuildObject): 1.71 + """Easily run mochitests. 1.72 + 1.73 + This currently contains just the basics for running mochitests. We may want 1.74 + to hook up result parsing, etc. 1.75 + """ 1.76 + 1.77 + def get_webapp_runtime_path(self): 1.78 + import mozinfo 1.79 + appname = 'webapprt-stub' + mozinfo.info.get('bin_suffix', '') 1.80 + if sys.platform.startswith('darwin'): 1.81 + appname = os.path.join(self.distdir, self.substs['MOZ_MACBUNDLE_NAME'], 1.82 + 'Contents', 'MacOS', appname) 1.83 + else: 1.84 + appname = os.path.join(self.distdir, 'bin', appname) 1.85 + return appname 1.86 + 1.87 + def __init__(self, *args, **kwargs): 1.88 + MozbuildObject.__init__(self, *args, **kwargs) 1.89 + 1.90 + # TODO Bug 794506 remove once mach integrates with virtualenv. 1.91 + build_path = os.path.join(self.topobjdir, 'build') 1.92 + if build_path not in sys.path: 1.93 + sys.path.append(build_path) 1.94 + 1.95 + self.tests_dir = os.path.join(self.topobjdir, '_tests') 1.96 + self.mochitest_dir = os.path.join(self.tests_dir, 'testing', 'mochitest') 1.97 + self.bin_dir = os.path.join(self.topobjdir, 'dist', 'bin') 1.98 + 1.99 + def run_b2g_test(self, test_paths=None, b2g_home=None, xre_path=None, 1.100 + total_chunks=None, this_chunk=None, no_window=None, 1.101 + **kwargs): 1.102 + """Runs a b2g mochitest. 1.103 + 1.104 + test_paths is an enumerable of paths to tests. It can be a relative path 1.105 + from the top source directory, an absolute filename, or a directory 1.106 + containing test files. 1.107 + """ 1.108 + # Need to call relpath before os.chdir() below. 1.109 + test_path = '' 1.110 + if test_paths: 1.111 + if len(test_paths) > 1: 1.112 + print('Warning: Only the first test path will be used.') 1.113 + test_path = self._wrap_path_argument(test_paths[0]).relpath() 1.114 + 1.115 + # TODO without os.chdir, chained imports fail below 1.116 + os.chdir(self.mochitest_dir) 1.117 + 1.118 + # The imp module can spew warnings if the modules below have 1.119 + # already been imported, ignore them. 1.120 + with warnings.catch_warnings(): 1.121 + warnings.simplefilter('ignore') 1.122 + 1.123 + import imp 1.124 + path = os.path.join(self.mochitest_dir, 'runtestsb2g.py') 1.125 + with open(path, 'r') as fh: 1.126 + imp.load_module('mochitest', fh, path, 1.127 + ('.py', 'r', imp.PY_SOURCE)) 1.128 + 1.129 + import mochitest 1.130 + from mochitest_options import B2GOptions 1.131 + 1.132 + parser = B2GOptions() 1.133 + options = parser.parse_args([])[0] 1.134 + 1.135 + test_path_dir = False; 1.136 + if test_path: 1.137 + test_root_file = mozpack.path.join(self.mochitest_dir, 'tests', test_path) 1.138 + if not os.path.exists(test_root_file): 1.139 + print('Specified test path does not exist: %s' % test_root_file) 1.140 + return 1 1.141 + if os.path.isdir(test_root_file): 1.142 + test_path_dir = True; 1.143 + options.testPath = test_path 1.144 + 1.145 + for k, v in kwargs.iteritems(): 1.146 + setattr(options, k, v) 1.147 + options.noWindow = no_window 1.148 + options.totalChunks = total_chunks 1.149 + options.thisChunk = this_chunk 1.150 + 1.151 + options.symbolsPath = os.path.join(self.distdir, 'crashreporter-symbols') 1.152 + 1.153 + options.consoleLevel = 'INFO' 1.154 + if conditions.is_b2g_desktop(self): 1.155 + 1.156 + options.profile = options.profile or os.environ.get('GAIA_PROFILE') 1.157 + if not options.profile: 1.158 + print(GAIA_PROFILE_NOT_FOUND % 'mochitest-b2g-desktop') 1.159 + return 1 1.160 + 1.161 + if os.path.isfile(os.path.join(options.profile, 'extensions', \ 1.162 + 'httpd@gaiamobile.org')): 1.163 + print(GAIA_PROFILE_IS_DEBUG % ('mochitest-b2g-desktop', 1.164 + options.profile)) 1.165 + return 1 1.166 + 1.167 + options.desktop = True 1.168 + options.app = self.get_binary_path() 1.169 + if not options.app.endswith('-bin'): 1.170 + options.app = '%s-bin' % options.app 1.171 + if not os.path.isfile(options.app): 1.172 + options.app = options.app[:-len('-bin')] 1.173 + 1.174 + return mochitest.run_desktop_mochitests(parser, options) 1.175 + 1.176 + try: 1.177 + which.which('adb') 1.178 + except which.WhichError: 1.179 + # TODO Find adb automatically if it isn't on the path 1.180 + print(ADB_NOT_FOUND % ('mochitest-remote', b2g_home)) 1.181 + return 1 1.182 + 1.183 + options.b2gPath = b2g_home 1.184 + options.logcat_dir = self.mochitest_dir 1.185 + options.httpdPath = self.mochitest_dir 1.186 + options.xrePath = xre_path 1.187 + return mochitest.run_remote_mochitests(parser, options) 1.188 + 1.189 + def run_desktop_test(self, context, suite=None, test_paths=None, debugger=None, 1.190 + debugger_args=None, slowscript=False, screenshot_on_fail = False, shuffle=False, keep_open=False, 1.191 + rerun_failures=False, no_autorun=False, repeat=0, run_until_failure=False, 1.192 + slow=False, chunk_by_dir=0, total_chunks=None, this_chunk=None, 1.193 + jsdebugger=False, debug_on_failure=False, start_at=None, end_at=None, 1.194 + e10s=False, dmd=False, dump_output_directory=None, 1.195 + dump_about_memory_after_test=False, dump_dmd_after_test=False, 1.196 + install_extension=None, quiet=False, environment=[], **kwargs): 1.197 + """Runs a mochitest. 1.198 + 1.199 + test_paths are path to tests. They can be a relative path from the 1.200 + top source directory, an absolute filename, or a directory containing 1.201 + test files. 1.202 + 1.203 + suite is the type of mochitest to run. It can be one of ('plain', 1.204 + 'chrome', 'browser', 'metro', 'a11y'). 1.205 + 1.206 + debugger is a program name or path to a binary (presumably a debugger) 1.207 + to run the test in. e.g. 'gdb' 1.208 + 1.209 + debugger_args are the arguments passed to the debugger. 1.210 + 1.211 + slowscript is true if the user has requested the SIGSEGV mechanism of 1.212 + invoking the slow script dialog. 1.213 + 1.214 + shuffle is whether test order should be shuffled (defaults to false). 1.215 + 1.216 + keep_open denotes whether to keep the browser open after tests 1.217 + complete. 1.218 + """ 1.219 + if rerun_failures and test_paths: 1.220 + print('Cannot specify both --rerun-failures and a test path.') 1.221 + return 1 1.222 + 1.223 + # Need to call relpath before os.chdir() below. 1.224 + if test_paths: 1.225 + test_paths = [self._wrap_path_argument(p).relpath() for p in test_paths] 1.226 + 1.227 + failure_file_path = os.path.join(self.statedir, 'mochitest_failures.json') 1.228 + 1.229 + if rerun_failures and not os.path.exists(failure_file_path): 1.230 + print('No failure file present. Did you run mochitests before?') 1.231 + return 1 1.232 + 1.233 + from StringIO import StringIO 1.234 + 1.235 + # runtests.py is ambiguous, so we load the file/module manually. 1.236 + if 'mochitest' not in sys.modules: 1.237 + import imp 1.238 + path = os.path.join(self.mochitest_dir, 'runtests.py') 1.239 + with open(path, 'r') as fh: 1.240 + imp.load_module('mochitest', fh, path, 1.241 + ('.py', 'r', imp.PY_SOURCE)) 1.242 + 1.243 + import mozinfo 1.244 + import mochitest 1.245 + from manifestparser import TestManifest 1.246 + from mozbuild.testing import TestResolver 1.247 + 1.248 + # This is required to make other components happy. Sad, isn't it? 1.249 + os.chdir(self.topobjdir) 1.250 + 1.251 + # Automation installs its own stream handler to stdout. Since we want 1.252 + # all logging to go through us, we just remove their handler. 1.253 + remove_handlers = [l for l in logging.getLogger().handlers 1.254 + if isinstance(l, logging.StreamHandler)] 1.255 + for handler in remove_handlers: 1.256 + logging.getLogger().removeHandler(handler) 1.257 + 1.258 + runner = mochitest.Mochitest() 1.259 + 1.260 + opts = mochitest.MochitestOptions() 1.261 + options, args = opts.parse_args([]) 1.262 + 1.263 + options.subsuite = '' 1.264 + flavor = suite 1.265 + 1.266 + # Need to set the suite options before verifyOptions below. 1.267 + if suite == 'plain': 1.268 + # Don't need additional options for plain. 1.269 + flavor = 'mochitest' 1.270 + elif suite == 'chrome': 1.271 + options.chrome = True 1.272 + elif suite == 'browser': 1.273 + options.browserChrome = True 1.274 + flavor = 'browser-chrome' 1.275 + elif suite == 'devtools': 1.276 + options.browserChrome = True 1.277 + options.subsuite = 'devtools' 1.278 + elif suite == 'metro': 1.279 + options.immersiveMode = True 1.280 + options.browserChrome = True 1.281 + elif suite == 'a11y': 1.282 + options.a11y = True 1.283 + elif suite == 'webapprt-content': 1.284 + options.webapprtContent = True 1.285 + options.app = self.get_webapp_runtime_path() 1.286 + elif suite == 'webapprt-chrome': 1.287 + options.webapprtChrome = True 1.288 + options.app = self.get_webapp_runtime_path() 1.289 + options.browserArgs.append("-test-mode") 1.290 + else: 1.291 + raise Exception('None or unrecognized mochitest suite type.') 1.292 + 1.293 + if dmd: 1.294 + options.dmdPath = self.bin_dir 1.295 + 1.296 + options.autorun = not no_autorun 1.297 + options.closeWhenDone = not keep_open 1.298 + options.slowscript = slowscript 1.299 + options.screenshotOnFail = screenshot_on_fail 1.300 + options.shuffle = shuffle 1.301 + options.consoleLevel = 'INFO' 1.302 + options.repeat = repeat 1.303 + options.runUntilFailure = run_until_failure 1.304 + options.runSlower = slow 1.305 + options.testingModulesDir = os.path.join(self.tests_dir, 'modules') 1.306 + options.extraProfileFiles.append(os.path.join(self.distdir, 'plugins')) 1.307 + options.symbolsPath = os.path.join(self.distdir, 'crashreporter-symbols') 1.308 + options.chunkByDir = chunk_by_dir 1.309 + options.totalChunks = total_chunks 1.310 + options.thisChunk = this_chunk 1.311 + options.jsdebugger = jsdebugger 1.312 + options.debugOnFailure = debug_on_failure 1.313 + options.startAt = start_at 1.314 + options.endAt = end_at 1.315 + options.e10s = e10s 1.316 + options.dumpAboutMemoryAfterTest = dump_about_memory_after_test 1.317 + options.dumpDMDAfterTest = dump_dmd_after_test 1.318 + options.dumpOutputDirectory = dump_output_directory 1.319 + options.quiet = quiet 1.320 + options.environment = environment 1.321 + 1.322 + options.failureFile = failure_file_path 1.323 + if install_extension != None: 1.324 + options.extensionsToInstall = [os.path.join(self.topsrcdir,install_extension)] 1.325 + 1.326 + for k, v in kwargs.iteritems(): 1.327 + setattr(options, k, v) 1.328 + 1.329 + if test_paths: 1.330 + resolver = self._spawn(TestResolver) 1.331 + 1.332 + tests = list(resolver.resolve_tests(paths=test_paths, flavor=flavor, 1.333 + cwd=context.cwd)) 1.334 + 1.335 + if not tests: 1.336 + print('No tests could be found in the path specified. Please ' 1.337 + 'specify a path that is a test file or is a directory ' 1.338 + 'containing tests.') 1.339 + return 1 1.340 + 1.341 + manifest = TestManifest() 1.342 + manifest.tests.extend(tests) 1.343 + 1.344 + options.manifestFile = manifest 1.345 + 1.346 + if rerun_failures: 1.347 + options.testManifest = failure_file_path 1.348 + 1.349 + if debugger: 1.350 + options.debugger = debugger 1.351 + 1.352 + if debugger_args: 1.353 + if options.debugger == None: 1.354 + print("--debugger-args passed, but no debugger specified.") 1.355 + return 1 1.356 + options.debuggerArgs = debugger_args 1.357 + 1.358 + options = opts.verifyOptions(options, runner) 1.359 + 1.360 + if options is None: 1.361 + raise Exception('mochitest option validator failed.') 1.362 + 1.363 + # We need this to enable colorization of output. 1.364 + self.log_manager.enable_unstructured() 1.365 + 1.366 + # Output processing is a little funky here. The old make targets 1.367 + # grepped the log output from TEST-UNEXPECTED-* and printed these lines 1.368 + # after test execution. Ideally the test runner would expose a Python 1.369 + # API for obtaining test results and we could just format failures 1.370 + # appropriately. Unfortunately, it doesn't yet do that. So, we capture 1.371 + # all output to a buffer then "grep" the buffer after test execution. 1.372 + # Bug 858197 tracks a Python API that would facilitate this. 1.373 + test_output = StringIO() 1.374 + handler = logging.StreamHandler(test_output) 1.375 + handler.addFilter(UnexpectedFilter()) 1.376 + handler.setFormatter(StructuredHumanFormatter(0, write_times=False)) 1.377 + logging.getLogger().addHandler(handler) 1.378 + 1.379 + result = runner.runTests(options) 1.380 + 1.381 + # Need to remove our buffering handler before we echo failures or else 1.382 + # it will catch them again! 1.383 + logging.getLogger().removeHandler(handler) 1.384 + self.log_manager.disable_unstructured() 1.385 + 1.386 + if test_output.getvalue(): 1.387 + result = 1 1.388 + for line in test_output.getvalue().splitlines(): 1.389 + self.log(logging.INFO, 'unexpected', {'msg': line}, '{msg}') 1.390 + 1.391 + return result 1.392 + 1.393 + 1.394 +def MochitestCommand(func): 1.395 + """Decorator that adds shared command arguments to mochitest commands.""" 1.396 + 1.397 + # This employs light Python magic. Keep in mind a decorator is just a 1.398 + # function that takes a function, does something with it, then returns a 1.399 + # (modified) function. Here, we chain decorators onto the passed in 1.400 + # function. 1.401 + 1.402 + debugger = CommandArgument('--debugger', '-d', metavar='DEBUGGER', 1.403 + help='Debugger binary to run test in. Program name or path.') 1.404 + func = debugger(func) 1.405 + 1.406 + debugger_args = CommandArgument('--debugger-args', 1.407 + metavar='DEBUGGER_ARGS', help='Arguments to pass to the debugger.') 1.408 + func = debugger_args(func) 1.409 + 1.410 + # Bug 933807 introduced JS_DISABLE_SLOW_SCRIPT_SIGNALS to avoid clever 1.411 + # segfaults induced by the slow-script-detecting logic for Ion/Odin JITted 1.412 + # code. If we don't pass this, the user will need to periodically type 1.413 + # "continue" to (safely) resume execution. There are ways to implement 1.414 + # automatic resuming; see the bug. 1.415 + slowscript = CommandArgument('--slowscript', action='store_true', 1.416 + help='Do not set the JS_DISABLE_SLOW_SCRIPT_SIGNALS env variable; when not set, recoverable but misleading SIGSEGV instances may occur in Ion/Odin JIT code') 1.417 + func = slowscript(func) 1.418 + 1.419 + screenshot_on_fail = CommandArgument('--screenshot-on-fail', action='store_true', 1.420 + help='Take screenshots on all test failures. Set $MOZ_UPLOAD_DIR to a directory for storing the screenshots.') 1.421 + func = screenshot_on_fail(func) 1.422 + 1.423 + shuffle = CommandArgument('--shuffle', action='store_true', 1.424 + help='Shuffle execution order.') 1.425 + func = shuffle(func) 1.426 + 1.427 + keep_open = CommandArgument('--keep-open', action='store_true', 1.428 + help='Keep the browser open after tests complete.') 1.429 + func = keep_open(func) 1.430 + 1.431 + rerun = CommandArgument('--rerun-failures', action='store_true', 1.432 + help='Run only the tests that failed during the last test run.') 1.433 + func = rerun(func) 1.434 + 1.435 + autorun = CommandArgument('--no-autorun', action='store_true', 1.436 + help='Do not starting running tests automatically.') 1.437 + func = autorun(func) 1.438 + 1.439 + repeat = CommandArgument('--repeat', type=int, default=0, 1.440 + help='Repeat the test the given number of times.') 1.441 + func = repeat(func) 1.442 + 1.443 + runUntilFailure = CommandArgument("--run-until-failure", action='store_true', 1.444 + help='Run tests repeatedly and stops on the first time a test fails. ' \ 1.445 + 'Default cap is 30 runs, which can be overwritten ' \ 1.446 + 'with the --repeat parameter.') 1.447 + func = runUntilFailure(func) 1.448 + 1.449 + slow = CommandArgument('--slow', action='store_true', 1.450 + help='Delay execution between tests.') 1.451 + func = slow(func) 1.452 + 1.453 + end_at = CommandArgument('--end-at', type=str, 1.454 + help='Stop running the test sequence at this test.') 1.455 + func = end_at(func) 1.456 + 1.457 + start_at = CommandArgument('--start-at', type=str, 1.458 + help='Start running the test sequence at this test.') 1.459 + func = start_at(func) 1.460 + 1.461 + chunk_dir = CommandArgument('--chunk-by-dir', type=int, 1.462 + help='Group tests together in chunks by this many top directories.') 1.463 + func = chunk_dir(func) 1.464 + 1.465 + chunk_total = CommandArgument('--total-chunks', type=int, 1.466 + help='Total number of chunks to split tests into.') 1.467 + func = chunk_total(func) 1.468 + 1.469 + this_chunk = CommandArgument('--this-chunk', type=int, 1.470 + help='If running tests by chunks, the number of the chunk to run.') 1.471 + func = this_chunk(func) 1.472 + 1.473 + hide_subtests = CommandArgument('--hide-subtests', action='store_true', 1.474 + help='If specified, will only log subtest results on failure or timeout.') 1.475 + func = hide_subtests(func) 1.476 + 1.477 + debug_on_failure = CommandArgument('--debug-on-failure', action='store_true', 1.478 + help='Breaks execution and enters the JS debugger on a test failure. ' \ 1.479 + 'Should be used together with --jsdebugger.') 1.480 + func = debug_on_failure(func) 1.481 + 1.482 + jsdebugger = CommandArgument('--jsdebugger', action='store_true', 1.483 + help='Start the browser JS debugger before running the test. Implies --no-autorun.') 1.484 + func = jsdebugger(func) 1.485 + 1.486 + this_chunk = CommandArgument('--e10s', action='store_true', 1.487 + help='Run tests with electrolysis preferences and test filtering enabled.') 1.488 + func = this_chunk(func) 1.489 + 1.490 + dmd = CommandArgument('--dmd', action='store_true', 1.491 + help='Run tests with DMD active.') 1.492 + func = dmd(func) 1.493 + 1.494 + dumpAboutMemory = CommandArgument('--dump-about-memory-after-test', action='store_true', 1.495 + help='Dump an about:memory log after every test.') 1.496 + func = dumpAboutMemory(func) 1.497 + 1.498 + dumpDMD = CommandArgument('--dump-dmd-after-test', action='store_true', 1.499 + help='Dump a DMD log after every test.') 1.500 + func = dumpDMD(func) 1.501 + 1.502 + dumpOutputDirectory = CommandArgument('--dump-output-directory', action='store', 1.503 + help='Specifies the directory in which to place dumped memory reports.') 1.504 + func = dumpOutputDirectory(func) 1.505 + 1.506 + path = CommandArgument('test_paths', default=None, nargs='*', 1.507 + metavar='TEST', 1.508 + help='Test to run. Can be specified as a single file, a ' \ 1.509 + 'directory, or omitted. If omitted, the entire test suite is ' \ 1.510 + 'executed.') 1.511 + func = path(func) 1.512 + 1.513 + install_extension = CommandArgument('--install-extension', 1.514 + help='Install given extension before running selected tests. ' \ 1.515 + 'Parameter is a path to xpi file.') 1.516 + func = install_extension(func) 1.517 + 1.518 + quiet = CommandArgument('--quiet', default=False, action='store_true', 1.519 + help='Do not print test log lines unless a failure occurs.') 1.520 + func = quiet(func) 1.521 + 1.522 + setenv = CommandArgument('--setenv', default=[], action='append', 1.523 + metavar='NAME=VALUE', dest='environment', 1.524 + help="Sets the given variable in the application's environment") 1.525 + func = setenv(func) 1.526 + 1.527 + return func 1.528 + 1.529 +def B2GCommand(func): 1.530 + """Decorator that adds shared command arguments to b2g mochitest commands.""" 1.531 + 1.532 + busybox = CommandArgument('--busybox', default=None, 1.533 + help='Path to busybox binary to install on device') 1.534 + func = busybox(func) 1.535 + 1.536 + logcatdir = CommandArgument('--logcat-dir', default=None, 1.537 + help='directory to store logcat dump files') 1.538 + func = logcatdir(func) 1.539 + 1.540 + profile = CommandArgument('--profile', default=None, 1.541 + help='for desktop testing, the path to the \ 1.542 + gaia profile to use') 1.543 + func = profile(func) 1.544 + 1.545 + geckopath = CommandArgument('--gecko-path', default=None, 1.546 + help='the path to a gecko distribution that should \ 1.547 + be installed on the emulator prior to test') 1.548 + func = geckopath(func) 1.549 + 1.550 + nowindow = CommandArgument('--no-window', action='store_true', default=False, 1.551 + help='Pass --no-window to the emulator') 1.552 + func = nowindow(func) 1.553 + 1.554 + sdcard = CommandArgument('--sdcard', default="10MB", 1.555 + help='Define size of sdcard: 1MB, 50MB...etc') 1.556 + func = sdcard(func) 1.557 + 1.558 + emulator = CommandArgument('--emulator', default='arm', 1.559 + help='Architecture of emulator to use: x86 or arm') 1.560 + func = emulator(func) 1.561 + 1.562 + marionette = CommandArgument('--marionette', default=None, 1.563 + help='host:port to use when connecting to Marionette') 1.564 + func = marionette(func) 1.565 + 1.566 + chunk_total = CommandArgument('--total-chunks', type=int, 1.567 + help='Total number of chunks to split tests into.') 1.568 + func = chunk_total(func) 1.569 + 1.570 + this_chunk = CommandArgument('--this-chunk', type=int, 1.571 + help='If running tests by chunks, the number of the chunk to run.') 1.572 + func = this_chunk(func) 1.573 + 1.574 + hide_subtests = CommandArgument('--hide-subtests', action='store_true', 1.575 + help='If specified, will only log subtest results on failure or timeout.') 1.576 + func = hide_subtests(func) 1.577 + 1.578 + path = CommandArgument('test_paths', default=None, nargs='*', 1.579 + metavar='TEST', 1.580 + help='Test to run. Can be specified as a single file, a ' \ 1.581 + 'directory, or omitted. If omitted, the entire test suite is ' \ 1.582 + 'executed.') 1.583 + func = path(func) 1.584 + 1.585 + return func 1.586 + 1.587 + 1.588 + 1.589 +@CommandProvider 1.590 +class MachCommands(MachCommandBase): 1.591 + @Command('mochitest-plain', category='testing', 1.592 + conditions=[conditions.is_firefox], 1.593 + description='Run a plain mochitest.') 1.594 + @MochitestCommand 1.595 + def run_mochitest_plain(self, test_paths, **kwargs): 1.596 + return self.run_mochitest(test_paths, 'plain', **kwargs) 1.597 + 1.598 + @Command('mochitest-chrome', category='testing', 1.599 + conditions=[conditions.is_firefox], 1.600 + description='Run a chrome mochitest.') 1.601 + @MochitestCommand 1.602 + def run_mochitest_chrome(self, test_paths, **kwargs): 1.603 + return self.run_mochitest(test_paths, 'chrome', **kwargs) 1.604 + 1.605 + @Command('mochitest-browser', category='testing', 1.606 + conditions=[conditions.is_firefox], 1.607 + description='Run a mochitest with browser chrome.') 1.608 + @MochitestCommand 1.609 + def run_mochitest_browser(self, test_paths, **kwargs): 1.610 + return self.run_mochitest(test_paths, 'browser', **kwargs) 1.611 + 1.612 + @Command('mochitest-devtools', category='testing', 1.613 + conditions=[conditions.is_firefox], 1.614 + description='Run a devtools mochitest with browser chrome.') 1.615 + @MochitestCommand 1.616 + def run_mochitest_devtools(self, test_paths, **kwargs): 1.617 + return self.run_mochitest(test_paths, 'devtools', **kwargs) 1.618 + 1.619 + @Command('mochitest-metro', category='testing', 1.620 + conditions=[conditions.is_firefox], 1.621 + description='Run a mochitest with metro browser chrome.') 1.622 + @MochitestCommand 1.623 + def run_mochitest_metro(self, test_paths, **kwargs): 1.624 + return self.run_mochitest(test_paths, 'metro', **kwargs) 1.625 + 1.626 + @Command('mochitest-a11y', category='testing', 1.627 + conditions=[conditions.is_firefox], 1.628 + description='Run an a11y mochitest.') 1.629 + @MochitestCommand 1.630 + def run_mochitest_a11y(self, test_paths, **kwargs): 1.631 + return self.run_mochitest(test_paths, 'a11y', **kwargs) 1.632 + 1.633 + @Command('webapprt-test-chrome', category='testing', 1.634 + conditions=[conditions.is_firefox], 1.635 + description='Run a webapprt chrome mochitest.') 1.636 + @MochitestCommand 1.637 + def run_mochitest_webapprt_chrome(self, test_paths, **kwargs): 1.638 + return self.run_mochitest(test_paths, 'webapprt-chrome', **kwargs) 1.639 + 1.640 + @Command('webapprt-test-content', category='testing', 1.641 + conditions=[conditions.is_firefox], 1.642 + description='Run a webapprt content mochitest.') 1.643 + @MochitestCommand 1.644 + def run_mochitest_webapprt_content(self, test_paths, **kwargs): 1.645 + return self.run_mochitest(test_paths, 'webapprt-content', **kwargs) 1.646 + 1.647 + def run_mochitest(self, test_paths, flavor, **kwargs): 1.648 + from mozbuild.controller.building import BuildDriver 1.649 + 1.650 + self._ensure_state_subdir_exists('.') 1.651 + 1.652 + driver = self._spawn(BuildDriver) 1.653 + driver.install_tests(remove=False) 1.654 + 1.655 + mochitest = self._spawn(MochitestRunner) 1.656 + 1.657 + return mochitest.run_desktop_test(self._mach_context, 1.658 + test_paths=test_paths, suite=flavor, **kwargs) 1.659 + 1.660 + 1.661 +# TODO For now b2g commands will only work with the emulator, 1.662 +# they should be modified to work with all devices. 1.663 +def is_emulator(cls): 1.664 + """Emulator needs to be configured.""" 1.665 + return cls.device_name.find('emulator') == 0 1.666 + 1.667 + 1.668 +@CommandProvider 1.669 +class B2GCommands(MachCommandBase): 1.670 + """So far these are only mochitest plain. They are 1.671 + implemented separately because their command lines 1.672 + are completely different. 1.673 + """ 1.674 + def __init__(self, context): 1.675 + MachCommandBase.__init__(self, context) 1.676 + 1.677 + for attr in ('b2g_home', 'xre_path', 'device_name'): 1.678 + setattr(self, attr, getattr(context, attr, None)) 1.679 + 1.680 + @Command('mochitest-remote', category='testing', 1.681 + description='Run a remote mochitest.', 1.682 + conditions=[conditions.is_b2g, is_emulator]) 1.683 + @B2GCommand 1.684 + def run_mochitest_remote(self, test_paths, **kwargs): 1.685 + from mozbuild.controller.building import BuildDriver 1.686 + 1.687 + self._ensure_state_subdir_exists('.') 1.688 + 1.689 + driver = self._spawn(BuildDriver) 1.690 + driver.install_tests(remove=False) 1.691 + 1.692 + mochitest = self._spawn(MochitestRunner) 1.693 + return mochitest.run_b2g_test(b2g_home=self.b2g_home, 1.694 + xre_path=self.xre_path, test_paths=test_paths, **kwargs) 1.695 + 1.696 + @Command('mochitest-b2g-desktop', category='testing', 1.697 + conditions=[conditions.is_b2g_desktop], 1.698 + description='Run a b2g desktop mochitest.') 1.699 + @B2GCommand 1.700 + def run_mochitest_b2g_desktop(self, test_paths, **kwargs): 1.701 + from mozbuild.controller.building import BuildDriver 1.702 + 1.703 + self._ensure_state_subdir_exists('.') 1.704 + 1.705 + driver = self._spawn(BuildDriver) 1.706 + driver.install_tests(remove=False) 1.707 + 1.708 + mochitest = self._spawn(MochitestRunner) 1.709 + return mochitest.run_b2g_test(test_paths=test_paths, **kwargs)