66import shutil
77import unittest
88
9- from color_runner import ColorTextResult , ColorTextRunner
9+ from color_runner import BufferingMixin , ColorTextRunner
1010
1111from 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