7676PTRN_IMPORT_ERROR = re .compile (r".*cannot import name \'(?P<module>.*)\'.*" , re .DOTALL )
7777PTRN_REMOTE_HOST = re .compile (r"(?P<user>\w+)@(?P<host>[\w.]+):(?P<path>.+)" )
7878PTRN_VALID_CSV_NAME = re .compile (r"unittests-\d{4}-\d{2}-\d{2}.csv" )
79+ PTRN_TEST_STATUS_INDIVIDUAL = re .compile (r"(?P<name>test[\w_]+ \(.+?\)) ... (?P<status>.+)" )
80+ PTRN_TEST_STATUS_ERROR = re .compile (r"(?P<status>.+): (?P<name>test[\w_]+ \(.+?\))" )
7981
8082
8183# ----------------------------------------------------------------------------------------------------------------------
@@ -216,6 +218,13 @@ def read_csv(path):
216218 return rows
217219
218220
221+ class TestStatus (object ):
222+ ERROR = 'error'
223+ FAIL = 'fail'
224+ SKIPPED = 'skipped'
225+ OK = 'ok'
226+
227+
219228# ----------------------------------------------------------------------------------------------------------------------
220229#
221230# result (output processing)
@@ -224,35 +233,88 @@ def read_csv(path):
224233class StatEntry (object ):
225234 def __init__ (self ):
226235 self .num_tests = - 1
227- self .num_errors = - 1
228- self .num_fails = - 1
229- self .num_skipped = - 1
236+ # reported stats
237+ self ._num_errors = - 1
238+ self ._num_fails = - 1
239+ self ._num_skipped = - 1
240+ # tracked stats
241+ self ._tracked = False
242+
243+ def _reset (self ):
244+ self ._num_fails = 0
245+ self ._num_errors = 0
246+ self ._num_skipped = 0
230247
231248 def all_ok (self ):
232- self .num_fails = 0
233- self .num_errors = 0
234- self .num_skipped = 0
249+ self ._reset ()
250+
251+ @property
252+ def num_errors (self ):
253+ return self ._num_errors
254+
255+ @num_errors .setter
256+ def num_errors (self , value ):
257+ if not self ._tracked :
258+ self ._num_errors = value
259+
260+ @property
261+ def num_fails (self ):
262+ return self ._num_fails
263+
264+ @num_fails .setter
265+ def num_fails (self , value ):
266+ if not self ._tracked :
267+ self ._num_fails = value
268+
269+ @property
270+ def num_skipped (self ):
271+ return self ._num_skipped
272+
273+ @num_skipped .setter
274+ def num_skipped (self , value ):
275+ if not self ._tracked :
276+ self ._num_skipped = value
235277
236278 @property
237279 def num_passes (self ):
238280 if self .num_tests > 0 :
239- return self .num_tests - (self .num_fails + self .num_errors + self .num_skipped )
281+ return self .num_tests - (self ._num_fails + self ._num_errors + self ._num_skipped )
240282 return - 1
241283
284+ def update (self , test_detailed_stats ):
285+ if len (test_detailed_stats ) > 0 :
286+ self ._tracked = True
287+ self ._reset ()
288+ for test , stats in test_detailed_stats .items ():
289+ stats = {s .lower () for s in stats }
290+ if TestStatus .ERROR in stats :
291+ self ._num_errors += 1
292+ elif TestStatus .FAIL in stats :
293+ self ._num_fails += 1
294+ else :
295+ for s in stats :
296+ if s .startswith (TestStatus .SKIPPED ):
297+ self ._num_skipped += 1
298+ break
299+
242300
243301def process_output (output_lines ):
244302 if isinstance (output_lines , str ):
245303 output_lines = output_lines .split ("\n " )
246304
247305 unittests = []
306+ # stats tracked per unittest
307+ unittest_tests = defaultdict (list )
248308 error_messages = defaultdict (set )
249309 java_exceptions = defaultdict (set )
250310 stats = defaultdict (StatEntry )
251311
252312 for line in output_lines :
253313 match = re .match (PTRN_UNITTEST , line )
254314 if match :
255- unittests .append (match .group ('unittest' ))
315+ unittest = match .group ('unittest' )
316+ unittests .append (unittest )
317+ unittest_tests .clear ()
256318 continue
257319
258320 # extract python reported python error messages
@@ -268,6 +330,16 @@ def process_output(output_lines):
268330 continue
269331
270332 # stats
333+ # tracking stats
334+ match = re .match (PTRN_TEST_STATUS_INDIVIDUAL , line )
335+ if not match :
336+ match = re .match (PTRN_TEST_STATUS_ERROR , line )
337+ if match :
338+ name = match .group ('name' )
339+ status = match .group ('status' )
340+ unittest_tests [name ].append (status )
341+ continue
342+
271343 if line .strip () == 'OK' :
272344 stats [unittests [- 1 ]].all_ok ()
273345 continue
@@ -286,6 +358,8 @@ def process_output(output_lines):
286358 match = re .match (PTRN_NUM_TESTS , line )
287359 if match :
288360 stats [unittests [- 1 ]].num_tests = int (match .group ('num_tests' ))
361+ stats [unittests [- 1 ]].update (unittest_tests )
362+ unittest_tests .clear ()
289363 continue
290364
291365 match = re .match (PTRN_FAILED , line )
@@ -299,6 +373,7 @@ def process_output(output_lines):
299373 stats [unittests [- 1 ]].num_fails = int (fails ) if fails else 0
300374 stats [unittests [- 1 ]].num_errors = int (errs ) if errs else 0
301375 stats [unittests [- 1 ]].num_skipped = int (skipped ) if skipped else 0
376+ continue
302377
303378 return unittests , error_messages , java_exceptions , stats
304379
@@ -712,6 +787,8 @@ def main(prog, args):
712787 parser = argparse .ArgumentParser (prog = prog ,
713788 description = "Run the standard python unittests." )
714789 parser .add_argument ("-v" , "--verbose" , help = "Verbose output." , action = "store_true" )
790+ parser .add_argument ("-n" , "--no_cpython" , help = "Do not run the tests with cpython (for comparison)." ,
791+ action = "store_true" )
715792 parser .add_argument ("-l" , "--limit" , help = "Limit the number of unittests to run." , default = None , type = int )
716793 parser .add_argument ("-t" , "--tests_path" , help = "Unittests path." , default = PATH_UNITTESTS )
717794 parser .add_argument ("-T" , "--timeout" , help = "Timeout per unittest run." , default = TIMEOUT , type = int )
@@ -754,7 +831,7 @@ def _fmt(t):
754831 unittests = get_unittests (flags .tests_path , limit = flags .limit , skip_tests = skip_tests )
755832
756833 # get cpython stats
757- if not flags .gate :
834+ if not flags .gate and not flags . no_cpython :
758835 log (HR )
759836 log ("[INFO] get cpython stats" )
760837 cpy_results = run_unittests (unittests , 60 * 5 , with_cpython = True )
0 commit comments