99 from unittest .runner import _WritelnDecorator
1010
1111# ANSI color codes
12- GREEN = ' \033 [92m'
13- RED = ' \033 [91m'
14- YELLOW = ' \033 [93m'
15- BOLD = ' \033 [1m'
16- RESET = ' \033 [0m'
12+ GREEN = " \033 [92m"
13+ RED = " \033 [91m"
14+ YELLOW = " \033 [93m"
15+ BOLD = " \033 [1m"
16+ RESET = " \033 [0m"
1717
1818
1919class ColorTextTestResult (unittest .TextTestResult ):
2020 """Custom test result class with colorized output and output buffering."""
2121
22- def __init__ (self , stream : "_WritelnDecorator" , descriptions : bool , verbosity : int ) -> None :
22+ def __init__ (
23+ self , stream : "_WritelnDecorator" , descriptions : bool , verbosity : int
24+ ) -> None :
2325 super ().__init__ (stream , descriptions , verbosity )
2426 self .buffer = True # Enable output buffering
2527 self .current_class = None
@@ -33,85 +35,96 @@ def startTest(self, test):
3335 test_class = test .__class__ .__name__
3436 if test_class != self .current_class :
3537 self .current_class = test_class
36- self .stream .write (' \n ' )
37- self .stream .write ('═' * 43 )
38- self .stream .write (' \n ' )
39- self .stream .write (f' { BOLD } { test_class } { RESET } \n ' )
40- self .stream .write ('─' * 43 )
41- self .stream .write (' \n ' )
38+ self .stream .write (" \n " )
39+ self .stream .write ("═" * 43 )
40+ self .stream .write (" \n " )
41+ self .stream .write (f" { BOLD } { test_class } { RESET } \n " )
42+ self .stream .write ("─" * 43 )
43+ self .stream .write (" \n " )
4244 self .stream .flush ()
4345
4446 def addSuccess (self , test ):
4547 """Called when a test passes."""
4648 super ().addSuccess (test )
4749 if self .showAll :
4850 test_method = test ._testMethodName
49- self .stream .write (f' { GREEN } ✓{ RESET } { test_method } \n ' )
51+ self .stream .write (f" { GREEN } ✓{ RESET } { test_method } \n " )
5052 self .stream .flush ()
5153 elif self .dots :
52- self .stream .write (f' { GREEN } .{ RESET } ' )
54+ self .stream .write (f" { GREEN } .{ RESET } " )
5355 self .stream .flush ()
5456
5557 def addError (self , test , err ):
5658 """Called when a test has an error."""
5759 super ().addError (test , err )
5860 if self .showAll :
5961 test_method = test ._testMethodName
60- self .stream .write (f' { RED } { BOLD } E{ RESET } { test_method } \n ' )
62+ self .stream .write (f" { RED } { BOLD } E{ RESET } { test_method } \n " )
6163 self .stream .flush ()
6264 elif self .dots :
63- self .stream .write (f' { RED } { BOLD } E{ RESET } ' )
65+ self .stream .write (f" { RED } { BOLD } E{ RESET } " )
6466 self .stream .flush ()
6567
6668 def addFailure (self , test , err ):
6769 """Called when a test fails."""
6870 super ().addFailure (test , err )
6971 if self .showAll :
7072 test_method = test ._testMethodName
71- self .stream .write (f' { RED } F{ RESET } { test_method } \n ' )
73+ self .stream .write (f" { RED } F{ RESET } { test_method } \n " )
7274 self .stream .flush ()
7375 elif self .dots :
74- self .stream .write (f' { RED } F{ RESET } ' )
76+ self .stream .write (f" { RED } F{ RESET } " )
7577 self .stream .flush ()
7678
7779 def addSkip (self , test , reason ):
7880 """Called when a test is skipped."""
7981 super ().addSkip (test , reason )
8082 if self .showAll :
8183 test_method = test ._testMethodName
82- self .stream .write (f' { YELLOW } s{ RESET } { test_method } (skipped: { reason } )\n ' )
84+ self .stream .write (f" { YELLOW } s{ RESET } { test_method } (skipped: { reason } )\n " )
8385 self .stream .flush ()
8486 elif self .dots :
85- self .stream .write (f' { YELLOW } s{ RESET } ' )
87+ self .stream .write (f" { YELLOW } s{ RESET } " )
8688 self .stream .flush ()
8789
8890 def printErrors (self ):
8991 """Print errors and failures with captured output."""
9092 if self .dots or self .showAll :
91- self .stream .write (' \n ' )
92- self .printErrorList (' ERROR' , self .errors )
93- self .printErrorList (' FAIL' , self .failures )
93+ self .stream .write (" \n " )
94+ self .printErrorList (" ERROR" , self .errors )
95+ self .printErrorList (" FAIL" , self .failures )
9496
9597 def printErrorList (self , flavour , errors ):
9698 """Print formatted error list with captured output."""
9799 for test , err in errors :
98- self .stream .write ('\n ' )
99- self .stream .write ('═' * 43 )
100- self .stream .write ('\n ' )
101- self .stream .write (f'{ RED } { BOLD } { flavour } : { self .getDescription (test )} { RESET } \n ' )
102- self .stream .write ('─' * 43 )
103- self .stream .write ('\n ' )
104- self .stream .write (f'{ err } \n ' )
100+ self .stream .write ("\n " )
101+ self .stream .write ("═" * 43 )
102+ self .stream .write ("\n " )
103+ self .stream .write (
104+ f"{ RED } { BOLD } { flavour } : { self .getDescription (test )} { RESET } \n "
105+ )
106+ self .stream .write ("─" * 43 )
107+ self .stream .write ("\n " )
108+ self .stream .write (f"{ err } \n " )
105109
106110
107111class ColorTestRunner (unittest .TextTestRunner ):
108112 """Custom test runner that uses ColorTextTestResult."""
109113
110114 resultclass = ColorTextTestResult # type: ignore[assignment]
111115
112- def __init__ (self , stream = None , descriptions = True , verbosity = 2 ,
113- failfast = False , buffer = True , resultclass = None , warnings = None ,
114- * , tb_locals = False ):
116+ def __init__ (
117+ self ,
118+ stream = None ,
119+ descriptions = True ,
120+ verbosity = 2 ,
121+ failfast = False ,
122+ buffer = True ,
123+ resultclass = None ,
124+ warnings = None ,
125+ * ,
126+ tb_locals = False ,
127+ ):
115128 super ().__init__ (
116129 stream = stream ,
117130 descriptions = descriptions ,
@@ -120,7 +133,7 @@ def __init__(self, stream=None, descriptions=True, verbosity=2,
120133 buffer = buffer ,
121134 resultclass = resultclass or ColorTextTestResult , # type: ignore[arg-type]
122135 warnings = warnings ,
123- tb_locals = tb_locals
136+ tb_locals = tb_locals ,
124137 )
125138
126139 def run (self , test ):
@@ -130,41 +143,46 @@ def run(self, test):
130143 elapsed = time .time () - start_time
131144
132145 # Print summary
133- self .stream .write (' \n ' )
134- self .stream .write ('═' * 43 )
135- self .stream .write (' \n ' )
146+ self .stream .write (" \n " )
147+ self .stream .write ("═" * 43 )
148+ self .stream .write (" \n " )
136149
137150 # Determine summary color and message
138151 if result .wasSuccessful ():
139152 color = GREEN
140- msg = f' { result .testsRun } tests passed in { elapsed :.2f} s'
153+ msg = f" { result .testsRun } tests passed in { elapsed :.2f} s"
141154 else :
142155 color = RED
143- passed = result .testsRun - len (result .failures ) - len (result .errors ) - len (result .skipped )
156+ passed = (
157+ result .testsRun
158+ - len (result .failures )
159+ - len (result .errors )
160+ - len (result .skipped )
161+ )
144162 failed = len (result .failures ) + len (result .errors )
145- parts = [f' { passed } passed' , f' { failed } failed' ]
163+ parts = [f" { passed } passed" , f" { failed } failed" ]
146164 if result .skipped :
147- parts .append (f' { len (result .skipped )} skipped' )
148- msg = f' { ", " .join (parts )} in { elapsed :.2f} s'
165+ parts .append (f" { len (result .skipped )} skipped" )
166+ msg = f" { ', ' .join (parts )} in { elapsed :.2f} s"
149167
150- self .stream .write (f' { color } { msg } { RESET } \n ' )
151- self .stream .write ('═' * 43 )
152- self .stream .write (' \n ' )
168+ self .stream .write (f" { color } { msg } { RESET } \n " )
169+ self .stream .write ("═" * 43 )
170+ self .stream .write (" \n " )
153171
154172 return result
155173
156174
157175def run_tests ():
158176 """Discover and run all tests with colorized output."""
159177 loader = unittest .TestLoader ()
160- start_dir = ' tests'
161- suite = loader .discover (start_dir , pattern = ' test_*.py' )
178+ start_dir = " tests"
179+ suite = loader .discover (start_dir , pattern = " test_*.py" )
162180
163181 runner = ColorTestRunner (verbosity = 2 )
164182 result = runner .run (suite )
165183
166184 return 0 if result .wasSuccessful () else 1
167185
168186
169- if __name__ == ' __main__' :
187+ if __name__ == " __main__" :
170188 sys .exit (run_tests ())
0 commit comments