b98c28dc
Vulkan bounded-waits spec: match inline-test convention
a73x 2026-04-18 11:31
Inline test blocks in src/vk_sync.zig match the existing codebase convention (139 tests across 11 src files). Removes references to a separate tests/vk_sync_test.zig. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
diff --git a/docs/superpowers/specs/2026-04-18-vulkan-bounded-waits-design.md b/docs/superpowers/specs/2026-04-18-vulkan-bounded-waits-design.md index c401a1d..54dafa7 100644 --- a/docs/superpowers/specs/2026-04-18-vulkan-bounded-waits-design.md +++ b/docs/superpowers/specs/2026-04-18-vulkan-bounded-waits-design.md @@ -223,8 +223,7 @@ A shell script `tests/check_unbounded_vk.sh` runs as part of `zig build test`. I **Exempt files:** - `src/vk_sync.zig` — the helper module itself. - `tests/**/*.zig` — test code may legitimately construct fences/swapchains and call raw `vkd` methods (`tests/vk_sync_test.zig` will need to call `vkd.waitForFences` on never-signaled fences to verify timeout behavior). - `src/vk_sync.zig` — the helper module itself (contains inline `test` blocks per codebase convention). The script exits non-zero if any match is found in non-exempt files, printing each match with file:line. No `// vk-unbounded-ok:` comment exemption — every legitimate unbounded wait goes through `waitIdleForShutdown` (already exempt as the canonical path). If a future case truly needs a raw call, it should add a method to `vk_sync.zig`, not bypass the gate. @@ -234,15 +233,15 @@ The choice of shell script over a Zig AST walker is deliberate: four identifier Three layers: **Unit tests (`tests/vk_sync_test.zig`):** **Unit tests — inline `test` blocks in `src/vk_sync.zig`** (following the existing codebase convention; all other Zig modules put `test "..."` blocks inline in the source file rather than in a separate `tests/` directory). - `waitFenceBounded` returns `error.VkWaitTimeout` for a never-signaled fence within `fence_wait_timeout_ns + 50ms` slack. - `waitFenceBounded` succeeds for a fence signaled before the call. - `acquireImageBounded` returns the image_index for a healthy swapchain. - `acquireImageBounded` returns `error.VkAcquireTimeout` when all images are in flight (saturate the swapchain by acquiring without submitting). - `acquireImageBounded` returns `error.OutOfDateKHR` for both `VK_SUBOPTIMAL_KHR` and `VK_ERROR_OUT_OF_DATE_KHR` results. - `logVkTimeout` emits exactly one log line when called 100 times in a tight loop (rate-limit working). - `logVkTimeout` emits a second log line if called again after 5+ seconds. The Vulkan-touching tests require a live `vk.DeviceWrapper`, which isn't available in pure unit tests. To keep the helpers testable without a Vulkan context, the testable surface is the non-Vulkan logic: - `logVkTimeout` emits exactly one log line when called 100 times in a tight loop (rate-limit working) — pure logic, atomics + `std.log.warn`. - `logVkTimeout` emits a second log line if called again after 5+ seconds (can be unit-tested with a mockable clock, or by refactoring the 5s threshold into a parameter for the test and passing a small duration). - Constants (`fence_wait_timeout_ns`, `acquire_timeout_ns`) are public and have the expected values. The Vulkan-touching behavior (`waitFenceBounded` returning `VkWaitTimeout` on a never-signaled fence, `acquireImageBounded` folding `suboptimal_khr` into `OutOfDateKHR`) is verified by inspection of the bindings' documented return values plus integration testing against the live renderer (see below). A full Vulkan-context test harness is out of scope for v1. **Integration test (manual or scripted):** simulate the wedge by submitting a fence that intentionally never signals (e.g., wait on a semaphore that's never signaled), then run a few frames of `drawCells`. Verify: no hang, log lines appear with backoff, main loop continues, Wayland input dispatch keeps working. This is hard to fully automate without a Vulkan mock layer; it can be scripted as a sanity check the human runs by hand. @@ -263,7 +262,7 @@ Suggested commit order: ### Build wiring `src/vk_sync.zig` is imported by both `src/renderer.zig` and `src/main.zig`. It needs to be a separate module in `build.zig`, similar to how `cell_instance` is wired (build.zig:311). Concretely: create `vk_sync_mod` with `addImport("vulkan", vulkan_module)`; have `renderer_mod`, `exe_mod`, `main_test_mod`, `renderer_test_mod`, and `capture_mod` each `addImport("vk_sync", vk_sync_mod)`. Also add a `vk_sync_tests` step pointing at `tests/vk_sync_test.zig` (with `addImport("vulkan", vulkan_module)` and `addImport("vk_sync", vk_sync_mod)`), and add it to the `test` step alongside the other test invocations. `src/vk_sync.zig` is imported by both `src/renderer.zig` and `src/main.zig`. It needs to be a separate module in `build.zig`, similar to how `cell_instance` is wired (build.zig:311). Concretely: create `vk_sync_mod` with `addImport("vulkan", vulkan_module)`; have `renderer_mod`, `exe_mod`, `main_test_mod`, `renderer_test_mod`, and `capture_mod` each `addImport("vk_sync", vk_sync_mod)`. Also create a `vk_sync_test_mod` (same root_source_file, separate module instance for the inline tests) and wire it to the `test` step via `b.addTest({.root_module = vk_sync_test_mod})`, matching the pattern used by `png_tests` (build.zig:308). The grep gate `tests/check_unbounded_vk.sh` is invoked from `build.zig`'s `test` step via `b.addSystemCommand(&.{ "tests/check_unbounded_vk.sh" })` and added as a step dependency.