Files
workinf_Blender_Wasm/my-tools/test_wbv2_checkpoints.py
mes123456 f3e21c6b98
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled
chore: checkpoint web Blender migration source, tools and docs
Accumulated uncommitted work: GHOST Web backend, WebGL/GPU web layer,
web workflow engine and protocol, tooling, docs and task records.
Large generated evidence, user-testing archives, build snapshots and
imported test assets remain untracked.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-11-10 11:18:01 +08:00

130 lines
7.1 KiB
Python

"""Checkpoint integration tests use synthetic Main fixtures only."""
import contextlib
import io
import json
import unittest
from unittest.mock import patch
import test_wbv2_local as local_fixtures
from wbv2_autopilot import write_json
class CheckpointTests(unittest.TestCase):
setUp = local_fixtures.LocalWorkflowTests.setUp
file = local_fixtures.LocalWorkflowTests.file
ready_result = local_fixtures.LocalWorkflowTests.ready_result
local_contract = local_fixtures.LocalWorkflowTests.local_contract
def prepare(self):
contract = self.local_contract()
self.file("build.py", "from pathlib import Path\np=Path('build-count.txt')\np.write_text(str(int(p.read_text())+1) if p.exists() else '1')\n")
native = contract["result"]["checks"][0]
native["kinds"].remove("source_build")
native["cacheInputs"] = ["source.cpp", "check.py", self.native_source]
contract["result"]["checks"].insert(0, {"id": "build", "argv": ["python3", "build.py"], "cwd": ".", "kinds": ["source_build"]})
contract["result"]["dependencies"].append("build.py")
write_json(self.root / "local-contract.json", contract)
return contract
def attempt(self):
with contextlib.redirect_stdout(io.StringIO()):
return self.runner.attempt(self.context, self.job)
def test_native_build_stays_incremental_and_runtime_success_is_reused(self):
self.prepare()
self.assertTrue(self.attempt())
first = self.runner.state["jobs"][self.job["id"]]["runDirectory"]
self.assertTrue(self.attempt())
self.assertEqual("2", (self.root / "build-count.txt").read_text())
self.assertEqual("1", (self.root / "executions.txt").read_text())
record = self.runner.state["jobs"][self.job["id"]]
self.assertTrue(self.runner.receipt_valid(record))
receipt = json.loads((self.root / record["receipt"]).read_text())
cached = next(check for check in receipt["checks"] if check["id"] == "native")
self.assertIn("reusedFrom", cached)
self.assertTrue(all(case["receipt"].startswith(first) for case in cached["caseBindings"]))
def test_later_failure_preserves_success_when_only_failed_step_is_repaired(self):
contract = self.prepare()
self.file("failure.py", "raise SystemExit(1)\n")
contract["result"]["dependencies"].append("failure.py")
contract["result"]["checks"].append({"id": "failure", "argv": ["python3", "failure.py"], "cwd": ".", "kinds": ["negative"]})
write_json(self.root / "local-contract.json", contract)
self.assertFalse(self.attempt())
record = self.runner.state["jobs"][self.job["id"]]
checkpoint = json.loads((self.root / record["runDirectory"] / "step-checkpoint.json").read_text())
self.assertEqual("failure", checkpoint["failures"][0]["checkId"])
self.assertEqual("passed", checkpoint["outcomes"]["native"])
self.file("failure.py", "raise SystemExit(0)\n")
self.assertTrue(self.attempt())
self.assertEqual("1", (self.root / "executions.txt").read_text())
def test_artifact_change_invalidates_cached_runtime(self):
self.prepare()
self.assertTrue(self.attempt())
self.file("engine.wasm", "changed synthetic artifact\n")
self.assertTrue(self.attempt())
self.assertEqual("2", (self.root / "executions.txt").read_text())
def test_failure_continues_independent_stage_and_blocks_dependent_stage(self):
contract = self.prepare()
self.file("failure.py", "raise SystemExit(1)\n")
self.file("independent.py", "from pathlib import Path\nPath('independent-ran').touch()\n")
contract["result"]["dependencies"] += ["failure.py", "independent.py"]
contract["result"]["checks"] += [
{"id": "failure", "argv": ["python3", "failure.py"], "cwd": ".", "kinds": ["negative"], "continueOnFailure": True},
{"id": "dependent", "argv": ["python3", "failure.py"], "cwd": ".", "kinds": ["negative"], "dependsOn": ["failure"]},
{"id": "independent", "argv": ["python3", "independent.py"], "cwd": ".", "kinds": ["positive"]},
]
write_json(self.root / "local-contract.json", contract)
self.assertFalse(self.attempt())
self.assertTrue((self.root / "independent-ran").exists())
record = self.runner.state["jobs"][self.job["id"]]
checkpoint = json.loads((self.root / record["runDirectory"] / "step-checkpoint.json").read_text())
self.assertEqual("blocked", checkpoint["outcomes"]["dependent"])
self.assertEqual("passed", checkpoint["outcomes"]["independent"])
self.assertNotIn("receipt", record)
def test_per_stage_timeout_overrides_global_timeout(self):
contract = self.prepare()
contract["result"]["checks"][1]["timeoutSeconds"] = 5
write_json(self.root / "local-contract.json", contract)
with patch.object(self.runner, "run_process", wraps=self.runner.run_process) as run:
self.assertTrue(self.attempt())
self.assertEqual(5, run.call_args_list[1].args[3])
def test_verification_requests_are_parent_bound_and_do_not_advance_queue(self):
self.prepare()
request = self.runner.directory / "requests" / "pending" / "valid.json"
write_json(request, {"task": self.task, "parentSha256": self.context["parentSha256"], "job": self.job["id"]})
self.assertEqual(self.job, self.runner.requested_job(self.context, [self.job]))
self.assertEqual(request, self.runner.pending_request)
self.assertTrue(request.is_file())
request.unlink()
write_json(request, {"task": self.task, "parentSha256": "0" * 64, "job": self.job["id"]})
with contextlib.redirect_stdout(io.StringIO()):
self.assertIsNone(self.runner.requested_job(self.context, [self.job]))
self.assertFalse(request.exists())
self.assertTrue((self.runner.directory / "requests" / "rejected" / "valid.json").is_file())
def test_process_receipt_includes_resource_observations(self):
self.prepare()
self.assertTrue(self.attempt())
record = self.runner.state["jobs"][self.job["id"]]
receipt = json.loads((self.root / record["receipt"]).read_text())
usage = receipt["checks"][0]["resourceUsage"]
self.assertGreaterEqual(usage["reapedChildCpuSeconds"], 0)
self.assertGreater(usage["samples"], 0)
def test_repeated_cli_start_returns_existing_owner_without_traceback(self):
import run_web_product_migration as cli
self.runner.state.update(state="waiting_local_work", pid=123, identity="fake", executionMode="local_only")
output = io.StringIO()
with patch.object(cli, "main", side_effect=RuntimeError("another Python autopilot owns the project")), patch.object(cli, "Autopilot", return_value=self.runner), patch.object(cli, "alive", return_value=True), patch.object(cli, "identity", return_value="fake"), contextlib.redirect_stdout(output):
self.assertEqual(0, cli.entrypoint())
self.assertEqual("already_running", json.loads(output.getvalue())["state"])
if __name__ == "__main__":
unittest.main()