import contextlib import io import threading import time import unittest from wbv2_runtime_contract import bind_runtime_publication import test_wbv2_task_dispatch as fixtures class RuntimeContractTests(unittest.TestCase): def test_ui_verification_tracks_shared_viewport_and_binding_code(self): result = bind_runtime_publication({ "dependencies": ["web/app/src/app/App.tsx"], "checks": [{"id": "browser", "argv": ["node", "test.mjs"], "cwd": ".", "kinds": ["chromium"]}], }) self.assertIn("web/app/src/three-adapter/viewport.ts", result["dependencies"]) self.assertIn("my-tools/wbv2_runtime_contract.py", result["dependencies"]) def test_backend_browser_verification_keeps_its_existing_scope(self): result = bind_runtime_publication({ "dependencies": ["web/protocol/web-engine.ts"], "checks": [{"id": "browser", "argv": ["node", "test.mjs"], "cwd": ".", "kinds": ["chromium"]}], }) self.assertNotIn("web/app/src/three-adapter/viewport.ts", result["dependencies"]) def test_new_work_is_selected_before_stale_verified_work(self): from wbv2_local_planner import LocalPlanner fixture = fixtures.AutoDispatchTests() fixture.setUp() try: jobs = fixture.jobs(2) fixture.runner.state["jobs"][jobs[0]["id"]] = {"state": "verified", "receipt": "missing.json", "receiptSha256": "0" * 64} LocalPlanner(fixture.root, fixture.runner.directory).refresh(fixture.context, jobs, set()) self.assertEqual(jobs[1]["id"], fixture.runner.choose_job(jobs)["id"]) finally: fixture.doCleanups() def test_known_webengine_build_includes_runtime_publication(self): result = {"checks": [{"id": "build", "kinds": ["source_build"], "argv": ["cmake", "--build", "build_web_blender6", "--target", "web_engine"], "cwd": "."}], "dependencies": [], "artifacts": []} bind_runtime_publication(result) self.assertEqual(["python3", "my-tools/build_local_webengine.py"], result["checks"][0]["argv"]) self.assertIn("tools/web/sync-web-engine-runtime.mjs", result["dependencies"]) self.assertIn("web/app/public/wasm-module-manifest.json", result["artifacts"]) def test_other_build_targets_keep_their_contract(self): argv = ["cmake", "--build", "build_blender_native", "--target", "blender"] result = {"checks": [{"id": "build", "kinds": ["source_build"], "argv": argv, "cwd": "."}], "dependencies": [], "artifacts": []} bind_runtime_publication(result) self.assertEqual(argv, result["checks"][0]["argv"]) self.assertEqual([], result["dependencies"]) def test_unchanged_wait_does_not_return_to_preflight_on_poll_deadline(self): fixture = fixtures.AutoDispatchTests() fixture.setUp() try: jobs = fixture.jobs(1) fixture.policy["localPollSeconds"] = 0.01 stop = threading.Timer(0.4, lambda: setattr(fixture.runner, "stop_requested", True)) stop.start() try: start = time.monotonic() with contextlib.redirect_stdout(io.StringIO()): fixture.runner.wait_for_local_work(fixture.context, jobs) self.assertGreater(time.monotonic() - start, 0.35) self.assertTrue(fixture.runner.stop_requested) finally: stop.join() finally: fixture.doCleanups()