testing/mochitest/mach_commands.py

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 # This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 # License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 4
michael@0 5 from __future__ import unicode_literals
michael@0 6
michael@0 7 import logging
michael@0 8 import mozpack.path
michael@0 9 import os
michael@0 10 import platform
michael@0 11 import sys
michael@0 12 import warnings
michael@0 13 import which
michael@0 14
michael@0 15 from mozbuild.base import (
michael@0 16 MachCommandBase,
michael@0 17 MachCommandConditions as conditions,
michael@0 18 MozbuildObject,
michael@0 19 )
michael@0 20
michael@0 21 from mach.decorators import (
michael@0 22 CommandArgument,
michael@0 23 CommandProvider,
michael@0 24 Command,
michael@0 25 )
michael@0 26
michael@0 27 from mach.logging import StructuredHumanFormatter
michael@0 28
michael@0 29 ADB_NOT_FOUND = '''
michael@0 30 The %s command requires the adb binary to be on your path.
michael@0 31
michael@0 32 If you have a B2G build, this can be found in
michael@0 33 '%s/out/host/<platform>/bin'.
michael@0 34 '''.lstrip()
michael@0 35
michael@0 36 GAIA_PROFILE_NOT_FOUND = '''
michael@0 37 The %s command requires a non-debug gaia profile. Either pass in --profile,
michael@0 38 or set the GAIA_PROFILE environment variable.
michael@0 39
michael@0 40 If you do not have a non-debug gaia profile, you can build one:
michael@0 41 $ git clone https://github.com/mozilla-b2g/gaia
michael@0 42 $ cd gaia
michael@0 43 $ make
michael@0 44
michael@0 45 The profile should be generated in a directory called 'profile'.
michael@0 46 '''.lstrip()
michael@0 47
michael@0 48 GAIA_PROFILE_IS_DEBUG = '''
michael@0 49 The %s command requires a non-debug gaia profile. The specified profile,
michael@0 50 %s, is a debug profile.
michael@0 51
michael@0 52 If you do not have a non-debug gaia profile, you can build one:
michael@0 53 $ git clone https://github.com/mozilla-b2g/gaia
michael@0 54 $ cd gaia
michael@0 55 $ make
michael@0 56
michael@0 57 The profile should be generated in a directory called 'profile'.
michael@0 58 '''.lstrip()
michael@0 59
michael@0 60
michael@0 61 class UnexpectedFilter(logging.Filter):
michael@0 62 def filter(self, record):
michael@0 63 msg = getattr(record, 'params', {}).get('msg', '')
michael@0 64 return 'TEST-UNEXPECTED-' in msg
michael@0 65
michael@0 66
michael@0 67 class MochitestRunner(MozbuildObject):
michael@0 68 """Easily run mochitests.
michael@0 69
michael@0 70 This currently contains just the basics for running mochitests. We may want
michael@0 71 to hook up result parsing, etc.
michael@0 72 """
michael@0 73
michael@0 74 def get_webapp_runtime_path(self):
michael@0 75 import mozinfo
michael@0 76 appname = 'webapprt-stub' + mozinfo.info.get('bin_suffix', '')
michael@0 77 if sys.platform.startswith('darwin'):
michael@0 78 appname = os.path.join(self.distdir, self.substs['MOZ_MACBUNDLE_NAME'],
michael@0 79 'Contents', 'MacOS', appname)
michael@0 80 else:
michael@0 81 appname = os.path.join(self.distdir, 'bin', appname)
michael@0 82 return appname
michael@0 83
michael@0 84 def __init__(self, *args, **kwargs):
michael@0 85 MozbuildObject.__init__(self, *args, **kwargs)
michael@0 86
michael@0 87 # TODO Bug 794506 remove once mach integrates with virtualenv.
michael@0 88 build_path = os.path.join(self.topobjdir, 'build')
michael@0 89 if build_path not in sys.path:
michael@0 90 sys.path.append(build_path)
michael@0 91
michael@0 92 self.tests_dir = os.path.join(self.topobjdir, '_tests')
michael@0 93 self.mochitest_dir = os.path.join(self.tests_dir, 'testing', 'mochitest')
michael@0 94 self.bin_dir = os.path.join(self.topobjdir, 'dist', 'bin')
michael@0 95
michael@0 96 def run_b2g_test(self, test_paths=None, b2g_home=None, xre_path=None,
michael@0 97 total_chunks=None, this_chunk=None, no_window=None,
michael@0 98 **kwargs):
michael@0 99 """Runs a b2g mochitest.
michael@0 100
michael@0 101 test_paths is an enumerable of paths to tests. It can be a relative path
michael@0 102 from the top source directory, an absolute filename, or a directory
michael@0 103 containing test files.
michael@0 104 """
michael@0 105 # Need to call relpath before os.chdir() below.
michael@0 106 test_path = ''
michael@0 107 if test_paths:
michael@0 108 if len(test_paths) > 1:
michael@0 109 print('Warning: Only the first test path will be used.')
michael@0 110 test_path = self._wrap_path_argument(test_paths[0]).relpath()
michael@0 111
michael@0 112 # TODO without os.chdir, chained imports fail below
michael@0 113 os.chdir(self.mochitest_dir)
michael@0 114
michael@0 115 # The imp module can spew warnings if the modules below have
michael@0 116 # already been imported, ignore them.
michael@0 117 with warnings.catch_warnings():
michael@0 118 warnings.simplefilter('ignore')
michael@0 119
michael@0 120 import imp
michael@0 121 path = os.path.join(self.mochitest_dir, 'runtestsb2g.py')
michael@0 122 with open(path, 'r') as fh:
michael@0 123 imp.load_module('mochitest', fh, path,
michael@0 124 ('.py', 'r', imp.PY_SOURCE))
michael@0 125
michael@0 126 import mochitest
michael@0 127 from mochitest_options import B2GOptions
michael@0 128
michael@0 129 parser = B2GOptions()
michael@0 130 options = parser.parse_args([])[0]
michael@0 131
michael@0 132 test_path_dir = False;
michael@0 133 if test_path:
michael@0 134 test_root_file = mozpack.path.join(self.mochitest_dir, 'tests', test_path)
michael@0 135 if not os.path.exists(test_root_file):
michael@0 136 print('Specified test path does not exist: %s' % test_root_file)
michael@0 137 return 1
michael@0 138 if os.path.isdir(test_root_file):
michael@0 139 test_path_dir = True;
michael@0 140 options.testPath = test_path
michael@0 141
michael@0 142 for k, v in kwargs.iteritems():
michael@0 143 setattr(options, k, v)
michael@0 144 options.noWindow = no_window
michael@0 145 options.totalChunks = total_chunks
michael@0 146 options.thisChunk = this_chunk
michael@0 147
michael@0 148 options.symbolsPath = os.path.join(self.distdir, 'crashreporter-symbols')
michael@0 149
michael@0 150 options.consoleLevel = 'INFO'
michael@0 151 if conditions.is_b2g_desktop(self):
michael@0 152
michael@0 153 options.profile = options.profile or os.environ.get('GAIA_PROFILE')
michael@0 154 if not options.profile:
michael@0 155 print(GAIA_PROFILE_NOT_FOUND % 'mochitest-b2g-desktop')
michael@0 156 return 1
michael@0 157
michael@0 158 if os.path.isfile(os.path.join(options.profile, 'extensions', \
michael@0 159 'httpd@gaiamobile.org')):
michael@0 160 print(GAIA_PROFILE_IS_DEBUG % ('mochitest-b2g-desktop',
michael@0 161 options.profile))
michael@0 162 return 1
michael@0 163
michael@0 164 options.desktop = True
michael@0 165 options.app = self.get_binary_path()
michael@0 166 if not options.app.endswith('-bin'):
michael@0 167 options.app = '%s-bin' % options.app
michael@0 168 if not os.path.isfile(options.app):
michael@0 169 options.app = options.app[:-len('-bin')]
michael@0 170
michael@0 171 return mochitest.run_desktop_mochitests(parser, options)
michael@0 172
michael@0 173 try:
michael@0 174 which.which('adb')
michael@0 175 except which.WhichError:
michael@0 176 # TODO Find adb automatically if it isn't on the path
michael@0 177 print(ADB_NOT_FOUND % ('mochitest-remote', b2g_home))
michael@0 178 return 1
michael@0 179
michael@0 180 options.b2gPath = b2g_home
michael@0 181 options.logcat_dir = self.mochitest_dir
michael@0 182 options.httpdPath = self.mochitest_dir
michael@0 183 options.xrePath = xre_path
michael@0 184 return mochitest.run_remote_mochitests(parser, options)
michael@0 185
michael@0 186 def run_desktop_test(self, context, suite=None, test_paths=None, debugger=None,
michael@0 187 debugger_args=None, slowscript=False, screenshot_on_fail = False, shuffle=False, keep_open=False,
michael@0 188 rerun_failures=False, no_autorun=False, repeat=0, run_until_failure=False,
michael@0 189 slow=False, chunk_by_dir=0, total_chunks=None, this_chunk=None,
michael@0 190 jsdebugger=False, debug_on_failure=False, start_at=None, end_at=None,
michael@0 191 e10s=False, dmd=False, dump_output_directory=None,
michael@0 192 dump_about_memory_after_test=False, dump_dmd_after_test=False,
michael@0 193 install_extension=None, quiet=False, environment=[], **kwargs):
michael@0 194 """Runs a mochitest.
michael@0 195
michael@0 196 test_paths are path to tests. They can be a relative path from the
michael@0 197 top source directory, an absolute filename, or a directory containing
michael@0 198 test files.
michael@0 199
michael@0 200 suite is the type of mochitest to run. It can be one of ('plain',
michael@0 201 'chrome', 'browser', 'metro', 'a11y').
michael@0 202
michael@0 203 debugger is a program name or path to a binary (presumably a debugger)
michael@0 204 to run the test in. e.g. 'gdb'
michael@0 205
michael@0 206 debugger_args are the arguments passed to the debugger.
michael@0 207
michael@0 208 slowscript is true if the user has requested the SIGSEGV mechanism of
michael@0 209 invoking the slow script dialog.
michael@0 210
michael@0 211 shuffle is whether test order should be shuffled (defaults to false).
michael@0 212
michael@0 213 keep_open denotes whether to keep the browser open after tests
michael@0 214 complete.
michael@0 215 """
michael@0 216 if rerun_failures and test_paths:
michael@0 217 print('Cannot specify both --rerun-failures and a test path.')
michael@0 218 return 1
michael@0 219
michael@0 220 # Need to call relpath before os.chdir() below.
michael@0 221 if test_paths:
michael@0 222 test_paths = [self._wrap_path_argument(p).relpath() for p in test_paths]
michael@0 223
michael@0 224 failure_file_path = os.path.join(self.statedir, 'mochitest_failures.json')
michael@0 225
michael@0 226 if rerun_failures and not os.path.exists(failure_file_path):
michael@0 227 print('No failure file present. Did you run mochitests before?')
michael@0 228 return 1
michael@0 229
michael@0 230 from StringIO import StringIO
michael@0 231
michael@0 232 # runtests.py is ambiguous, so we load the file/module manually.
michael@0 233 if 'mochitest' not in sys.modules:
michael@0 234 import imp
michael@0 235 path = os.path.join(self.mochitest_dir, 'runtests.py')
michael@0 236 with open(path, 'r') as fh:
michael@0 237 imp.load_module('mochitest', fh, path,
michael@0 238 ('.py', 'r', imp.PY_SOURCE))
michael@0 239
michael@0 240 import mozinfo
michael@0 241 import mochitest
michael@0 242 from manifestparser import TestManifest
michael@0 243 from mozbuild.testing import TestResolver
michael@0 244
michael@0 245 # This is required to make other components happy. Sad, isn't it?
michael@0 246 os.chdir(self.topobjdir)
michael@0 247
michael@0 248 # Automation installs its own stream handler to stdout. Since we want
michael@0 249 # all logging to go through us, we just remove their handler.
michael@0 250 remove_handlers = [l for l in logging.getLogger().handlers
michael@0 251 if isinstance(l, logging.StreamHandler)]
michael@0 252 for handler in remove_handlers:
michael@0 253 logging.getLogger().removeHandler(handler)
michael@0 254
michael@0 255 runner = mochitest.Mochitest()
michael@0 256
michael@0 257 opts = mochitest.MochitestOptions()
michael@0 258 options, args = opts.parse_args([])
michael@0 259
michael@0 260 options.subsuite = ''
michael@0 261 flavor = suite
michael@0 262
michael@0 263 # Need to set the suite options before verifyOptions below.
michael@0 264 if suite == 'plain':
michael@0 265 # Don't need additional options for plain.
michael@0 266 flavor = 'mochitest'
michael@0 267 elif suite == 'chrome':
michael@0 268 options.chrome = True
michael@0 269 elif suite == 'browser':
michael@0 270 options.browserChrome = True
michael@0 271 flavor = 'browser-chrome'
michael@0 272 elif suite == 'devtools':
michael@0 273 options.browserChrome = True
michael@0 274 options.subsuite = 'devtools'
michael@0 275 elif suite == 'metro':
michael@0 276 options.immersiveMode = True
michael@0 277 options.browserChrome = True
michael@0 278 elif suite == 'a11y':
michael@0 279 options.a11y = True
michael@0 280 elif suite == 'webapprt-content':
michael@0 281 options.webapprtContent = True
michael@0 282 options.app = self.get_webapp_runtime_path()
michael@0 283 elif suite == 'webapprt-chrome':
michael@0 284 options.webapprtChrome = True
michael@0 285 options.app = self.get_webapp_runtime_path()
michael@0 286 options.browserArgs.append("-test-mode")
michael@0 287 else:
michael@0 288 raise Exception('None or unrecognized mochitest suite type.')
michael@0 289
michael@0 290 if dmd:
michael@0 291 options.dmdPath = self.bin_dir
michael@0 292
michael@0 293 options.autorun = not no_autorun
michael@0 294 options.closeWhenDone = not keep_open
michael@0 295 options.slowscript = slowscript
michael@0 296 options.screenshotOnFail = screenshot_on_fail
michael@0 297 options.shuffle = shuffle
michael@0 298 options.consoleLevel = 'INFO'
michael@0 299 options.repeat = repeat
michael@0 300 options.runUntilFailure = run_until_failure
michael@0 301 options.runSlower = slow
michael@0 302 options.testingModulesDir = os.path.join(self.tests_dir, 'modules')
michael@0 303 options.extraProfileFiles.append(os.path.join(self.distdir, 'plugins'))
michael@0 304 options.symbolsPath = os.path.join(self.distdir, 'crashreporter-symbols')
michael@0 305 options.chunkByDir = chunk_by_dir
michael@0 306 options.totalChunks = total_chunks
michael@0 307 options.thisChunk = this_chunk
michael@0 308 options.jsdebugger = jsdebugger
michael@0 309 options.debugOnFailure = debug_on_failure
michael@0 310 options.startAt = start_at
michael@0 311 options.endAt = end_at
michael@0 312 options.e10s = e10s
michael@0 313 options.dumpAboutMemoryAfterTest = dump_about_memory_after_test
michael@0 314 options.dumpDMDAfterTest = dump_dmd_after_test
michael@0 315 options.dumpOutputDirectory = dump_output_directory
michael@0 316 options.quiet = quiet
michael@0 317 options.environment = environment
michael@0 318
michael@0 319 options.failureFile = failure_file_path
michael@0 320 if install_extension != None:
michael@0 321 options.extensionsToInstall = [os.path.join(self.topsrcdir,install_extension)]
michael@0 322
michael@0 323 for k, v in kwargs.iteritems():
michael@0 324 setattr(options, k, v)
michael@0 325
michael@0 326 if test_paths:
michael@0 327 resolver = self._spawn(TestResolver)
michael@0 328
michael@0 329 tests = list(resolver.resolve_tests(paths=test_paths, flavor=flavor,
michael@0 330 cwd=context.cwd))
michael@0 331
michael@0 332 if not tests:
michael@0 333 print('No tests could be found in the path specified. Please '
michael@0 334 'specify a path that is a test file or is a directory '
michael@0 335 'containing tests.')
michael@0 336 return 1
michael@0 337
michael@0 338 manifest = TestManifest()
michael@0 339 manifest.tests.extend(tests)
michael@0 340
michael@0 341 options.manifestFile = manifest
michael@0 342
michael@0 343 if rerun_failures:
michael@0 344 options.testManifest = failure_file_path
michael@0 345
michael@0 346 if debugger:
michael@0 347 options.debugger = debugger
michael@0 348
michael@0 349 if debugger_args:
michael@0 350 if options.debugger == None:
michael@0 351 print("--debugger-args passed, but no debugger specified.")
michael@0 352 return 1
michael@0 353 options.debuggerArgs = debugger_args
michael@0 354
michael@0 355 options = opts.verifyOptions(options, runner)
michael@0 356
michael@0 357 if options is None:
michael@0 358 raise Exception('mochitest option validator failed.')
michael@0 359
michael@0 360 # We need this to enable colorization of output.
michael@0 361 self.log_manager.enable_unstructured()
michael@0 362
michael@0 363 # Output processing is a little funky here. The old make targets
michael@0 364 # grepped the log output from TEST-UNEXPECTED-* and printed these lines
michael@0 365 # after test execution. Ideally the test runner would expose a Python
michael@0 366 # API for obtaining test results and we could just format failures
michael@0 367 # appropriately. Unfortunately, it doesn't yet do that. So, we capture
michael@0 368 # all output to a buffer then "grep" the buffer after test execution.
michael@0 369 # Bug 858197 tracks a Python API that would facilitate this.
michael@0 370 test_output = StringIO()
michael@0 371 handler = logging.StreamHandler(test_output)
michael@0 372 handler.addFilter(UnexpectedFilter())
michael@0 373 handler.setFormatter(StructuredHumanFormatter(0, write_times=False))
michael@0 374 logging.getLogger().addHandler(handler)
michael@0 375
michael@0 376 result = runner.runTests(options)
michael@0 377
michael@0 378 # Need to remove our buffering handler before we echo failures or else
michael@0 379 # it will catch them again!
michael@0 380 logging.getLogger().removeHandler(handler)
michael@0 381 self.log_manager.disable_unstructured()
michael@0 382
michael@0 383 if test_output.getvalue():
michael@0 384 result = 1
michael@0 385 for line in test_output.getvalue().splitlines():
michael@0 386 self.log(logging.INFO, 'unexpected', {'msg': line}, '{msg}')
michael@0 387
michael@0 388 return result
michael@0 389
michael@0 390
michael@0 391 def MochitestCommand(func):
michael@0 392 """Decorator that adds shared command arguments to mochitest commands."""
michael@0 393
michael@0 394 # This employs light Python magic. Keep in mind a decorator is just a
michael@0 395 # function that takes a function, does something with it, then returns a
michael@0 396 # (modified) function. Here, we chain decorators onto the passed in
michael@0 397 # function.
michael@0 398
michael@0 399 debugger = CommandArgument('--debugger', '-d', metavar='DEBUGGER',
michael@0 400 help='Debugger binary to run test in. Program name or path.')
michael@0 401 func = debugger(func)
michael@0 402
michael@0 403 debugger_args = CommandArgument('--debugger-args',
michael@0 404 metavar='DEBUGGER_ARGS', help='Arguments to pass to the debugger.')
michael@0 405 func = debugger_args(func)
michael@0 406
michael@0 407 # Bug 933807 introduced JS_DISABLE_SLOW_SCRIPT_SIGNALS to avoid clever
michael@0 408 # segfaults induced by the slow-script-detecting logic for Ion/Odin JITted
michael@0 409 # code. If we don't pass this, the user will need to periodically type
michael@0 410 # "continue" to (safely) resume execution. There are ways to implement
michael@0 411 # automatic resuming; see the bug.
michael@0 412 slowscript = CommandArgument('--slowscript', action='store_true',
michael@0 413 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')
michael@0 414 func = slowscript(func)
michael@0 415
michael@0 416 screenshot_on_fail = CommandArgument('--screenshot-on-fail', action='store_true',
michael@0 417 help='Take screenshots on all test failures. Set $MOZ_UPLOAD_DIR to a directory for storing the screenshots.')
michael@0 418 func = screenshot_on_fail(func)
michael@0 419
michael@0 420 shuffle = CommandArgument('--shuffle', action='store_true',
michael@0 421 help='Shuffle execution order.')
michael@0 422 func = shuffle(func)
michael@0 423
michael@0 424 keep_open = CommandArgument('--keep-open', action='store_true',
michael@0 425 help='Keep the browser open after tests complete.')
michael@0 426 func = keep_open(func)
michael@0 427
michael@0 428 rerun = CommandArgument('--rerun-failures', action='store_true',
michael@0 429 help='Run only the tests that failed during the last test run.')
michael@0 430 func = rerun(func)
michael@0 431
michael@0 432 autorun = CommandArgument('--no-autorun', action='store_true',
michael@0 433 help='Do not starting running tests automatically.')
michael@0 434 func = autorun(func)
michael@0 435
michael@0 436 repeat = CommandArgument('--repeat', type=int, default=0,
michael@0 437 help='Repeat the test the given number of times.')
michael@0 438 func = repeat(func)
michael@0 439
michael@0 440 runUntilFailure = CommandArgument("--run-until-failure", action='store_true',
michael@0 441 help='Run tests repeatedly and stops on the first time a test fails. ' \
michael@0 442 'Default cap is 30 runs, which can be overwritten ' \
michael@0 443 'with the --repeat parameter.')
michael@0 444 func = runUntilFailure(func)
michael@0 445
michael@0 446 slow = CommandArgument('--slow', action='store_true',
michael@0 447 help='Delay execution between tests.')
michael@0 448 func = slow(func)
michael@0 449
michael@0 450 end_at = CommandArgument('--end-at', type=str,
michael@0 451 help='Stop running the test sequence at this test.')
michael@0 452 func = end_at(func)
michael@0 453
michael@0 454 start_at = CommandArgument('--start-at', type=str,
michael@0 455 help='Start running the test sequence at this test.')
michael@0 456 func = start_at(func)
michael@0 457
michael@0 458 chunk_dir = CommandArgument('--chunk-by-dir', type=int,
michael@0 459 help='Group tests together in chunks by this many top directories.')
michael@0 460 func = chunk_dir(func)
michael@0 461
michael@0 462 chunk_total = CommandArgument('--total-chunks', type=int,
michael@0 463 help='Total number of chunks to split tests into.')
michael@0 464 func = chunk_total(func)
michael@0 465
michael@0 466 this_chunk = CommandArgument('--this-chunk', type=int,
michael@0 467 help='If running tests by chunks, the number of the chunk to run.')
michael@0 468 func = this_chunk(func)
michael@0 469
michael@0 470 hide_subtests = CommandArgument('--hide-subtests', action='store_true',
michael@0 471 help='If specified, will only log subtest results on failure or timeout.')
michael@0 472 func = hide_subtests(func)
michael@0 473
michael@0 474 debug_on_failure = CommandArgument('--debug-on-failure', action='store_true',
michael@0 475 help='Breaks execution and enters the JS debugger on a test failure. ' \
michael@0 476 'Should be used together with --jsdebugger.')
michael@0 477 func = debug_on_failure(func)
michael@0 478
michael@0 479 jsdebugger = CommandArgument('--jsdebugger', action='store_true',
michael@0 480 help='Start the browser JS debugger before running the test. Implies --no-autorun.')
michael@0 481 func = jsdebugger(func)
michael@0 482
michael@0 483 this_chunk = CommandArgument('--e10s', action='store_true',
michael@0 484 help='Run tests with electrolysis preferences and test filtering enabled.')
michael@0 485 func = this_chunk(func)
michael@0 486
michael@0 487 dmd = CommandArgument('--dmd', action='store_true',
michael@0 488 help='Run tests with DMD active.')
michael@0 489 func = dmd(func)
michael@0 490
michael@0 491 dumpAboutMemory = CommandArgument('--dump-about-memory-after-test', action='store_true',
michael@0 492 help='Dump an about:memory log after every test.')
michael@0 493 func = dumpAboutMemory(func)
michael@0 494
michael@0 495 dumpDMD = CommandArgument('--dump-dmd-after-test', action='store_true',
michael@0 496 help='Dump a DMD log after every test.')
michael@0 497 func = dumpDMD(func)
michael@0 498
michael@0 499 dumpOutputDirectory = CommandArgument('--dump-output-directory', action='store',
michael@0 500 help='Specifies the directory in which to place dumped memory reports.')
michael@0 501 func = dumpOutputDirectory(func)
michael@0 502
michael@0 503 path = CommandArgument('test_paths', default=None, nargs='*',
michael@0 504 metavar='TEST',
michael@0 505 help='Test to run. Can be specified as a single file, a ' \
michael@0 506 'directory, or omitted. If omitted, the entire test suite is ' \
michael@0 507 'executed.')
michael@0 508 func = path(func)
michael@0 509
michael@0 510 install_extension = CommandArgument('--install-extension',
michael@0 511 help='Install given extension before running selected tests. ' \
michael@0 512 'Parameter is a path to xpi file.')
michael@0 513 func = install_extension(func)
michael@0 514
michael@0 515 quiet = CommandArgument('--quiet', default=False, action='store_true',
michael@0 516 help='Do not print test log lines unless a failure occurs.')
michael@0 517 func = quiet(func)
michael@0 518
michael@0 519 setenv = CommandArgument('--setenv', default=[], action='append',
michael@0 520 metavar='NAME=VALUE', dest='environment',
michael@0 521 help="Sets the given variable in the application's environment")
michael@0 522 func = setenv(func)
michael@0 523
michael@0 524 return func
michael@0 525
michael@0 526 def B2GCommand(func):
michael@0 527 """Decorator that adds shared command arguments to b2g mochitest commands."""
michael@0 528
michael@0 529 busybox = CommandArgument('--busybox', default=None,
michael@0 530 help='Path to busybox binary to install on device')
michael@0 531 func = busybox(func)
michael@0 532
michael@0 533 logcatdir = CommandArgument('--logcat-dir', default=None,
michael@0 534 help='directory to store logcat dump files')
michael@0 535 func = logcatdir(func)
michael@0 536
michael@0 537 profile = CommandArgument('--profile', default=None,
michael@0 538 help='for desktop testing, the path to the \
michael@0 539 gaia profile to use')
michael@0 540 func = profile(func)
michael@0 541
michael@0 542 geckopath = CommandArgument('--gecko-path', default=None,
michael@0 543 help='the path to a gecko distribution that should \
michael@0 544 be installed on the emulator prior to test')
michael@0 545 func = geckopath(func)
michael@0 546
michael@0 547 nowindow = CommandArgument('--no-window', action='store_true', default=False,
michael@0 548 help='Pass --no-window to the emulator')
michael@0 549 func = nowindow(func)
michael@0 550
michael@0 551 sdcard = CommandArgument('--sdcard', default="10MB",
michael@0 552 help='Define size of sdcard: 1MB, 50MB...etc')
michael@0 553 func = sdcard(func)
michael@0 554
michael@0 555 emulator = CommandArgument('--emulator', default='arm',
michael@0 556 help='Architecture of emulator to use: x86 or arm')
michael@0 557 func = emulator(func)
michael@0 558
michael@0 559 marionette = CommandArgument('--marionette', default=None,
michael@0 560 help='host:port to use when connecting to Marionette')
michael@0 561 func = marionette(func)
michael@0 562
michael@0 563 chunk_total = CommandArgument('--total-chunks', type=int,
michael@0 564 help='Total number of chunks to split tests into.')
michael@0 565 func = chunk_total(func)
michael@0 566
michael@0 567 this_chunk = CommandArgument('--this-chunk', type=int,
michael@0 568 help='If running tests by chunks, the number of the chunk to run.')
michael@0 569 func = this_chunk(func)
michael@0 570
michael@0 571 hide_subtests = CommandArgument('--hide-subtests', action='store_true',
michael@0 572 help='If specified, will only log subtest results on failure or timeout.')
michael@0 573 func = hide_subtests(func)
michael@0 574
michael@0 575 path = CommandArgument('test_paths', default=None, nargs='*',
michael@0 576 metavar='TEST',
michael@0 577 help='Test to run. Can be specified as a single file, a ' \
michael@0 578 'directory, or omitted. If omitted, the entire test suite is ' \
michael@0 579 'executed.')
michael@0 580 func = path(func)
michael@0 581
michael@0 582 return func
michael@0 583
michael@0 584
michael@0 585
michael@0 586 @CommandProvider
michael@0 587 class MachCommands(MachCommandBase):
michael@0 588 @Command('mochitest-plain', category='testing',
michael@0 589 conditions=[conditions.is_firefox],
michael@0 590 description='Run a plain mochitest.')
michael@0 591 @MochitestCommand
michael@0 592 def run_mochitest_plain(self, test_paths, **kwargs):
michael@0 593 return self.run_mochitest(test_paths, 'plain', **kwargs)
michael@0 594
michael@0 595 @Command('mochitest-chrome', category='testing',
michael@0 596 conditions=[conditions.is_firefox],
michael@0 597 description='Run a chrome mochitest.')
michael@0 598 @MochitestCommand
michael@0 599 def run_mochitest_chrome(self, test_paths, **kwargs):
michael@0 600 return self.run_mochitest(test_paths, 'chrome', **kwargs)
michael@0 601
michael@0 602 @Command('mochitest-browser', category='testing',
michael@0 603 conditions=[conditions.is_firefox],
michael@0 604 description='Run a mochitest with browser chrome.')
michael@0 605 @MochitestCommand
michael@0 606 def run_mochitest_browser(self, test_paths, **kwargs):
michael@0 607 return self.run_mochitest(test_paths, 'browser', **kwargs)
michael@0 608
michael@0 609 @Command('mochitest-devtools', category='testing',
michael@0 610 conditions=[conditions.is_firefox],
michael@0 611 description='Run a devtools mochitest with browser chrome.')
michael@0 612 @MochitestCommand
michael@0 613 def run_mochitest_devtools(self, test_paths, **kwargs):
michael@0 614 return self.run_mochitest(test_paths, 'devtools', **kwargs)
michael@0 615
michael@0 616 @Command('mochitest-metro', category='testing',
michael@0 617 conditions=[conditions.is_firefox],
michael@0 618 description='Run a mochitest with metro browser chrome.')
michael@0 619 @MochitestCommand
michael@0 620 def run_mochitest_metro(self, test_paths, **kwargs):
michael@0 621 return self.run_mochitest(test_paths, 'metro', **kwargs)
michael@0 622
michael@0 623 @Command('mochitest-a11y', category='testing',
michael@0 624 conditions=[conditions.is_firefox],
michael@0 625 description='Run an a11y mochitest.')
michael@0 626 @MochitestCommand
michael@0 627 def run_mochitest_a11y(self, test_paths, **kwargs):
michael@0 628 return self.run_mochitest(test_paths, 'a11y', **kwargs)
michael@0 629
michael@0 630 @Command('webapprt-test-chrome', category='testing',
michael@0 631 conditions=[conditions.is_firefox],
michael@0 632 description='Run a webapprt chrome mochitest.')
michael@0 633 @MochitestCommand
michael@0 634 def run_mochitest_webapprt_chrome(self, test_paths, **kwargs):
michael@0 635 return self.run_mochitest(test_paths, 'webapprt-chrome', **kwargs)
michael@0 636
michael@0 637 @Command('webapprt-test-content', category='testing',
michael@0 638 conditions=[conditions.is_firefox],
michael@0 639 description='Run a webapprt content mochitest.')
michael@0 640 @MochitestCommand
michael@0 641 def run_mochitest_webapprt_content(self, test_paths, **kwargs):
michael@0 642 return self.run_mochitest(test_paths, 'webapprt-content', **kwargs)
michael@0 643
michael@0 644 def run_mochitest(self, test_paths, flavor, **kwargs):
michael@0 645 from mozbuild.controller.building import BuildDriver
michael@0 646
michael@0 647 self._ensure_state_subdir_exists('.')
michael@0 648
michael@0 649 driver = self._spawn(BuildDriver)
michael@0 650 driver.install_tests(remove=False)
michael@0 651
michael@0 652 mochitest = self._spawn(MochitestRunner)
michael@0 653
michael@0 654 return mochitest.run_desktop_test(self._mach_context,
michael@0 655 test_paths=test_paths, suite=flavor, **kwargs)
michael@0 656
michael@0 657
michael@0 658 # TODO For now b2g commands will only work with the emulator,
michael@0 659 # they should be modified to work with all devices.
michael@0 660 def is_emulator(cls):
michael@0 661 """Emulator needs to be configured."""
michael@0 662 return cls.device_name.find('emulator') == 0
michael@0 663
michael@0 664
michael@0 665 @CommandProvider
michael@0 666 class B2GCommands(MachCommandBase):
michael@0 667 """So far these are only mochitest plain. They are
michael@0 668 implemented separately because their command lines
michael@0 669 are completely different.
michael@0 670 """
michael@0 671 def __init__(self, context):
michael@0 672 MachCommandBase.__init__(self, context)
michael@0 673
michael@0 674 for attr in ('b2g_home', 'xre_path', 'device_name'):
michael@0 675 setattr(self, attr, getattr(context, attr, None))
michael@0 676
michael@0 677 @Command('mochitest-remote', category='testing',
michael@0 678 description='Run a remote mochitest.',
michael@0 679 conditions=[conditions.is_b2g, is_emulator])
michael@0 680 @B2GCommand
michael@0 681 def run_mochitest_remote(self, test_paths, **kwargs):
michael@0 682 from mozbuild.controller.building import BuildDriver
michael@0 683
michael@0 684 self._ensure_state_subdir_exists('.')
michael@0 685
michael@0 686 driver = self._spawn(BuildDriver)
michael@0 687 driver.install_tests(remove=False)
michael@0 688
michael@0 689 mochitest = self._spawn(MochitestRunner)
michael@0 690 return mochitest.run_b2g_test(b2g_home=self.b2g_home,
michael@0 691 xre_path=self.xre_path, test_paths=test_paths, **kwargs)
michael@0 692
michael@0 693 @Command('mochitest-b2g-desktop', category='testing',
michael@0 694 conditions=[conditions.is_b2g_desktop],
michael@0 695 description='Run a b2g desktop mochitest.')
michael@0 696 @B2GCommand
michael@0 697 def run_mochitest_b2g_desktop(self, test_paths, **kwargs):
michael@0 698 from mozbuild.controller.building import BuildDriver
michael@0 699
michael@0 700 self._ensure_state_subdir_exists('.')
michael@0 701
michael@0 702 driver = self._spawn(BuildDriver)
michael@0 703 driver.install_tests(remove=False)
michael@0 704
michael@0 705 mochitest = self._spawn(MochitestRunner)
michael@0 706 return mochitest.run_b2g_test(test_paths=test_paths, **kwargs)

mercurial