Repository navigation
Conversation
3388ba7 to
2df8565
Compare
GPU scenarios are about to stop depending on the runner's hardware: they will plant a simulated host instead, so they run on GitHub-hosted runners on every PR. A few premises cannot be simulated, such as a model actually generating tokens on the device, and those need a way to say so. @requires-real-gpu implies @requires-gpu and also skips unless the run sets E2E_HARDWARE=real. The default is `simulated`. Any other value aborts the run, because a misspelt `real` would otherwise skip every real-GPU scenario and still report green. Every self-hosted GPU lane, per-PR and nightly, sets E2E_HARDWARE=real now, and the WSL lanes forward it through WSLENV. Nothing carries the tag yet, so no scenario changes lanes in this commit. Signed-off-by: Roman Inflianskas <Roman.Inflianskas@amd.com>
GPU scenarios could only run where the runner had the right hardware. The CLI reads the real machine, so detection and refusal paths that never touch the device were confined to the GPU lanes, multi-GPU premises to the hosts with several cards, and WSL premises to the one WSL lane. Scenarios now name the machine they need in a Given step, for example "a machine with eight AMD Instinct GPUs", "a bare-metal Linux machine with an AMD GPU" or "a WSL machine with an AMD GPU passed through". The step plants a directory laid out the way the kernel lays out /dev, /sys and /proc, points ROCM_CLI_TEST_HOST_ROOT at it, and isolates the commands from the runner: - Commands get a PATH with the hardware tools removed (uname, lspci, ldconfig, rocminfo, amd-smi, the kernel log, the WSL interop shells and others). The tools the simulated machine has come back as a fake-host-tool stand-in that answers from canned output. - The runner's GPU visibility masks and ROCm variables are unset. - HOME is moved into the scenario. "A managed runtime is active" plants a runtime record whose vllm is a fake-vllm stand-in. A serve on a simulated machine therefore runs the real engine selection, runtime lookup, launch and readiness wait, and only the process at the end is simulated. Nineteen scenarios move onto simulated machines and run on every Linux lane, including the GitHub-hosted one: examine-04/10/11/17, diagnose-08/15/16/17/18, driver-install-01..03 and serve-11/13/15/16/19/20/21. examine-29 repeats examine-04 against the real machine, so a real GPU lane still covers detection there, including the Windows lane. Every scenario left on @requires-gpu needs a physical device or a runtime really installed for one, so those become @requires-real-gpu. Signed-off-by: Roman Inflianskas <Roman.Inflianskas@amd.com>
Scenarios on a simulated machine fail only when the CLI misreads the fixture. If a kernel or driver update moves something the fixture assumes, those scenarios keep passing while the CLI fails on hardware. That is how the KFD target came to be read from a file no kernel has. Two @nightly @requires-real-gpu scenarios now hold the fixture against a real machine. On a bare-metal GPU host they check: - each KFD GPU node states gfx_target_version inside its properties file - the property keys the fixture writes are present - gpu_id files exist - render nodes match drm_render_minor - CPU nodes report a target of 0 - the AMD DRM cards have vendor and device files - `lspci -nn -D` lines have the expected shape On a WSL2 distribution they check /dev/dxg, libdxcore.so and the microsoft-standard-WSL2 kernel string. Each difference is reported along with what to update. Signed-off-by: Roman Inflianskas <Roman.Inflianskas@amd.com>
3c6e9a1 to
124b181
Compare
|
Rebased onto
Local results are in the updated description. The full mock suite ran 237 scenarios. The 3 unexpected failures were all local: a port already held on the shared test machine, and two load flakes that pass on their own. The PR stays in draft until required CI is green. |
tomastola
left a comment
There was a problem hiding this comment.
Review — needs work
Full review of the whole change (base 1c33248c, head 124b1815).
Blocking
- examine-17's Then step can pass without asserting anything —
tests/e2e-cucumber/tests/e2e/examine_steps.rs:1014-1022(assert_gpus_match_kfd_nodes)
The step returns silently whenkfd_gpu_node_versions(...)isNoneor empty, and again whenhost_has_lspciis false. The scenario now plants eight simulated GPUs, so those early returns are exactly what hides a planted tree the CLI no longer reads. If the planting,world.host_pathor the CLI's probe root regress, the scenario stays green. The siblingassert_gpu_target_matches_kfdgot a "nothing may quietly decline" guard in this PR; this step did not.
Confidence 70 · logic · Fix: whenworld.simulated_hostisSome, assert the node count equalshost.gpus.len()and fail instead of returning. Skip thelspcibail-out for simulated hosts.
Non-blocking
fake-host-toolanswers plausibly for argv nobody planted —tests/e2e-cucumber/src/bin/fake-host-tool.rs:43-49
Lookup falls back from the full command line to the bare tool name, souname -norlspciwith different flags return the canned-a/-nn -Dtext with exit 0. If the CLI changes itslspciflags, the stand-in will not disagree, which weakens the drift protection this PR is built around.
Confidence 70 · logic · Fix: fall back to the bare name only for argument-less tools (mpirun,lsmod); otherwise exit 127 with a loud stderr message.- Stale tag name in comments —
.github/workflows/ci.yml:1527says "@requires-gpuscenarios resolve to skip here", andfeatures/dash.feature:329says "@requires-wslrather than@requires-gpu"; the skipping tag is now@requires-real-gpu.
Confidence 85 · mechanical · Fix: rename in both. plant_simulated_hostpanics on a second call into the same root —simulated_host.rs:437(link(), symlink already exists). Loud, but undocumented; two Given steps in one scenario would trigger it.
Confidence 70 · mechanical · Fix: document it, or fail with a message naming the double plant.- Unused
Platform::Wsl2 { rocdxg_ready: false }variant —simulated_host.rs:137, 247-250, 317hasldconfig/rocdxgbranches nothing constructs, so they are untested dead code.
Confidence 70 · mechanical · Fix: drop the variant and branches, or add a scenario that uses it. - README does not tell GPU-box users about
E2E_HARDWARE— the tag table mentions it, but the environment-variable table and run examples do not. A plaincargo xtask e2eon a GPU machine now skips every@requires-real-gpuscenario without saying why where people look.
Confidence 70 · documentation · Fix: add anE2E_HARDWARErow (defaultsimulated) and anE2E_HARDWARE=real cargo xtask e2eexample.
Decisions for the author
- Coverage moves off real hardware, and Windows/macOS lose scenarios — tradeoff
- serve-13 (
serve-no-gpu-fails-fast) was@requires-no-gpu, so it ran on any no-GPU lane including Windows and macOS; it is now@requires-os:linuxon a simulated host. examine-10 went from@requires-bare-metal(skips only on WSL) to@requires-os:linux, so--no-frameworksis no longer checked on Windows or macOS. - serve-13/15/16/19/20/21, examine-10/11/17, diagnose-08/15/16/17/18 and driver-install-01..03 no longer run against a real GPU or WSL host. Only examine-04 has a real twin (examine-29); examine-30/31 check layout assumptions only. Real masked-device
serverefusals and real-WSLdiagnose/driver-plan paths run in no lane. - The simulated run is hermetic and runs on every PR, which is a real gain. Cheap mitigations: a
@requires-real-gpucopy of serve-15 and a@requires-wslcopy of diagnose-17, as examine-29 does for examine-04; keep a non-simulated no-GPU variant for Windows/macOS. Note the description's "each moved scenario still asserts the same behaviour" and "gain coverage" do not mention the Windows/macOS loss.
- serve-13 (
- Fake vLLM ignores everything except
--hostand--port— tradeoff (src/bin/fake-vllm.rs:30-36)
serve-11 proves selection, runtime lookup, launch and readiness, but not that the CLI passes sane vLLM flags;--port=Nwould be ignored and fall back to 8000. Recording argv under the root and asserting the expected flags would close this; acceptable if the flags are covered elsewhere. - serve-11 failed in the test plan on a port conflict (127.0.0.1:11435) — non-blocking-improvement
A fixed port in a hermetic harness invites this on shared runners; an ephemeral port would remove the flake.
nowycondro
left a comment
There was a problem hiding this comment.
🤖 Automated review (Claude Code) · 124b181
I read the full diff for this PR (simulated-host infrastructure, expectation/tag plumbing, CI workflow changes, and the migrated e2e feature/step files), cloned the repo at the PR head commit, and cross-checked the two concerns from @tomastola's existing CHANGES_REQUESTED review against the current code. No Rust toolchain is available in this environment, so nothing was compiled or executed — everything below is from static reading of the diff and surrounding code.
Confirmed still open (both from the existing review)
-
examine-17's assertion can still pass without checking anything —
tests/e2e-cucumber/tests/e2e/examine_steps.rs:1012-1016(assert_gpus_match_kfd_nodes). The function returns silently ifkfd_gpu_node_versions(...)isNone/empty, before ever comparing against the planted host. Compare this to its siblingassert_gpu_target_matches_kfd(same file, lines 945-956), which was given an explicitif let Some(expected) = world.simulated_host....gfx_target() { assert_eq!(...) }guard so "nothing below may quietly decline to check it."assert_gpus_match_kfd_nodeshas no equivalent direct comparison againstworld.simulated_host.as_ref().map(|s| s.host.gpus.len()). The feature comment atfeatures/examine.feature:226-227("the step's premises always hold") documents the intent but the code doesn't enforce it — a regression in planting,world.host_path, or the CLI's probe root would make this scenario pass vacuously instead of failing. Confidence: high (70-80%), this is a real gap, not a false positive. -
Coverage-loss tradeoff is real and still present — verified via
git grepat head:features/model_serving.feature:135(serve-13) now carries@requires-os:linuxonly, down from@requires-no-gpu(which also ran on Windows/macOS).features/examine.feature:111(examine-10) is similarly now@requires-os:linux, down from@requires-bare-metal(which only skipped WSL). Both comments in the feature files candidly explain the simulated premise, but the net effect for those two scenarios is that Windows/macOS lanes no longer exercise GPU-required-refusal and--no-frameworksbehavior. This is a tradeoff for the author/reviewers to accept or mitigate (e.g. a non-simulated Windows/macOS variant), not something I'd block on myself, but it is accurately described in the prior review and still applies at this head commit.
Additional observation (non-blocking, low confidence)
tests/e2e-cucumber/src/simulated_host.rs(filter_real_path/link_stand_ins, around line 402) rebuilds the filtered-PATH fixture from scratch on every scenario invocation — a freshread_dir+stat+symlink pass over every PATH directory per scenario. With 19+ scenarios now going through this path per run, this adds I/O overhead to the per-PR suite. This is a performance nit, not a correctness issue, and I did not measure actual wall-clock impact.
Not independently re-verified
I did not re-derive every item in the prior review's non-blocking list (fake-host-tool argv fallback, stale @requires-gpu tag mentions in comments, plant_simulated_host panic-on-double-call, unused Wsl2 { rocdxg_ready: false } variant, README gap, fake-vllm flag handling, serve-11 port conflict) — a spot read of the code is consistent with those being accurate, but I did not independently trace each one line-by-line given the medium-effort budget for this pass. I found no additional correctness bugs beyond what's above and what's already flagged.
🤖 Generated with Claude Code
nowycondro
left a comment
There was a problem hiding this comment.
🔴 Automated review · pr-review-watcher · 124b181
Review — needs work
Full review of the whole change, run as five independent file-grouped passes plus consolidation.
Implements ROCMAI-677 ("E2E: describe GPU hosts with simulated machines so GPU-visibility scenarios run on every PR"); the PR description's claims (HardwareMode gating, simulated_host.rs layout, fake-vllm/fake-host-tool stand-ins, nineteen scenarios moved to simulated machines, examine-29/30/31 real-hardware/drift checks) match what the diff contains, verified across passes with no mismatch found.
Blocking
- CI check "E2E tests (MI300X, current)" is failing on the reviewed commit —
.github/workflows/e2e-selfhosted.yml(self-hosted MI300X lane); run: https://github.com/ROCm/rocm-cli/actions/runs/40314879688/job/121545019785
Confirmed viagh pr view 517 --json statusCheckRollup(conclusion: FAILURE) and the rendered Actions page, which shows the "Run E2E tests on GPU hardware" step exiting with code 1; every other check on the PR is green. Full job logs were not retrievable from this environment (the GitHub Actions REST log/run API endpoints returned 403/Access Denied through an egress proxy), so the specific failing scenario or assertion could not be identified. This PR's owne2e-selfhosted.ymlchanges are what first setE2E_HARDWARE: realon this exact lane, so the failure may be the first real exercise of the new@requires-real-gpugating on that hardware rather than an unrelated flake — that is plausible, not established.
Confidence 100 · architectural · Fix: obtain the job's actual log/annotation (needs sign-in or maintainer access), determine whether it's a genuine regression in the new real-GPU gating/hardware-state assumptions or a known self-hosted-runner flake, and resolve or re-run before merge.
Non-blocking
ldconfigcache header reports a fixed count of 5 libraries that doesn't match the number actually written —tests/e2e-cucumber/src/simulated_host.rs:308-320. The cannedldconfig -poutput always prints "5 libs found in cache" but only 4 unconditional entries are written (a 5th,librocdxg.so, is added only forPlatform::Wsl2 { rocdxg_ready: true }), so on bare-metal and WSL2-not-ready machines the printed header disagrees with the printed list below it. No consumer currently parses the header count (rocm-core's checks match by soname substring), so nothing currently breaks, but it's a fidelity gap in a fixture whose whole point is to match realldconfigoutput.
Confidence 80 · mechanical · Fix: compute the header count from the number of lines actually emitted rather than hardcoding it.bare_metal_layout_drift's GPU-topology checks early-return when the simulated side reports zero GPUs —tests/e2e-cucumber/src/simulated_host.rs:520-557(let Some((_, simulated_props)) = simulated_gpus.first() else { return drift; }). Two drift checks (non-zerogfx_target_versionon the CPU KFD node; DRM card vendor/device id shape) never execute when comparing against a no-GPU simulated host. Every current call site only drives this withinstinct_mi300x(1), so it's unreachable today, but it's a latent gap if this drift check is ever reused for a no-GPU scenario.
Confidence 70 · logic · Fix: run those two checks unconditionally, independent of whether the simulated side has a GPU.fake-host-tool.rsredeclaresTOOL_OUTPUT_DIRandTEST_HOST_ROOT_ENVinstead of importing them fromsimulated_host.rs—tests/e2e-cucumber/src/bin/fake-host-tool.rs:22,24vstests/e2e-cucumber/src/simulated_host.rs:38,41. The two copies are held in sync only by a "must match" doc comment with no compiler enforcement; the sibling binaryfake-vllm.rs:17proves a[[bin]]target in this crate canuse e2e_cucumber::...to reach the crate's own lib, so this duplication isn't architecturally forced (unlike the separate, legitimately-forced duplication againstrocm_core::hardware_root::TEST_HOST_ROOT_ENV, whichfake-host-tool.rscannot reach).
Confidence 75 · mechanical · Fix: import the two constants frome2e_cucumber::simulated_hostinstead of redefining them.
Decisions for the author
@id:examine-detects-gpu-and-driver(examine-04) keeps its scenario id while changing what it verifies — tradeoff. Before this PR the id denoted "verified against the real installed GPU" (rare,@requires-gpu-gated, self-hosted-only runs); after, the same id denotes "verified against a simulated Instinct GPU" (frequent,@requires-os:linux, every Linux PR lane), with a new, narrower id (examine-detects-this-machines-gpu-and-driver, examine-29) now covering what the old id used to mean, and a nightly drift check (examine-30/31) mitigating simulated/real divergence. This is well-reasoned and documented in the feature-file comments, but if any external dashboard or flaky-test tracker keys history by scenario id, examine-04's historical baseline will now be read against a very different population of runs. Worth confirming no such tracker exists, or that the discontinuity is acceptable.
Positive signals
bare_metal_layout_drift,wsl_layout_drift, andlspci_drift(simulated_host.rs) are explicit, tested mechanisms for catching simulation/reality divergence — e.g. a regression test pins the exactgfx_target_version-in-propertiesvs standalone-file bug that motivated simulating hardware in the first place — directly addressing the core risk that a simulated-host suite could drift from what real hardware actually does.HardwareMode::parse(expectation.rs) fails closed: an unrecognizedE2E_HARDWAREvalue panics the run rather than silently defaulting tosimulated, so a typo can't quietly skip every real-GPU scenario while CI stays green — and the one call site (tests/e2e.rs) is wired to actually propagate that panic rather than swallow it.assert_vllm_default's new simulated-only readiness assertion (serving_steps.rs) closes a real gap:rocm serveexits 0 regardless of readiness, so without this check a simulated vLLM that never came up would have passed silently.feature_body(expectation.rs) reads the actual.featurefile text to assert a scenario's premise line is really present, so deleting the underlying Gherkin step would fail this test rather than silently certifying stale coverage.
Deployment notes
None — no product code changes; this PR only changes which CI lanes exercise which existing e2e scenarios and adds new test infrastructure.
What this covered
Every file in the diff, read completely at PR head 124b1815e0ef5359513dece166627e3f81f9e04b against base d59f596f6e08297c207f008021eecb37defc3558 (32 files, +1991/-261), split across five independent file-grouped passes: (1) tests/e2e-cucumber/src/simulated_host.rs; (2) tests/e2e-cucumber/src/expectation.rs + mock_server.rs + lib.rs; (3) tests/e2e-cucumber/src/bin/fake-host-tool.rs + fake-vllm.rs + tests/e2e/simulated_host_steps.rs + tests/e2e.rs; (4) the remaining tests/e2e/*_steps.rs files plus all ten changed .feature files; (5) docs/testing.md, docs/ci-hardware-testing.md, the three .github/workflows/*.yml files, tests/e2e-cucumber/Cargo.toml, tests/e2e-cucumber/README.md. Each pass also read relevant surrounding/consumer code outside its primary scope (e.g. crates/rocm-core/src/examine.rs, openmpi.rs, apps/rocm/src/service_launch.rs, engines/vllm/src/runtime.rs) to verify specific cross-file claims. Step 7 (design review) questions were answered from material the five passes already surfaced (scenario-id continuity, simulation fidelity, real-vs-simulated gating design) rather than a separate fan-out round.
CI: gh pr view 517 --json statusCheckRollup read in full; one check (E2E tests, MI300X, current) reports FAILURE, all others SUCCESS/SKIPPED — reflected above as a blocking finding. Job logs were not retrievable (GitHub Actions REST log/run endpoints returned 403 from this environment's egress proxy); only the rendered Actions page's step name and exit code were available.
Did not run: the history pass (git log/git blame on modified regions) and prior-changes pass (review comments on earlier changes to these files) were not run this round — simulated_host.rs, the fake binaries, and the simulated-host step definitions are new files with no history, and the remaining changed files' history was not separately mined given the depth already reached by the code and impact-analysis passes; re-run these specifically if deeper confidence is wanted. No cargo build/cargo test/clippy was executed in this environment; all findings were settled by reading source against source, not execution — the PR's own description reports cargo test, clippy, and a 237-scenario local run as green except three explained, non-blocking flakes (serve-11, comfyui-04, automations-04), consistent with what the diff shows but not independently re-run here.
Ticket ROCMAI-677 retrieved by key (title, status, issue type only — no comments): "E2E: describe GPU hosts with simulated machines so GPU-visibility scenarios run on every PR," status In Progress. Matches the change's own description of itself.
Not reconciled against any PR discussion, review comments, or earlier review rounds — none were read, per method.
Summary
Lets an E2E scenario describe the GPU machine it needs (eight Instinct GPUs, a WSL2 distribution with GPU passthrough, a bare-metal host, a box with no GPU) instead of depending on the runner's hardware. Built on #510's
ROCM_CLI_TEST_HOST_ROOT(now onmain), scenarios that only need the CLI to see a GPU run on every Linux lane, including the GitHub-hostedE2E testslane on every PR. Three commits:E2E_HARDWARE=simulated|realand@requires-real-gpu.@requires-real-gpuimplies@requires-gpuand also skips unless the run setsE2E_HARDWARE=real.simulated. Any other value aborts the run, so a misspeltrealcan't quietly skip the real-GPU scenarios and still go green.real: per-PR MI300X, Strix Ubuntu, Strix Windows, WSL and W7900; nightly also R9700 and MI350P. The WSL lanes forward it throughWSLENV.tests/e2e-cucumber/src/simulated_host.rs)./dev,/sysand/proc: the KFD topology (gfx_target_versioninside each node'sproperties), DRM cards,/dev/kfd//dev/dxg,/proc/version,/etc/os-release.PATHwithout the machine-describing tools (uname,lspci,ldconfig,rocminfo,amd-smi,mpirun, the kernel log, the WSL interop shells). The tools the simulated machine has come back as afake-host-toolstand-in answering from canned output. The runner's GPU scoping (HIP_VISIBLE_DEVICES,ROCR_VISIBLE_DEVICES,ROCM_PATH, ...) is unset andHOMEmoves into the scenario.Given a managed runtime is activeon a simulated machine plants a runtime whosevllmis afake-vllmstand-in, so a serve runs the CLI's real engine selection, runtime lookup, managed launch and readiness wait; only the process at the end is simulated.@requires-os:linux: examine-04/10/11/17, diagnose-08/15/16/17/18, driver-install-01..03, serve-11/13/15/16/19/20/21.@requires-gpuneeds a physical device or a runtime really installed for one, so those become@requires-real-gpu. examine-29 (@id:examine-detects-this-machines-gpu-and-driver) repeats examine-04 against the real machine on every GPU lane, the Windows one included./dev,/sys,/proc/versionandlspciof a real machine, so a kernel or driver change that moves something fails there, naming the difference, instead of the simulated scenarios passing while the CLI fails on hardware.Not simulated: package-manager state, discovered ROCm installs, and the identity of the user running the suite (
$USER,id -Gn,staton the planted device nodes). No simulated scenario asserts on them. Container detection does read under the host root, so a simulated machine is never in a container.Risk: low for the product (no product code changes); medium for the suite, since it changes which lanes run 19 scenarios. Each moved scenario still asserts the same behaviour.
Scenario coverage (AGENTS.md §3)
No CLI behaviour changes. This PR changes which machines existing scenarios run against, and adds examine-29/30/31. The moved scenarios gain coverage: several used to run only on the WSL lane or multi-GPU hosts and now run on every Linux lane. The real-kernel cross-check is kept by examine-29 (every real GPU lane) and examine-30/31 (nightly).
Test plan
On this head, rebased on
mainat 1c33248:cargo xtask e2e, full default suite on a WSL2 machine with no GPU: 237 scenarios ran (72 resolved to skip: real GPU, nightly, lifecycle, WSL, Windows, docker), 2 expected xfails, 3 unexpected failures, each explained:Every scenario this PR touches passed in that run except serve-11 (above). examine-29/30/31 need real hardware, so they skip there.
cargo test -p e2e-cucumber --lib --bins --test feature_naming: 186 + 1 + 4 pass.cargo check -p e2e-cucumber --test e2eis clean.cargo test -p xtask: 317 pass.cargo clippy --workspace --all-targetsand-p e2e-cucumber --test e2ewith-D warnings, on Linux and onx86_64-pc-windows-gnu: clean.cargo fmt --checkis clean.cargo test -p rocm/-p rocm-corewere not run locally: this PR changes no product code. CI runs them.If this PR fixes a bug, searched
tests/e2e-cucumber/expectations.tomlfor the fixed ticket ID and removed/narrowed any now-stale xfail rows. (n/a; none of the moved scenarios has an xfail row)If this PR adds a new subcommand or subsystem, its domain implementation lives in its own file per
docs/architecture.md. (n/a)Every new or changed user-facing message was read against the code path that runs after it. (n/a, no user-facing message changes)