timeout, options.isolates,
options.command_prefix,
options.extra_flags,
- False,
+ False, # Keep i18n on by default.
options.random_seed,
- True)
+ True, # No sorting of test cases.
+ 0, # Don't rerun failing tests.
+ 0) # No use of a rerun-failing-tests maximum.
# Find available test suites and read test cases from them.
variables = {
default=False, action="store_true")
result.add_option("--json-test-results",
help="Path to a file for storing json results.")
+ result.add_option("--rerun-failures-count",
+ help=("Number of times to rerun each failing test case. "
+ "Very slow tests will be rerun only once."),
+ default=0, type="int")
+ result.add_option("--rerun-failures-max",
+ help="Maximum number of failing test cases to rerun.",
+ default=100, type="int")
result.add_option("--shard-count",
help="Split testsuites into this number of shards",
default=1, type="int")
options.extra_flags,
options.no_i18n,
options.random_seed,
- options.no_sorting)
+ options.no_sorting,
+ options.rerun_failures_count,
+ options.rerun_failures_max)
# TODO(all): Combine "simulator" and "simulator_run".
simulator_run = not options.dont_skip_simulator_slow_tests and \
self.remaining = num_tests
self.failed = []
self.crashed = 0
+ self.reran_tests = 0
def _RunPerfSafe(self, fun):
try:
print("PerfData exception: %s" % e)
self.perf_failures = True
+ def _GetJob(self, test):
+ command = self.GetCommand(test)
+ timeout = self.context.timeout
+ if ("--stress-opt" in test.flags or
+ "--stress-opt" in self.context.mode_flags or
+ "--stress-opt" in self.context.extra_flags):
+ timeout *= 4
+ if test.dependency is not None:
+ dep_command = [ c.replace(test.path, test.dependency) for c in command ]
+ else:
+ dep_command = None
+ return Job(command, dep_command, test.id, timeout, self.context.verbose)
+
+ def _MaybeRerun(self, pool, test):
+ if test.run <= self.context.rerun_failures_count:
+ # Possibly rerun this test if its run count is below the maximum per
+ # test.
+ if test.run == 1:
+ # Count the overall number of reran tests on the first rerun.
+ if self.reran_tests < self.context.rerun_failures_max:
+ self.reran_tests += 1
+ else:
+ # Don't rerun this if the overall number of rerun tests has been
+ # reached.
+ return
+ if test.run >= 2 and test.duration > self.context.timeout / 20:
+ # Rerun slow tests at most once.
+ return
+
+ # Rerun this test.
+ test.duration = None
+ test.output = None
+ test.run += 1
+ pool.add([self._GetJob(test)])
+ self.remaining += 1
+
def Run(self, jobs):
self.indicator.Starting()
self._RunInternal(jobs)
assert test.id >= 0
test_map[test.id] = test
try:
- command = self.GetCommand(test)
+ queue.append([self._GetJob(test)])
except Exception, e:
# If this failed, save the exception and re-raise it later (after
# all other tests have had a chance to run).
queued_exception = e
continue
- timeout = self.context.timeout
- if ("--stress-opt" in test.flags or
- "--stress-opt" in self.context.mode_flags or
- "--stress-opt" in self.context.extra_flags):
- timeout *= 4
- if test.dependency is not None:
- dep_command = [ c.replace(test.path, test.dependency) for c in command ]
- else:
- dep_command = None
- job = Job(command, dep_command, test.id, timeout, self.context.verbose)
- queue.append([job])
try:
it = pool.imap_unordered(RunTest, queue)
for result in it:
self.succeeded += 1
self.remaining -= 1
self.indicator.HasRun(test, has_unexpected_output)
+ if has_unexpected_output:
+ # Rerun test failures after the indicator has processed the results.
+ self._MaybeRerun(pool, test)
finally:
pool.terminate()
self._RunPerfSafe(lambda: self.perf_data_manager.close())
class Context():
def __init__(self, arch, mode, shell_dir, mode_flags, verbose, timeout,
isolates, command_prefix, extra_flags, noi18n, random_seed,
- no_sorting):
+ no_sorting, rerun_failures_count, rerun_failures_max):
self.arch = arch
self.mode = mode
self.shell_dir = shell_dir
self.noi18n = noi18n
self.random_seed = random_seed
self.no_sorting = no_sorting
+ self.rerun_failures_count = rerun_failures_count
+ self.rerun_failures_max = rerun_failures_max
def Pack(self):
return [self.arch, self.mode, self.mode_flags, self.timeout, self.isolates,
self.command_prefix, self.extra_flags, self.noi18n,
- self.random_seed, self.no_sorting]
+ self.random_seed, self.no_sorting, self.rerun_failures_count,
+ self.rerun_failures_max]
@staticmethod
def Unpack(packed):
# For the order of the fields, refer to Pack() above.
return Context(packed[0], packed[1], None, packed[2], False,
packed[3], packed[4], packed[5], packed[6], packed[7],
- packed[8], packed[9])
+ packed[8], packed[9], packed[10], packed[11])
self.output = None
self.id = None # int, used to map result back to TestCase instance
self.duration = None # assigned during execution
+ self.run = 1 # The nth time this test is executed.
def CopyAddingFlags(self, flags):
copy = TestCase(self.suite, self.path, self.flags + flags, self.dependency)
test = TestCase(str(task[0]), task[1], task[2], task[3])
test.outcomes = set(task[4])
test.id = task[5]
+ test.run = 1
return test
def SetSuiteObject(self, suites):