testing/mochitest/mach_commands.py

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:9497c5053fb3
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/.
4
5 from __future__ import unicode_literals
6
7 import logging
8 import mozpack.path
9 import os
10 import platform
11 import sys
12 import warnings
13 import which
14
15 from mozbuild.base import (
16 MachCommandBase,
17 MachCommandConditions as conditions,
18 MozbuildObject,
19 )
20
21 from mach.decorators import (
22 CommandArgument,
23 CommandProvider,
24 Command,
25 )
26
27 from mach.logging import StructuredHumanFormatter
28
29 ADB_NOT_FOUND = '''
30 The %s command requires the adb binary to be on your path.
31
32 If you have a B2G build, this can be found in
33 '%s/out/host/<platform>/bin'.
34 '''.lstrip()
35
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.
39
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
44
45 The profile should be generated in a directory called 'profile'.
46 '''.lstrip()
47
48 GAIA_PROFILE_IS_DEBUG = '''
49 The %s command requires a non-debug gaia profile. The specified profile,
50 %s, is a debug profile.
51
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
56
57 The profile should be generated in a directory called 'profile'.
58 '''.lstrip()
59
60
61 class UnexpectedFilter(logging.Filter):
62 def filter(self, record):
63 msg = getattr(record, 'params', {}).get('msg', '')
64 return 'TEST-UNEXPECTED-' in msg
65
66
67 class MochitestRunner(MozbuildObject):
68 """Easily run mochitests.
69
70 This currently contains just the basics for running mochitests. We may want
71 to hook up result parsing, etc.
72 """
73
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
83
84 def __init__(self, *args, **kwargs):
85 MozbuildObject.__init__(self, *args, **kwargs)
86
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)
91
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')
95
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.
100
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()
111
112 # TODO without os.chdir, chained imports fail below
113 os.chdir(self.mochitest_dir)
114
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')
119
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))
125
126 import mochitest
127 from mochitest_options import B2GOptions
128
129 parser = B2GOptions()
130 options = parser.parse_args([])[0]
131
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
141
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
147
148 options.symbolsPath = os.path.join(self.distdir, 'crashreporter-symbols')
149
150 options.consoleLevel = 'INFO'
151 if conditions.is_b2g_desktop(self):
152
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
157
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
163
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')]
170
171 return mochitest.run_desktop_mochitests(parser, options)
172
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
179
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)
185
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.
195
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.
199
200 suite is the type of mochitest to run. It can be one of ('plain',
201 'chrome', 'browser', 'metro', 'a11y').
202
203 debugger is a program name or path to a binary (presumably a debugger)
204 to run the test in. e.g. 'gdb'
205
206 debugger_args are the arguments passed to the debugger.
207
208 slowscript is true if the user has requested the SIGSEGV mechanism of
209 invoking the slow script dialog.
210
211 shuffle is whether test order should be shuffled (defaults to false).
212
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
219
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]
223
224 failure_file_path = os.path.join(self.statedir, 'mochitest_failures.json')
225
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
229
230 from StringIO import StringIO
231
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))
239
240 import mozinfo
241 import mochitest
242 from manifestparser import TestManifest
243 from mozbuild.testing import TestResolver
244
245 # This is required to make other components happy. Sad, isn't it?
246 os.chdir(self.topobjdir)
247
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)
254
255 runner = mochitest.Mochitest()
256
257 opts = mochitest.MochitestOptions()
258 options, args = opts.parse_args([])
259
260 options.subsuite = ''
261 flavor = suite
262
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.')
289
290 if dmd:
291 options.dmdPath = self.bin_dir
292
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
318
319 options.failureFile = failure_file_path
320 if install_extension != None:
321 options.extensionsToInstall = [os.path.join(self.topsrcdir,install_extension)]
322
323 for k, v in kwargs.iteritems():
324 setattr(options, k, v)
325
326 if test_paths:
327 resolver = self._spawn(TestResolver)
328
329 tests = list(resolver.resolve_tests(paths=test_paths, flavor=flavor,
330 cwd=context.cwd))
331
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
337
338 manifest = TestManifest()
339 manifest.tests.extend(tests)
340
341 options.manifestFile = manifest
342
343 if rerun_failures:
344 options.testManifest = failure_file_path
345
346 if debugger:
347 options.debugger = debugger
348
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
354
355 options = opts.verifyOptions(options, runner)
356
357 if options is None:
358 raise Exception('mochitest option validator failed.')
359
360 # We need this to enable colorization of output.
361 self.log_manager.enable_unstructured()
362
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)
375
376 result = runner.runTests(options)
377
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()
382
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}')
387
388 return result
389
390
391 def MochitestCommand(func):
392 """Decorator that adds shared command arguments to mochitest commands."""
393
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.
398
399 debugger = CommandArgument('--debugger', '-d', metavar='DEBUGGER',
400 help='Debugger binary to run test in. Program name or path.')
401 func = debugger(func)
402
403 debugger_args = CommandArgument('--debugger-args',
404 metavar='DEBUGGER_ARGS', help='Arguments to pass to the debugger.')
405 func = debugger_args(func)
406
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)
415
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)
419
420 shuffle = CommandArgument('--shuffle', action='store_true',
421 help='Shuffle execution order.')
422 func = shuffle(func)
423
424 keep_open = CommandArgument('--keep-open', action='store_true',
425 help='Keep the browser open after tests complete.')
426 func = keep_open(func)
427
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)
431
432 autorun = CommandArgument('--no-autorun', action='store_true',
433 help='Do not starting running tests automatically.')
434 func = autorun(func)
435
436 repeat = CommandArgument('--repeat', type=int, default=0,
437 help='Repeat the test the given number of times.')
438 func = repeat(func)
439
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)
445
446 slow = CommandArgument('--slow', action='store_true',
447 help='Delay execution between tests.')
448 func = slow(func)
449
450 end_at = CommandArgument('--end-at', type=str,
451 help='Stop running the test sequence at this test.')
452 func = end_at(func)
453
454 start_at = CommandArgument('--start-at', type=str,
455 help='Start running the test sequence at this test.')
456 func = start_at(func)
457
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)
461
462 chunk_total = CommandArgument('--total-chunks', type=int,
463 help='Total number of chunks to split tests into.')
464 func = chunk_total(func)
465
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)
469
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)
473
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)
478
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)
482
483 this_chunk = CommandArgument('--e10s', action='store_true',
484 help='Run tests with electrolysis preferences and test filtering enabled.')
485 func = this_chunk(func)
486
487 dmd = CommandArgument('--dmd', action='store_true',
488 help='Run tests with DMD active.')
489 func = dmd(func)
490
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)
494
495 dumpDMD = CommandArgument('--dump-dmd-after-test', action='store_true',
496 help='Dump a DMD log after every test.')
497 func = dumpDMD(func)
498
499 dumpOutputDirectory = CommandArgument('--dump-output-directory', action='store',
500 help='Specifies the directory in which to place dumped memory reports.')
501 func = dumpOutputDirectory(func)
502
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)
509
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)
514
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)
518
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)
523
524 return func
525
526 def B2GCommand(func):
527 """Decorator that adds shared command arguments to b2g mochitest commands."""
528
529 busybox = CommandArgument('--busybox', default=None,
530 help='Path to busybox binary to install on device')
531 func = busybox(func)
532
533 logcatdir = CommandArgument('--logcat-dir', default=None,
534 help='directory to store logcat dump files')
535 func = logcatdir(func)
536
537 profile = CommandArgument('--profile', default=None,
538 help='for desktop testing, the path to the \
539 gaia profile to use')
540 func = profile(func)
541
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)
546
547 nowindow = CommandArgument('--no-window', action='store_true', default=False,
548 help='Pass --no-window to the emulator')
549 func = nowindow(func)
550
551 sdcard = CommandArgument('--sdcard', default="10MB",
552 help='Define size of sdcard: 1MB, 50MB...etc')
553 func = sdcard(func)
554
555 emulator = CommandArgument('--emulator', default='arm',
556 help='Architecture of emulator to use: x86 or arm')
557 func = emulator(func)
558
559 marionette = CommandArgument('--marionette', default=None,
560 help='host:port to use when connecting to Marionette')
561 func = marionette(func)
562
563 chunk_total = CommandArgument('--total-chunks', type=int,
564 help='Total number of chunks to split tests into.')
565 func = chunk_total(func)
566
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)
570
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)
574
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)
581
582 return func
583
584
585
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)
594
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)
601
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)
608
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)
615
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)
622
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)
629
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)
636
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)
643
644 def run_mochitest(self, test_paths, flavor, **kwargs):
645 from mozbuild.controller.building import BuildDriver
646
647 self._ensure_state_subdir_exists('.')
648
649 driver = self._spawn(BuildDriver)
650 driver.install_tests(remove=False)
651
652 mochitest = self._spawn(MochitestRunner)
653
654 return mochitest.run_desktop_test(self._mach_context,
655 test_paths=test_paths, suite=flavor, **kwargs)
656
657
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
663
664
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)
673
674 for attr in ('b2g_home', 'xre_path', 'device_name'):
675 setattr(self, attr, getattr(context, attr, None))
676
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
683
684 self._ensure_state_subdir_exists('.')
685
686 driver = self._spawn(BuildDriver)
687 driver.install_tests(remove=False)
688
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)
692
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
699
700 self._ensure_state_subdir_exists('.')
701
702 driver = self._spawn(BuildDriver)
703 driver.install_tests(remove=False)
704
705 mochitest = self._spawn(MochitestRunner)
706 return mochitest.run_b2g_test(test_paths=test_paths, **kwargs)

mercurial