169 lines
5.8 KiB
Python
169 lines
5.8 KiB
Python
import sys
|
|
|
|
|
|
def create_display(opts, tests, total_tests, workers):
|
|
if opts.quiet:
|
|
return NopDisplay()
|
|
|
|
num_tests = len(tests)
|
|
of_total = (" of %d" % total_tests) if (num_tests != total_tests) else ""
|
|
header = "-- Testing: %d%s tests, %d workers --" % (num_tests, of_total, workers)
|
|
|
|
progress_bar = None
|
|
if opts.succinct and opts.useProgressBar:
|
|
import lit.ProgressBar
|
|
|
|
try:
|
|
tc = lit.ProgressBar.TerminalController()
|
|
progress_bar = lit.ProgressBar.ProgressBar(tc, header)
|
|
header = None
|
|
except ValueError:
|
|
progress_bar = lit.ProgressBar.SimpleProgressBar("Testing: ")
|
|
|
|
return Display(opts, tests, header, progress_bar)
|
|
|
|
|
|
class ProgressPredictor(object):
|
|
def __init__(self, tests):
|
|
self.completed = 0
|
|
self.time_elapsed = 0.0
|
|
self.predictable_tests_remaining = 0
|
|
self.predictable_time_remaining = 0.0
|
|
self.unpredictable_tests_remaining = 0
|
|
|
|
for test in tests:
|
|
if test.previous_elapsed:
|
|
self.predictable_tests_remaining += 1
|
|
self.predictable_time_remaining += test.previous_elapsed
|
|
else:
|
|
self.unpredictable_tests_remaining += 1
|
|
|
|
def update(self, test):
|
|
self.completed += 1
|
|
self.time_elapsed += test.result.elapsed
|
|
|
|
if test.previous_elapsed:
|
|
self.predictable_tests_remaining -= 1
|
|
self.predictable_time_remaining -= test.previous_elapsed
|
|
else:
|
|
self.unpredictable_tests_remaining -= 1
|
|
|
|
# NOTE: median would be more precise, but might be too slow.
|
|
average_test_time = (self.time_elapsed + self.predictable_time_remaining) / (
|
|
self.completed + self.predictable_tests_remaining
|
|
)
|
|
unpredictable_time_remaining = (
|
|
average_test_time * self.unpredictable_tests_remaining
|
|
)
|
|
total_time_remaining = (
|
|
self.predictable_time_remaining + unpredictable_time_remaining
|
|
)
|
|
total_time = self.time_elapsed + total_time_remaining
|
|
|
|
if total_time > 0:
|
|
return self.time_elapsed / total_time
|
|
return 0
|
|
|
|
|
|
class NopDisplay(object):
|
|
def print_header(self):
|
|
pass
|
|
|
|
def update(self, test):
|
|
pass
|
|
|
|
def clear(self, interrupted):
|
|
pass
|
|
|
|
|
|
class Display(object):
|
|
def __init__(self, opts, tests, header, progress_bar):
|
|
self.opts = opts
|
|
self.num_tests = len(tests)
|
|
self.header = header
|
|
self.progress_predictor = ProgressPredictor(tests) if progress_bar else None
|
|
self.progress_bar = progress_bar
|
|
self.completed = 0
|
|
|
|
def print_header(self):
|
|
if self.header:
|
|
print(self.header)
|
|
if self.progress_bar:
|
|
self.progress_bar.update(0.0, "")
|
|
|
|
def update(self, test):
|
|
self.completed += 1
|
|
|
|
show_result = (
|
|
test.isFailure()
|
|
or self.opts.showAllOutput
|
|
or (not self.opts.quiet and not self.opts.succinct)
|
|
)
|
|
if show_result:
|
|
if self.progress_bar:
|
|
self.progress_bar.clear(interrupted=False)
|
|
self.print_result(test)
|
|
|
|
if self.progress_bar:
|
|
if test.isFailure():
|
|
self.progress_bar.barColor = "RED"
|
|
percent = self.progress_predictor.update(test)
|
|
self.progress_bar.update(percent, test.getFullName())
|
|
|
|
def clear(self, interrupted):
|
|
if self.progress_bar:
|
|
self.progress_bar.clear(interrupted)
|
|
|
|
def print_result(self, test):
|
|
# Show the test result line.
|
|
test_name = test.getFullName()
|
|
print(
|
|
"%s: %s (%d of %d)"
|
|
% (test.result.code.name, test_name, self.completed, self.num_tests)
|
|
)
|
|
|
|
# Show the test failure output, if requested.
|
|
if (test.isFailure() and self.opts.showOutput) or self.opts.showAllOutput:
|
|
if test.isFailure():
|
|
print(
|
|
"%s TEST '%s' FAILED %s" % ("*" * 20, test.getFullName(), "*" * 20)
|
|
)
|
|
out = test.result.output
|
|
# Encode/decode so that, when using Python 3.6.5 in Windows 10,
|
|
# print(out) doesn't raise UnicodeEncodeError if out contains
|
|
# special characters. However, Python 2 might try to decode
|
|
# as part of the encode call if out is already encoded, so skip
|
|
# encoding if it raises UnicodeDecodeError.
|
|
if sys.stdout.encoding:
|
|
try:
|
|
out = out.encode(encoding=sys.stdout.encoding, errors="replace")
|
|
except UnicodeDecodeError:
|
|
pass
|
|
# Python 2 can raise UnicodeDecodeError here too in cases
|
|
# where the stdout encoding is ASCII. Ignore decode errors
|
|
# in this case.
|
|
out = out.decode(encoding=sys.stdout.encoding, errors="ignore")
|
|
print(out)
|
|
print("*" * 20)
|
|
|
|
# Report test metrics, if present.
|
|
if test.result.metrics:
|
|
print("%s TEST '%s' RESULTS %s" % ("*" * 10, test.getFullName(), "*" * 10))
|
|
items = sorted(test.result.metrics.items())
|
|
for metric_name, value in items:
|
|
print("%s: %s " % (metric_name, value.format()))
|
|
print("*" * 10)
|
|
|
|
# Report micro-tests, if present
|
|
if test.result.microResults:
|
|
items = sorted(test.result.microResults.items())
|
|
for micro_test_name, micro_test in items:
|
|
print("%s MICRO-TEST: %s" % ("*" * 3, micro_test_name))
|
|
|
|
if micro_test.metrics:
|
|
sorted_metrics = sorted(micro_test.metrics.items())
|
|
for metric_name, value in sorted_metrics:
|
|
print(" %s: %s " % (metric_name, value.format()))
|
|
|
|
# Ensure the output is flushed.
|
|
sys.stdout.flush()
|