michael@0: # This Source Code Form is subject to the terms of the Mozilla Public michael@0: # License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: # file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: from __future__ import unicode_literals michael@0: michael@0: import logging michael@0: import mozpack.path michael@0: import os michael@0: import platform michael@0: import sys michael@0: import warnings michael@0: import which michael@0: michael@0: from mozbuild.base import ( michael@0: MachCommandBase, michael@0: MachCommandConditions as conditions, michael@0: MozbuildObject, michael@0: ) michael@0: michael@0: from mach.decorators import ( michael@0: CommandArgument, michael@0: CommandProvider, michael@0: Command, michael@0: ) michael@0: michael@0: from mach.logging import StructuredHumanFormatter michael@0: michael@0: ADB_NOT_FOUND = ''' michael@0: The %s command requires the adb binary to be on your path. michael@0: michael@0: If you have a B2G build, this can be found in michael@0: '%s/out/host//bin'. michael@0: '''.lstrip() michael@0: michael@0: GAIA_PROFILE_NOT_FOUND = ''' michael@0: The %s command requires a non-debug gaia profile. Either pass in --profile, michael@0: or set the GAIA_PROFILE environment variable. michael@0: michael@0: If you do not have a non-debug gaia profile, you can build one: michael@0: $ git clone https://github.com/mozilla-b2g/gaia michael@0: $ cd gaia michael@0: $ make michael@0: michael@0: The profile should be generated in a directory called 'profile'. michael@0: '''.lstrip() michael@0: michael@0: GAIA_PROFILE_IS_DEBUG = ''' michael@0: The %s command requires a non-debug gaia profile. The specified profile, michael@0: %s, is a debug profile. michael@0: michael@0: If you do not have a non-debug gaia profile, you can build one: michael@0: $ git clone https://github.com/mozilla-b2g/gaia michael@0: $ cd gaia michael@0: $ make michael@0: michael@0: The profile should be generated in a directory called 'profile'. michael@0: '''.lstrip() michael@0: michael@0: michael@0: class UnexpectedFilter(logging.Filter): michael@0: def filter(self, record): michael@0: msg = getattr(record, 'params', {}).get('msg', '') michael@0: return 'TEST-UNEXPECTED-' in msg michael@0: michael@0: michael@0: class MochitestRunner(MozbuildObject): michael@0: """Easily run mochitests. michael@0: michael@0: This currently contains just the basics for running mochitests. We may want michael@0: to hook up result parsing, etc. michael@0: """ michael@0: michael@0: def get_webapp_runtime_path(self): michael@0: import mozinfo michael@0: appname = 'webapprt-stub' + mozinfo.info.get('bin_suffix', '') michael@0: if sys.platform.startswith('darwin'): michael@0: appname = os.path.join(self.distdir, self.substs['MOZ_MACBUNDLE_NAME'], michael@0: 'Contents', 'MacOS', appname) michael@0: else: michael@0: appname = os.path.join(self.distdir, 'bin', appname) michael@0: return appname michael@0: michael@0: def __init__(self, *args, **kwargs): michael@0: MozbuildObject.__init__(self, *args, **kwargs) michael@0: michael@0: # TODO Bug 794506 remove once mach integrates with virtualenv. michael@0: build_path = os.path.join(self.topobjdir, 'build') michael@0: if build_path not in sys.path: michael@0: sys.path.append(build_path) michael@0: michael@0: self.tests_dir = os.path.join(self.topobjdir, '_tests') michael@0: self.mochitest_dir = os.path.join(self.tests_dir, 'testing', 'mochitest') michael@0: self.bin_dir = os.path.join(self.topobjdir, 'dist', 'bin') michael@0: michael@0: def run_b2g_test(self, test_paths=None, b2g_home=None, xre_path=None, michael@0: total_chunks=None, this_chunk=None, no_window=None, michael@0: **kwargs): michael@0: """Runs a b2g mochitest. michael@0: michael@0: test_paths is an enumerable of paths to tests. It can be a relative path michael@0: from the top source directory, an absolute filename, or a directory michael@0: containing test files. michael@0: """ michael@0: # Need to call relpath before os.chdir() below. michael@0: test_path = '' michael@0: if test_paths: michael@0: if len(test_paths) > 1: michael@0: print('Warning: Only the first test path will be used.') michael@0: test_path = self._wrap_path_argument(test_paths[0]).relpath() michael@0: michael@0: # TODO without os.chdir, chained imports fail below michael@0: os.chdir(self.mochitest_dir) michael@0: michael@0: # The imp module can spew warnings if the modules below have michael@0: # already been imported, ignore them. michael@0: with warnings.catch_warnings(): michael@0: warnings.simplefilter('ignore') michael@0: michael@0: import imp michael@0: path = os.path.join(self.mochitest_dir, 'runtestsb2g.py') michael@0: with open(path, 'r') as fh: michael@0: imp.load_module('mochitest', fh, path, michael@0: ('.py', 'r', imp.PY_SOURCE)) michael@0: michael@0: import mochitest michael@0: from mochitest_options import B2GOptions michael@0: michael@0: parser = B2GOptions() michael@0: options = parser.parse_args([])[0] michael@0: michael@0: test_path_dir = False; michael@0: if test_path: michael@0: test_root_file = mozpack.path.join(self.mochitest_dir, 'tests', test_path) michael@0: if not os.path.exists(test_root_file): michael@0: print('Specified test path does not exist: %s' % test_root_file) michael@0: return 1 michael@0: if os.path.isdir(test_root_file): michael@0: test_path_dir = True; michael@0: options.testPath = test_path michael@0: michael@0: for k, v in kwargs.iteritems(): michael@0: setattr(options, k, v) michael@0: options.noWindow = no_window michael@0: options.totalChunks = total_chunks michael@0: options.thisChunk = this_chunk michael@0: michael@0: options.symbolsPath = os.path.join(self.distdir, 'crashreporter-symbols') michael@0: michael@0: options.consoleLevel = 'INFO' michael@0: if conditions.is_b2g_desktop(self): michael@0: michael@0: options.profile = options.profile or os.environ.get('GAIA_PROFILE') michael@0: if not options.profile: michael@0: print(GAIA_PROFILE_NOT_FOUND % 'mochitest-b2g-desktop') michael@0: return 1 michael@0: michael@0: if os.path.isfile(os.path.join(options.profile, 'extensions', \ michael@0: 'httpd@gaiamobile.org')): michael@0: print(GAIA_PROFILE_IS_DEBUG % ('mochitest-b2g-desktop', michael@0: options.profile)) michael@0: return 1 michael@0: michael@0: options.desktop = True michael@0: options.app = self.get_binary_path() michael@0: if not options.app.endswith('-bin'): michael@0: options.app = '%s-bin' % options.app michael@0: if not os.path.isfile(options.app): michael@0: options.app = options.app[:-len('-bin')] michael@0: michael@0: return mochitest.run_desktop_mochitests(parser, options) michael@0: michael@0: try: michael@0: which.which('adb') michael@0: except which.WhichError: michael@0: # TODO Find adb automatically if it isn't on the path michael@0: print(ADB_NOT_FOUND % ('mochitest-remote', b2g_home)) michael@0: return 1 michael@0: michael@0: options.b2gPath = b2g_home michael@0: options.logcat_dir = self.mochitest_dir michael@0: options.httpdPath = self.mochitest_dir michael@0: options.xrePath = xre_path michael@0: return mochitest.run_remote_mochitests(parser, options) michael@0: michael@0: def run_desktop_test(self, context, suite=None, test_paths=None, debugger=None, michael@0: debugger_args=None, slowscript=False, screenshot_on_fail = False, shuffle=False, keep_open=False, michael@0: rerun_failures=False, no_autorun=False, repeat=0, run_until_failure=False, michael@0: slow=False, chunk_by_dir=0, total_chunks=None, this_chunk=None, michael@0: jsdebugger=False, debug_on_failure=False, start_at=None, end_at=None, michael@0: e10s=False, dmd=False, dump_output_directory=None, michael@0: dump_about_memory_after_test=False, dump_dmd_after_test=False, michael@0: install_extension=None, quiet=False, environment=[], **kwargs): michael@0: """Runs a mochitest. michael@0: michael@0: test_paths are path to tests. They can be a relative path from the michael@0: top source directory, an absolute filename, or a directory containing michael@0: test files. michael@0: michael@0: suite is the type of mochitest to run. It can be one of ('plain', michael@0: 'chrome', 'browser', 'metro', 'a11y'). michael@0: michael@0: debugger is a program name or path to a binary (presumably a debugger) michael@0: to run the test in. e.g. 'gdb' michael@0: michael@0: debugger_args are the arguments passed to the debugger. michael@0: michael@0: slowscript is true if the user has requested the SIGSEGV mechanism of michael@0: invoking the slow script dialog. michael@0: michael@0: shuffle is whether test order should be shuffled (defaults to false). michael@0: michael@0: keep_open denotes whether to keep the browser open after tests michael@0: complete. michael@0: """ michael@0: if rerun_failures and test_paths: michael@0: print('Cannot specify both --rerun-failures and a test path.') michael@0: return 1 michael@0: michael@0: # Need to call relpath before os.chdir() below. michael@0: if test_paths: michael@0: test_paths = [self._wrap_path_argument(p).relpath() for p in test_paths] michael@0: michael@0: failure_file_path = os.path.join(self.statedir, 'mochitest_failures.json') michael@0: michael@0: if rerun_failures and not os.path.exists(failure_file_path): michael@0: print('No failure file present. Did you run mochitests before?') michael@0: return 1 michael@0: michael@0: from StringIO import StringIO michael@0: michael@0: # runtests.py is ambiguous, so we load the file/module manually. michael@0: if 'mochitest' not in sys.modules: michael@0: import imp michael@0: path = os.path.join(self.mochitest_dir, 'runtests.py') michael@0: with open(path, 'r') as fh: michael@0: imp.load_module('mochitest', fh, path, michael@0: ('.py', 'r', imp.PY_SOURCE)) michael@0: michael@0: import mozinfo michael@0: import mochitest michael@0: from manifestparser import TestManifest michael@0: from mozbuild.testing import TestResolver michael@0: michael@0: # This is required to make other components happy. Sad, isn't it? michael@0: os.chdir(self.topobjdir) michael@0: michael@0: # Automation installs its own stream handler to stdout. Since we want michael@0: # all logging to go through us, we just remove their handler. michael@0: remove_handlers = [l for l in logging.getLogger().handlers michael@0: if isinstance(l, logging.StreamHandler)] michael@0: for handler in remove_handlers: michael@0: logging.getLogger().removeHandler(handler) michael@0: michael@0: runner = mochitest.Mochitest() michael@0: michael@0: opts = mochitest.MochitestOptions() michael@0: options, args = opts.parse_args([]) michael@0: michael@0: options.subsuite = '' michael@0: flavor = suite michael@0: michael@0: # Need to set the suite options before verifyOptions below. michael@0: if suite == 'plain': michael@0: # Don't need additional options for plain. michael@0: flavor = 'mochitest' michael@0: elif suite == 'chrome': michael@0: options.chrome = True michael@0: elif suite == 'browser': michael@0: options.browserChrome = True michael@0: flavor = 'browser-chrome' michael@0: elif suite == 'devtools': michael@0: options.browserChrome = True michael@0: options.subsuite = 'devtools' michael@0: elif suite == 'metro': michael@0: options.immersiveMode = True michael@0: options.browserChrome = True michael@0: elif suite == 'a11y': michael@0: options.a11y = True michael@0: elif suite == 'webapprt-content': michael@0: options.webapprtContent = True michael@0: options.app = self.get_webapp_runtime_path() michael@0: elif suite == 'webapprt-chrome': michael@0: options.webapprtChrome = True michael@0: options.app = self.get_webapp_runtime_path() michael@0: options.browserArgs.append("-test-mode") michael@0: else: michael@0: raise Exception('None or unrecognized mochitest suite type.') michael@0: michael@0: if dmd: michael@0: options.dmdPath = self.bin_dir michael@0: michael@0: options.autorun = not no_autorun michael@0: options.closeWhenDone = not keep_open michael@0: options.slowscript = slowscript michael@0: options.screenshotOnFail = screenshot_on_fail michael@0: options.shuffle = shuffle michael@0: options.consoleLevel = 'INFO' michael@0: options.repeat = repeat michael@0: options.runUntilFailure = run_until_failure michael@0: options.runSlower = slow michael@0: options.testingModulesDir = os.path.join(self.tests_dir, 'modules') michael@0: options.extraProfileFiles.append(os.path.join(self.distdir, 'plugins')) michael@0: options.symbolsPath = os.path.join(self.distdir, 'crashreporter-symbols') michael@0: options.chunkByDir = chunk_by_dir michael@0: options.totalChunks = total_chunks michael@0: options.thisChunk = this_chunk michael@0: options.jsdebugger = jsdebugger michael@0: options.debugOnFailure = debug_on_failure michael@0: options.startAt = start_at michael@0: options.endAt = end_at michael@0: options.e10s = e10s michael@0: options.dumpAboutMemoryAfterTest = dump_about_memory_after_test michael@0: options.dumpDMDAfterTest = dump_dmd_after_test michael@0: options.dumpOutputDirectory = dump_output_directory michael@0: options.quiet = quiet michael@0: options.environment = environment michael@0: michael@0: options.failureFile = failure_file_path michael@0: if install_extension != None: michael@0: options.extensionsToInstall = [os.path.join(self.topsrcdir,install_extension)] michael@0: michael@0: for k, v in kwargs.iteritems(): michael@0: setattr(options, k, v) michael@0: michael@0: if test_paths: michael@0: resolver = self._spawn(TestResolver) michael@0: michael@0: tests = list(resolver.resolve_tests(paths=test_paths, flavor=flavor, michael@0: cwd=context.cwd)) michael@0: michael@0: if not tests: michael@0: print('No tests could be found in the path specified. Please ' michael@0: 'specify a path that is a test file or is a directory ' michael@0: 'containing tests.') michael@0: return 1 michael@0: michael@0: manifest = TestManifest() michael@0: manifest.tests.extend(tests) michael@0: michael@0: options.manifestFile = manifest michael@0: michael@0: if rerun_failures: michael@0: options.testManifest = failure_file_path michael@0: michael@0: if debugger: michael@0: options.debugger = debugger michael@0: michael@0: if debugger_args: michael@0: if options.debugger == None: michael@0: print("--debugger-args passed, but no debugger specified.") michael@0: return 1 michael@0: options.debuggerArgs = debugger_args michael@0: michael@0: options = opts.verifyOptions(options, runner) michael@0: michael@0: if options is None: michael@0: raise Exception('mochitest option validator failed.') michael@0: michael@0: # We need this to enable colorization of output. michael@0: self.log_manager.enable_unstructured() michael@0: michael@0: # Output processing is a little funky here. The old make targets michael@0: # grepped the log output from TEST-UNEXPECTED-* and printed these lines michael@0: # after test execution. Ideally the test runner would expose a Python michael@0: # API for obtaining test results and we could just format failures michael@0: # appropriately. Unfortunately, it doesn't yet do that. So, we capture michael@0: # all output to a buffer then "grep" the buffer after test execution. michael@0: # Bug 858197 tracks a Python API that would facilitate this. michael@0: test_output = StringIO() michael@0: handler = logging.StreamHandler(test_output) michael@0: handler.addFilter(UnexpectedFilter()) michael@0: handler.setFormatter(StructuredHumanFormatter(0, write_times=False)) michael@0: logging.getLogger().addHandler(handler) michael@0: michael@0: result = runner.runTests(options) michael@0: michael@0: # Need to remove our buffering handler before we echo failures or else michael@0: # it will catch them again! michael@0: logging.getLogger().removeHandler(handler) michael@0: self.log_manager.disable_unstructured() michael@0: michael@0: if test_output.getvalue(): michael@0: result = 1 michael@0: for line in test_output.getvalue().splitlines(): michael@0: self.log(logging.INFO, 'unexpected', {'msg': line}, '{msg}') michael@0: michael@0: return result michael@0: michael@0: michael@0: def MochitestCommand(func): michael@0: """Decorator that adds shared command arguments to mochitest commands.""" michael@0: michael@0: # This employs light Python magic. Keep in mind a decorator is just a michael@0: # function that takes a function, does something with it, then returns a michael@0: # (modified) function. Here, we chain decorators onto the passed in michael@0: # function. michael@0: michael@0: debugger = CommandArgument('--debugger', '-d', metavar='DEBUGGER', michael@0: help='Debugger binary to run test in. Program name or path.') michael@0: func = debugger(func) michael@0: michael@0: debugger_args = CommandArgument('--debugger-args', michael@0: metavar='DEBUGGER_ARGS', help='Arguments to pass to the debugger.') michael@0: func = debugger_args(func) michael@0: michael@0: # Bug 933807 introduced JS_DISABLE_SLOW_SCRIPT_SIGNALS to avoid clever michael@0: # segfaults induced by the slow-script-detecting logic for Ion/Odin JITted michael@0: # code. If we don't pass this, the user will need to periodically type michael@0: # "continue" to (safely) resume execution. There are ways to implement michael@0: # automatic resuming; see the bug. michael@0: slowscript = CommandArgument('--slowscript', action='store_true', michael@0: 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: func = slowscript(func) michael@0: michael@0: screenshot_on_fail = CommandArgument('--screenshot-on-fail', action='store_true', michael@0: help='Take screenshots on all test failures. Set $MOZ_UPLOAD_DIR to a directory for storing the screenshots.') michael@0: func = screenshot_on_fail(func) michael@0: michael@0: shuffle = CommandArgument('--shuffle', action='store_true', michael@0: help='Shuffle execution order.') michael@0: func = shuffle(func) michael@0: michael@0: keep_open = CommandArgument('--keep-open', action='store_true', michael@0: help='Keep the browser open after tests complete.') michael@0: func = keep_open(func) michael@0: michael@0: rerun = CommandArgument('--rerun-failures', action='store_true', michael@0: help='Run only the tests that failed during the last test run.') michael@0: func = rerun(func) michael@0: michael@0: autorun = CommandArgument('--no-autorun', action='store_true', michael@0: help='Do not starting running tests automatically.') michael@0: func = autorun(func) michael@0: michael@0: repeat = CommandArgument('--repeat', type=int, default=0, michael@0: help='Repeat the test the given number of times.') michael@0: func = repeat(func) michael@0: michael@0: runUntilFailure = CommandArgument("--run-until-failure", action='store_true', michael@0: help='Run tests repeatedly and stops on the first time a test fails. ' \ michael@0: 'Default cap is 30 runs, which can be overwritten ' \ michael@0: 'with the --repeat parameter.') michael@0: func = runUntilFailure(func) michael@0: michael@0: slow = CommandArgument('--slow', action='store_true', michael@0: help='Delay execution between tests.') michael@0: func = slow(func) michael@0: michael@0: end_at = CommandArgument('--end-at', type=str, michael@0: help='Stop running the test sequence at this test.') michael@0: func = end_at(func) michael@0: michael@0: start_at = CommandArgument('--start-at', type=str, michael@0: help='Start running the test sequence at this test.') michael@0: func = start_at(func) michael@0: michael@0: chunk_dir = CommandArgument('--chunk-by-dir', type=int, michael@0: help='Group tests together in chunks by this many top directories.') michael@0: func = chunk_dir(func) michael@0: michael@0: chunk_total = CommandArgument('--total-chunks', type=int, michael@0: help='Total number of chunks to split tests into.') michael@0: func = chunk_total(func) michael@0: michael@0: this_chunk = CommandArgument('--this-chunk', type=int, michael@0: help='If running tests by chunks, the number of the chunk to run.') michael@0: func = this_chunk(func) michael@0: michael@0: hide_subtests = CommandArgument('--hide-subtests', action='store_true', michael@0: help='If specified, will only log subtest results on failure or timeout.') michael@0: func = hide_subtests(func) michael@0: michael@0: debug_on_failure = CommandArgument('--debug-on-failure', action='store_true', michael@0: help='Breaks execution and enters the JS debugger on a test failure. ' \ michael@0: 'Should be used together with --jsdebugger.') michael@0: func = debug_on_failure(func) michael@0: michael@0: jsdebugger = CommandArgument('--jsdebugger', action='store_true', michael@0: help='Start the browser JS debugger before running the test. Implies --no-autorun.') michael@0: func = jsdebugger(func) michael@0: michael@0: this_chunk = CommandArgument('--e10s', action='store_true', michael@0: help='Run tests with electrolysis preferences and test filtering enabled.') michael@0: func = this_chunk(func) michael@0: michael@0: dmd = CommandArgument('--dmd', action='store_true', michael@0: help='Run tests with DMD active.') michael@0: func = dmd(func) michael@0: michael@0: dumpAboutMemory = CommandArgument('--dump-about-memory-after-test', action='store_true', michael@0: help='Dump an about:memory log after every test.') michael@0: func = dumpAboutMemory(func) michael@0: michael@0: dumpDMD = CommandArgument('--dump-dmd-after-test', action='store_true', michael@0: help='Dump a DMD log after every test.') michael@0: func = dumpDMD(func) michael@0: michael@0: dumpOutputDirectory = CommandArgument('--dump-output-directory', action='store', michael@0: help='Specifies the directory in which to place dumped memory reports.') michael@0: func = dumpOutputDirectory(func) michael@0: michael@0: path = CommandArgument('test_paths', default=None, nargs='*', michael@0: metavar='TEST', michael@0: help='Test to run. Can be specified as a single file, a ' \ michael@0: 'directory, or omitted. If omitted, the entire test suite is ' \ michael@0: 'executed.') michael@0: func = path(func) michael@0: michael@0: install_extension = CommandArgument('--install-extension', michael@0: help='Install given extension before running selected tests. ' \ michael@0: 'Parameter is a path to xpi file.') michael@0: func = install_extension(func) michael@0: michael@0: quiet = CommandArgument('--quiet', default=False, action='store_true', michael@0: help='Do not print test log lines unless a failure occurs.') michael@0: func = quiet(func) michael@0: michael@0: setenv = CommandArgument('--setenv', default=[], action='append', michael@0: metavar='NAME=VALUE', dest='environment', michael@0: help="Sets the given variable in the application's environment") michael@0: func = setenv(func) michael@0: michael@0: return func michael@0: michael@0: def B2GCommand(func): michael@0: """Decorator that adds shared command arguments to b2g mochitest commands.""" michael@0: michael@0: busybox = CommandArgument('--busybox', default=None, michael@0: help='Path to busybox binary to install on device') michael@0: func = busybox(func) michael@0: michael@0: logcatdir = CommandArgument('--logcat-dir', default=None, michael@0: help='directory to store logcat dump files') michael@0: func = logcatdir(func) michael@0: michael@0: profile = CommandArgument('--profile', default=None, michael@0: help='for desktop testing, the path to the \ michael@0: gaia profile to use') michael@0: func = profile(func) michael@0: michael@0: geckopath = CommandArgument('--gecko-path', default=None, michael@0: help='the path to a gecko distribution that should \ michael@0: be installed on the emulator prior to test') michael@0: func = geckopath(func) michael@0: michael@0: nowindow = CommandArgument('--no-window', action='store_true', default=False, michael@0: help='Pass --no-window to the emulator') michael@0: func = nowindow(func) michael@0: michael@0: sdcard = CommandArgument('--sdcard', default="10MB", michael@0: help='Define size of sdcard: 1MB, 50MB...etc') michael@0: func = sdcard(func) michael@0: michael@0: emulator = CommandArgument('--emulator', default='arm', michael@0: help='Architecture of emulator to use: x86 or arm') michael@0: func = emulator(func) michael@0: michael@0: marionette = CommandArgument('--marionette', default=None, michael@0: help='host:port to use when connecting to Marionette') michael@0: func = marionette(func) michael@0: michael@0: chunk_total = CommandArgument('--total-chunks', type=int, michael@0: help='Total number of chunks to split tests into.') michael@0: func = chunk_total(func) michael@0: michael@0: this_chunk = CommandArgument('--this-chunk', type=int, michael@0: help='If running tests by chunks, the number of the chunk to run.') michael@0: func = this_chunk(func) michael@0: michael@0: hide_subtests = CommandArgument('--hide-subtests', action='store_true', michael@0: help='If specified, will only log subtest results on failure or timeout.') michael@0: func = hide_subtests(func) michael@0: michael@0: path = CommandArgument('test_paths', default=None, nargs='*', michael@0: metavar='TEST', michael@0: help='Test to run. Can be specified as a single file, a ' \ michael@0: 'directory, or omitted. If omitted, the entire test suite is ' \ michael@0: 'executed.') michael@0: func = path(func) michael@0: michael@0: return func michael@0: michael@0: michael@0: michael@0: @CommandProvider michael@0: class MachCommands(MachCommandBase): michael@0: @Command('mochitest-plain', category='testing', michael@0: conditions=[conditions.is_firefox], michael@0: description='Run a plain mochitest.') michael@0: @MochitestCommand michael@0: def run_mochitest_plain(self, test_paths, **kwargs): michael@0: return self.run_mochitest(test_paths, 'plain', **kwargs) michael@0: michael@0: @Command('mochitest-chrome', category='testing', michael@0: conditions=[conditions.is_firefox], michael@0: description='Run a chrome mochitest.') michael@0: @MochitestCommand michael@0: def run_mochitest_chrome(self, test_paths, **kwargs): michael@0: return self.run_mochitest(test_paths, 'chrome', **kwargs) michael@0: michael@0: @Command('mochitest-browser', category='testing', michael@0: conditions=[conditions.is_firefox], michael@0: description='Run a mochitest with browser chrome.') michael@0: @MochitestCommand michael@0: def run_mochitest_browser(self, test_paths, **kwargs): michael@0: return self.run_mochitest(test_paths, 'browser', **kwargs) michael@0: michael@0: @Command('mochitest-devtools', category='testing', michael@0: conditions=[conditions.is_firefox], michael@0: description='Run a devtools mochitest with browser chrome.') michael@0: @MochitestCommand michael@0: def run_mochitest_devtools(self, test_paths, **kwargs): michael@0: return self.run_mochitest(test_paths, 'devtools', **kwargs) michael@0: michael@0: @Command('mochitest-metro', category='testing', michael@0: conditions=[conditions.is_firefox], michael@0: description='Run a mochitest with metro browser chrome.') michael@0: @MochitestCommand michael@0: def run_mochitest_metro(self, test_paths, **kwargs): michael@0: return self.run_mochitest(test_paths, 'metro', **kwargs) michael@0: michael@0: @Command('mochitest-a11y', category='testing', michael@0: conditions=[conditions.is_firefox], michael@0: description='Run an a11y mochitest.') michael@0: @MochitestCommand michael@0: def run_mochitest_a11y(self, test_paths, **kwargs): michael@0: return self.run_mochitest(test_paths, 'a11y', **kwargs) michael@0: michael@0: @Command('webapprt-test-chrome', category='testing', michael@0: conditions=[conditions.is_firefox], michael@0: description='Run a webapprt chrome mochitest.') michael@0: @MochitestCommand michael@0: def run_mochitest_webapprt_chrome(self, test_paths, **kwargs): michael@0: return self.run_mochitest(test_paths, 'webapprt-chrome', **kwargs) michael@0: michael@0: @Command('webapprt-test-content', category='testing', michael@0: conditions=[conditions.is_firefox], michael@0: description='Run a webapprt content mochitest.') michael@0: @MochitestCommand michael@0: def run_mochitest_webapprt_content(self, test_paths, **kwargs): michael@0: return self.run_mochitest(test_paths, 'webapprt-content', **kwargs) michael@0: michael@0: def run_mochitest(self, test_paths, flavor, **kwargs): michael@0: from mozbuild.controller.building import BuildDriver michael@0: michael@0: self._ensure_state_subdir_exists('.') michael@0: michael@0: driver = self._spawn(BuildDriver) michael@0: driver.install_tests(remove=False) michael@0: michael@0: mochitest = self._spawn(MochitestRunner) michael@0: michael@0: return mochitest.run_desktop_test(self._mach_context, michael@0: test_paths=test_paths, suite=flavor, **kwargs) michael@0: michael@0: michael@0: # TODO For now b2g commands will only work with the emulator, michael@0: # they should be modified to work with all devices. michael@0: def is_emulator(cls): michael@0: """Emulator needs to be configured.""" michael@0: return cls.device_name.find('emulator') == 0 michael@0: michael@0: michael@0: @CommandProvider michael@0: class B2GCommands(MachCommandBase): michael@0: """So far these are only mochitest plain. They are michael@0: implemented separately because their command lines michael@0: are completely different. michael@0: """ michael@0: def __init__(self, context): michael@0: MachCommandBase.__init__(self, context) michael@0: michael@0: for attr in ('b2g_home', 'xre_path', 'device_name'): michael@0: setattr(self, attr, getattr(context, attr, None)) michael@0: michael@0: @Command('mochitest-remote', category='testing', michael@0: description='Run a remote mochitest.', michael@0: conditions=[conditions.is_b2g, is_emulator]) michael@0: @B2GCommand michael@0: def run_mochitest_remote(self, test_paths, **kwargs): michael@0: from mozbuild.controller.building import BuildDriver michael@0: michael@0: self._ensure_state_subdir_exists('.') michael@0: michael@0: driver = self._spawn(BuildDriver) michael@0: driver.install_tests(remove=False) michael@0: michael@0: mochitest = self._spawn(MochitestRunner) michael@0: return mochitest.run_b2g_test(b2g_home=self.b2g_home, michael@0: xre_path=self.xre_path, test_paths=test_paths, **kwargs) michael@0: michael@0: @Command('mochitest-b2g-desktop', category='testing', michael@0: conditions=[conditions.is_b2g_desktop], michael@0: description='Run a b2g desktop mochitest.') michael@0: @B2GCommand michael@0: def run_mochitest_b2g_desktop(self, test_paths, **kwargs): michael@0: from mozbuild.controller.building import BuildDriver michael@0: michael@0: self._ensure_state_subdir_exists('.') michael@0: michael@0: driver = self._spawn(BuildDriver) michael@0: driver.install_tests(remove=False) michael@0: michael@0: mochitest = self._spawn(MochitestRunner) michael@0: return mochitest.run_b2g_test(test_paths=test_paths, **kwargs)