Skip to content

Commit 3a7eb39

Browse files
committed
merge with HEAD
1 parent b469721 commit 3a7eb39

File tree

4 files changed

+87
-71
lines changed

4 files changed

+87
-71
lines changed

test/color_runner.py

Lines changed: 4 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -35,37 +35,13 @@ def _restoreStdout(self):
3535
class ProgressMixin:
3636
test_count = 0
3737
progress_counter = 0
38-
fail_count = 0
39-
40-
def addError(self, test, err):
41-
self.fail_count += 1
42-
super().addError(test, err)
43-
44-
def addFailure(self, test, err):
45-
self.fail_count += 1
46-
super().addFailure(test, err)
47-
48-
def addUnexpectedSuccess(self, test):
49-
self.fail_count += 1
50-
super().addUnexpectedSuccess(test)
51-
52-
def writeProgressPrefix(self):
53-
if self.showAll:
54-
base_progress = f'[{self.progress_counter}/{self.test_count}'
55-
rtn = len(base_progress)
56-
progress = with_color(CYAN, base_progress)
57-
if self.fail_count:
58-
fail_str = f'-{self.fail_count}'
59-
progress += with_color(RED, fail_str)
60-
rtn += len(fail_str)
61-
progress += with_color(CYAN, '] ')
62-
self.stream.write(progress)
63-
return rtn + 2
64-
return 0
6538

6639
def startTest(self, test):
40+
assert self.test_count > 0
6741
self.progress_counter += 1
68-
self.writeProgressPrefix()
42+
if self.showAll:
43+
progress = f'[{self.progress_counter}/{self.test_count}] '
44+
self.stream.write(with_color(CYAN, progress))
6945
super().startTest(test)
7046

7147

@@ -86,9 +62,6 @@ class ColorTextRunner(unittest.TextTestRunner):
8662
"""Subclass of TextTestRunner that uses ColorTextResult"""
8763
resultclass = ColorTextResult # type: ignore
8864

89-
def __init__(self, *args, **kwargs):
90-
super().__init__(*args, verbosity=2, **kwargs)
91-
9265
def _makeResult(self):
9366
result = super()._makeResult()
9467
result.test_count = self.test_count

test/parallel_testsuite.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,37 @@ def addTest(self, test):
115115
super().addTest(test)
116116
test.is_parallel = True
117117

118+
def printOneResult(self, res):
119+
self.progress_counter += 1
120+
progress = f'[{self.progress_counter}/{self.num_tests}] '
121+
122+
if res.test_result == 'success':
123+
msg = 'ok'
124+
color = GREEN
125+
elif res.test_result == 'errored':
126+
msg = 'ERROR'
127+
color = RED
128+
elif res.test_result == 'failed':
129+
msg = 'FAIL'
130+
color = RED
131+
elif res.test_result == 'skipped':
132+
reason = res.skipped[0][1]
133+
msg = f"skipped '{reason}'"
134+
color = CYAN
135+
elif res.test_result == 'unexpected success':
136+
msg = 'unexpected success'
137+
color = RED
138+
elif res.test_result == 'expected failure':
139+
color = RED
140+
msg = 'expected failure'
141+
else:
142+
assert False, f'unhandled test result {res.test_result}'
143+
144+
if res.test_result != 'skipped':
145+
msg += f' ({res.elapsed:.2f}s)'
146+
147+
errlog(f'{with_color(CYAN, progress)}{res.test} ... {with_color(color, msg)}')
148+
118149
def run(self, result):
119150
# The 'spawn' method is used on windows and it can be useful to set this on
120151
# all platforms when debugging multiprocessing issues. Without this we

test/runner.py

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -429,16 +429,12 @@ def run_tests(options, suites):
429429
testRunner = xmlrunner.XMLTestRunner(output=output, verbosity=2,
430430
failfast=options.failfast)
431431
print('Writing XML test output to ' + os.path.abspath(output.name))
432-
elif options.ansi and not options.verbose:
433-
# When not in verbose mode and ansi color output is available use our nice single-line
432+
elif options.buffer and options.ansi and not options.verbose:
433+
# When buffering is enabled and ansi color output is available use our nice single-line
434434
# result display.
435-
testRunner = SingleLineTestRunner(failfast=options.failfast)
435+
testRunner = SingleLineTestRunner(verbosity=2, failfast=options.failfast)
436436
else:
437-
if not options.ansi:
438-
print('using verbose test runner (ANSI not avilable)')
439-
else:
440-
print('using verbose test runner (verbose output requested)')
441-
testRunner = ColorTextRunner(failfast=options.failfast)
437+
testRunner = ColorTextRunner(verbosity=2, failfast=options.failfast)
442438

443439
total_core_time = 0
444440
run_start_time = time.perf_counter()
@@ -476,9 +472,7 @@ def parse_args():
476472
'test. Implies --cores=1. Defaults to true when running a single test')
477473
parser.add_argument('--no-clean', action='store_true',
478474
help='Do not clean the temporary directory before each test run')
479-
parser.add_argument('--verbose', '-v', action='count', default=0,
480-
help="Show test stdout and stderr, and don't use the single-line test reporting. "
481-
'Specifying `-v` twice will enable test framework logging (i.e. EMTEST_VERBOSE)')
475+
parser.add_argument('--verbose', '-v', action='store_true')
482476
# TODO: Replace with BooleanOptionalAction once we can depend on python3.9
483477
parser.add_argument('--ansi', action='store_true', default=None)
484478
parser.add_argument('--no-ansi', action='store_false', dest='ansi', default=None)

test/single_line_runner.py

Lines changed: 47 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import shutil
77
import unittest
88

9-
from color_runner import ColorTextResult, ColorTextRunner
9+
from color_runner import BufferingMixin, ColorTextRunner
1010

1111
from tools.colored_logger import CYAN, GREEN, RED, with_color
1212

@@ -20,47 +20,65 @@ def term_width():
2020
return shutil.get_terminal_size()[0]
2121

2222

23-
class SingleLineTestResult(ColorTextResult):
23+
class SingleLineTestResult(BufferingMixin, unittest.TextTestResult):
2424
"""Similar to the standard TextTestResult but uses ANSI escape codes
2525
for color output and reusing a single line on the terminal.
2626
"""
2727

28-
def writeStatus(self, test, msg, color, line_pos):
29-
# Because the message can include the skip reason (which can be very long sometimes), truncate
30-
# it to a reasonable length to avoid exceeding line length.
31-
if len(msg) > 30:
32-
msg = msg[:30]
33-
# Format the line so that it fix within the terminal width, unless its less then min_len
28+
def __init__(self, *args, **kwargs):
29+
super().__init__(*args, **kwargs)
30+
self.progress_counter = 0
31+
32+
def writeStatusLine(self, line):
33+
clearline(self._original_stderr)
34+
self._original_stderr.write(line)
35+
self._original_stderr.flush()
36+
37+
def updateStatus(self, test, msg, color):
38+
progress = f'[{self.progress_counter}/{self.test_count}] '
39+
# Format the line so that it fix within the terminal width, unless it's less then min_len
3440
# in which case there is not much we can do, and we just overflow the line.
35-
min_len = line_pos + len(msg) + 5
41+
min_len = len(progress) + len(msg) + 5
3642
test_name = str(test)
3743
if term_width() > min_len:
3844
max_name = term_width() - min_len
3945
test_name = test_name[:max_name]
40-
line = f'{test_name} ... {with_color(color, msg)}'
41-
self._original_stderr.write(line)
42-
43-
def _write_status(self, test, status):
44-
clearline(self._original_stderr)
45-
pos = self.writeProgressPrefix()
46-
# Add some color to the status message
47-
if status == 'ok':
48-
color = GREEN
49-
elif status.isupper():
50-
color = RED
51-
# Use a newline when a test fails, so you can see a list of failures while
52-
# the other tests are still running
53-
status += '\n'
54-
else:
55-
color = CYAN
56-
self.writeStatus(test, status, color, pos)
57-
self._original_stderr.flush()
46+
line = f'{with_color(CYAN, progress)}{test_name} ... {with_color(color, msg)}'
47+
self.writeStatusLine(line)
5848

5949
def startTest(self, test):
6050
self.progress_counter += 1
61-
# We explictly don't call TextTestResult.startTest here since we delay all printing
62-
# of results until `_write_status`
51+
assert self.test_count > 0
52+
# Note: We explicitly do not use `super()` here but instead call `unittest.TestResult`. i.e.
53+
# we skip the superclass (since we don't want its specific behaviour) and instead call its
54+
# superclass.
6355
unittest.TestResult.startTest(self, test)
56+
if self.progress_counter == 1:
57+
self.updateStatus(test, '', GREEN)
58+
59+
def addSuccess(self, test):
60+
unittest.TestResult.addSuccess(self, test)
61+
self.updateStatus(test, 'ok', GREEN)
62+
63+
def addFailure(self, test, err):
64+
unittest.TestResult.addFailure(self, test, err)
65+
self.updateStatus(test, 'FAIL', RED)
66+
67+
def addError(self, test, err):
68+
unittest.TestResult.addError(self, test, err)
69+
self.updateStatus(test, 'ERROR', RED)
70+
71+
def addExpectedFailure(self, test, err):
72+
unittest.TestResult.addExpectedFailure(self, test, err)
73+
self.updateStatus(test, 'expected failure', RED)
74+
75+
def addUnexpectedSuccess(self, test, err):
76+
unittest.TestResult.addUnexpectedSuccess(self, test, err)
77+
self.updateStatus(test, 'UNEXPECTED SUCCESS', RED)
78+
79+
def addSkip(self, test, reason):
80+
unittest.TestResult.addSkip(self, test, reason)
81+
self.updateStatus(test, f"skipped '{reason}'", CYAN)
6482

6583
def printErrors(self):
6684
# All tests have been run at this point so print a final newline

0 commit comments

Comments
 (0)