Skip to content

test(#883): align GPU capability infra with the CTSolvers contract - #893

Merged
ocots merged 1 commit into
mainfrom
test/gpu-capability-contract
Aug 28, 2026
Merged

ocots merged 1 commit into
mainfrom
test/gpu-capability-contract

Conversation

@ocots

@ocots ocots commented Aug 28, 2026

Copy link
Copy Markdown
Member

Closes #883.

Problem

test/helpers/capabilities.jl implemented the Handbook's capability-gated-test policy, but partially:

  • Asymmetric loud guard. On a kkt runner whose driver broke, test/suite/flows/test_gpu_routing.jl failed loudly (if on_gpu_runner(); @test is_cuda_on()), but test/suite/builders/test_options_forwarding.jl — which imported only is_cuda_on — reported Broken, indistinguishable from a laptop.
  • on_gpu_runner() matched RUNNER_NAME exactly (in ("kkt", "occidata")). The self-hosted runners register as kkt-runner / occidata-runner, so the check was dead on CI — the identical bug CTSolvers fixed in CTSolvers#223.
  • test/suite/solve/test_canonical.jl guarded a whole GPU block behind a bare if is_cuda_on() with only an @info on the else — a silent skip.
  • A duplicate is_cuda_on() still sat in test/runtests.jl, and there was no central enforcement equivalent to CTSolvers' test_environment_contract.jl.

Change — full alignment with the CTSolvers pattern (#190 + #223)

File Change
test/runtests.jl TestCapabilities is now a Main-bound module of constants: CUDA_FUNCTIONAL, ON_GPU_RUNNER (matches the kkt/occidata substring of RUNNER_NAME), GPU_EXTENSION_ARMED. Duplicate is_cuda_on() removed.
test/helpers/capabilities.jl deleted — the functions become the constants above
test/suite/solve/test_canonical.jl, test/suite/builders/test_options_forwarding.jl, test/suite/flows/test_gpu_routing.jl read Main.TestCapabilities.*; every device guard keeps a Test.@test_skip else; the per-site if on_gpu_runner() block is removed (now central)
test/suite/environment/test_environment_contract.jl new — asserts the extension is armed on every runner, requires the device on a GPU runner (if ON_GPU_RUNNER; @test CUDA_FUNCTIONAL), and greps the suite for the isdefined-against-Main and unbraced-device-guard anti-patterns
CHANGELOG.md bullet under [2.2.0-beta] → Testing

No package behaviour change — test infrastructure only.

Verification

  • suite/environment + suite/flows/test_gpu_routing + suite/builders/test_options_forwarding: 44 pass, 2 broken (the deliberate @test_skip device sites).
  • RUNNER_NAME=kkt-runner julia --project -e 'using Pkg; Pkg.test(; test_args=["test/suite/environment"])' → the contract test fails loudly on @test CUDA_FUNCTIONAL (no device on this machine), proving the loud path fires. Unset → green.
  • Full suite running; result will be posted here.

Out of scope

  • Adding an occidata job to .github/workflows/CI.yml — kkt is still this repo's only GPU job; ON_GPU_RUNNER matching both is future-proofing.
  • Restructuring test_canonical.jl's GPU loop into per-leaf @test_skip.

🤖 Generated with Claude Code

A lost GPU device on the `kkt` runner failed loudly in test_gpu_routing.jl
but skipped silently in test_options_forwarding.jl, which never imported the
`on_gpu_runner()` guard. And `on_gpu_runner()` matched `RUNNER_NAME` exactly
("kkt"), dead against the runner's registered name `kkt-runner` — the same
bug CTSolvers fixed in #223.

Full alignment with the CTSolvers pattern (#190 + #223):

- `TestCapabilities` moves into test/runtests.jl as a Main-bound module of
  constants (`CUDA_FUNCTIONAL`, `ON_GPU_RUNNER`, `GPU_EXTENSION_ARMED`).
  `ON_GPU_RUNNER` now matches the `kkt`/`occidata` substring of RUNNER_NAME.
  test/helpers/capabilities.jl (functions) is deleted.
- The three consumers (test_canonical, test_options_forwarding,
  test_gpu_routing) read `Main.TestCapabilities.*`; every device guard has a
  `Test.@test_skip` else branch.
- New test/suite/environment/test_environment_contract.jl centralises the
  loud device requirement (`if ON_GPU_RUNNER; @test CUDA_FUNCTIONAL`) and
  greps the suite for the isdefined-against-Main and unbraced-device-guard
  anti-patterns.

Verified: suite/environment + suite/flows/test_gpu_routing +
suite/builders/test_options_forwarding green (44 pass, 2 broken); with
RUNNER_NAME=kkt-runner the contract test fails loudly on the absent device,
as designed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@ocots ocots added run ci github-runner Trigger the CI workflow on GitHub-hosted runners run ci occidata-runner Trigger the CI workflow on the self-hosted occidata runner (GPU/CUDA) labels Aug 28, 2026
@ocots

ocots commented Aug 28, 2026

Copy link
Copy Markdown
Member Author

Full suite green on this branch: 2246 pass, 2 broken, 0 fail (14m53s).

  • suite/environment/test_environment_contract.jl — 3 pass (extension armed, both anti-pattern greps isempty).
  • The 2 Broken are the deliberate @test_skip device sites in test_gpu_routing.jl and test_options_forwarding.jl.
  • +3 vs main (2243) = the new contract test's assertions.

Loud-path check: RUNNER_NAME=kkt-runner julia --project -e 'using Pkg; Pkg.test(; test_args=["test/suite/environment"])' → 3 passed, 1 failed (the @test CUDA_FUNCTIONAL fails on this device-less machine, as designed). Unset → green.

@ocots
ocots merged commit 0eef3a4 into main Aug 28, 2026
18 of 19 checks passed
@ocots
ocots deleted the test/gpu-capability-contract branch August 28, 2026 13:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

run ci github-runner Trigger the CI workflow on GitHub-hosted runners run ci occidata-runner Trigger the CI workflow on the self-hosted occidata runner (GPU/CUDA)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

GPU device loss fails loudly in one test file and silently in the other

1 participant