| 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() |