|
@@ -125,13 +125,12 @@ def _process_report_writer(
|
|
|
original_write_text = reporting._write_text
|
|
original_write_text = reporting._write_text
|
|
|
first_write = True
|
|
first_write = True
|
|
|
|
|
|
|
|
- def synchronized_write(path: Path, content: str) -> tuple[int, int]:
|
|
|
|
|
|
|
+ def synchronized_write(path: Path, content: str) -> None:
|
|
|
nonlocal first_write
|
|
nonlocal first_write
|
|
|
- identity = original_write_text(path, content)
|
|
|
|
|
|
|
+ original_write_text(path, content)
|
|
|
if first_write:
|
|
if first_write:
|
|
|
first_write = False
|
|
first_write = False
|
|
|
first_temp_barrier.wait()
|
|
first_temp_barrier.wait()
|
|
|
- return identity
|
|
|
|
|
|
|
|
|
|
reporting._write_text = synchronized_write
|
|
reporting._write_text = synchronized_write
|
|
|
try:
|
|
try:
|
|
@@ -144,7 +143,7 @@ def _process_report_writer(
|
|
|
result_queue.put(("error", marker, repr(exc)))
|
|
result_queue.put(("error", marker, repr(exc)))
|
|
|
return
|
|
return
|
|
|
result_queue.put(
|
|
result_queue.put(
|
|
|
- ("ok", marker, json_path.name, markdown_path.name)
|
|
|
|
|
|
|
+ ("ok", marker, json_path.parent.name, markdown_path.parent.name)
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@@ -152,37 +151,57 @@ def _assert_concurrent_report_pairs(
|
|
|
output_dir: Path,
|
|
output_dir: Path,
|
|
|
markers: list[str],
|
|
markers: list[str],
|
|
|
) -> None:
|
|
) -> None:
|
|
|
- json_paths = sorted(output_dir.glob("*.json"))
|
|
|
|
|
- markdown_paths = sorted(output_dir.glob("*.md"))
|
|
|
|
|
- assert len(json_paths) == len(markers)
|
|
|
|
|
- assert len(markdown_paths) == len(markers)
|
|
|
|
|
- assert {path.stem for path in json_paths} == {
|
|
|
|
|
- path.stem for path in markdown_paths
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ published_dirs = sorted(
|
|
|
|
|
+ path
|
|
|
|
|
+ for path in output_dir.iterdir()
|
|
|
|
|
+ if not path.name.startswith(".")
|
|
|
|
|
+ )
|
|
|
|
|
+ assert len(published_dirs) == len(markers)
|
|
|
|
|
+ assert all(path.is_symlink() for path in published_dirs)
|
|
|
actual_markers = set()
|
|
actual_markers = set()
|
|
|
- for json_path in json_paths:
|
|
|
|
|
|
|
+ targets = set()
|
|
|
|
|
+ for published_dir in published_dirs:
|
|
|
|
|
+ target = published_dir.readlink()
|
|
|
|
|
+ assert not target.is_absolute()
|
|
|
|
|
+ targets.add(target)
|
|
|
|
|
+ json_path = published_dir / "report.json"
|
|
|
|
|
+ markdown_path = published_dir / "report.md"
|
|
|
marker = json.loads(json_path.read_text())["target"]["model"]
|
|
marker = json.loads(json_path.read_text())["target"]["model"]
|
|
|
- markdown = json_path.with_suffix(".md").read_text()
|
|
|
|
|
|
|
+ markdown = markdown_path.read_text()
|
|
|
actual_markers.add(marker)
|
|
actual_markers.add(marker)
|
|
|
assert f"- Model: `{marker}`" in markdown
|
|
assert f"- Model: `{marker}`" in markdown
|
|
|
assert actual_markers == set(markers)
|
|
assert actual_markers == set(markers)
|
|
|
|
|
+ assert len(targets) == len(markers)
|
|
|
_assert_no_publication_artifacts(output_dir)
|
|
_assert_no_publication_artifacts(output_dir)
|
|
|
|
|
|
|
|
|
|
|
|
|
def _assert_no_publication_artifacts(output_dir: Path) -> None:
|
|
def _assert_no_publication_artifacts(output_dir: Path) -> None:
|
|
|
|
|
+ entries = list(output_dir.iterdir())
|
|
|
leftovers = [
|
|
leftovers = [
|
|
|
path.name
|
|
path.name
|
|
|
- for path in output_dir.iterdir()
|
|
|
|
|
|
|
+ for path in entries
|
|
|
if path.name.endswith((".tmp", ".lock"))
|
|
if path.name.endswith((".tmp", ".lock"))
|
|
|
or "placeholder" in path.name
|
|
or "placeholder" in path.name
|
|
|
|
|
+ or path.suffix in {".json", ".md"}
|
|
|
]
|
|
]
|
|
|
assert leftovers == []
|
|
assert leftovers == []
|
|
|
- report_paths = [
|
|
|
|
|
|
|
+ published_dirs = [
|
|
|
path
|
|
path
|
|
|
- for path in output_dir.iterdir()
|
|
|
|
|
- if path.suffix in {".json", ".md"}
|
|
|
|
|
|
|
+ for path in entries
|
|
|
|
|
+ if path.is_symlink()
|
|
|
|
|
+ and path.readlink().name.startswith(".")
|
|
|
|
|
+ and path.readlink().name.endswith(".bundle")
|
|
|
]
|
|
]
|
|
|
- assert all(path.stat().st_size > 0 for path in report_paths)
|
|
|
|
|
|
|
+ published_targets = {path.readlink() for path in published_dirs}
|
|
|
|
|
+ private_bundles = {
|
|
|
|
|
+ Path(path.name)
|
|
|
|
|
+ for path in entries
|
|
|
|
|
+ if path.name.startswith(".") and path.is_dir()
|
|
|
|
|
+ }
|
|
|
|
|
+ assert private_bundles == published_targets
|
|
|
|
|
+ for published_dir in published_dirs:
|
|
|
|
|
+ assert (published_dir / "report.json").stat().st_size > 0
|
|
|
|
|
+ assert (published_dir / "report.md").stat().st_size > 0
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
@pytest.mark.parametrize(
|
|
@@ -582,14 +601,39 @@ def test_markdown_is_rendered_from_the_same_json_facts():
|
|
|
assert payload == report.model_dump(mode="json")
|
|
assert payload == report.model_dump(mode="json")
|
|
|
|
|
|
|
|
|
|
|
|
|
-def test_atomic_write_creates_matching_unique_json_and_markdown_paths(tmp_path: Path):
|
|
|
|
|
|
|
+def test_markdown_preserves_runtime_error_and_semantic_failures_together():
|
|
|
reporting = _reporting_module()
|
|
reporting = _reporting_module()
|
|
|
report = reporting.build_benchmark_report(
|
|
report = reporting.build_benchmark_report(
|
|
|
- _config(), [_run()], mock=True
|
|
|
|
|
|
|
+ _config(),
|
|
|
|
|
+ [
|
|
|
|
|
+ _run(
|
|
|
|
|
+ status="failed",
|
|
|
|
|
+ error="runtime exploded",
|
|
|
|
|
+ semantic_failures=[
|
|
|
|
|
+ "expected web_search event",
|
|
|
|
|
+ "expected two visible answers",
|
|
|
|
|
+ ],
|
|
|
|
|
+ )
|
|
|
|
|
+ ],
|
|
|
|
|
+ mock=True,
|
|
|
)
|
|
)
|
|
|
|
|
+
|
|
|
|
|
+ markdown = reporting.render_markdown(report)
|
|
|
|
|
+
|
|
|
|
|
+ assert "runtime exploded" in markdown
|
|
|
|
|
+ assert "expected web_search event" in markdown
|
|
|
|
|
+ assert "expected two visible answers" in markdown
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def test_atomic_write_publishes_complete_bundle_through_relative_symlink(
|
|
|
|
|
+ tmp_path: Path,
|
|
|
|
|
+):
|
|
|
|
|
+ reporting = _reporting_module()
|
|
|
|
|
+ report = _marker_report("atomic-bundle")
|
|
|
timestamp = "20260714T010203Z"
|
|
timestamp = "20260714T010203Z"
|
|
|
- (tmp_path / f"{timestamp}.json").write_text("existing")
|
|
|
|
|
- (tmp_path / f"{timestamp}.md").write_text("existing")
|
|
|
|
|
|
|
+ competitor = tmp_path / timestamp
|
|
|
|
|
+ competitor.mkdir()
|
|
|
|
|
+ (competitor / "sentinel").write_text("competitor")
|
|
|
|
|
|
|
|
json_path, markdown_path = reporting.write_benchmark_report(
|
|
json_path, markdown_path = reporting.write_benchmark_report(
|
|
|
report,
|
|
report,
|
|
@@ -597,229 +641,219 @@ def test_atomic_write_creates_matching_unique_json_and_markdown_paths(tmp_path:
|
|
|
timestamp=timestamp,
|
|
timestamp=timestamp,
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
- assert json_path.name == f"{timestamp}-1.json"
|
|
|
|
|
- assert markdown_path.name == f"{timestamp}-1.md"
|
|
|
|
|
|
|
+ published_dir = tmp_path / f"{timestamp}-1"
|
|
|
|
|
+ assert json_path == published_dir / "report.json"
|
|
|
|
|
+ assert markdown_path == published_dir / "report.md"
|
|
|
|
|
+ assert published_dir.is_symlink()
|
|
|
|
|
+ bundle_target = published_dir.readlink()
|
|
|
|
|
+ assert not bundle_target.is_absolute()
|
|
|
|
|
+ assert bundle_target.name.startswith(f".{timestamp}.")
|
|
|
|
|
+ assert bundle_target.name.endswith(".bundle")
|
|
|
|
|
+ assert competitor.joinpath("sentinel").read_text() == "competitor"
|
|
|
assert json.loads(json_path.read_text()) == report.model_dump(mode="json")
|
|
assert json.loads(json_path.read_text()) == report.model_dump(mode="json")
|
|
|
assert markdown_path.read_text() == reporting.render_markdown(report)
|
|
assert markdown_path.read_text() == reporting.render_markdown(report)
|
|
|
_assert_no_publication_artifacts(tmp_path)
|
|
_assert_no_publication_artifacts(tmp_path)
|
|
|
|
|
|
|
|
|
|
|
|
|
-def test_json_created_immediately_before_link_wins_and_writer_retries(
|
|
|
|
|
|
|
+@pytest.mark.parametrize("collision_kind", ["create", "replace"])
|
|
|
|
|
+def test_symlink_collision_retries_without_removing_or_replacing_competitor(
|
|
|
tmp_path: Path,
|
|
tmp_path: Path,
|
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
|
|
|
+ collision_kind: str,
|
|
|
):
|
|
):
|
|
|
reporting = _reporting_module()
|
|
reporting = _reporting_module()
|
|
|
timestamp = "20260714T011223Z"
|
|
timestamp = "20260714T011223Z"
|
|
|
- external_json = tmp_path / f"{timestamp}.json"
|
|
|
|
|
- real_link = reporting.os.link
|
|
|
|
|
- link_calls = 0
|
|
|
|
|
|
|
+ public_path = tmp_path / timestamp
|
|
|
|
|
+ real_symlink = reporting.os.symlink
|
|
|
|
|
+ real_replace = reporting.os.replace
|
|
|
|
|
+ symlink_calls = 0
|
|
|
|
|
+
|
|
|
|
|
+ if collision_kind == "replace":
|
|
|
|
|
+ old_target = tmp_path / "old-competitor"
|
|
|
|
|
+ old_target.mkdir()
|
|
|
|
|
+ real_symlink(old_target.name, public_path)
|
|
|
|
|
|
|
|
- def create_before_link(source: Path, destination: Path) -> None:
|
|
|
|
|
- nonlocal link_calls
|
|
|
|
|
- link_calls += 1
|
|
|
|
|
|
|
+ def collide_before_symlink(source: str, destination: Path) -> None:
|
|
|
|
|
+ nonlocal symlink_calls
|
|
|
|
|
+ symlink_calls += 1
|
|
|
destination = Path(destination)
|
|
destination = Path(destination)
|
|
|
- if link_calls == 1:
|
|
|
|
|
- destination.write_text("external json")
|
|
|
|
|
- real_link(source, destination)
|
|
|
|
|
|
|
+ if symlink_calls == 1:
|
|
|
|
|
+ if collision_kind == "create":
|
|
|
|
|
+ destination.mkdir()
|
|
|
|
|
+ (destination / "sentinel").write_text("competitor")
|
|
|
|
|
+ else:
|
|
|
|
|
+ new_target = tmp_path / "new-competitor"
|
|
|
|
|
+ new_target.mkdir()
|
|
|
|
|
+ replacement = tmp_path / "competitor-replacement"
|
|
|
|
|
+ real_symlink(new_target.name, replacement)
|
|
|
|
|
+ real_replace(replacement, destination)
|
|
|
|
|
+ real_symlink(source, destination)
|
|
|
|
|
|
|
|
- monkeypatch.setattr(reporting.os, "link", create_before_link)
|
|
|
|
|
|
|
+ monkeypatch.setattr(reporting.os, "symlink", collide_before_symlink)
|
|
|
|
|
|
|
|
json_path, markdown_path = reporting.write_benchmark_report(
|
|
json_path, markdown_path = reporting.write_benchmark_report(
|
|
|
- _marker_report("json-link-collision"),
|
|
|
|
|
|
|
+ _marker_report(f"symlink-{collision_kind}-collision"),
|
|
|
tmp_path,
|
|
tmp_path,
|
|
|
timestamp=timestamp,
|
|
timestamp=timestamp,
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
- assert link_calls == 3
|
|
|
|
|
- assert external_json.read_text() == "external json"
|
|
|
|
|
- assert not (tmp_path / f"{timestamp}.md").exists()
|
|
|
|
|
- assert json_path.name == f"{timestamp}-1.json"
|
|
|
|
|
- assert markdown_path.name == f"{timestamp}-1.md"
|
|
|
|
|
|
|
+ assert symlink_calls == 2
|
|
|
|
|
+ assert json_path.parent == tmp_path / f"{timestamp}-1"
|
|
|
|
|
+ assert markdown_path.parent == json_path.parent
|
|
|
|
|
+ if collision_kind == "create":
|
|
|
|
|
+ assert public_path.joinpath("sentinel").read_text() == "competitor"
|
|
|
|
|
+ else:
|
|
|
|
|
+ assert public_path.readlink() == Path("new-competitor")
|
|
|
|
|
+ assert json.loads(json_path.read_text())["target"]["model"] == (
|
|
|
|
|
+ f"symlink-{collision_kind}-collision"
|
|
|
|
|
+ )
|
|
|
_assert_no_publication_artifacts(tmp_path)
|
|
_assert_no_publication_artifacts(tmp_path)
|
|
|
|
|
|
|
|
|
|
|
|
|
-def test_json_replaced_immediately_before_link_wins_and_writer_retries(
|
|
|
|
|
- tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
|
|
|
|
|
+def test_bundle_is_complete_and_matching_before_single_publication(
|
|
|
|
|
+ tmp_path: Path,
|
|
|
|
|
+ monkeypatch: pytest.MonkeyPatch,
|
|
|
):
|
|
):
|
|
|
reporting = _reporting_module()
|
|
reporting = _reporting_module()
|
|
|
- timestamp = "20260714T011224Z"
|
|
|
|
|
- external_json = tmp_path / f"{timestamp}.json"
|
|
|
|
|
- external_json.write_text("old external json")
|
|
|
|
|
- replacement = tmp_path / "competitor-replacement.json"
|
|
|
|
|
- real_link = reporting.os.link
|
|
|
|
|
- real_replace = reporting.os.replace
|
|
|
|
|
- link_calls = 0
|
|
|
|
|
|
|
+ real_symlink = reporting.os.symlink
|
|
|
|
|
+ publication_snapshots: list[tuple[str, str]] = []
|
|
|
|
|
|
|
|
- def replace_before_link(source: Path, destination: Path) -> None:
|
|
|
|
|
- nonlocal link_calls
|
|
|
|
|
- link_calls += 1
|
|
|
|
|
|
|
+ def inspect_complete_bundle(source: str, destination: Path) -> None:
|
|
|
destination = Path(destination)
|
|
destination = Path(destination)
|
|
|
- if link_calls == 1:
|
|
|
|
|
- replacement.write_text("new external json")
|
|
|
|
|
- real_replace(replacement, destination)
|
|
|
|
|
- real_link(source, destination)
|
|
|
|
|
|
|
+ bundle = destination.parent / source
|
|
|
|
|
+ json_text = bundle.joinpath("report.json").read_text()
|
|
|
|
|
+ markdown_text = bundle.joinpath("report.md").read_text()
|
|
|
|
|
+ marker = json.loads(json_text)["target"]["model"]
|
|
|
|
|
+ assert f"- Model: `{marker}`" in markdown_text
|
|
|
|
|
+ publication_snapshots.append((json_text, markdown_text))
|
|
|
|
|
+ real_symlink(source, destination)
|
|
|
|
|
|
|
|
- monkeypatch.setattr(reporting.os, "link", replace_before_link)
|
|
|
|
|
|
|
+ monkeypatch.setattr(reporting.os, "symlink", inspect_complete_bundle)
|
|
|
|
|
|
|
|
json_path, markdown_path = reporting.write_benchmark_report(
|
|
json_path, markdown_path = reporting.write_benchmark_report(
|
|
|
- _marker_report("json-link-replacement"),
|
|
|
|
|
|
|
+ _marker_report("complete-before-publish"),
|
|
|
tmp_path,
|
|
tmp_path,
|
|
|
- timestamp=timestamp,
|
|
|
|
|
|
|
+ timestamp="20260714T011224Z",
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
- assert link_calls == 3
|
|
|
|
|
- assert external_json.read_text() == "new external json"
|
|
|
|
|
- assert json_path.name == f"{timestamp}-1.json"
|
|
|
|
|
- assert markdown_path.name == f"{timestamp}-1.md"
|
|
|
|
|
|
|
+ assert len(publication_snapshots) == 1
|
|
|
|
|
+ assert json_path.parent == markdown_path.parent
|
|
|
|
|
+ assert json_path.parent.is_symlink()
|
|
|
|
|
+ assert json_path.read_text() == publication_snapshots[0][0]
|
|
|
|
|
+ assert markdown_path.read_text() == publication_snapshots[0][1]
|
|
|
_assert_no_publication_artifacts(tmp_path)
|
|
_assert_no_publication_artifacts(tmp_path)
|
|
|
|
|
|
|
|
|
|
|
|
|
-@pytest.mark.parametrize("collision_kind", ["create", "replace"])
|
|
|
|
|
-def test_markdown_link_conflict_removes_owned_json_link_and_retries(
|
|
|
|
|
|
|
+def test_report_files_and_bundle_directory_are_fsynced_before_publication(
|
|
|
tmp_path: Path,
|
|
tmp_path: Path,
|
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
|
- collision_kind: str,
|
|
|
|
|
):
|
|
):
|
|
|
reporting = _reporting_module()
|
|
reporting = _reporting_module()
|
|
|
- timestamp = "20260714T011225Z"
|
|
|
|
|
- base_json = tmp_path / f"{timestamp}.json"
|
|
|
|
|
- external_markdown = tmp_path / f"{timestamp}.md"
|
|
|
|
|
- replacement = tmp_path / "competitor-replacement.md"
|
|
|
|
|
- real_link = reporting.os.link
|
|
|
|
|
- real_replace = reporting.os.replace
|
|
|
|
|
- link_calls = 0
|
|
|
|
|
|
|
+ real_fsync = reporting.os.fsync
|
|
|
|
|
+ real_symlink = reporting.os.symlink
|
|
|
|
|
+ fsync_calls = 0
|
|
|
|
|
+ symlink_calls = 0
|
|
|
|
|
|
|
|
- if collision_kind == "replace":
|
|
|
|
|
- external_markdown.write_text("old external markdown")
|
|
|
|
|
|
|
+ def record_fsync(file_descriptor: int) -> None:
|
|
|
|
|
+ nonlocal fsync_calls
|
|
|
|
|
+ fsync_calls += 1
|
|
|
|
|
+ real_fsync(file_descriptor)
|
|
|
|
|
|
|
|
- def conflict_on_markdown(source: Path, destination: Path) -> None:
|
|
|
|
|
- nonlocal link_calls
|
|
|
|
|
- link_calls += 1
|
|
|
|
|
- destination = Path(destination)
|
|
|
|
|
- if destination == external_markdown:
|
|
|
|
|
- if collision_kind == "replace":
|
|
|
|
|
- replacement.write_text("external markdown")
|
|
|
|
|
- real_replace(replacement, destination)
|
|
|
|
|
- else:
|
|
|
|
|
- destination.write_text("external markdown")
|
|
|
|
|
- real_link(source, destination)
|
|
|
|
|
|
|
+ def inspect_before_symlink(source: str, destination: Path) -> None:
|
|
|
|
|
+ nonlocal symlink_calls
|
|
|
|
|
+ symlink_calls += 1
|
|
|
|
|
+ assert fsync_calls == 3
|
|
|
|
|
+ real_symlink(source, destination)
|
|
|
|
|
|
|
|
- monkeypatch.setattr(reporting.os, "link", conflict_on_markdown)
|
|
|
|
|
|
|
+ monkeypatch.setattr(reporting.os, "fsync", record_fsync)
|
|
|
|
|
+ monkeypatch.setattr(reporting.os, "symlink", inspect_before_symlink)
|
|
|
|
|
|
|
|
- json_path, markdown_path = reporting.write_benchmark_report(
|
|
|
|
|
- _marker_report("markdown-link-collision"),
|
|
|
|
|
|
|
+ reporting.write_benchmark_report(
|
|
|
|
|
+ _marker_report("fsync-before-publish"),
|
|
|
tmp_path,
|
|
tmp_path,
|
|
|
- timestamp=timestamp,
|
|
|
|
|
|
|
+ timestamp="20260714T011225Z",
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
- assert link_calls == 4
|
|
|
|
|
- assert not base_json.exists()
|
|
|
|
|
- assert external_markdown.read_text() == "external markdown"
|
|
|
|
|
- assert json_path.name == f"{timestamp}-1.json"
|
|
|
|
|
- assert markdown_path.name == f"{timestamp}-1.md"
|
|
|
|
|
|
|
+ assert symlink_calls == 1
|
|
|
|
|
+ assert fsync_calls == 4
|
|
|
_assert_no_publication_artifacts(tmp_path)
|
|
_assert_no_publication_artifacts(tmp_path)
|
|
|
|
|
|
|
|
|
|
|
|
|
-def test_markdown_conflict_does_not_remove_replaced_json_target(
|
|
|
|
|
|
|
+def test_file_write_failure_removes_only_unpublished_private_bundle(
|
|
|
tmp_path: Path,
|
|
tmp_path: Path,
|
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
|
):
|
|
):
|
|
|
reporting = _reporting_module()
|
|
reporting = _reporting_module()
|
|
|
- timestamp = "20260714T011226Z"
|
|
|
|
|
- base_json = tmp_path / f"{timestamp}.json"
|
|
|
|
|
- base_markdown = tmp_path / f"{timestamp}.md"
|
|
|
|
|
- competitor_json = tmp_path / "competitor.json"
|
|
|
|
|
- real_link = reporting.os.link
|
|
|
|
|
- real_replace = reporting.os.replace
|
|
|
|
|
|
|
+ original_write_text = reporting._write_text
|
|
|
|
|
+ write_calls = 0
|
|
|
|
|
|
|
|
- def replace_json_before_markdown_conflict(
|
|
|
|
|
- source: Path,
|
|
|
|
|
- destination: Path,
|
|
|
|
|
- ) -> None:
|
|
|
|
|
- destination = Path(destination)
|
|
|
|
|
- if destination == base_markdown:
|
|
|
|
|
- competitor_json.write_text("external json")
|
|
|
|
|
- real_replace(competitor_json, base_json)
|
|
|
|
|
- base_markdown.write_text("external markdown")
|
|
|
|
|
- real_link(source, destination)
|
|
|
|
|
|
|
+ def fail_second_file(path: Path, content: str) -> None:
|
|
|
|
|
+ nonlocal write_calls
|
|
|
|
|
+ write_calls += 1
|
|
|
|
|
+ if write_calls == 2:
|
|
|
|
|
+ raise OSError("simulated report file failure")
|
|
|
|
|
+ original_write_text(path, content)
|
|
|
|
|
|
|
|
- monkeypatch.setattr(
|
|
|
|
|
- reporting.os,
|
|
|
|
|
- "link",
|
|
|
|
|
- replace_json_before_markdown_conflict,
|
|
|
|
|
- )
|
|
|
|
|
|
|
+ monkeypatch.setattr(reporting, "_write_text", fail_second_file)
|
|
|
|
|
|
|
|
- json_path, markdown_path = reporting.write_benchmark_report(
|
|
|
|
|
- _marker_report("replaced-json-before-cleanup"),
|
|
|
|
|
- tmp_path,
|
|
|
|
|
- timestamp=timestamp,
|
|
|
|
|
- )
|
|
|
|
|
|
|
+ with pytest.raises(
|
|
|
|
|
+ reporting.BenchmarkReportWriteError,
|
|
|
|
|
+ match="failed to write benchmark reports",
|
|
|
|
|
+ ):
|
|
|
|
|
+ reporting.write_benchmark_report(
|
|
|
|
|
+ _marker_report("file-write-failure"),
|
|
|
|
|
+ tmp_path,
|
|
|
|
|
+ timestamp="20260714T011226Z",
|
|
|
|
|
+ )
|
|
|
|
|
|
|
|
- assert base_json.read_text() == "external json"
|
|
|
|
|
- assert base_markdown.read_text() == "external markdown"
|
|
|
|
|
- assert json_path.name == f"{timestamp}-1.json"
|
|
|
|
|
- assert markdown_path.name == f"{timestamp}-1.md"
|
|
|
|
|
- _assert_no_publication_artifacts(tmp_path)
|
|
|
|
|
|
|
+ assert list(tmp_path.iterdir()) == []
|
|
|
|
|
|
|
|
|
|
|
|
|
-def test_link_exception_cleans_only_owned_links_and_temps(
|
|
|
|
|
|
|
+def test_bundle_fsync_failure_removes_unpublished_private_bundle(
|
|
|
tmp_path: Path,
|
|
tmp_path: Path,
|
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
|
):
|
|
):
|
|
|
reporting = _reporting_module()
|
|
reporting = _reporting_module()
|
|
|
- timestamp = "20260714T011227Z"
|
|
|
|
|
- base_json = tmp_path / f"{timestamp}.json"
|
|
|
|
|
- base_markdown = tmp_path / f"{timestamp}.md"
|
|
|
|
|
- competitor_json = tmp_path / "competitor.json"
|
|
|
|
|
- real_link = reporting.os.link
|
|
|
|
|
- real_replace = reporting.os.replace
|
|
|
|
|
|
|
+ original_fsync_directory = reporting._fsync_directory
|
|
|
|
|
|
|
|
- def fail_markdown_link(source: Path, destination: Path) -> None:
|
|
|
|
|
- destination = Path(destination)
|
|
|
|
|
- if destination == base_markdown:
|
|
|
|
|
- competitor_json.write_text("external json")
|
|
|
|
|
- real_replace(competitor_json, base_json)
|
|
|
|
|
- base_markdown.write_text("external markdown")
|
|
|
|
|
- raise OSError("simulated markdown link failure")
|
|
|
|
|
- real_link(source, destination)
|
|
|
|
|
|
|
+ def fail_bundle_fsync(path: Path) -> None:
|
|
|
|
|
+ if path.name.endswith(".bundle"):
|
|
|
|
|
+ raise OSError("simulated bundle fsync failure")
|
|
|
|
|
+ original_fsync_directory(path)
|
|
|
|
|
|
|
|
- monkeypatch.setattr(reporting.os, "link", fail_markdown_link)
|
|
|
|
|
|
|
+ monkeypatch.setattr(reporting, "_fsync_directory", fail_bundle_fsync)
|
|
|
|
|
|
|
|
with pytest.raises(
|
|
with pytest.raises(
|
|
|
reporting.BenchmarkReportWriteError,
|
|
reporting.BenchmarkReportWriteError,
|
|
|
match="failed to write benchmark reports",
|
|
match="failed to write benchmark reports",
|
|
|
):
|
|
):
|
|
|
reporting.write_benchmark_report(
|
|
reporting.write_benchmark_report(
|
|
|
- _marker_report("link-exception"),
|
|
|
|
|
|
|
+ _marker_report("bundle-fsync-failure"),
|
|
|
tmp_path,
|
|
tmp_path,
|
|
|
- timestamp=timestamp,
|
|
|
|
|
|
|
+ timestamp="20260714T011227Z",
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
- assert base_json.read_text() == "external json"
|
|
|
|
|
- assert base_markdown.read_text() == "external markdown"
|
|
|
|
|
- _assert_no_publication_artifacts(tmp_path)
|
|
|
|
|
|
|
+ assert list(tmp_path.iterdir()) == []
|
|
|
|
|
|
|
|
|
|
|
|
|
-def test_link_interrupt_cleans_owned_links_and_temps_before_propagating(
|
|
|
|
|
|
|
+def test_symlink_failure_removes_unpublished_bundle_without_public_rollback(
|
|
|
tmp_path: Path,
|
|
tmp_path: Path,
|
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
|
):
|
|
):
|
|
|
reporting = _reporting_module()
|
|
reporting = _reporting_module()
|
|
|
- real_link = reporting.os.link
|
|
|
|
|
- link_calls = 0
|
|
|
|
|
|
|
|
|
|
- def interrupt_markdown_link(source: Path, destination: Path) -> None:
|
|
|
|
|
- nonlocal link_calls
|
|
|
|
|
- link_calls += 1
|
|
|
|
|
- if link_calls == 2:
|
|
|
|
|
- raise KeyboardInterrupt
|
|
|
|
|
- real_link(source, destination)
|
|
|
|
|
|
|
+ def fail_symlink(source: str, destination: Path) -> None:
|
|
|
|
|
+ del source, destination
|
|
|
|
|
+ raise OSError("simulated symlink failure")
|
|
|
|
|
|
|
|
- monkeypatch.setattr(reporting.os, "link", interrupt_markdown_link)
|
|
|
|
|
|
|
+ monkeypatch.setattr(reporting.os, "symlink", fail_symlink)
|
|
|
|
|
|
|
|
- with pytest.raises(KeyboardInterrupt):
|
|
|
|
|
|
|
+ with pytest.raises(
|
|
|
|
|
+ reporting.BenchmarkReportWriteError,
|
|
|
|
|
+ match="failed to write benchmark reports",
|
|
|
|
|
+ ):
|
|
|
reporting.write_benchmark_report(
|
|
reporting.write_benchmark_report(
|
|
|
- _marker_report("link-interrupt"),
|
|
|
|
|
|
|
+ _marker_report("symlink-failure"),
|
|
|
tmp_path,
|
|
tmp_path,
|
|
|
timestamp="20260714T011228Z",
|
|
timestamp="20260714T011228Z",
|
|
|
)
|
|
)
|
|
@@ -827,73 +861,73 @@ def test_link_interrupt_cleans_owned_links_and_temps_before_propagating(
|
|
|
assert list(tmp_path.iterdir()) == []
|
|
assert list(tmp_path.iterdir()) == []
|
|
|
|
|
|
|
|
|
|
|
|
|
-def test_temp_fsync_failure_removes_owned_temps(
|
|
|
|
|
|
|
+def test_symlink_interrupt_removes_unpublished_bundle_before_propagating(
|
|
|
tmp_path: Path,
|
|
tmp_path: Path,
|
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
|
):
|
|
):
|
|
|
reporting = _reporting_module()
|
|
reporting = _reporting_module()
|
|
|
|
|
|
|
|
- def failing_fsync(file_descriptor: int) -> None:
|
|
|
|
|
- del file_descriptor
|
|
|
|
|
- raise OSError("simulated temp fsync failure")
|
|
|
|
|
|
|
+ def interrupt_symlink(source: str, destination: Path) -> None:
|
|
|
|
|
+ del source, destination
|
|
|
|
|
+ raise KeyboardInterrupt
|
|
|
|
|
|
|
|
- monkeypatch.setattr(reporting.os, "fsync", failing_fsync)
|
|
|
|
|
|
|
+ monkeypatch.setattr(reporting.os, "symlink", interrupt_symlink)
|
|
|
|
|
|
|
|
- with pytest.raises(
|
|
|
|
|
- reporting.BenchmarkReportWriteError,
|
|
|
|
|
- match="failed to write benchmark reports",
|
|
|
|
|
- ):
|
|
|
|
|
|
|
+ with pytest.raises(KeyboardInterrupt):
|
|
|
reporting.write_benchmark_report(
|
|
reporting.write_benchmark_report(
|
|
|
- _marker_report("temp-fsync-failure"),
|
|
|
|
|
|
|
+ _marker_report("symlink-interrupt"),
|
|
|
tmp_path,
|
|
tmp_path,
|
|
|
- timestamp="20260714T012334Z",
|
|
|
|
|
|
|
+ timestamp="20260714T011229Z",
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
assert list(tmp_path.iterdir()) == []
|
|
assert list(tmp_path.iterdir()) == []
|
|
|
|
|
|
|
|
|
|
|
|
|
-def test_both_temps_are_complete_and_fsynced_before_first_link(
|
|
|
|
|
|
|
+def test_post_publication_directory_fsync_failure_keeps_public_bundle(
|
|
|
tmp_path: Path,
|
|
tmp_path: Path,
|
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
|
):
|
|
):
|
|
|
reporting = _reporting_module()
|
|
reporting = _reporting_module()
|
|
|
- timestamp = "20260714T040506Z"
|
|
|
|
|
- real_fsync = reporting.os.fsync
|
|
|
|
|
- real_link = reporting.os.link
|
|
|
|
|
|
|
+ original_fsync_directory = reporting._fsync_directory
|
|
|
fsync_calls = 0
|
|
fsync_calls = 0
|
|
|
- link_calls = 0
|
|
|
|
|
|
|
|
|
|
- def record_fsync(file_descriptor: int) -> None:
|
|
|
|
|
|
|
+ def fail_output_directory_fsync(path: Path) -> None:
|
|
|
nonlocal fsync_calls
|
|
nonlocal fsync_calls
|
|
|
fsync_calls += 1
|
|
fsync_calls += 1
|
|
|
- real_fsync(file_descriptor)
|
|
|
|
|
|
|
+ if fsync_calls == 2:
|
|
|
|
|
+ raise OSError("simulated output directory fsync failure")
|
|
|
|
|
+ original_fsync_directory(path)
|
|
|
|
|
|
|
|
- def inspect_before_link(source: Path, destination: Path) -> None:
|
|
|
|
|
- nonlocal link_calls
|
|
|
|
|
- link_calls += 1
|
|
|
|
|
- temp_paths = sorted(tmp_path.glob("*.tmp"))
|
|
|
|
|
- assert len(temp_paths) == 2
|
|
|
|
|
- assert all(path.stat().st_size > 0 for path in temp_paths)
|
|
|
|
|
- assert fsync_calls == 2
|
|
|
|
|
- real_link(source, destination)
|
|
|
|
|
|
|
+ monkeypatch.setattr(
|
|
|
|
|
+ reporting,
|
|
|
|
|
+ "_fsync_directory",
|
|
|
|
|
+ fail_output_directory_fsync,
|
|
|
|
|
+ )
|
|
|
|
|
|
|
|
- monkeypatch.setattr(reporting.os, "fsync", record_fsync)
|
|
|
|
|
- monkeypatch.setattr(reporting.os, "link", inspect_before_link)
|
|
|
|
|
|
|
+ with pytest.raises(
|
|
|
|
|
+ reporting.BenchmarkReportWriteError,
|
|
|
|
|
+ match="failed to write benchmark reports",
|
|
|
|
|
+ ):
|
|
|
|
|
+ reporting.write_benchmark_report(
|
|
|
|
|
+ _marker_report("published-before-fsync-failure"),
|
|
|
|
|
+ tmp_path,
|
|
|
|
|
+ timestamp="20260714T011230Z",
|
|
|
|
|
+ )
|
|
|
|
|
|
|
|
- json_path, markdown_path = reporting.write_benchmark_report(
|
|
|
|
|
- _marker_report("complete-temps"),
|
|
|
|
|
- tmp_path,
|
|
|
|
|
- timestamp=timestamp,
|
|
|
|
|
|
|
+ published_dir = tmp_path / "20260714T011230Z"
|
|
|
|
|
+ assert published_dir.is_symlink()
|
|
|
|
|
+ assert (
|
|
|
|
|
+ json.loads(published_dir.joinpath("report.json").read_text())["target"]["model"]
|
|
|
|
|
+ == "published-before-fsync-failure"
|
|
|
|
|
+ )
|
|
|
|
|
+ assert "- Model: `published-before-fsync-failure`" in (
|
|
|
|
|
+ published_dir.joinpath("report.md").read_text()
|
|
|
)
|
|
)
|
|
|
-
|
|
|
|
|
- assert link_calls == 2
|
|
|
|
|
- assert json.loads(json_path.read_text())["target"]["model"] == "complete-temps"
|
|
|
|
|
- assert "- Model: `complete-temps`" in markdown_path.read_text()
|
|
|
|
|
_assert_no_publication_artifacts(tmp_path)
|
|
_assert_no_publication_artifacts(tmp_path)
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("stress_round", range(3))
|
|
@pytest.mark.parametrize("stress_round", range(3))
|
|
|
-def test_concurrent_thread_writers_reserve_distinct_matching_pairs(
|
|
|
|
|
|
|
+def test_concurrent_thread_writers_publish_distinct_matching_bundles(
|
|
|
tmp_path: Path,
|
|
tmp_path: Path,
|
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
|
stress_round: int,
|
|
stress_round: int,
|
|
@@ -904,16 +938,15 @@ def test_concurrent_thread_writers_reserve_distinct_matching_pairs(
|
|
|
f"thread-{stress_round}-writer-{index}"
|
|
f"thread-{stress_round}-writer-{index}"
|
|
|
for index in range(writer_count)
|
|
for index in range(writer_count)
|
|
|
]
|
|
]
|
|
|
- first_temp_barrier = threading.Barrier(writer_count, timeout=15)
|
|
|
|
|
|
|
+ first_file_barrier = threading.Barrier(writer_count, timeout=15)
|
|
|
original_write_text = reporting._write_text
|
|
original_write_text = reporting._write_text
|
|
|
thread_state = threading.local()
|
|
thread_state = threading.local()
|
|
|
|
|
|
|
|
- def synchronized_write(path: Path, content: str) -> tuple[int, int]:
|
|
|
|
|
- identity = original_write_text(path, content)
|
|
|
|
|
|
|
+ def synchronized_write(path: Path, content: str) -> None:
|
|
|
|
|
+ original_write_text(path, content)
|
|
|
if not getattr(thread_state, "first_write_done", False):
|
|
if not getattr(thread_state, "first_write_done", False):
|
|
|
thread_state.first_write_done = True
|
|
thread_state.first_write_done = True
|
|
|
- first_temp_barrier.wait()
|
|
|
|
|
- return identity
|
|
|
|
|
|
|
+ first_file_barrier.wait()
|
|
|
|
|
|
|
|
monkeypatch.setattr(reporting, "_write_text", synchronized_write)
|
|
monkeypatch.setattr(reporting, "_write_text", synchronized_write)
|
|
|
|
|
|
|
@@ -929,12 +962,16 @@ def test_concurrent_thread_writers_reserve_distinct_matching_pairs(
|
|
|
)
|
|
)
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
- assert len({json_path.stem for json_path, _ in results}) == writer_count
|
|
|
|
|
|
|
+ assert len({json_path.parent for json_path, _ in results}) == writer_count
|
|
|
|
|
+ assert all(
|
|
|
|
|
+ json_path.parent == markdown_path.parent
|
|
|
|
|
+ for json_path, markdown_path in results
|
|
|
|
|
+ )
|
|
|
_assert_concurrent_report_pairs(tmp_path, markers)
|
|
_assert_concurrent_report_pairs(tmp_path, markers)
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("stress_round", range(2))
|
|
@pytest.mark.parametrize("stress_round", range(2))
|
|
|
-def test_concurrent_process_writers_reserve_distinct_matching_pairs(
|
|
|
|
|
|
|
+def test_concurrent_process_writers_publish_distinct_matching_bundles(
|
|
|
tmp_path: Path,
|
|
tmp_path: Path,
|
|
|
stress_round: int,
|
|
stress_round: int,
|
|
|
):
|
|
):
|
|
@@ -944,7 +981,7 @@ def test_concurrent_process_writers_reserve_distinct_matching_pairs(
|
|
|
f"process-{stress_round}-writer-{index}"
|
|
f"process-{stress_round}-writer-{index}"
|
|
|
for index in range(writer_count)
|
|
for index in range(writer_count)
|
|
|
]
|
|
]
|
|
|
- first_temp_barrier = context.Barrier(writer_count, timeout=20)
|
|
|
|
|
|
|
+ first_file_barrier = context.Barrier(writer_count, timeout=20)
|
|
|
result_queue = context.Queue()
|
|
result_queue = context.Queue()
|
|
|
processes = [
|
|
processes = [
|
|
|
context.Process(
|
|
context.Process(
|
|
@@ -953,7 +990,7 @@ def test_concurrent_process_writers_reserve_distinct_matching_pairs(
|
|
|
str(tmp_path),
|
|
str(tmp_path),
|
|
|
marker,
|
|
marker,
|
|
|
f"20260714T03040{stress_round}Z",
|
|
f"20260714T03040{stress_round}Z",
|
|
|
- first_temp_barrier,
|
|
|
|
|
|
|
+ first_file_barrier,
|
|
|
result_queue,
|
|
result_queue,
|
|
|
),
|
|
),
|
|
|
)
|
|
)
|
|
@@ -968,5 +1005,6 @@ def test_concurrent_process_writers_reserve_distinct_matching_pairs(
|
|
|
|
|
|
|
|
assert all(process.exitcode == 0 for process in processes)
|
|
assert all(process.exitcode == 0 for process in processes)
|
|
|
assert all(result[0] == "ok" for result in results), results
|
|
assert all(result[0] == "ok" for result in results), results
|
|
|
- assert len({result[2][:-5] for result in results}) == writer_count
|
|
|
|
|
|
|
+ assert all(result[2] == result[3] for result in results)
|
|
|
|
|
+ assert len({result[2] for result in results}) == writer_count
|
|
|
_assert_concurrent_report_pairs(tmp_path, markers)
|
|
_assert_concurrent_report_pairs(tmp_path, markers)
|