@@ -932,5 +932,154 @@ def test_auto_mark_directory_no_results_raises(self):
932932 auto_mark_directory (test_dir , verbose = False )
933933
934934
935+ class TestAutoMarkFileRestoresOnCrash (unittest .TestCase ):
936+ """Stripped markers must be restored when the test runner crashes."""
937+
938+ def test_stripped_markers_restored_when_crash (self ):
939+ """Markers stripped before run must be restored for unobserved tests on crash."""
940+ test_code = f"""\
941+ import unittest
942+
943+ class TestA(unittest.TestCase):
944+ @unittest.expectedFailure # { COMMENT }
945+ def test_foo(self):
946+ pass
947+
948+ @unittest.expectedFailure # { COMMENT }
949+ def test_bar(self):
950+ pass
951+
952+ @unittest.expectedFailure # { COMMENT }
953+ def test_baz(self):
954+ pass
955+ """
956+ with tempfile .TemporaryDirectory () as tmpdir :
957+ test_file = pathlib .Path (tmpdir ) / "test_example.py"
958+ test_file .write_text (test_code )
959+
960+ # Simulate a crashed run that only observed test_foo (failed)
961+ # test_bar and test_baz never ran due to crash
962+ mock_result = TestResult ()
963+ mock_result .tests_result = "" # no Tests result line (crash)
964+ mock_result .tests = [
965+ Test (
966+ name = "test_foo" ,
967+ path = "test.test_example.TestA.test_foo" ,
968+ result = "fail" ,
969+ error_message = "AssertionError: 1 != 2" ,
970+ ),
971+ ]
972+
973+ with mock .patch (
974+ "update_lib.cmd_auto_mark.run_test" , return_value = mock_result
975+ ):
976+ auto_mark_file (test_file , verbose = False )
977+
978+ contents = test_file .read_text ()
979+ # test_bar and test_baz were not observed — their markers must be restored
980+ self .assertIn ("def test_bar" , contents )
981+ self .assertIn ("def test_baz" , contents )
982+ # Count expectedFailure markers: all 3 should be present
983+ self .assertEqual (contents .count ("expectedFailure" ), 3 , contents )
984+
985+ def test_stripped_markers_removed_when_complete_run (self ):
986+ """Markers are properly removed when the run completes normally."""
987+ test_code = f"""\
988+ import unittest
989+
990+ class TestA(unittest.TestCase):
991+ @unittest.expectedFailure # { COMMENT }
992+ def test_foo(self):
993+ pass
994+
995+ @unittest.expectedFailure # { COMMENT }
996+ def test_bar(self):
997+ pass
998+ """
999+ with tempfile .TemporaryDirectory () as tmpdir :
1000+ test_file = pathlib .Path (tmpdir ) / "test_example.py"
1001+ test_file .write_text (test_code )
1002+
1003+ # Simulate a complete run where test_foo fails but test_bar passes
1004+ mock_result = TestResult ()
1005+ mock_result .tests_result = "FAILURE" # normal completion
1006+ mock_result .tests = [
1007+ Test (
1008+ name = "test_foo" ,
1009+ path = "test.test_example.TestA.test_foo" ,
1010+ result = "fail" ,
1011+ error_message = "AssertionError" ,
1012+ ),
1013+ ]
1014+ # test_bar passes → shows as unexpected success
1015+ mock_result .unexpected_successes = [
1016+ Test (
1017+ name = "test_bar" ,
1018+ path = "test.test_example.TestA.test_bar" ,
1019+ result = "unexpected success" ,
1020+ ),
1021+ ]
1022+
1023+ with mock .patch (
1024+ "update_lib.cmd_auto_mark.run_test" , return_value = mock_result
1025+ ):
1026+ auto_mark_file (test_file , verbose = False )
1027+
1028+ contents = test_file .read_text ()
1029+ # test_foo should still have marker (re-added)
1030+ self .assertEqual (contents .count ("expectedFailure" ), 1 , contents )
1031+ self .assertIn ("def test_foo" , contents )
1032+
1033+
1034+ class TestAutoMarkDirectoryRestoresOnCrash (unittest .TestCase ):
1035+ """Stripped markers must be restored for directory runs that crash."""
1036+
1037+ def test_stripped_markers_restored_when_crash (self ):
1038+ test_code = f"""\
1039+ import unittest
1040+
1041+ class TestA(unittest.TestCase):
1042+ @unittest.expectedFailure # { COMMENT }
1043+ def test_foo(self):
1044+ pass
1045+
1046+ @unittest.expectedFailure # { COMMENT }
1047+ def test_bar(self):
1048+ pass
1049+ """
1050+ with tempfile .TemporaryDirectory () as tmpdir :
1051+ test_dir = pathlib .Path (tmpdir ) / "test_example"
1052+ test_dir .mkdir ()
1053+ test_file = test_dir / "test_sub.py"
1054+ test_file .write_text (test_code )
1055+
1056+ mock_result = TestResult ()
1057+ mock_result .tests_result = "" # crash
1058+ mock_result .tests = [
1059+ Test (
1060+ name = "test_foo" ,
1061+ path = "test.test_example.test_sub.TestA.test_foo" ,
1062+ result = "fail" ,
1063+ ),
1064+ ]
1065+
1066+ with (
1067+ mock .patch (
1068+ "update_lib.cmd_auto_mark.run_test" , return_value = mock_result
1069+ ),
1070+ mock .patch (
1071+ "update_lib.cmd_auto_mark.get_test_module_name" ,
1072+ side_effect = lambda p : (
1073+ "test_example" if p == test_dir else "test_example.test_sub"
1074+ ),
1075+ ),
1076+ ):
1077+ auto_mark_directory (test_dir , verbose = False )
1078+
1079+ contents = test_file .read_text ()
1080+ # Both markers must be present (unobserved test_bar restored)
1081+ self .assertEqual (contents .count ("expectedFailure" ), 2 , contents )
1082+
1083+
9351084if __name__ == "__main__" :
9361085 unittest .main ()
0 commit comments