Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
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 | # Integrates the xpcshell test runner with mach. |
michael@0 | 6 | |
michael@0 | 7 | from __future__ import unicode_literals, print_function |
michael@0 | 8 | |
michael@0 | 9 | import mozpack.path |
michael@0 | 10 | import logging |
michael@0 | 11 | import os |
michael@0 | 12 | import shutil |
michael@0 | 13 | import sys |
michael@0 | 14 | import urllib2 |
michael@0 | 15 | |
michael@0 | 16 | from StringIO import StringIO |
michael@0 | 17 | |
michael@0 | 18 | from mozbuild.base import ( |
michael@0 | 19 | MachCommandBase, |
michael@0 | 20 | MozbuildObject, |
michael@0 | 21 | MachCommandConditions as conditions, |
michael@0 | 22 | ) |
michael@0 | 23 | |
michael@0 | 24 | from mach.decorators import ( |
michael@0 | 25 | CommandArgument, |
michael@0 | 26 | CommandProvider, |
michael@0 | 27 | Command, |
michael@0 | 28 | ) |
michael@0 | 29 | |
michael@0 | 30 | ADB_NOT_FOUND = ''' |
michael@0 | 31 | The %s command requires the adb binary to be on your path. |
michael@0 | 32 | |
michael@0 | 33 | If you have a B2G build, this can be found in |
michael@0 | 34 | '%s/out/host/<platform>/bin'. |
michael@0 | 35 | '''.lstrip() |
michael@0 | 36 | |
michael@0 | 37 | BUSYBOX_URL = 'http://www.busybox.net/downloads/binaries/latest/busybox-armv7l' |
michael@0 | 38 | |
michael@0 | 39 | |
michael@0 | 40 | if sys.version_info[0] < 3: |
michael@0 | 41 | unicode_type = unicode |
michael@0 | 42 | else: |
michael@0 | 43 | unicode_type = str |
michael@0 | 44 | |
michael@0 | 45 | # Simple filter to omit the message emitted as a test file begins. |
michael@0 | 46 | class TestStartFilter(logging.Filter): |
michael@0 | 47 | def filter(self, record): |
michael@0 | 48 | return not record.params['msg'].endswith("running test ...") |
michael@0 | 49 | |
michael@0 | 50 | # This should probably be consolidated with similar classes in other test |
michael@0 | 51 | # runners. |
michael@0 | 52 | class InvalidTestPathError(Exception): |
michael@0 | 53 | """Exception raised when the test path is not valid.""" |
michael@0 | 54 | |
michael@0 | 55 | |
michael@0 | 56 | class XPCShellRunner(MozbuildObject): |
michael@0 | 57 | """Run xpcshell tests.""" |
michael@0 | 58 | def run_suite(self, **kwargs): |
michael@0 | 59 | from manifestparser import TestManifest |
michael@0 | 60 | manifest = TestManifest(manifests=[os.path.join(self.topobjdir, |
michael@0 | 61 | '_tests', 'xpcshell', 'xpcshell.ini')]) |
michael@0 | 62 | |
michael@0 | 63 | return self._run_xpcshell_harness(manifest=manifest, **kwargs) |
michael@0 | 64 | |
michael@0 | 65 | def run_test(self, test_paths, interactive=False, |
michael@0 | 66 | keep_going=False, sequential=False, shuffle=False, |
michael@0 | 67 | debugger=None, debuggerArgs=None, debuggerInteractive=None, |
michael@0 | 68 | rerun_failures=False, |
michael@0 | 69 | # ignore parameters from other platforms' options |
michael@0 | 70 | **kwargs): |
michael@0 | 71 | """Runs an individual xpcshell test.""" |
michael@0 | 72 | from mozbuild.testing import TestResolver |
michael@0 | 73 | from manifestparser import TestManifest |
michael@0 | 74 | |
michael@0 | 75 | # TODO Bug 794506 remove once mach integrates with virtualenv. |
michael@0 | 76 | build_path = os.path.join(self.topobjdir, 'build') |
michael@0 | 77 | if build_path not in sys.path: |
michael@0 | 78 | sys.path.append(build_path) |
michael@0 | 79 | |
michael@0 | 80 | if test_paths == ['all']: |
michael@0 | 81 | self.run_suite(interactive=interactive, |
michael@0 | 82 | keep_going=keep_going, shuffle=shuffle, sequential=sequential, |
michael@0 | 83 | debugger=debugger, debuggerArgs=debuggerArgs, |
michael@0 | 84 | debuggerInteractive=debuggerInteractive, |
michael@0 | 85 | rerun_failures=rerun_failures) |
michael@0 | 86 | return |
michael@0 | 87 | |
michael@0 | 88 | resolver = self._spawn(TestResolver) |
michael@0 | 89 | tests = list(resolver.resolve_tests(paths=test_paths, flavor='xpcshell', |
michael@0 | 90 | cwd=self.cwd)) |
michael@0 | 91 | |
michael@0 | 92 | if not tests: |
michael@0 | 93 | raise InvalidTestPathError('We could not find an xpcshell test ' |
michael@0 | 94 | 'for the passed test path. Please select a path that is ' |
michael@0 | 95 | 'a test file or is a directory containing xpcshell tests.') |
michael@0 | 96 | |
michael@0 | 97 | # Dynamically write out a manifest holding all the discovered tests. |
michael@0 | 98 | manifest = TestManifest() |
michael@0 | 99 | manifest.tests.extend(tests) |
michael@0 | 100 | |
michael@0 | 101 | args = { |
michael@0 | 102 | 'interactive': interactive, |
michael@0 | 103 | 'keep_going': keep_going, |
michael@0 | 104 | 'shuffle': shuffle, |
michael@0 | 105 | 'sequential': sequential, |
michael@0 | 106 | 'debugger': debugger, |
michael@0 | 107 | 'debuggerArgs': debuggerArgs, |
michael@0 | 108 | 'debuggerInteractive': debuggerInteractive, |
michael@0 | 109 | 'rerun_failures': rerun_failures, |
michael@0 | 110 | 'manifest': manifest, |
michael@0 | 111 | } |
michael@0 | 112 | |
michael@0 | 113 | return self._run_xpcshell_harness(**args) |
michael@0 | 114 | |
michael@0 | 115 | def _run_xpcshell_harness(self, manifest, |
michael@0 | 116 | test_path=None, shuffle=False, interactive=False, |
michael@0 | 117 | keep_going=False, sequential=False, |
michael@0 | 118 | debugger=None, debuggerArgs=None, debuggerInteractive=None, |
michael@0 | 119 | rerun_failures=False): |
michael@0 | 120 | |
michael@0 | 121 | # Obtain a reference to the xpcshell test runner. |
michael@0 | 122 | import runxpcshelltests |
michael@0 | 123 | |
michael@0 | 124 | dummy_log = StringIO() |
michael@0 | 125 | xpcshell = runxpcshelltests.XPCShellTests(log=dummy_log) |
michael@0 | 126 | self.log_manager.enable_unstructured() |
michael@0 | 127 | |
michael@0 | 128 | xpcshell_filter = TestStartFilter() |
michael@0 | 129 | self.log_manager.terminal_handler.addFilter(xpcshell_filter) |
michael@0 | 130 | |
michael@0 | 131 | tests_dir = os.path.join(self.topobjdir, '_tests', 'xpcshell') |
michael@0 | 132 | modules_dir = os.path.join(self.topobjdir, '_tests', 'modules') |
michael@0 | 133 | # We want output from the test to be written immediately if we are only |
michael@0 | 134 | # running a single test. |
michael@0 | 135 | verbose_output = test_path is not None or (manifest and len(manifest.test_paths())==1) |
michael@0 | 136 | |
michael@0 | 137 | args = { |
michael@0 | 138 | 'manifest': manifest, |
michael@0 | 139 | 'xpcshell': os.path.join(self.bindir, 'xpcshell'), |
michael@0 | 140 | 'mozInfo': os.path.join(self.topobjdir, 'mozinfo.json'), |
michael@0 | 141 | 'symbolsPath': os.path.join(self.distdir, 'crashreporter-symbols'), |
michael@0 | 142 | 'interactive': interactive, |
michael@0 | 143 | 'keepGoing': keep_going, |
michael@0 | 144 | 'logfiles': False, |
michael@0 | 145 | 'sequential': sequential, |
michael@0 | 146 | 'shuffle': shuffle, |
michael@0 | 147 | 'testsRootDir': tests_dir, |
michael@0 | 148 | 'testingModulesDir': modules_dir, |
michael@0 | 149 | 'profileName': 'firefox', |
michael@0 | 150 | 'verbose': test_path is not None, |
michael@0 | 151 | 'xunitFilename': os.path.join(self.statedir, 'xpchsell.xunit.xml'), |
michael@0 | 152 | 'xunitName': 'xpcshell', |
michael@0 | 153 | 'pluginsPath': os.path.join(self.distdir, 'plugins'), |
michael@0 | 154 | 'debugger': debugger, |
michael@0 | 155 | 'debuggerArgs': debuggerArgs, |
michael@0 | 156 | 'debuggerInteractive': debuggerInteractive, |
michael@0 | 157 | 'on_message': (lambda obj, msg: xpcshell.log.info(msg.decode('utf-8', 'replace'))) \ |
michael@0 | 158 | if verbose_output else None, |
michael@0 | 159 | } |
michael@0 | 160 | |
michael@0 | 161 | if test_path is not None: |
michael@0 | 162 | args['testPath'] = test_path |
michael@0 | 163 | |
michael@0 | 164 | # A failure manifest is written by default. If --rerun-failures is |
michael@0 | 165 | # specified and a prior failure manifest is found, the prior manifest |
michael@0 | 166 | # will be run. A new failure manifest is always written over any |
michael@0 | 167 | # prior failure manifest. |
michael@0 | 168 | failure_manifest_path = os.path.join(self.statedir, 'xpcshell.failures.ini') |
michael@0 | 169 | rerun_manifest_path = os.path.join(self.statedir, 'xpcshell.rerun.ini') |
michael@0 | 170 | if os.path.exists(failure_manifest_path) and rerun_failures: |
michael@0 | 171 | shutil.move(failure_manifest_path, rerun_manifest_path) |
michael@0 | 172 | args['manifest'] = rerun_manifest_path |
michael@0 | 173 | elif os.path.exists(failure_manifest_path): |
michael@0 | 174 | os.remove(failure_manifest_path) |
michael@0 | 175 | elif rerun_failures: |
michael@0 | 176 | print("No failures were found to re-run.") |
michael@0 | 177 | return 0 |
michael@0 | 178 | args['failureManifest'] = failure_manifest_path |
michael@0 | 179 | |
michael@0 | 180 | # Python through 2.7.2 has issues with unicode in some of the |
michael@0 | 181 | # arguments. Work around that. |
michael@0 | 182 | filtered_args = {} |
michael@0 | 183 | for k, v in args.items(): |
michael@0 | 184 | if isinstance(v, unicode_type): |
michael@0 | 185 | v = v.encode('utf-8') |
michael@0 | 186 | |
michael@0 | 187 | if isinstance(k, unicode_type): |
michael@0 | 188 | k = k.encode('utf-8') |
michael@0 | 189 | |
michael@0 | 190 | filtered_args[k] = v |
michael@0 | 191 | |
michael@0 | 192 | result = xpcshell.runTests(**filtered_args) |
michael@0 | 193 | |
michael@0 | 194 | self.log_manager.terminal_handler.removeFilter(xpcshell_filter) |
michael@0 | 195 | self.log_manager.disable_unstructured() |
michael@0 | 196 | |
michael@0 | 197 | if not result and not xpcshell.sequential: |
michael@0 | 198 | print("Tests were run in parallel. Try running with --sequential " |
michael@0 | 199 | "to make sure the failures were not caused by this.") |
michael@0 | 200 | return int(not result) |
michael@0 | 201 | |
michael@0 | 202 | class AndroidXPCShellRunner(MozbuildObject): |
michael@0 | 203 | """Get specified DeviceManager""" |
michael@0 | 204 | def get_devicemanager(self, devicemanager, ip, port, remote_test_root): |
michael@0 | 205 | from mozdevice import devicemanagerADB, devicemanagerSUT |
michael@0 | 206 | dm = None |
michael@0 | 207 | if devicemanager == "adb": |
michael@0 | 208 | if ip: |
michael@0 | 209 | dm = devicemanagerADB.DeviceManagerADB(ip, port, packageName=None, deviceRoot=remote_test_root) |
michael@0 | 210 | else: |
michael@0 | 211 | dm = devicemanagerADB.DeviceManagerADB(packageName=None, deviceRoot=remote_test_root) |
michael@0 | 212 | else: |
michael@0 | 213 | if ip: |
michael@0 | 214 | dm = devicemanagerSUT.DeviceManagerSUT(ip, port, deviceRoot=remote_test_root) |
michael@0 | 215 | else: |
michael@0 | 216 | raise Exception("You must provide a device IP to connect to via the --ip option") |
michael@0 | 217 | return dm |
michael@0 | 218 | |
michael@0 | 219 | """Run Android xpcshell tests.""" |
michael@0 | 220 | def run_test(self, |
michael@0 | 221 | test_paths, keep_going, |
michael@0 | 222 | devicemanager, ip, port, remote_test_root, no_setup, local_apk, |
michael@0 | 223 | # ignore parameters from other platforms' options |
michael@0 | 224 | **kwargs): |
michael@0 | 225 | # TODO Bug 794506 remove once mach integrates with virtualenv. |
michael@0 | 226 | build_path = os.path.join(self.topobjdir, 'build') |
michael@0 | 227 | if build_path not in sys.path: |
michael@0 | 228 | sys.path.append(build_path) |
michael@0 | 229 | |
michael@0 | 230 | import remotexpcshelltests |
michael@0 | 231 | |
michael@0 | 232 | dm = self.get_devicemanager(devicemanager, ip, port, remote_test_root) |
michael@0 | 233 | |
michael@0 | 234 | options = remotexpcshelltests.RemoteXPCShellOptions() |
michael@0 | 235 | options.shuffle = False |
michael@0 | 236 | options.sequential = True |
michael@0 | 237 | options.interactive = False |
michael@0 | 238 | options.debugger = None |
michael@0 | 239 | options.debuggerArgs = None |
michael@0 | 240 | options.setup = not no_setup |
michael@0 | 241 | options.keepGoing = keep_going |
michael@0 | 242 | options.objdir = self.topobjdir |
michael@0 | 243 | options.localLib = os.path.join(self.topobjdir, 'dist/fennec') |
michael@0 | 244 | options.localBin = os.path.join(self.topobjdir, 'dist/bin') |
michael@0 | 245 | options.testingModulesDir = os.path.join(self.topobjdir, '_tests/modules') |
michael@0 | 246 | options.mozInfo = os.path.join(self.topobjdir, 'mozinfo.json') |
michael@0 | 247 | options.manifest = os.path.join(self.topobjdir, '_tests/xpcshell/xpcshell_android.ini') |
michael@0 | 248 | options.symbolsPath = os.path.join(self.distdir, 'crashreporter-symbols') |
michael@0 | 249 | if local_apk: |
michael@0 | 250 | options.localAPK = local_apk |
michael@0 | 251 | else: |
michael@0 | 252 | for file in os.listdir(os.path.join(options.objdir, "dist")): |
michael@0 | 253 | if file.endswith(".apk") and file.startswith("fennec"): |
michael@0 | 254 | options.localAPK = os.path.join(options.objdir, "dist") |
michael@0 | 255 | options.localAPK = os.path.join(options.localAPK, file) |
michael@0 | 256 | print ("using APK: " + options.localAPK) |
michael@0 | 257 | break |
michael@0 | 258 | else: |
michael@0 | 259 | raise Exception("You must specify an APK") |
michael@0 | 260 | |
michael@0 | 261 | if test_paths == ['all']: |
michael@0 | 262 | testdirs = [] |
michael@0 | 263 | options.testPath = None |
michael@0 | 264 | options.verbose = False |
michael@0 | 265 | else: |
michael@0 | 266 | if len(test_paths) > 1: |
michael@0 | 267 | print('Warning: only the first test path argument will be used.') |
michael@0 | 268 | testdirs = test_paths[0] |
michael@0 | 269 | options.testPath = test_paths[0] |
michael@0 | 270 | options.verbose = True |
michael@0 | 271 | dummy_log = StringIO() |
michael@0 | 272 | xpcshell = remotexpcshelltests.XPCShellRemote(dm, options, args=testdirs, log=dummy_log) |
michael@0 | 273 | self.log_manager.enable_unstructured() |
michael@0 | 274 | |
michael@0 | 275 | xpcshell_filter = TestStartFilter() |
michael@0 | 276 | self.log_manager.terminal_handler.addFilter(xpcshell_filter) |
michael@0 | 277 | |
michael@0 | 278 | result = xpcshell.runTests(xpcshell='xpcshell', |
michael@0 | 279 | testClass=remotexpcshelltests.RemoteXPCShellTestThread, |
michael@0 | 280 | testdirs=testdirs, |
michael@0 | 281 | mobileArgs=xpcshell.mobileArgs, |
michael@0 | 282 | **options.__dict__) |
michael@0 | 283 | |
michael@0 | 284 | self.log_manager.terminal_handler.removeFilter(xpcshell_filter) |
michael@0 | 285 | self.log_manager.disable_unstructured() |
michael@0 | 286 | |
michael@0 | 287 | return int(not result) |
michael@0 | 288 | |
michael@0 | 289 | class B2GXPCShellRunner(MozbuildObject): |
michael@0 | 290 | def __init__(self, *args, **kwargs): |
michael@0 | 291 | MozbuildObject.__init__(self, *args, **kwargs) |
michael@0 | 292 | |
michael@0 | 293 | # TODO Bug 794506 remove once mach integrates with virtualenv. |
michael@0 | 294 | build_path = os.path.join(self.topobjdir, 'build') |
michael@0 | 295 | if build_path not in sys.path: |
michael@0 | 296 | sys.path.append(build_path) |
michael@0 | 297 | |
michael@0 | 298 | build_path = os.path.join(self.topsrcdir, 'build') |
michael@0 | 299 | if build_path not in sys.path: |
michael@0 | 300 | sys.path.append(build_path) |
michael@0 | 301 | |
michael@0 | 302 | self.tests_dir = os.path.join(self.topobjdir, '_tests') |
michael@0 | 303 | self.xpcshell_dir = os.path.join(self.tests_dir, 'xpcshell') |
michael@0 | 304 | self.bin_dir = os.path.join(self.distdir, 'bin') |
michael@0 | 305 | |
michael@0 | 306 | def _download_busybox(self, b2g_home): |
michael@0 | 307 | system_bin = os.path.join(b2g_home, 'out', 'target', 'product', 'generic', 'system', 'bin') |
michael@0 | 308 | busybox_path = os.path.join(system_bin, 'busybox') |
michael@0 | 309 | |
michael@0 | 310 | if os.path.isfile(busybox_path): |
michael@0 | 311 | return busybox_path |
michael@0 | 312 | |
michael@0 | 313 | if not os.path.isdir(system_bin): |
michael@0 | 314 | os.makedirs(system_bin) |
michael@0 | 315 | |
michael@0 | 316 | try: |
michael@0 | 317 | data = urllib2.urlopen(BUSYBOX_URL) |
michael@0 | 318 | except urllib2.URLError: |
michael@0 | 319 | print('There was a problem downloading busybox. Proceeding without it,' \ |
michael@0 | 320 | 'initial setup will be slow.') |
michael@0 | 321 | return |
michael@0 | 322 | |
michael@0 | 323 | with open(busybox_path, 'wb') as f: |
michael@0 | 324 | f.write(data.read()) |
michael@0 | 325 | return busybox_path |
michael@0 | 326 | |
michael@0 | 327 | def run_test(self, test_paths, b2g_home=None, busybox=None, |
michael@0 | 328 | # ignore parameters from other platforms' options |
michael@0 | 329 | **kwargs): |
michael@0 | 330 | try: |
michael@0 | 331 | import which |
michael@0 | 332 | which.which('adb') |
michael@0 | 333 | except which.WhichError: |
michael@0 | 334 | # TODO Find adb automatically if it isn't on the path |
michael@0 | 335 | print(ADB_NOT_FOUND % ('mochitest-remote', b2g_home)) |
michael@0 | 336 | sys.exit(1) |
michael@0 | 337 | |
michael@0 | 338 | test_path = None |
michael@0 | 339 | if test_paths: |
michael@0 | 340 | if len(test_paths) > 1: |
michael@0 | 341 | print('Warning: Only the first test path will be used.') |
michael@0 | 342 | |
michael@0 | 343 | test_path = self._wrap_path_argument(test_paths[0]).relpath() |
michael@0 | 344 | |
michael@0 | 345 | import runtestsb2g |
michael@0 | 346 | parser = runtestsb2g.B2GOptions() |
michael@0 | 347 | options, args = parser.parse_args([]) |
michael@0 | 348 | |
michael@0 | 349 | options.b2g_path = b2g_home |
michael@0 | 350 | options.busybox = busybox or os.environ.get('BUSYBOX') |
michael@0 | 351 | options.emulator = 'arm' |
michael@0 | 352 | options.localLib = self.bin_dir |
michael@0 | 353 | options.localBin = self.bin_dir |
michael@0 | 354 | options.logcat_dir = self.xpcshell_dir |
michael@0 | 355 | options.manifest = os.path.join(self.xpcshell_dir, 'xpcshell_b2g.ini') |
michael@0 | 356 | options.mozInfo = os.path.join(self.topobjdir, 'mozinfo.json') |
michael@0 | 357 | options.objdir = self.topobjdir |
michael@0 | 358 | options.symbolsPath = os.path.join(self.distdir, 'crashreporter-symbols'), |
michael@0 | 359 | options.testingModulesDir = os.path.join(self.tests_dir, 'modules') |
michael@0 | 360 | options.testsRootDir = self.xpcshell_dir |
michael@0 | 361 | options.testPath = test_path |
michael@0 | 362 | options.use_device_libs = True |
michael@0 | 363 | |
michael@0 | 364 | if not options.busybox: |
michael@0 | 365 | options.busybox = self._download_busybox(b2g_home) |
michael@0 | 366 | |
michael@0 | 367 | return runtestsb2g.run_remote_xpcshell(parser, options, args) |
michael@0 | 368 | |
michael@0 | 369 | def is_platform_supported(cls): |
michael@0 | 370 | """Must have a Firefox, Android or B2G build.""" |
michael@0 | 371 | return conditions.is_android(cls) or \ |
michael@0 | 372 | conditions.is_b2g(cls) or \ |
michael@0 | 373 | conditions.is_firefox(cls) |
michael@0 | 374 | |
michael@0 | 375 | @CommandProvider |
michael@0 | 376 | class MachCommands(MachCommandBase): |
michael@0 | 377 | def __init__(self, context): |
michael@0 | 378 | MachCommandBase.__init__(self, context) |
michael@0 | 379 | |
michael@0 | 380 | for attr in ('b2g_home', 'device_name'): |
michael@0 | 381 | setattr(self, attr, getattr(context, attr, None)) |
michael@0 | 382 | |
michael@0 | 383 | @Command('xpcshell-test', category='testing', |
michael@0 | 384 | conditions=[is_platform_supported], |
michael@0 | 385 | description='Run XPCOM Shell tests.') |
michael@0 | 386 | @CommandArgument('test_paths', default='all', nargs='*', metavar='TEST', |
michael@0 | 387 | help='Test to run. Can be specified as a single JS file, a directory, ' |
michael@0 | 388 | 'or omitted. If omitted, the entire test suite is executed.') |
michael@0 | 389 | @CommandArgument("--debugger", default=None, metavar='DEBUGGER', |
michael@0 | 390 | help = "Run xpcshell under the given debugger.") |
michael@0 | 391 | @CommandArgument("--debugger-args", default=None, metavar='ARGS', type=str, |
michael@0 | 392 | dest = "debuggerArgs", |
michael@0 | 393 | help = "pass the given args to the debugger _before_ " |
michael@0 | 394 | "the application on the command line") |
michael@0 | 395 | @CommandArgument("--debugger-interactive", action = "store_true", |
michael@0 | 396 | dest = "debuggerInteractive", |
michael@0 | 397 | help = "prevents the test harness from redirecting " |
michael@0 | 398 | "stdout and stderr for interactive debuggers") |
michael@0 | 399 | @CommandArgument('--interactive', '-i', action='store_true', |
michael@0 | 400 | help='Open an xpcshell prompt before running tests.') |
michael@0 | 401 | @CommandArgument('--keep-going', '-k', action='store_true', |
michael@0 | 402 | help='Continue running tests after a SIGINT is received.') |
michael@0 | 403 | @CommandArgument('--sequential', action='store_true', |
michael@0 | 404 | help='Run the tests sequentially.') |
michael@0 | 405 | @CommandArgument('--shuffle', '-s', action='store_true', |
michael@0 | 406 | help='Randomize the execution order of tests.') |
michael@0 | 407 | @CommandArgument('--rerun-failures', action='store_true', |
michael@0 | 408 | help='Reruns failures from last time.') |
michael@0 | 409 | @CommandArgument('--devicemanager', default='adb', type=str, |
michael@0 | 410 | help='(Android) Type of devicemanager to use for communication: adb or sut') |
michael@0 | 411 | @CommandArgument('--ip', type=str, default=None, |
michael@0 | 412 | help='(Android) IP address of device') |
michael@0 | 413 | @CommandArgument('--port', type=int, default=20701, |
michael@0 | 414 | help='(Android) Port of device') |
michael@0 | 415 | @CommandArgument('--remote_test_root', type=str, default=None, |
michael@0 | 416 | help='(Android) Remote test root such as /mnt/sdcard or /data/local') |
michael@0 | 417 | @CommandArgument('--no-setup', action='store_true', |
michael@0 | 418 | help='(Android) Do not copy files to device') |
michael@0 | 419 | @CommandArgument('--local-apk', type=str, default=None, |
michael@0 | 420 | help='(Android) Use specified Fennec APK') |
michael@0 | 421 | @CommandArgument('--busybox', type=str, default=None, |
michael@0 | 422 | help='(B2G) Path to busybox binary (speeds up installation of tests).') |
michael@0 | 423 | def run_xpcshell_test(self, **params): |
michael@0 | 424 | from mozbuild.controller.building import BuildDriver |
michael@0 | 425 | |
michael@0 | 426 | # We should probably have a utility function to ensure the tree is |
michael@0 | 427 | # ready to run tests. Until then, we just create the state dir (in |
michael@0 | 428 | # case the tree wasn't built with mach). |
michael@0 | 429 | self._ensure_state_subdir_exists('.') |
michael@0 | 430 | |
michael@0 | 431 | driver = self._spawn(BuildDriver) |
michael@0 | 432 | driver.install_tests(remove=False) |
michael@0 | 433 | |
michael@0 | 434 | if conditions.is_android(self): |
michael@0 | 435 | xpcshell = self._spawn(AndroidXPCShellRunner) |
michael@0 | 436 | elif conditions.is_b2g(self): |
michael@0 | 437 | xpcshell = self._spawn(B2GXPCShellRunner) |
michael@0 | 438 | params['b2g_home'] = self.b2g_home |
michael@0 | 439 | else: |
michael@0 | 440 | xpcshell = self._spawn(XPCShellRunner) |
michael@0 | 441 | xpcshell.cwd = self._mach_context.cwd |
michael@0 | 442 | |
michael@0 | 443 | try: |
michael@0 | 444 | return xpcshell.run_test(**params) |
michael@0 | 445 | except InvalidTestPathError as e: |
michael@0 | 446 | print(e.message) |
michael@0 | 447 | return 1 |