xref: /netbsd-src/external/apache2/llvm/dist/llvm/utils/lit/lit/display.py (revision 82d56013d7b633d116a93943de88e08335357a7c)
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