ConspiracyBench / scripts /test_text_edge_cases.py
stationhouse's picture gowrishkumar's picture
Stress test solver (B4) (#334)
61dad6b
Raw History Blame Contribute Delete
4.82 kB
"""Regression test: valid question text that a line splitter or a log writer chokes on.
- U+2028, U+2029 and U+0085 are legal unescaped inside a JSON string, but str.splitlines()
breaks lines at them, so a valid question was rejected as two broken lines.
- A lone surrogate ("\\ud800", usually half an emoji) is valid JSON and passes schema.json,
but is not text; an eval run dies writing its log. validate_questions.py must catch it.
Run from the repo root:
python -m unittest scripts/test_text_edge_cases.py
"""
import ast
import json
import shutil
import subprocess
import sys
import tempfile
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
ITEM = {
"id": "cb-pending",
"category": "other",
"question": "placeholder",
"ground_truth_answer": "A",
"ground_truth_source": "https://example.com",
}
def run_scripts(pending_bytes: bytes, scripts):
"""Run each script in a scratch copy of the repo whose pending/ holds only pending_bytes."""
with tempfile.TemporaryDirectory() as tmp:
repo = Path(tmp)
shutil.copytree(ROOT / "scripts", repo / "scripts")
shutil.copytree(ROOT / "questions", repo / "questions")
shutil.copy(ROOT / "schema.json", repo / "schema.json")
for f in (repo / "questions" / "pending").glob("*.jsonl"):
f.unlink()
(repo / "questions" / "pending" / "t.jsonl").write_bytes(pending_bytes)
results = {}
for script in scripts:
results[script] = subprocess.run(
[sys.executable, str(repo / "scripts" / script)],
cwd=repo, capture_output=True, text=True, encoding="utf-8",
)
# Bytes, not read_text(): text mode would turn CRLF into LF and hide it.
corpus = (repo / "questions" / "corpus.jsonl").read_bytes().decode("utf-8")
return results, corpus
class TextEdgeCaseTest(unittest.TestCase):
def test_no_script_splits_a_file_with_splitlines(self):
"""Static check, which also covers check_duplicates.py (it needs fastembed to run)."""
found = []
for path in sorted((ROOT / "scripts").glob("*.py")):
for node in ast.walk(ast.parse(path.read_text(encoding="utf-8"))):
if (
isinstance(node, ast.Call)
and isinstance(node.func, ast.Attribute)
and node.func.attr == "splitlines"
and isinstance(node.func.value, ast.Call)
and isinstance(node.func.value.func, ast.Attribute)
and node.func.value.func.attr == "read_text"
):
found.append(f"{path.name}:{node.lineno}")
self.assertEqual(found, [], "read_text(...).splitlines(): use .split('\\n')")
def test_separator_inside_a_string_is_one_valid_question(self):
for name, char in (("U+2028", "
"), ("U+2029", "
"), ("U+0085", "\u0085")):
with self.subTest(name):
question = f"first half{char}second half?"
line = json.dumps({**ITEM, "question": question}, ensure_ascii=False).encode("utf-8")
results, corpus = run_scripts(line + b"\n", ("validate_questions.py", "assign_ids.py"))
for script, run in results.items():
self.assertEqual(run.returncode, 0, f"{script}: {run.stdout[-300:]}{run.stderr[-300:]}")
stored = [json.loads(l) for l in corpus.split("\n") if l.strip()][-1]
self.assertEqual(stored["question"], question)
def test_assign_ids_writes_lf_line_endings(self):
# Text mode writes CRLF on Windows; corpus.jsonl is committed with LF.
line = json.dumps({**ITEM, "question": "plain?"}).encode("ascii")
results, corpus = run_scripts(line + b"\n", ("assign_ids.py",))
self.assertEqual(results["assign_ids.py"].returncode, 0)
self.assertFalse("\r" in corpus, "assign_ids.py wrote CRLF line endings")
def test_a_question_of_only_whitespace_is_reported_by_the_validator(self):
line = json.dumps({**ITEM, "question": " "}).encode("ascii")
results, _ = run_scripts(line + b"\n", ("validate_questions.py",))
run = results["validate_questions.py"]
self.assertEqual(run.returncode, 1)
self.assertIn("pending/t.jsonl:1", run.stdout)
def test_lone_surrogate_is_reported_by_the_validator(self):
line = json.dumps({**ITEM, "question": "half an emoji \ud83d"}).encode("ascii")
results, _ = run_scripts(line + b"\n", ("validate_questions.py",))
run = results["validate_questions.py"]
self.assertEqual(run.returncode, 1)
self.assertIn("pending/t.jsonl:1: contains a lone surrogate", run.stdout)
if __name__ == "__main__":
unittest.main()