Browse Source

test: runner support for flaky tests

Adding --flaky-tests option, to allow regarding flaky tests failures
as non-fatal.

Currently only observed by the TapProgressIndicator, which will
add a # TODO directive to tests classified as flaky. According to the
TAP specification, the test harness is supposed to treat failures
that have a # TODO directive as non-fatal.

Ported from df3a2b2cf2

PR-URL: https://github.com/nodejs/node/pull/2424
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: João Reis <reis@janeasystems.com>
Reviewed-By: Sakthipriyan Vairamani <thechargingvolcano@gmail.com>
v4.0.0-rc
Alexis Campailla 10 years ago
parent
commit
c8caf8d616
  1. 44
      tools/test.py

44
tools/test.py

@ -61,8 +61,9 @@ VERBOSE = False
class ProgressIndicator(object): class ProgressIndicator(object):
def __init__(self, cases): def __init__(self, cases, flaky_tests_mode):
self.cases = cases self.cases = cases
self.flaky_tests_mode = flaky_tests_mode
self.parallel_queue = Queue(len(cases)) self.parallel_queue = Queue(len(cases))
self.sequential_queue = Queue(len(cases)) self.sequential_queue = Queue(len(cases))
for case in cases: for case in cases:
@ -251,7 +252,10 @@ class TapProgressIndicator(SimpleProgressIndicator):
self._done += 1 self._done += 1
command = basename(output.command[-1]) command = basename(output.command[-1])
if output.UnexpectedOutput(): if output.UnexpectedOutput():
logger.info('not ok %i - %s' % (self._done, command)) status_line = 'not ok %i - %s' % (self._done, command)
if FLAKY in output.test.outcomes and self.flaky_tests_mode == DONTCARE:
status_line = status_line + ' # TODO : Fix flaky test'
logger.info(status_line)
for l in output.output.stderr.splitlines(): for l in output.output.stderr.splitlines():
logger.info('#' + l) logger.info('#' + l)
for l in output.output.stdout.splitlines(): for l in output.output.stdout.splitlines():
@ -262,7 +266,10 @@ class TapProgressIndicator(SimpleProgressIndicator):
logger.info( logger.info(
'ok %i - %s # skip %s' % (self._done, command, skip.group(1))) 'ok %i - %s # skip %s' % (self._done, command, skip.group(1)))
else: else:
logger.info('ok %i - %s' % (self._done, command)) status_line = 'ok %i - %s' % (self._done, command)
if FLAKY in output.test.outcomes:
status_line = status_line + ' # TODO : Fix flaky test'
logger.info(status_line)
duration = output.test.duration duration = output.test.duration
@ -280,8 +287,8 @@ class TapProgressIndicator(SimpleProgressIndicator):
class CompactProgressIndicator(ProgressIndicator): class CompactProgressIndicator(ProgressIndicator):
def __init__(self, cases, templates): def __init__(self, cases, flaky_tests_mode, templates):
super(CompactProgressIndicator, self).__init__(cases) super(CompactProgressIndicator, self).__init__(cases, flaky_tests_mode)
self.templates = templates self.templates = templates
self.last_status_length = 0 self.last_status_length = 0
self.start_time = time.time() self.start_time = time.time()
@ -336,13 +343,13 @@ class CompactProgressIndicator(ProgressIndicator):
class ColorProgressIndicator(CompactProgressIndicator): class ColorProgressIndicator(CompactProgressIndicator):
def __init__(self, cases): def __init__(self, cases, flaky_tests_mode):
templates = { templates = {
'status_line': "[%(mins)02i:%(secs)02i|\033[34m%%%(remaining) 4d\033[0m|\033[32m+%(passed) 4d\033[0m|\033[31m-%(failed) 4d\033[0m]: %(test)s", 'status_line': "[%(mins)02i:%(secs)02i|\033[34m%%%(remaining) 4d\033[0m|\033[32m+%(passed) 4d\033[0m|\033[31m-%(failed) 4d\033[0m]: %(test)s",
'stdout': "\033[1m%s\033[0m", 'stdout': "\033[1m%s\033[0m",
'stderr': "\033[31m%s\033[0m", 'stderr': "\033[31m%s\033[0m",
} }
super(ColorProgressIndicator, self).__init__(cases, templates) super(ColorProgressIndicator, self).__init__(cases, flaky_tests_mode, templates)
def ClearLine(self, last_line_length): def ClearLine(self, last_line_length):
print "\033[1K\r", print "\033[1K\r",
@ -350,7 +357,7 @@ class ColorProgressIndicator(CompactProgressIndicator):
class MonochromeProgressIndicator(CompactProgressIndicator): class MonochromeProgressIndicator(CompactProgressIndicator):
def __init__(self, cases): def __init__(self, cases, flaky_tests_mode):
templates = { templates = {
'status_line': "[%(mins)02i:%(secs)02i|%%%(remaining) 4d|+%(passed) 4d|-%(failed) 4d]: %(test)s", 'status_line': "[%(mins)02i:%(secs)02i|%%%(remaining) 4d|+%(passed) 4d|-%(failed) 4d]: %(test)s",
'stdout': '%s', 'stdout': '%s',
@ -358,7 +365,7 @@ class MonochromeProgressIndicator(CompactProgressIndicator):
'clear': lambda last_line_length: ("\r" + (" " * last_line_length) + "\r"), 'clear': lambda last_line_length: ("\r" + (" " * last_line_length) + "\r"),
'max_length': 78 'max_length': 78
} }
super(MonochromeProgressIndicator, self).__init__(cases, templates) super(MonochromeProgressIndicator, self).__init__(cases, flaky_tests_mode, templates)
def ClearLine(self, last_line_length): def ClearLine(self, last_line_length):
print ("\r" + (" " * last_line_length) + "\r"), print ("\r" + (" " * last_line_length) + "\r"),
@ -780,8 +787,8 @@ class Context(object):
def GetTimeout(self, mode): def GetTimeout(self, mode):
return self.timeout * TIMEOUT_SCALEFACTOR[ARCH_GUESS or 'ia32'][mode] return self.timeout * TIMEOUT_SCALEFACTOR[ARCH_GUESS or 'ia32'][mode]
def RunTestCases(cases_to_run, progress, tasks): def RunTestCases(cases_to_run, progress, tasks, flaky_tests_mode):
progress = PROGRESS_INDICATORS[progress](cases_to_run) progress = PROGRESS_INDICATORS[progress](cases_to_run, flaky_tests_mode)
return progress.Run(tasks) return progress.Run(tasks)
@ -805,7 +812,8 @@ OKAY = 'okay'
TIMEOUT = 'timeout' TIMEOUT = 'timeout'
CRASH = 'crash' CRASH = 'crash'
SLOW = 'slow' SLOW = 'slow'
FLAKY = 'flaky'
DONTCARE = 'dontcare'
class Expression(object): class Expression(object):
pass pass
@ -1253,6 +1261,9 @@ def BuildOptions():
default=False, action="store_true") default=False, action="store_true")
result.add_option("--cat", help="Print the source of the tests", result.add_option("--cat", help="Print the source of the tests",
default=False, action="store_true") default=False, action="store_true")
result.add_option("--flaky-tests",
help="Regard tests marked as flaky (run|skip|dontcare)",
default="run")
result.add_option("--warn-unused", help="Report unused rules", result.add_option("--warn-unused", help="Report unused rules",
default=False, action="store_true") default=False, action="store_true")
result.add_option("-j", help="The number of parallel tasks to run", result.add_option("-j", help="The number of parallel tasks to run",
@ -1303,6 +1314,9 @@ def ProcessOptions(options):
return False return False
if options.J: if options.J:
options.j = multiprocessing.cpu_count() options.j = multiprocessing.cpu_count()
if options.flaky_tests not in ["run", "skip", "dontcare"]:
print "Unknown flaky-tests mode %s" % options.flaky_tests
return False
return True return True
@ -1505,7 +1519,9 @@ def Main():
result = None result = None
def DoSkip(case): def DoSkip(case):
return SKIP in case.outcomes or SLOW in case.outcomes if SKIP in case.outcomes or SLOW in case.outcomes:
return True
return FLAKY in case.outcomes and options.flaky_tests == SKIP
cases_to_run = [ c for c in all_cases if not DoSkip(c) ] cases_to_run = [ c for c in all_cases if not DoSkip(c) ]
if options.run is not None: if options.run is not None:
# Must ensure the list of tests is sorted before selecting, to avoid # Must ensure the list of tests is sorted before selecting, to avoid
@ -1522,7 +1538,7 @@ def Main():
else: else:
try: try:
start = time.time() start = time.time()
if RunTestCases(cases_to_run, options.progress, options.j): if RunTestCases(cases_to_run, options.progress, options.j, options.flaky_tests):
result = 0 result = 0
else: else:
result = 1 result = 1

Loading…
Cancel
Save