109467b48Spatrickimport sys 209467b48Spatrick 309467b48Spatrick 409467b48Spatrickdef create_display(opts, tests, total_tests, workers): 509467b48Spatrick if opts.quiet: 609467b48Spatrick return NopDisplay() 709467b48Spatrick 8*73471bf0Spatrick num_tests = len(tests) 9*73471bf0Spatrick of_total = (' of %d' % total_tests) if (num_tests != total_tests) else '' 10*73471bf0Spatrick header = '-- Testing: %d%s tests, %d workers --' % ( 11*73471bf0Spatrick num_tests, of_total, workers) 1209467b48Spatrick 1309467b48Spatrick progress_bar = None 1409467b48Spatrick if opts.succinct and opts.useProgressBar: 1509467b48Spatrick import lit.ProgressBar 1609467b48Spatrick try: 1709467b48Spatrick tc = lit.ProgressBar.TerminalController() 1809467b48Spatrick progress_bar = lit.ProgressBar.ProgressBar(tc, header) 1909467b48Spatrick header = None 2009467b48Spatrick except ValueError: 2109467b48Spatrick progress_bar = lit.ProgressBar.SimpleProgressBar('Testing: ') 2209467b48Spatrick 2309467b48Spatrick return Display(opts, tests, header, progress_bar) 2409467b48Spatrick 2509467b48Spatrick 26*73471bf0Spatrickclass ProgressPredictor(object): 27*73471bf0Spatrick def __init__(self, tests): 28*73471bf0Spatrick self.completed = 0 29*73471bf0Spatrick self.time_elapsed = 0.0 30*73471bf0Spatrick self.predictable_tests_remaining = 0 31*73471bf0Spatrick self.predictable_time_remaining = 0.0 32*73471bf0Spatrick self.unpredictable_tests_remaining = 0 33*73471bf0Spatrick 34*73471bf0Spatrick for test in tests: 35*73471bf0Spatrick if test.previous_elapsed: 36*73471bf0Spatrick self.predictable_tests_remaining += 1 37*73471bf0Spatrick self.predictable_time_remaining += test.previous_elapsed 38*73471bf0Spatrick else: 39*73471bf0Spatrick self.unpredictable_tests_remaining += 1 40*73471bf0Spatrick 41*73471bf0Spatrick def update(self, test): 42*73471bf0Spatrick self.completed += 1 43*73471bf0Spatrick self.time_elapsed += test.result.elapsed 44*73471bf0Spatrick 45*73471bf0Spatrick if test.previous_elapsed: 46*73471bf0Spatrick self.predictable_tests_remaining -= 1 47*73471bf0Spatrick self.predictable_time_remaining -= test.previous_elapsed 48*73471bf0Spatrick else: 49*73471bf0Spatrick self.unpredictable_tests_remaining -= 1 50*73471bf0Spatrick 51*73471bf0Spatrick # NOTE: median would be more precise, but might be too slow. 52*73471bf0Spatrick average_test_time = (self.time_elapsed + self.predictable_time_remaining) / \ 53*73471bf0Spatrick (self.completed + self.predictable_tests_remaining) 54*73471bf0Spatrick unpredictable_time_remaining = average_test_time * \ 55*73471bf0Spatrick self.unpredictable_tests_remaining 56*73471bf0Spatrick total_time_remaining = self.predictable_time_remaining + unpredictable_time_remaining 57*73471bf0Spatrick total_time = self.time_elapsed + total_time_remaining 58*73471bf0Spatrick 59*73471bf0Spatrick if total_time > 0: 60*73471bf0Spatrick return self.time_elapsed / total_time 61*73471bf0Spatrick return 0 62*73471bf0Spatrick 63*73471bf0Spatrick 6409467b48Spatrickclass NopDisplay(object): 6509467b48Spatrick def print_header(self): pass 6609467b48Spatrick def update(self, test): pass 6709467b48Spatrick def clear(self, interrupted): pass 6809467b48Spatrick 6909467b48Spatrick 7009467b48Spatrickclass Display(object): 7109467b48Spatrick def __init__(self, opts, tests, header, progress_bar): 7209467b48Spatrick self.opts = opts 73*73471bf0Spatrick self.num_tests = len(tests) 7409467b48Spatrick self.header = header 75*73471bf0Spatrick self.progress_predictor = ProgressPredictor( 76*73471bf0Spatrick tests) if progress_bar else None 7709467b48Spatrick self.progress_bar = progress_bar 7809467b48Spatrick self.completed = 0 7909467b48Spatrick 8009467b48Spatrick def print_header(self): 8109467b48Spatrick if self.header: 8209467b48Spatrick print(self.header) 8309467b48Spatrick if self.progress_bar: 8409467b48Spatrick self.progress_bar.update(0.0, '') 8509467b48Spatrick 8609467b48Spatrick def update(self, test): 8709467b48Spatrick self.completed += 1 8809467b48Spatrick 8909467b48Spatrick show_result = test.isFailure() or \ 9009467b48Spatrick self.opts.showAllOutput or \ 9109467b48Spatrick (not self.opts.quiet and not self.opts.succinct) 9209467b48Spatrick if show_result: 9309467b48Spatrick if self.progress_bar: 9409467b48Spatrick self.progress_bar.clear(interrupted=False) 9509467b48Spatrick self.print_result(test) 9609467b48Spatrick 9709467b48Spatrick if self.progress_bar: 9809467b48Spatrick if test.isFailure(): 9909467b48Spatrick self.progress_bar.barColor = 'RED' 100*73471bf0Spatrick percent = self.progress_predictor.update(test) 10109467b48Spatrick self.progress_bar.update(percent, test.getFullName()) 10209467b48Spatrick 10309467b48Spatrick def clear(self, interrupted): 10409467b48Spatrick if self.progress_bar: 10509467b48Spatrick self.progress_bar.clear(interrupted) 10609467b48Spatrick 10709467b48Spatrick def print_result(self, test): 10809467b48Spatrick # Show the test result line. 10909467b48Spatrick test_name = test.getFullName() 11009467b48Spatrick print('%s: %s (%d of %d)' % (test.result.code.name, test_name, 111*73471bf0Spatrick self.completed, self.num_tests)) 11209467b48Spatrick 11309467b48Spatrick # Show the test failure output, if requested. 11409467b48Spatrick if (test.isFailure() and self.opts.showOutput) or \ 11509467b48Spatrick self.opts.showAllOutput: 11609467b48Spatrick if test.isFailure(): 11709467b48Spatrick print("%s TEST '%s' FAILED %s" % ('*'*20, test.getFullName(), 11809467b48Spatrick '*'*20)) 11909467b48Spatrick out = test.result.output 12009467b48Spatrick # Encode/decode so that, when using Python 3.6.5 in Windows 10, 12109467b48Spatrick # print(out) doesn't raise UnicodeEncodeError if out contains 12209467b48Spatrick # special characters. However, Python 2 might try to decode 12309467b48Spatrick # as part of the encode call if out is already encoded, so skip 12409467b48Spatrick # encoding if it raises UnicodeDecodeError. 12509467b48Spatrick if sys.stdout.encoding: 12609467b48Spatrick try: 12709467b48Spatrick out = out.encode(encoding=sys.stdout.encoding, 12809467b48Spatrick errors="replace") 12909467b48Spatrick except UnicodeDecodeError: 13009467b48Spatrick pass 131097a140dSpatrick # Python 2 can raise UnicodeDecodeError here too in cases 132097a140dSpatrick # where the stdout encoding is ASCII. Ignore decode errors 133097a140dSpatrick # in this case. 134097a140dSpatrick out = out.decode(encoding=sys.stdout.encoding, errors="ignore") 13509467b48Spatrick print(out) 13609467b48Spatrick print("*" * 20) 13709467b48Spatrick 13809467b48Spatrick # Report test metrics, if present. 13909467b48Spatrick if test.result.metrics: 14009467b48Spatrick print("%s TEST '%s' RESULTS %s" % ('*'*10, test.getFullName(), 14109467b48Spatrick '*'*10)) 14209467b48Spatrick items = sorted(test.result.metrics.items()) 14309467b48Spatrick for metric_name, value in items: 14409467b48Spatrick print('%s: %s ' % (metric_name, value.format())) 14509467b48Spatrick print("*" * 10) 14609467b48Spatrick 14709467b48Spatrick # Report micro-tests, if present 14809467b48Spatrick if test.result.microResults: 14909467b48Spatrick items = sorted(test.result.microResults.items()) 15009467b48Spatrick for micro_test_name, micro_test in items: 15109467b48Spatrick print("%s MICRO-TEST: %s" % 15209467b48Spatrick ('*'*3, micro_test_name)) 15309467b48Spatrick 15409467b48Spatrick if micro_test.metrics: 15509467b48Spatrick sorted_metrics = sorted(micro_test.metrics.items()) 15609467b48Spatrick for metric_name, value in sorted_metrics: 15709467b48Spatrick print(' %s: %s ' % (metric_name, value.format())) 15809467b48Spatrick 15909467b48Spatrick # Ensure the output is flushed. 16009467b48Spatrick sys.stdout.flush() 161