@@ -93,9 +93,9 @@ def log(self, msg='', incomplete=False):
93
93
if self .report_incomplete and self .incomplete_line :
94
94
first_line , newline , rest = msg .partition ('\n ' )
95
95
pad = len (self .incomplete_line )
96
- print (f"\r { first_line :{pad }} { newline } { rest } " , end = end , flush = True )
96
+ print (f"\r { first_line :{pad }} { newline } { rest } " , file = sys . stderr , end = end , flush = True )
97
97
else :
98
- print (msg , end = end , flush = True )
98
+ print (msg , file = sys . stderr , end = end , flush = True )
99
99
self .incomplete_line = msg if incomplete else None
100
100
101
101
@@ -178,8 +178,8 @@ class TestResult:
178
178
duration : float = 0.0
179
179
180
180
181
- def format_exception (err ):
182
- _ , exc , tb = err
181
+ def format_exception (exc ):
182
+ tb = exc . __traceback__
183
183
while tb and '__unittest' in tb .tb_frame .f_globals :
184
184
tb = tb .tb_next
185
185
exc .__traceback__ = tb
@@ -229,19 +229,19 @@ def addSuccess(self, test):
229
229
230
230
def addFailure (self , test , err ):
231
231
super ().addFailure (test , err )
232
- self .report_result (self .make_result (test , status = TestStatus .FAILURE , param = format_exception (err )))
232
+ self .report_result (self .make_result (test , status = TestStatus .FAILURE , param = format_exception (err [ 1 ] )))
233
233
234
234
def addError (self , test , err ):
235
235
super ().addError (test , err )
236
- self .report_result (self .make_result (test , status = TestStatus .ERROR , param = format_exception (err )))
236
+ self .report_result (self .make_result (test , status = TestStatus .ERROR , param = format_exception (err [ 1 ] )))
237
237
238
238
def addSkip (self , test , reason ):
239
239
super ().addSkip (test , reason )
240
240
self .report_result (self .make_result (test , status = TestStatus .SKIPPED , param = reason ))
241
241
242
242
def addExpectedFailure (self , test , err ):
243
243
super ().addExpectedFailure (test , err )
244
- self .report_result (self .make_result (test , status = TestStatus .EXPECTED_FAILURE , param = format_exception (err )))
244
+ self .report_result (self .make_result (test , status = TestStatus .EXPECTED_FAILURE , param = format_exception (err [ 1 ] )))
245
245
246
246
def addUnexpectedSuccess (self , test ):
247
247
super ().addUnexpectedSuccess (test )
@@ -267,7 +267,14 @@ def emit(self, **data):
267
267
pass
268
268
269
269
def report_result (self , result : TestResult ):
270
- self .emit (event = 'testResult' , status = result .status , test = result .test_id , param = result .param , out_pos = out_tell ())
270
+ self .emit (
271
+ event = 'testResult' ,
272
+ status = result .status ,
273
+ test = result .test_id ,
274
+ param = result .param ,
275
+ out_pos = out_tell (),
276
+ duration = result .duration ,
277
+ )
271
278
272
279
def startTest (self , test ):
273
280
super ().startTest (test )
@@ -403,20 +410,22 @@ def generate_mx_report(self, path: str):
403
410
# noinspection PyTypeChecker
404
411
json .dump (report_data , f )
405
412
406
- def generate_tags (self ):
413
+ def generate_tags (self , append = False ):
407
414
by_file = defaultdict (list )
408
415
for result in self .results :
409
416
by_file [result .test_id .test_file ].append (result )
410
417
for test_file , results in by_file .items ():
411
418
config = config_for_file (test_file )
412
419
tag_file = config .get_tag_file (test_file )
413
420
if not tag_file :
414
- log (f"WARNNING: no tag directory for test file { result . test_id . test_file } " )
421
+ log (f"WARNNING: no tag directory for test file { test_file } " )
415
422
continue
423
+ tags = {result .test_id .test_name for result in results if result .status == TestStatus .SUCCESS }
424
+ if append :
425
+ tags |= {test .test_name for test in read_tags (test_file , config )}
416
426
with open (tag_file , 'w' ) as f :
417
- for result in results :
418
- if result .status == TestStatus .SUCCESS :
419
- f .write (f'*graalpython.lib-python.3.{ result .test_id .test_name } \n ' )
427
+ for test_name in sorted (tags ):
428
+ f .write (f'{ test_name } \n ' )
420
429
421
430
422
431
def interrupt_process (process : subprocess .Popen ):
@@ -534,6 +543,7 @@ def process_event(event):
534
543
status = event ['status' ],
535
544
param = event .get ('param' ),
536
545
output = test_output ,
546
+ duration = event .get ('duration' ),
537
547
)
538
548
self .report_result (result )
539
549
last_started_test = None
@@ -754,6 +764,8 @@ def expand_specifier_paths(specifiers: list[TestSpecifier]) -> list[TestSpecifie
754
764
else :
755
765
expanded_paths += path .rglob ("test*.py" )
756
766
else :
767
+ if config .tags_dir and path .name == '__init__.py' :
768
+ path = path .parent
757
769
expanded_paths .append (path )
758
770
expanded_paths .sort ()
759
771
for path in expanded_paths :
@@ -793,7 +805,8 @@ def path_for_comparison(p: Path):
793
805
return p .parent / p .name .removesuffix ('.py' )
794
806
795
807
796
- def collect (all_specifiers : list [TestSpecifier ], * , use_tags = False , ignore = None , partial = None ) -> list [TestSuite ]:
808
+ def collect (all_specifiers : list [TestSpecifier ], * , use_tags = False , ignore = None , partial = None ,
809
+ continue_on_errors = False ) -> list [TestSuite ]:
797
810
to_run = []
798
811
all_specifiers = expand_specifier_paths (all_specifiers )
799
812
if ignore :
@@ -819,7 +832,13 @@ def collect(all_specifiers: list[TestSpecifier], *, use_tags=False, ignore=None,
819
832
for test_file , specifiers in specifiers_by_file .items ():
820
833
if not test_file .exists ():
821
834
sys .exit (f"File does not exist: { test_file } " )
822
- collected = collect_module (test_file , specifiers , use_tags = use_tags , partial = partial )
835
+ try :
836
+ collected = collect_module (test_file , specifiers , use_tags = use_tags , partial = partial )
837
+ except Exception as e :
838
+ if continue_on_errors :
839
+ log (f"WARNING: Failed to collect { test_file } :\n { format_exception (e )} " )
840
+ continue
841
+ raise e
823
842
if collected :
824
843
to_run .append (collected )
825
844
return to_run
@@ -887,10 +906,14 @@ def main():
887
906
help = "Exit immediately after the first failure" )
888
907
parser .add_argument ('--all' , action = 'store_true' ,
889
908
help = "Run tests that are normally not enabled due to tags" )
890
- parser .add_argument ('--retag' , action = 'store_true ' ,
909
+ parser .add_argument ('--retag' , dest = 'retag_mode' , action = 'store_const' , const = 'replace ' ,
891
910
help = "Run tests and regenerate tags based on the results. Implies --all, --tagged and -n" )
911
+ parser .add_argument ('--retag-append' , dest = 'retag_mode' , action = 'store_const' , const = 'append' ,
912
+ help = "Like --retag, but doesn't remove existing tags. Useful for regtagging subsets of tests" )
892
913
parser .add_argument ('--collect-only' , action = 'store_true' ,
893
914
help = "Print found tests IDs without running tests" )
915
+ parser .add_argument ('--continue-on-collection-errors' , action = 'store_true' ,
916
+ help = "Collection errors are not fatal" )
894
917
parser .add_argument ('--durations' , type = int , default = 0 ,
895
918
help = "Show durations of N slowest tests (-1 to show all)" )
896
919
parser .add_argument ('--mx-report' ,
@@ -919,7 +942,7 @@ def main():
919
942
920
943
args = parser .parse_args ()
921
944
922
- if args .retag :
945
+ if args .retag_mode :
923
946
args .all = True
924
947
args .tagged = True
925
948
args .num_processes = args .num_processes or 1
@@ -951,7 +974,13 @@ def main():
951
974
selected_str , total_str = partial_env .split ('/' , 1 )
952
975
partial = int (selected_str ) - 1 , int (total_str )
953
976
954
- tests = collect (args .tests , use_tags = (not args .all ), ignore = args .ignore , partial = partial )
977
+ tests = collect (
978
+ args .tests ,
979
+ use_tags = (not args .all ),
980
+ ignore = args .ignore ,
981
+ partial = partial ,
982
+ continue_on_errors = args .continue_on_collection_errors ,
983
+ )
955
984
if args .collect_only :
956
985
for test_suite in tests :
957
986
for test in test_suite .collected_tests :
@@ -978,8 +1007,8 @@ def main():
978
1007
runner .run_tests (tests )
979
1008
if args .mx_report :
980
1009
runner .generate_mx_report (args .mx_report )
981
- if args .retag :
982
- runner .generate_tags ()
1010
+ if args .retag_mode :
1011
+ runner .generate_tags (append = ( args . retag_mode == 'append' ) )
983
1012
return
984
1013
if runner .tests_failed ():
985
1014
sys .exit (1 )
0 commit comments