1import sys 2 3 4def create_display(opts, tests, total_tests, workers): 5 if opts.quiet: 6 return NopDisplay() 7 8 num_tests = len(tests) 9 of_total = (' of %d' % total_tests) if (num_tests != total_tests) else '' 10 header = '-- Testing: %d%s tests, %d workers --' % ( 11 num_tests, of_total, workers) 12 13 progress_bar = None 14 if opts.succinct and opts.useProgressBar: 15 import lit.ProgressBar 16 try: 17 tc = lit.ProgressBar.TerminalController() 18 progress_bar = lit.ProgressBar.ProgressBar(tc, header) 19 header = None 20 except ValueError: 21 progress_bar = lit.ProgressBar.SimpleProgressBar('Testing: ') 22 23 return Display(opts, tests, header, progress_bar) 24 25 26class ProgressPredictor(object): 27 def __init__(self, tests): 28 self.completed = 0 29 self.time_elapsed = 0.0 30 self.predictable_tests_remaining = 0 31 self.predictable_time_remaining = 0.0 32 self.unpredictable_tests_remaining = 0 33 34 for test in tests: 35 if test.previous_elapsed: 36 self.predictable_tests_remaining += 1 37 self.predictable_time_remaining += test.previous_elapsed 38 else: 39 self.unpredictable_tests_remaining += 1 40 41 def update(self, test): 42 self.completed += 1 43 self.time_elapsed += test.result.elapsed 44 45 if test.previous_elapsed: 46 self.predictable_tests_remaining -= 1 47 self.predictable_time_remaining -= test.previous_elapsed 48 else: 49 self.unpredictable_tests_remaining -= 1 50 51 # NOTE: median would be more precise, but might be too slow. 52 average_test_time = (self.time_elapsed + self.predictable_time_remaining) / \ 53 (self.completed + self.predictable_tests_remaining) 54 unpredictable_time_remaining = average_test_time * \ 55 self.unpredictable_tests_remaining 56 total_time_remaining = self.predictable_time_remaining + unpredictable_time_remaining 57 total_time = self.time_elapsed + total_time_remaining 58 59 if total_time > 0: 60 return self.time_elapsed / total_time 61 return 0 62 63 64class NopDisplay(object): 65 def print_header(self): pass 66 def update(self, test): pass 67 def clear(self, interrupted): pass 68 69 70class Display(object): 71 def __init__(self, opts, tests, header, progress_bar): 72 self.opts = opts 73 self.num_tests = len(tests) 74 self.header = header 75 self.progress_predictor = ProgressPredictor( 76 tests) if progress_bar else None 77 self.progress_bar = progress_bar 78 self.completed = 0 79 80 def print_header(self): 81 if self.header: 82 print(self.header) 83 if self.progress_bar: 84 self.progress_bar.update(0.0, '') 85 86 def update(self, test): 87 self.completed += 1 88 89 show_result = test.isFailure() or \ 90 self.opts.showAllOutput or \ 91 (not self.opts.quiet and not self.opts.succinct) 92 if show_result: 93 if self.progress_bar: 94 self.progress_bar.clear(interrupted=False) 95 self.print_result(test) 96 97 if self.progress_bar: 98 if test.isFailure(): 99 self.progress_bar.barColor = 'RED' 100 percent = self.progress_predictor.update(test) 101 self.progress_bar.update(percent, test.getFullName()) 102 103 def clear(self, interrupted): 104 if self.progress_bar: 105 self.progress_bar.clear(interrupted) 106 107 def print_result(self, test): 108 # Show the test result line. 109 test_name = test.getFullName() 110 print('%s: %s (%d of %d)' % (test.result.code.name, test_name, 111 self.completed, self.num_tests)) 112 113 # Show the test failure output, if requested. 114 if (test.isFailure() and self.opts.showOutput) or \ 115 self.opts.showAllOutput: 116 if test.isFailure(): 117 print("%s TEST '%s' FAILED %s" % ('*'*20, test.getFullName(), 118 '*'*20)) 119 out = test.result.output 120 # Encode/decode so that, when using Python 3.6.5 in Windows 10, 121 # print(out) doesn't raise UnicodeEncodeError if out contains 122 # special characters. However, Python 2 might try to decode 123 # as part of the encode call if out is already encoded, so skip 124 # encoding if it raises UnicodeDecodeError. 125 if sys.stdout.encoding: 126 try: 127 out = out.encode(encoding=sys.stdout.encoding, 128 errors="replace") 129 except UnicodeDecodeError: 130 pass 131 # Python 2 can raise UnicodeDecodeError here too in cases 132 # where the stdout encoding is ASCII. Ignore decode errors 133 # in this case. 134 out = out.decode(encoding=sys.stdout.encoding, errors="ignore") 135 print(out) 136 print("*" * 20) 137 138 # Report test metrics, if present. 139 if test.result.metrics: 140 print("%s TEST '%s' RESULTS %s" % ('*'*10, test.getFullName(), 141 '*'*10)) 142 items = sorted(test.result.metrics.items()) 143 for metric_name, value in items: 144 print('%s: %s ' % (metric_name, value.format())) 145 print("*" * 10) 146 147 # Report micro-tests, if present 148 if test.result.microResults: 149 items = sorted(test.result.microResults.items()) 150 for micro_test_name, micro_test in items: 151 print("%s MICRO-TEST: %s" % 152 ('*'*3, micro_test_name)) 153 154 if micro_test.metrics: 155 sorted_metrics = sorted(micro_test.metrics.items()) 156 for metric_name, value in sorted_metrics: 157 print(' %s: %s ' % (metric_name, value.format())) 158 159 # Ensure the output is flushed. 160 sys.stdout.flush() 161