layout/tools/reftest/runreftest.py

Wed, 31 Dec 2014 13:27:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 13:27:57 +0100
branch
TOR_BUG_3246
changeset 6
8bccb770b82d
permissions
-rw-r--r--

Ignore runtime configuration files generated during quality assurance.

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 """
michael@0 6 Runs the reftest test harness.
michael@0 7 """
michael@0 8
michael@0 9 from optparse import OptionParser
michael@0 10 import collections
michael@0 11 import json
michael@0 12 import multiprocessing
michael@0 13 import os
michael@0 14 import re
michael@0 15 import shutil
michael@0 16 import subprocess
michael@0 17 import sys
michael@0 18 import threading
michael@0 19
michael@0 20 SCRIPT_DIRECTORY = os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0])))
michael@0 21 sys.path.insert(0, SCRIPT_DIRECTORY)
michael@0 22
michael@0 23 from automation import Automation
michael@0 24 from automationutils import (
michael@0 25 addCommonOptions,
michael@0 26 getDebuggerInfo,
michael@0 27 isURL,
michael@0 28 processLeakLog
michael@0 29 )
michael@0 30 import mozprofile
michael@0 31
michael@0 32 def categoriesToRegex(categoryList):
michael@0 33 return "\\(" + ', '.join(["(?P<%s>\\d+) %s" % c for c in categoryList]) + "\\)"
michael@0 34 summaryLines = [('Successful', [('pass', 'pass'), ('loadOnly', 'load only')]),
michael@0 35 ('Unexpected', [('fail', 'unexpected fail'),
michael@0 36 ('pass', 'unexpected pass'),
michael@0 37 ('asserts', 'unexpected asserts'),
michael@0 38 ('fixedAsserts', 'unexpected fixed asserts'),
michael@0 39 ('failedLoad', 'failed load'),
michael@0 40 ('exception', 'exception')]),
michael@0 41 ('Known problems', [('knownFail', 'known fail'),
michael@0 42 ('knownAsserts', 'known asserts'),
michael@0 43 ('random', 'random'),
michael@0 44 ('skipped', 'skipped'),
michael@0 45 ('slow', 'slow')])]
michael@0 46
michael@0 47 # Python's print is not threadsafe.
michael@0 48 printLock = threading.Lock()
michael@0 49
michael@0 50 class ReftestThread(threading.Thread):
michael@0 51 def __init__(self, cmdlineArgs):
michael@0 52 threading.Thread.__init__(self)
michael@0 53 self.cmdlineArgs = cmdlineArgs
michael@0 54 self.summaryMatches = {}
michael@0 55 self.retcode = -1
michael@0 56 for text, _ in summaryLines:
michael@0 57 self.summaryMatches[text] = None
michael@0 58
michael@0 59 def run(self):
michael@0 60 with printLock:
michael@0 61 print "Starting thread with", self.cmdlineArgs
michael@0 62 sys.stdout.flush()
michael@0 63 process = subprocess.Popen(self.cmdlineArgs, stdout=subprocess.PIPE)
michael@0 64 for chunk in self.chunkForMergedOutput(process.stdout):
michael@0 65 with printLock:
michael@0 66 print chunk,
michael@0 67 sys.stdout.flush()
michael@0 68 self.retcode = process.wait()
michael@0 69
michael@0 70 def chunkForMergedOutput(self, logsource):
michael@0 71 """Gather lines together that should be printed as one atomic unit.
michael@0 72 Individual test results--anything between 'REFTEST TEST-START' and
michael@0 73 'REFTEST TEST-END' lines--are an atomic unit. Lines with data from
michael@0 74 summaries are parsed and the data stored for later aggregation.
michael@0 75 Other lines are considered their own atomic units and are permitted
michael@0 76 to intermix freely."""
michael@0 77 testStartRegex = re.compile("^REFTEST TEST-START")
michael@0 78 testEndRegex = re.compile("^REFTEST TEST-END")
michael@0 79 summaryHeadRegex = re.compile("^REFTEST INFO \\| Result summary:")
michael@0 80 summaryRegexFormatString = "^REFTEST INFO \\| (?P<message>{text}): (?P<total>\\d+) {regex}"
michael@0 81 summaryRegexStrings = [summaryRegexFormatString.format(text=text,
michael@0 82 regex=categoriesToRegex(categories))
michael@0 83 for (text, categories) in summaryLines]
michael@0 84 summaryRegexes = [re.compile(regex) for regex in summaryRegexStrings]
michael@0 85
michael@0 86 for line in logsource:
michael@0 87 if testStartRegex.search(line) is not None:
michael@0 88 chunkedLines = [line]
michael@0 89 for lineToBeChunked in logsource:
michael@0 90 chunkedLines.append(lineToBeChunked)
michael@0 91 if testEndRegex.search(lineToBeChunked) is not None:
michael@0 92 break
michael@0 93 yield ''.join(chunkedLines)
michael@0 94 continue
michael@0 95
michael@0 96 haveSuppressedSummaryLine = False
michael@0 97 for regex in summaryRegexes:
michael@0 98 match = regex.search(line)
michael@0 99 if match is not None:
michael@0 100 self.summaryMatches[match.group('message')] = match
michael@0 101 haveSuppressedSummaryLine = True
michael@0 102 break
michael@0 103 if haveSuppressedSummaryLine:
michael@0 104 continue
michael@0 105
michael@0 106 if summaryHeadRegex.search(line) is None:
michael@0 107 yield line
michael@0 108
michael@0 109 class RefTest(object):
michael@0 110
michael@0 111 oldcwd = os.getcwd()
michael@0 112
michael@0 113 def __init__(self, automation=None):
michael@0 114 self.automation = automation or Automation()
michael@0 115
michael@0 116 def getFullPath(self, path):
michael@0 117 "Get an absolute path relative to self.oldcwd."
michael@0 118 return os.path.normpath(os.path.join(self.oldcwd, os.path.expanduser(path)))
michael@0 119
michael@0 120 def getManifestPath(self, path):
michael@0 121 "Get the path of the manifest, and for remote testing this function is subclassed to point to remote manifest"
michael@0 122 path = self.getFullPath(path)
michael@0 123 if os.path.isdir(path):
michael@0 124 defaultManifestPath = os.path.join(path, 'reftest.list')
michael@0 125 if os.path.exists(defaultManifestPath):
michael@0 126 path = defaultManifestPath
michael@0 127 else:
michael@0 128 defaultManifestPath = os.path.join(path, 'crashtests.list')
michael@0 129 if os.path.exists(defaultManifestPath):
michael@0 130 path = defaultManifestPath
michael@0 131 return path
michael@0 132
michael@0 133 def makeJSString(self, s):
michael@0 134 return '"%s"' % re.sub(r'([\\"])', r'\\\1', s)
michael@0 135
michael@0 136 def createReftestProfile(self, options, manifest, server='localhost',
michael@0 137 special_powers=True, profile_to_clone=None):
michael@0 138 """
michael@0 139 Sets up a profile for reftest.
michael@0 140 'manifest' is the path to the reftest.list file we want to test with. This is used in
michael@0 141 the remote subclass in remotereftest.py so we can write it to a preference for the
michael@0 142 bootstrap extension.
michael@0 143 """
michael@0 144
michael@0 145 locations = mozprofile.permissions.ServerLocations()
michael@0 146 locations.add_host(server, port=0)
michael@0 147 locations.add_host('<file>', port=0)
michael@0 148
michael@0 149 # Set preferences for communication between our command line arguments
michael@0 150 # and the reftest harness. Preferences that are required for reftest
michael@0 151 # to work should instead be set in reftest-cmdline.js .
michael@0 152 prefs = {}
michael@0 153 prefs['reftest.timeout'] = options.timeout * 1000
michael@0 154 if options.totalChunks:
michael@0 155 prefs['reftest.totalChunks'] = options.totalChunks
michael@0 156 if options.thisChunk:
michael@0 157 prefs['reftest.thisChunk'] = options.thisChunk
michael@0 158 if options.logFile:
michael@0 159 prefs['reftest.logFile'] = options.logFile
michael@0 160 if options.ignoreWindowSize:
michael@0 161 prefs['reftest.ignoreWindowSize'] = True
michael@0 162 if options.filter:
michael@0 163 prefs['reftest.filter'] = options.filter
michael@0 164 if options.shuffle:
michael@0 165 prefs['reftest.shuffle'] = True
michael@0 166 prefs['reftest.focusFilterMode'] = options.focusFilterMode
michael@0 167
michael@0 168 # Ensure that telemetry is disabled, so we don't connect to the telemetry
michael@0 169 # server in the middle of the tests.
michael@0 170 prefs['toolkit.telemetry.enabled'] = False
michael@0 171 # Likewise for safebrowsing.
michael@0 172 prefs['browser.safebrowsing.enabled'] = False
michael@0 173 prefs['browser.safebrowsing.malware.enabled'] = False
michael@0 174 # And for snippets.
michael@0 175 prefs['browser.snippets.enabled'] = False
michael@0 176 prefs['browser.snippets.syncPromo.enabled'] = False
michael@0 177 # And for useragent updates.
michael@0 178 prefs['general.useragent.updates.enabled'] = False
michael@0 179 # And for webapp updates. Yes, it is supposed to be an integer.
michael@0 180 prefs['browser.webapps.checkForUpdates'] = 0
michael@0 181
michael@0 182 if options.e10s:
michael@0 183 prefs['browser.tabs.remote.autostart'] = True
michael@0 184
michael@0 185 for v in options.extraPrefs:
michael@0 186 thispref = v.split('=')
michael@0 187 if len(thispref) < 2:
michael@0 188 print "Error: syntax error in --setpref=" + v
michael@0 189 sys.exit(1)
michael@0 190 prefs[thispref[0]] = mozprofile.Preferences.cast(thispref[1].strip())
michael@0 191
michael@0 192 # install the reftest extension bits into the profile
michael@0 193 addons = []
michael@0 194 addons.append(os.path.join(SCRIPT_DIRECTORY, "reftest"))
michael@0 195
michael@0 196 # I would prefer to use "--install-extension reftest/specialpowers", but that requires tight coordination with
michael@0 197 # release engineering and landing on multiple branches at once.
michael@0 198 if special_powers and (manifest.endswith('crashtests.list') or manifest.endswith('jstests.list')):
michael@0 199 addons.append(os.path.join(SCRIPT_DIRECTORY, 'specialpowers'))
michael@0 200
michael@0 201 # Install distributed extensions, if application has any.
michael@0 202 distExtDir = os.path.join(options.app[ : options.app.rfind(os.sep)], "distribution", "extensions")
michael@0 203 if os.path.isdir(distExtDir):
michael@0 204 for f in os.listdir(distExtDir):
michael@0 205 addons.append(os.path.join(distExtDir, f))
michael@0 206
michael@0 207 # Install custom extensions.
michael@0 208 for f in options.extensionsToInstall:
michael@0 209 addons.append(self.getFullPath(f))
michael@0 210
michael@0 211 kwargs = { 'addons': addons,
michael@0 212 'preferences': prefs,
michael@0 213 'locations': locations }
michael@0 214 if profile_to_clone:
michael@0 215 profile = mozprofile.Profile.clone(profile_to_clone, **kwargs)
michael@0 216 else:
michael@0 217 profile = mozprofile.Profile(**kwargs)
michael@0 218
michael@0 219 self.copyExtraFilesToProfile(options, profile)
michael@0 220 return profile
michael@0 221
michael@0 222 def buildBrowserEnv(self, options, profileDir):
michael@0 223 browserEnv = self.automation.environment(xrePath = options.xrePath, debugger=options.debugger)
michael@0 224 browserEnv["XPCOM_DEBUG_BREAK"] = "stack"
michael@0 225
michael@0 226 for v in options.environment:
michael@0 227 ix = v.find("=")
michael@0 228 if ix <= 0:
michael@0 229 print "Error: syntax error in --setenv=" + v
michael@0 230 return None
michael@0 231 browserEnv[v[:ix]] = v[ix + 1:]
michael@0 232
michael@0 233 # Enable leaks detection to its own log file.
michael@0 234 self.leakLogFile = os.path.join(profileDir, "runreftest_leaks.log")
michael@0 235 browserEnv["XPCOM_MEM_BLOAT_LOG"] = self.leakLogFile
michael@0 236 return browserEnv
michael@0 237
michael@0 238 def cleanup(self, profileDir):
michael@0 239 if profileDir:
michael@0 240 shutil.rmtree(profileDir, True)
michael@0 241
michael@0 242 def runTests(self, testPath, options, cmdlineArgs = None):
michael@0 243 if not options.runTestsInParallel:
michael@0 244 return self.runSerialTests(testPath, options, cmdlineArgs)
michael@0 245
michael@0 246 cpuCount = multiprocessing.cpu_count()
michael@0 247
michael@0 248 # We have the directive, technology, and machine to run multiple test instances.
michael@0 249 # Experimentation says that reftests are not overly CPU-intensive, so we can run
michael@0 250 # multiple jobs per CPU core.
michael@0 251 #
michael@0 252 # Our Windows machines in automation seem to get upset when we run a lot of
michael@0 253 # simultaneous tests on them, so tone things down there.
michael@0 254 if sys.platform == 'win32':
michael@0 255 jobsWithoutFocus = cpuCount
michael@0 256 else:
michael@0 257 jobsWithoutFocus = 2 * cpuCount
michael@0 258
michael@0 259 totalJobs = jobsWithoutFocus + 1
michael@0 260 perProcessArgs = [sys.argv[:] for i in range(0, totalJobs)]
michael@0 261
michael@0 262 # First job is only needs-focus tests. Remaining jobs are non-needs-focus and chunked.
michael@0 263 perProcessArgs[0].insert(-1, "--focus-filter-mode=needs-focus")
michael@0 264 for (chunkNumber, jobArgs) in enumerate(perProcessArgs[1:], start=1):
michael@0 265 jobArgs[-1:-1] = ["--focus-filter-mode=non-needs-focus",
michael@0 266 "--total-chunks=%d" % jobsWithoutFocus,
michael@0 267 "--this-chunk=%d" % chunkNumber]
michael@0 268
michael@0 269 for jobArgs in perProcessArgs:
michael@0 270 try:
michael@0 271 jobArgs.remove("--run-tests-in-parallel")
michael@0 272 except:
michael@0 273 pass
michael@0 274 jobArgs.insert(-1, "--no-run-tests-in-parallel")
michael@0 275 jobArgs[0:0] = [sys.executable, "-u"]
michael@0 276
michael@0 277 threads = [ReftestThread(args) for args in perProcessArgs[1:]]
michael@0 278 for t in threads:
michael@0 279 t.start()
michael@0 280
michael@0 281 while True:
michael@0 282 # The test harness in each individual thread will be doing timeout
michael@0 283 # handling on its own, so we shouldn't need to worry about any of
michael@0 284 # the threads hanging for arbitrarily long.
michael@0 285 for t in threads:
michael@0 286 t.join(10)
michael@0 287 if not any(t.is_alive() for t in threads):
michael@0 288 break
michael@0 289
michael@0 290 # Run the needs-focus tests serially after the other ones, so we don't
michael@0 291 # have to worry about races between the needs-focus tests *actually*
michael@0 292 # needing focus and the dummy windows in the non-needs-focus tests
michael@0 293 # trying to focus themselves.
michael@0 294 focusThread = ReftestThread(perProcessArgs[0])
michael@0 295 focusThread.start()
michael@0 296 focusThread.join()
michael@0 297
michael@0 298 # Output the summaries that the ReftestThread filters suppressed.
michael@0 299 summaryObjects = [collections.defaultdict(int) for s in summaryLines]
michael@0 300 for t in threads:
michael@0 301 for (summaryObj, (text, categories)) in zip(summaryObjects, summaryLines):
michael@0 302 threadMatches = t.summaryMatches[text]
michael@0 303 for (attribute, description) in categories:
michael@0 304 amount = int(threadMatches.group(attribute) if threadMatches else 0)
michael@0 305 summaryObj[attribute] += amount
michael@0 306 amount = int(threadMatches.group('total') if threadMatches else 0)
michael@0 307 summaryObj['total'] += amount
michael@0 308
michael@0 309 print 'REFTEST INFO | Result summary:'
michael@0 310 for (summaryObj, (text, categories)) in zip(summaryObjects, summaryLines):
michael@0 311 details = ', '.join(["%d %s" % (summaryObj[attribute], description) for (attribute, description) in categories])
michael@0 312 print 'REFTEST INFO | ' + text + ': ' + str(summaryObj['total']) + ' (' + details + ')'
michael@0 313
michael@0 314 return int(any(t.retcode != 0 for t in threads))
michael@0 315
michael@0 316 def runSerialTests(self, testPath, options, cmdlineArgs = None):
michael@0 317 debuggerInfo = getDebuggerInfo(self.oldcwd, options.debugger, options.debuggerArgs,
michael@0 318 options.debuggerInteractive);
michael@0 319
michael@0 320 profileDir = None
michael@0 321 try:
michael@0 322 reftestlist = self.getManifestPath(testPath)
michael@0 323 if cmdlineArgs == None:
michael@0 324 cmdlineArgs = ['-reftest', reftestlist]
michael@0 325 profile = self.createReftestProfile(options, reftestlist)
michael@0 326 profileDir = profile.profile # name makes more sense
michael@0 327
michael@0 328 # browser environment
michael@0 329 browserEnv = self.buildBrowserEnv(options, profileDir)
michael@0 330
michael@0 331 self.automation.log.info("REFTEST INFO | runreftest.py | Running tests: start.\n")
michael@0 332 status = self.automation.runApp(None, browserEnv, options.app, profileDir,
michael@0 333 cmdlineArgs,
michael@0 334 utilityPath = options.utilityPath,
michael@0 335 xrePath=options.xrePath,
michael@0 336 debuggerInfo=debuggerInfo,
michael@0 337 symbolsPath=options.symbolsPath,
michael@0 338 # give the JS harness 30 seconds to deal
michael@0 339 # with its own timeouts
michael@0 340 timeout=options.timeout + 30.0)
michael@0 341 processLeakLog(self.leakLogFile, options.leakThreshold)
michael@0 342 self.automation.log.info("\nREFTEST INFO | runreftest.py | Running tests: end.")
michael@0 343 finally:
michael@0 344 self.cleanup(profileDir)
michael@0 345 return status
michael@0 346
michael@0 347 def copyExtraFilesToProfile(self, options, profile):
michael@0 348 "Copy extra files or dirs specified on the command line to the testing profile."
michael@0 349 profileDir = profile.profile
michael@0 350 for f in options.extraProfileFiles:
michael@0 351 abspath = self.getFullPath(f)
michael@0 352 if os.path.isfile(abspath):
michael@0 353 if os.path.basename(abspath) == 'user.js':
michael@0 354 extra_prefs = mozprofile.Preferences.read_prefs(abspath)
michael@0 355 profile.set_preferences(extra_prefs)
michael@0 356 else:
michael@0 357 shutil.copy2(abspath, profileDir)
michael@0 358 elif os.path.isdir(abspath):
michael@0 359 dest = os.path.join(profileDir, os.path.basename(abspath))
michael@0 360 shutil.copytree(abspath, dest)
michael@0 361 else:
michael@0 362 self.automation.log.warning("WARNING | runreftest.py | Failed to copy %s to profile", abspath)
michael@0 363 continue
michael@0 364
michael@0 365
michael@0 366 class ReftestOptions(OptionParser):
michael@0 367
michael@0 368 def __init__(self, automation=None):
michael@0 369 self.automation = automation or Automation()
michael@0 370 OptionParser.__init__(self)
michael@0 371 defaults = {}
michael@0 372
michael@0 373 # we want to pass down everything from automation.__all__
michael@0 374 addCommonOptions(self,
michael@0 375 defaults=dict(zip(self.automation.__all__,
michael@0 376 [getattr(self.automation, x) for x in self.automation.__all__])))
michael@0 377 self.automation.addCommonOptions(self)
michael@0 378 self.add_option("--appname",
michael@0 379 action = "store", type = "string", dest = "app",
michael@0 380 default = os.path.join(SCRIPT_DIRECTORY, automation.DEFAULT_APP),
michael@0 381 help = "absolute path to application, overriding default")
michael@0 382 self.add_option("--extra-profile-file",
michael@0 383 action = "append", dest = "extraProfileFiles",
michael@0 384 default = [],
michael@0 385 help = "copy specified files/dirs to testing profile")
michael@0 386 self.add_option("--timeout",
michael@0 387 action = "store", dest = "timeout", type = "int",
michael@0 388 default = 5 * 60, # 5 minutes per bug 479518
michael@0 389 help = "reftest will timeout in specified number of seconds. [default %default s].")
michael@0 390 self.add_option("--leak-threshold",
michael@0 391 action = "store", type = "int", dest = "leakThreshold",
michael@0 392 default = 0,
michael@0 393 help = "fail if the number of bytes leaked through "
michael@0 394 "refcounted objects (or bytes in classes with "
michael@0 395 "MOZ_COUNT_CTOR and MOZ_COUNT_DTOR) is greater "
michael@0 396 "than the given number")
michael@0 397 self.add_option("--utility-path",
michael@0 398 action = "store", type = "string", dest = "utilityPath",
michael@0 399 default = self.automation.DIST_BIN,
michael@0 400 help = "absolute path to directory containing utility "
michael@0 401 "programs (xpcshell, ssltunnel, certutil)")
michael@0 402 defaults["utilityPath"] = self.automation.DIST_BIN
michael@0 403
michael@0 404 self.add_option("--total-chunks",
michael@0 405 type = "int", dest = "totalChunks",
michael@0 406 help = "how many chunks to split the tests up into")
michael@0 407 defaults["totalChunks"] = None
michael@0 408
michael@0 409 self.add_option("--this-chunk",
michael@0 410 type = "int", dest = "thisChunk",
michael@0 411 help = "which chunk to run between 1 and --total-chunks")
michael@0 412 defaults["thisChunk"] = None
michael@0 413
michael@0 414 self.add_option("--log-file",
michael@0 415 action = "store", type = "string", dest = "logFile",
michael@0 416 default = None,
michael@0 417 help = "file to log output to in addition to stdout")
michael@0 418 defaults["logFile"] = None
michael@0 419
michael@0 420 self.add_option("--skip-slow-tests",
michael@0 421 dest = "skipSlowTests", action = "store_true",
michael@0 422 help = "skip tests marked as slow when running")
michael@0 423 defaults["skipSlowTests"] = False
michael@0 424
michael@0 425 self.add_option("--ignore-window-size",
michael@0 426 dest = "ignoreWindowSize", action = "store_true",
michael@0 427 help = "ignore the window size, which may cause spurious failures and passes")
michael@0 428 defaults["ignoreWindowSize"] = False
michael@0 429
michael@0 430 self.add_option("--install-extension",
michael@0 431 action = "append", dest = "extensionsToInstall",
michael@0 432 help = "install the specified extension in the testing profile. "
michael@0 433 "The extension file's name should be <id>.xpi where <id> is "
michael@0 434 "the extension's id as indicated in its install.rdf. "
michael@0 435 "An optional path can be specified too.")
michael@0 436 defaults["extensionsToInstall"] = []
michael@0 437
michael@0 438 self.add_option("--run-tests-in-parallel",
michael@0 439 action = "store_true", dest = "runTestsInParallel",
michael@0 440 help = "run tests in parallel if possible")
michael@0 441 self.add_option("--no-run-tests-in-parallel",
michael@0 442 action = "store_false", dest = "runTestsInParallel",
michael@0 443 help = "do not run tests in parallel")
michael@0 444 defaults["runTestsInParallel"] = False
michael@0 445
michael@0 446 self.add_option("--setenv",
michael@0 447 action = "append", type = "string",
michael@0 448 dest = "environment", metavar = "NAME=VALUE",
michael@0 449 help = "sets the given variable in the application's "
michael@0 450 "environment")
michael@0 451 defaults["environment"] = []
michael@0 452
michael@0 453 self.add_option("--filter",
michael@0 454 action = "store", type="string", dest = "filter",
michael@0 455 help = "specifies a regular expression (as could be passed to the JS "
michael@0 456 "RegExp constructor) to test against URLs in the reftest manifest; "
michael@0 457 "only test items that have a matching test URL will be run.")
michael@0 458 defaults["filter"] = None
michael@0 459
michael@0 460 self.add_option("--shuffle",
michael@0 461 action = "store_true", dest = "shuffle",
michael@0 462 help = "run reftests in random order")
michael@0 463 defaults["shuffle"] = False
michael@0 464
michael@0 465 self.add_option("--focus-filter-mode",
michael@0 466 action = "store", type = "string", dest = "focusFilterMode",
michael@0 467 help = "filters tests to run by whether they require focus. "
michael@0 468 "Valid values are `all', `needs-focus', or `non-needs-focus'. "
michael@0 469 "Defaults to `all'.")
michael@0 470 defaults["focusFilterMode"] = "all"
michael@0 471
michael@0 472 self.add_option("--e10s",
michael@0 473 action = "store_true",
michael@0 474 dest = "e10s",
michael@0 475 help = "enables content processes")
michael@0 476 defaults["e10s"] = False
michael@0 477
michael@0 478 self.set_defaults(**defaults)
michael@0 479
michael@0 480 def verifyCommonOptions(self, options, reftest):
michael@0 481 if options.totalChunks is not None and options.thisChunk is None:
michael@0 482 self.error("thisChunk must be specified when totalChunks is specified")
michael@0 483
michael@0 484 if options.totalChunks:
michael@0 485 if not 1 <= options.thisChunk <= options.totalChunks:
michael@0 486 self.error("thisChunk must be between 1 and totalChunks")
michael@0 487
michael@0 488 if options.logFile:
michael@0 489 options.logFile = reftest.getFullPath(options.logFile)
michael@0 490
michael@0 491 if options.xrePath is not None:
michael@0 492 if not os.access(options.xrePath, os.F_OK):
michael@0 493 self.error("--xre-path '%s' not found" % options.xrePath)
michael@0 494 if not os.path.isdir(options.xrePath):
michael@0 495 self.error("--xre-path '%s' is not a directory" % options.xrePath)
michael@0 496 options.xrePath = reftest.getFullPath(options.xrePath)
michael@0 497
michael@0 498 if options.runTestsInParallel:
michael@0 499 if options.logFile is not None:
michael@0 500 self.error("cannot specify logfile with parallel tests")
michael@0 501 if options.totalChunks is not None and options.thisChunk is None:
michael@0 502 self.error("cannot specify thisChunk or totalChunks with parallel tests")
michael@0 503 if options.focusFilterMode != "all":
michael@0 504 self.error("cannot specify focusFilterMode with parallel tests")
michael@0 505 if options.debugger is not None:
michael@0 506 self.error("cannot specify a debugger with parallel tests")
michael@0 507
michael@0 508 return options
michael@0 509
michael@0 510 def main():
michael@0 511 automation = Automation()
michael@0 512 parser = ReftestOptions(automation)
michael@0 513 reftest = RefTest(automation)
michael@0 514
michael@0 515 options, args = parser.parse_args()
michael@0 516 if len(args) != 1:
michael@0 517 print >>sys.stderr, "No reftest.list specified."
michael@0 518 sys.exit(1)
michael@0 519
michael@0 520 options = parser.verifyCommonOptions(options, reftest)
michael@0 521
michael@0 522 options.app = reftest.getFullPath(options.app)
michael@0 523 if not os.path.exists(options.app):
michael@0 524 print """Error: Path %(app)s doesn't exist.
michael@0 525 Are you executing $objdir/_tests/reftest/runreftest.py?""" \
michael@0 526 % {"app": options.app}
michael@0 527 sys.exit(1)
michael@0 528
michael@0 529 if options.xrePath is None:
michael@0 530 options.xrePath = os.path.dirname(options.app)
michael@0 531
michael@0 532 if options.symbolsPath and not isURL(options.symbolsPath):
michael@0 533 options.symbolsPath = reftest.getFullPath(options.symbolsPath)
michael@0 534 options.utilityPath = reftest.getFullPath(options.utilityPath)
michael@0 535
michael@0 536 sys.exit(reftest.runTests(args[0], options))
michael@0 537
michael@0 538 if __name__ == "__main__":
michael@0 539 main()

mercurial