Skip to content

Commit aa7598b

Browse files
committed
unmark successful tests
1 parent 1a1d484 commit aa7598b

File tree

8 files changed

+93
-55
lines changed

8 files changed

+93
-55
lines changed

Lib/test/test_copy.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -207,8 +207,6 @@ def __eq__(self, other):
207207
self.assertIsNot(y, x)
208208
self.assertEqual(y.foo, x.foo)
209209

210-
# TODO: RUSTPYTHON
211-
@unittest.expectedFailure
212210
def test_copy_inst_getnewargs_ex(self):
213211
class C(int):
214212
def __new__(cls, *, foo):
@@ -507,8 +505,6 @@ def __eq__(self, other):
507505
self.assertEqual(y.foo, x.foo)
508506
self.assertIsNot(y.foo, x.foo)
509507

510-
# TODO: RUSTPYTHON
511-
@unittest.expectedFailure
512508
def test_deepcopy_inst_getnewargs_ex(self):
513509
class C(int):
514510
def __new__(cls, *, foo):

Lib/test/test_csv.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -698,7 +698,6 @@ def test_copy(self):
698698
dialect = csv.get_dialect(name)
699699
self.assertRaises(TypeError, copy.copy, dialect)
700700

701-
@unittest.expectedFailure # TODO: RUSTPYTHON
702701
def test_pickle(self):
703702
for name in csv.list_dialects():
704703
dialect = csv.get_dialect(name)

Lib/test/test_descr.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5258,7 +5258,6 @@ def _check_reduce(self, proto, obj, args=(), kwargs={}, state=None,
52585258
self.assertEqual(obj.__reduce_ex__(proto), reduce_value)
52595259
self.assertEqual(obj.__reduce__(), reduce_value)
52605260

5261-
@unittest.expectedFailure # TODO: RUSTPYTHON
52625261
def test_reduce(self):
52635262
protocols = range(pickle.HIGHEST_PROTOCOL + 1)
52645263
args = (-101, "spam")
@@ -5382,7 +5381,6 @@ class C16(list):
53825381
for proto in protocols:
53835382
self._check_reduce(proto, obj, listitems=list(obj))
53845383

5385-
@unittest.expectedFailure # TODO: RUSTPYTHON
53865384
def test_special_method_lookup(self):
53875385
protocols = range(pickle.HIGHEST_PROTOCOL + 1)
53885386
class Picky:
@@ -5515,7 +5513,6 @@ class E(C):
55155513
y = pickle_copier.copy(x)
55165514
self._assert_is_copy(x, y)
55175515

5518-
@unittest.expectedFailure # TODO: RUSTPYTHON
55195516
def test_reduce_copying(self):
55205517
# Tests pickling and copying new-style classes and objects.
55215518
global C1

Lib/test/test_enum.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2130,7 +2130,6 @@ class NEI(NamedInt, Enum):
21302130
test_pickle_dump_load(self.assertIs, NEI.y)
21312131
test_pickle_dump_load(self.assertIs, NEI)
21322132

2133-
@unittest.expectedFailure # TODO: RUSTPYTHON; fails on pickle
21342133
def test_subclasses_with_getnewargs_ex(self):
21352134
class NamedInt(int):
21362135
__qualname__ = 'NamedInt' # needed for pickle protocol 4

Lib/test/test_lzma.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -409,8 +409,6 @@ def test_decompressor_bigmem(self, size):
409409

410410
# Pickling raises an exception; there's no way to serialize an lzma_stream.
411411

412-
# TODO: RUSTPYTHON
413-
@unittest.expectedFailure
414412
def test_pickle(self):
415413
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
416414
with self.assertRaises(TypeError):
@@ -2194,4 +2192,4 @@ def test_filter_properties_roundtrip(self):
21942192

21952193

21962194
if __name__ == "__main__":
2197-
unittest.main()
2195+
unittest.main()

Lib/test/test_memoryio.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -745,8 +745,6 @@ def test_init(self):
745745
def test_issue5449(self):
746746
super().test_issue5449()
747747

748-
# TODO: RUSTPYTHON
749-
@unittest.expectedFailure
750748
def test_pickling(self):
751749
super().test_pickling()
752750

@@ -777,8 +775,6 @@ def test_truncate(self):
777775
def test_write(self):
778776
super().test_write()
779777

780-
# TODO: RUSTPYTHON
781-
@unittest.expectedFailure
782778
def test_getstate(self):
783779
memio = self.ioclass()
784780
state = memio.__getstate__()
@@ -911,8 +907,6 @@ def test_newline_none(self):
911907
def test_newlines_property(self):
912908
super().test_newlines_property()
913909

914-
# TODO: RUSTPYTHON
915-
@unittest.expectedFailure
916910
def test_pickling(self):
917911
super().test_pickling()
918912

@@ -954,8 +948,6 @@ def test_widechar(self):
954948
self.assertEqual(memio.tell(), len(buf) * 2)
955949
self.assertEqual(memio.getvalue(), buf + buf)
956950

957-
# TODO: RUSTPYTHON
958-
@unittest.expectedFailure
959951
def test_getstate(self):
960952
memio = self.ioclass()
961953
state = memio.__getstate__()
@@ -1006,8 +998,6 @@ def test_newline_cr(self):
1006998
def test_newline_crlf(self):
1007999
super().test_newline_crlf()
10081000

1009-
# TODO: RUSTPYTHON
1010-
@unittest.expectedFailure
10111001
def test_newline_default(self):
10121002
super().test_newline_default()
10131003

@@ -1016,8 +1006,6 @@ def test_newline_default(self):
10161006
def test_newline_empty(self):
10171007
super().test_newline_empty()
10181008

1019-
# TODO: RUSTPYTHON
1020-
@unittest.expectedFailure
10211009
def test_newline_lf(self):
10221010
super().test_newline_lf()
10231011

Lib/test/test_pickle.py

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,6 @@ def dumps(self, arg, proto=None, **kwargs):
9797
def test_picklebuffer_error(self): # TODO(RUSTPYTHON): Remove this test when it passes
9898
return super().test_picklebuffer_error()
9999

100-
# TODO: RUSTPYTHON
101-
@unittest.expectedFailure
102-
def test_bad_getattr(self): # TODO(RUSTPYTHON): Remove this test when it passes
103-
return super().test_bad_getattr()
104100

105101
# TODO: RUSTPYTHON
106102
@unittest.expectedFailure
@@ -135,15 +131,7 @@ def loads(self, buf, **kwds):
135131
def test_c_methods(self): # TODO(RUSTPYTHON): Remove this test when it passes
136132
return super().test_c_methods()
137133

138-
# TODO: RUSTPYTHON
139-
@unittest.expectedFailure
140-
def test_complex_newobj_ex(self): # TODO(RUSTPYTHON): Remove this test when it passes
141-
return super().test_complex_newobj_ex()
142134

143-
# TODO: RUSTPYTHON
144-
@unittest.expectedFailure
145-
def test_py_methods(self): # TODO(RUSTPYTHON): Remove this test when it passes
146-
return super().test_py_methods()
147135

148136
# TODO: RUSTPYTHON
149137
@unittest.expectedFailure
@@ -239,10 +227,6 @@ def loads(self, buf, **kwds):
239227
def test_c_methods(self): # TODO(RUSTPYTHON): Remove this test when it passes
240228
return super().test_c_methods()
241229

242-
# TODO: RUSTPYTHON
243-
@unittest.expectedFailure
244-
def test_complex_newobj_ex(self): # TODO(RUSTPYTHON): Remove this test when it passes
245-
return super().test_complex_newobj_ex()
246230

247231
# TODO: RUSTPYTHON
248232
@unittest.expectedFailure
@@ -259,10 +243,6 @@ def test_correctly_quoted_string(self): # TODO(RUSTPYTHON): Remove this test whe
259243
def test_load_python2_str_as_bytes(self): # TODO(RUSTPYTHON): Remove this test when it passes
260244
return super().test_load_python2_str_as_bytes()
261245

262-
# TODO: RUSTPYTHON
263-
@unittest.expectedFailure
264-
def test_py_methods(self): # TODO(RUSTPYTHON): Remove this test when it passes
265-
return super().test_py_methods()
266246

267247
# TODO: RUSTPYTHON
268248
@unittest.expectedFailure

scripts/fix_test.py

Lines changed: 92 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,17 @@
2323
"""
2424

2525
import argparse
26+
import ast
2627
import shutil
2728
import sys
2829
from pathlib import Path
2930

30-
from lib_updater import PatchSpec, UtMethod, apply_patches
31+
from lib_updater import (
32+
COMMENT,
33+
PatchSpec,
34+
UtMethod,
35+
apply_patches,
36+
)
3137

3238

3339
def parse_args():
@@ -61,15 +67,18 @@ def __str__(self):
6167
class TestResult:
6268
tests_result: str = ""
6369
tests = []
70+
unexpected_successes = [] # Tests that passed but were marked as expectedFailure
6471
stdout = ""
6572

6673
def __str__(self):
67-
return f"TestResult(tests_result={self.tests_result},tests={len(self.tests)})"
74+
return f"TestResult(tests_result={self.tests_result},tests={len(self.tests)},unexpected_successes={len(self.unexpected_successes)})"
6875

6976

7077
def parse_results(result):
7178
lines = result.stdout.splitlines()
7279
test_results = TestResult()
80+
test_results.tests = []
81+
test_results.unexpected_successes = []
7382
test_results.stdout = result.stdout
7483
in_test_results = False
7584
for line in lines:
@@ -107,6 +116,19 @@ def parse_results(result):
107116
res = line.split("== Tests result: ")[1]
108117
res = res.split(" ")[0]
109118
test_results.tests_result = res
119+
# Parse: "UNEXPECTED SUCCESS: test_name (path)"
120+
elif line.startswith("UNEXPECTED SUCCESS: "):
121+
rest = line[len("UNEXPECTED SUCCESS: ") :]
122+
# Format: "test_name (path)"
123+
first_space = rest.find(" ")
124+
if first_space > 0:
125+
test = Test()
126+
test.name = rest[:first_space]
127+
path_part = rest[first_space:].strip()
128+
if path_part.startswith("(") and path_part.endswith(")"):
129+
test.path = path_part[1:-1]
130+
test.result = "unexpected_success"
131+
test_results.unexpected_successes.append(test)
110132
return test_results
111133

112134

@@ -117,6 +139,47 @@ def path_to_test(path) -> list[str]:
117139
return parts[-2:] # Get class name and method name
118140

119141

142+
def remove_expected_failures(contents: str, tests_to_remove: set[tuple[str, str]]) -> str:
143+
"""Remove @unittest.expectedFailure decorators from tests that now pass."""
144+
if not tests_to_remove:
145+
return contents
146+
147+
tree = ast.parse(contents)
148+
lines = contents.splitlines()
149+
lines_to_remove = set()
150+
151+
for node in ast.walk(tree):
152+
if not isinstance(node, ast.ClassDef):
153+
continue
154+
class_name = node.name
155+
for item in node.body:
156+
if not isinstance(item, (ast.FunctionDef, ast.AsyncFunctionDef)):
157+
continue
158+
method_name = item.name
159+
if (class_name, method_name) not in tests_to_remove:
160+
continue
161+
162+
# Find and mark expectedFailure decorators for removal
163+
for dec in item.decorator_list:
164+
dec_line = dec.lineno - 1 # 0-indexed
165+
line_content = lines[dec_line]
166+
167+
# Check if it's @unittest.expectedFailure with TODO: RUSTPYTHON
168+
if "expectedFailure" in line_content and COMMENT in line_content:
169+
lines_to_remove.add(dec_line)
170+
# Also check the line before for a standalone TODO comment
171+
if dec_line > 0:
172+
prev_line = lines[dec_line - 1].strip()
173+
if prev_line.startswith("#") and COMMENT in prev_line:
174+
lines_to_remove.add(dec_line - 1)
175+
176+
# Remove lines in reverse order to maintain line numbers
177+
for line_idx in sorted(lines_to_remove, reverse=True):
178+
del lines[line_idx]
179+
180+
return "\n".join(lines) + "\n" if lines else ""
181+
182+
120183
def build_patches(test_parts_set: set[tuple[str, str]]) -> dict:
121184
"""Convert failing tests to lib_updater patch format."""
122185
patches = {}
@@ -190,20 +253,38 @@ def run_test(test_name):
190253
f = test_path.read_text(encoding="utf-8")
191254

192255
# Collect failing tests (with deduplication for subtests)
193-
seen_tests = set() # Track (class_name, method_name) to avoid duplicates
256+
failing_tests = set() # Track (class_name, method_name) to avoid duplicates
194257
for test in tests.tests:
195258
if test.result == "fail" or test.result == "error":
196259
test_parts = path_to_test(test.path)
197260
if len(test_parts) == 2:
198261
test_key = tuple(test_parts)
199-
if test_key not in seen_tests:
200-
seen_tests.add(test_key)
201-
print(f"Marking test: {test_parts[0]}.{test_parts[1]}")
202-
203-
# Apply patches using lib_updater
204-
if seen_tests:
205-
patches = build_patches(seen_tests)
262+
if test_key not in failing_tests:
263+
failing_tests.add(test_key)
264+
print(f"Marking as failing: {test_parts[0]}.{test_parts[1]}")
265+
266+
# Collect unexpected successes (tests that now pass but have expectedFailure)
267+
unexpected_successes = set()
268+
for test in tests.unexpected_successes:
269+
test_parts = path_to_test(test.path)
270+
if len(test_parts) == 2:
271+
test_key = tuple(test_parts)
272+
if test_key not in unexpected_successes:
273+
unexpected_successes.add(test_key)
274+
print(f"Removing expectedFailure: {test_parts[0]}.{test_parts[1]}")
275+
276+
# Remove expectedFailure from tests that now pass
277+
if unexpected_successes:
278+
f = remove_expected_failures(f, unexpected_successes)
279+
280+
# Apply patches for failing tests
281+
if failing_tests:
282+
patches = build_patches(failing_tests)
206283
f = apply_patches(f, patches)
284+
285+
# Write changes if any modifications were made
286+
if failing_tests or unexpected_successes:
207287
test_path.write_text(f, encoding="utf-8")
208288

209-
print(f"Modified {len(seen_tests)} tests")
289+
print(f"Added expectedFailure to {len(failing_tests)} tests")
290+
print(f"Removed expectedFailure from {len(unexpected_successes)} tests")

0 commit comments

Comments
 (0)