testing/mochitest/mach_commands.py

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

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

mercurial