mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 10:44:36 +08:00
Preserve decorator rationale during canonical test copying
Reject differing decorator source blocks even when their ASTs match. Cover inline and multiline marker comments, guarded async methods, and CLI no-write behavior. Assisted-by: OpenAI Codex:model identifier unavailable
This commit is contained in:
1 parent
691a3586d4
commit
ea0bc900d2
2 files changed
+94
No files matched your search
@@ -74,6 +74,15 @@ def copy_test(source: str, destination: str, qualified_name: str) -> str:
|
||||
raise ValueError("Source and destination indentation differs")
|
||||
source_lines = source.splitlines(keepends=True)
|
||||
destination_lines = destination.splitlines(keepends=True)
|
||||
# AST equality omits comments, including project marker rationales. Require
|
||||
# the complete decorator block to match before replacing the definition.
|
||||
if (
|
||||
source_lines[src_start : src.lineno - 1]
|
||||
!= destination_lines[dst_start : dst.lineno - 1]
|
||||
):
|
||||
raise ValueError(
|
||||
"Source and destination decorator text differs; preserve markers explicitly"
|
||||
)
|
||||
definition = source_lines[src_start:src_end]
|
||||
result = "".join(
|
||||
destination_lines[:dst_start] + definition + destination_lines[dst_end:]
|
||||
|
||||
@@ -82,6 +82,91 @@ class Finder:
|
||||
with self.assertRaisesRegex(ValueError, "Expected one class"):
|
||||
copy_test(local, self.destination, "Finder.example")
|
||||
|
||||
def test_decorator_comments_must_match_exactly(self):
|
||||
for source_marker, destination_marker in (
|
||||
(
|
||||
"@unittest.expectedFailure",
|
||||
"@unittest.expectedFailure # TODO: RUSTPYTHON; reason",
|
||||
),
|
||||
(
|
||||
"@unittest.expectedFailure # original reason",
|
||||
"@unittest.expectedFailure # changed reason",
|
||||
),
|
||||
(
|
||||
"@unittest.expectedFailure\n @other",
|
||||
"@unittest.expectedFailure\n # marker rationale\n @other",
|
||||
),
|
||||
(
|
||||
"@unittest.expectedFailureIf(\n condition,\n 'reason',\n )",
|
||||
"@unittest.expectedFailureIf(\n condition, # project limitation\n 'reason',\n )",
|
||||
),
|
||||
):
|
||||
with self.subTest(destination_marker=destination_marker):
|
||||
source = self.source.replace(
|
||||
" def example():",
|
||||
f" {source_marker}\n def example():",
|
||||
)
|
||||
destination = self.destination.replace(
|
||||
" def example():",
|
||||
f" {destination_marker}\n def example():",
|
||||
)
|
||||
with self.assertRaisesRegex(ValueError, "decorator text differs"):
|
||||
copy_test(source, destination, "Finder.example")
|
||||
|
||||
def test_identical_decorator_comments_are_preserved(self):
|
||||
prefix = (
|
||||
" @unittest.expectedFailure # TODO: RUSTPYTHON; reason\n"
|
||||
" # Keep this rationale with the marker.\n"
|
||||
" def example():"
|
||||
)
|
||||
for declaration in ("def example():", "async def example():"):
|
||||
with self.subTest(declaration=declaration):
|
||||
source = self.source.replace(" def example():", prefix)
|
||||
destination = self.destination.replace(" def example():", prefix)
|
||||
source = source.replace("def example():", declaration)
|
||||
destination = destination.replace("def example():", declaration)
|
||||
self.assertEqual(
|
||||
copy_test(source, destination, "Finder.example"),
|
||||
destination.replace("below 800", "below 850"),
|
||||
)
|
||||
|
||||
def test_cli_does_not_write_on_decorator_comment_mismatch(self):
|
||||
with tempfile.TemporaryDirectory() as temp:
|
||||
source = Path(temp) / "source.py"
|
||||
destination = Path(temp) / "destination.py"
|
||||
source.write_text(
|
||||
self.source.replace(
|
||||
" def example():",
|
||||
" @unittest.expectedFailure\n def example():",
|
||||
)
|
||||
)
|
||||
destination.write_text(
|
||||
self.destination.replace(
|
||||
" def example():",
|
||||
" @unittest.expectedFailure # TODO: RUSTPYTHON; reason\n"
|
||||
" def example():",
|
||||
)
|
||||
)
|
||||
before = destination.read_bytes()
|
||||
with (
|
||||
contextlib.redirect_stderr(io.StringIO()) as errors,
|
||||
self.assertRaises(SystemExit),
|
||||
):
|
||||
main(
|
||||
[
|
||||
str(source),
|
||||
str(destination),
|
||||
"--test",
|
||||
"Finder.example",
|
||||
"--source-sha256",
|
||||
hashlib.sha256(source.read_bytes()).hexdigest(),
|
||||
"--destination-sha256",
|
||||
hashlib.sha256(before).hexdigest(),
|
||||
]
|
||||
)
|
||||
self.assertIn("decorator text differs", errors.getvalue())
|
||||
self.assertEqual(destination.read_bytes(), before)
|
||||
|
||||
def test_return_annotation_and_type_parameters_are_rejected(self):
|
||||
for declaration in ("def example() -> int:", "def example[T]():"):
|
||||
with self.subTest(declaration=declaration):
|
||||
|
||||
Reference in new issue
Block a user