Translate Types and Utilities parity tests
Some checks failed
Rust API gates / api-gates (push) Has been cancelled

This commit is contained in:
2026-08-08 13:58:25 +02:00
parent 39ee61525a
commit 1c0dfdc35c
19 changed files with 3482 additions and 3318 deletions

View File

@@ -70,7 +70,7 @@ PARITY_MARKER_RE = re.compile(
re.MULTILINE,
)
RUST_TEST_RE = re.compile(r"(?:#\[[^\]]+\]\s*)*fn\s+([A-Za-z_][A-Za-z0-9_]*)\s*\(", re.MULTILINE)
EXPECTED_TESTS = 1295
EXPECTED_TESTS = 1289
PARITY_FILES = (
Path("tests/compat/tests/generated_parity.rs"),
Path("tests/PARITY.md"),
@@ -106,6 +106,50 @@ def source_files(root: Path) -> list[Path]:
return sorted(p for p in root.rglob("*.cs") if "obj" not in p.parts and "bin" not in p.parts)
def mask_csharp_comments(text: str) -> str:
"""Blank comments without shifting source offsets or touching string literals."""
masked = list(text)
index = 0
state = "code"
while index < len(text):
char = text[index]
following = text[index + 1] if index + 1 < len(text) else ""
if state == "code":
if char == '"':
state = "string"
elif char == "'":
state = "char"
elif char == "/" and following == "/":
masked[index] = masked[index + 1] = " "
index += 1
state = "line_comment"
elif char == "/" and following == "*":
masked[index] = masked[index + 1] = " "
index += 1
state = "block_comment"
elif state == "string":
if char == '"' and following == '"':
index += 1
elif char == '"' and (index == 0 or text[index - 1] != "\\"):
state = "code"
elif state == "char":
if char == "'" and (index == 0 or text[index - 1] != "\\"):
state = "code"
elif state == "line_comment":
if char in "\r\n":
state = "code"
else:
masked[index] = " "
elif char == "*" and following == "/":
masked[index] = masked[index + 1] = " "
index += 1
state = "code"
elif char not in "\r\n":
masked[index] = " "
index += 1
return "".join(masked)
def namespace_at(text: str, position: int) -> str:
matches = [m for m in NAMESPACE_RE.finditer(text) if m.start() <= position]
return matches[-1].group(1) if matches else ""
@@ -278,10 +322,11 @@ def helper_types(roots: tuple[Path, ...], upstream: Path) -> dict[str, str]:
for root in roots:
for path in source_files(root):
text = path.read_text(encoding="utf-8-sig")
if TEST_ATTR_RE.search(text):
searchable = mask_csharp_comments(text)
if TEST_ATTR_RE.search(searchable):
continue
relative = path.relative_to(upstream).as_posix()
for match in CLASS_RE.finditer(text):
for match in CLASS_RE.finditer(searchable):
helpers.setdefault(match.group(1), relative)
return helpers
@@ -293,29 +338,33 @@ def extract_tests(upstream: Path) -> list[dict[str, object]]:
for root in roots:
for path in source_files(root):
text = path.read_text(encoding="utf-8-sig")
searchable = mask_csharp_comments(text)
relative = path.relative_to(upstream).as_posix()
dependencies = sorted(
helper_path
for helper, helper_path in helpers.items()
if helper_path != relative and re.search(rf"\b{re.escape(helper)}\b", text)
if helper_path != relative and re.search(rf"\b{re.escape(helper)}\b", searchable)
)
attrs = list(TEST_ATTR_RE.finditer(text))
attrs = list(TEST_ATTR_RE.finditer(searchable))
for attr_index, attr in enumerate(attrs):
next_attr = attrs[attr_index + 1].start() if attr_index + 1 < len(attrs) else len(text)
method = METHOD_RE.search(text, attr.end(), next_attr + 1200)
next_attr = attrs[attr_index + 1].start() if attr_index + 1 < len(attrs) else len(searchable)
method = METHOD_RE.search(searchable, attr.end(), next_attr + 1200)
if method is None:
method = METHOD_RE.search(text, attr.end())
method = METHOD_RE.search(searchable, attr.end())
if method is None:
raise RuntimeError(f"No test method after {relative}:{text.count(chr(10), 0, attr.start()) + 1}")
attribute_block = searchable[searchable.rfind("}", 0, method.start()) + 1 : method.start()]
if attr.group(1) == "Test" and "[TestCase" in attribute_block:
continue
name = method.group(1)
class_match = enclosing_class_match(text, method.start())
class_match = enclosing_class_match(searchable, method.start())
fixture = class_match.group(1) if class_match else "UnknownFixture"
attr_text = " ".join(attr.group(0).split())
parameters = " ".join((attr.group(2) or "").split()) or None
line = text.count("\n", 0, attr.start()) + 1
category_text = text[attr.start() : method.start()]
category_text = searchable[attr.start() : method.start()]
if class_match:
category_text += attributes_before(text, class_match.start())
category_text += attributes_before(searchable, class_match.start())
categories = sorted(set(CATEGORY_RE.findall(category_text)))
tests.append(
{