test(e2e): drive the Lemonade recovery scenario with a planted runtime - #351
Draft
rominf wants to merge 3 commits into
Draft
test(e2e): drive the Lemonade recovery scenario with a planted runtime#351rominf wants to merge 3 commits into
rominf wants to merge 3 commits into
Conversation
The Windows lane compiled the whole release graph twice, adding 3-4 minutes to the one job that alone determines when CI goes green. The Build step builds rocm and rocmd plainly, but the lifecycle E2E step then runs `cargo xtask e2e`, which builds them for itself with `--features rocm/e2e-test-hooks`. That is a different feature resolution, so cargo recompiles rocm, rocmd, rocm-core, reqwest, rustls, ring and the rest rather than reusing any of it. The cache is not at fault: it restores with a full match on every run. Export ROCM_CLI_BINARY / ROCM_CLI_ROCMD_BINARY so xtask skips its build and uses what is already there. Measured on four consecutive runs, the second release build took 2m58 to 4m22. The binaries stay hook-free, unlike the prebuilt lanes in e2e-selfhosted.yml and nightly.yml. Those run the full suite, whose scripted failure seams are compiled out without the feature. E2E_ONLY_LIFECYCLE restricts this lane to @lifecycle scenarios, none of which use a seam, and the lane packages and installs through the real installer -- so it should ship exactly what a release ships. A contract test pins the invariant. The existing test enforcing the opposite rule covers only e2e-selfhosted.yml and nightly.yml, which is why this drift went unnoticed in ci.yml. Signed-off-by: Roman Inflianskas <Roman.Inflianskas@amd.com>
…_DIR Review follow-up on the prebuilt-binary reuse change. The exported binary paths hard-coded `$PWD\target\`, which a future job-level CARGO_TARGET_DIR would silently invalidate — the guard would then fail on a path nothing builds to. Derive $targetDir the same way the PowerShell prebuilt lanes in nightly.yml and e2e-selfhosted.yml already do, which retires the caveat comment that documented the limitation. `xtask e2e` resolves a relative override against the invocation dir (resolve_caller_path), so dropping `$PWD\` changes nothing at runtime. Also: - Test-Path -LiteralPath, so a target dir containing PowerShell wildcard metacharacters cannot make the guard match the wrong path. - Pin the full release-path assignment in the contract test. Asserting only that the variable names appear would pass for `target\debug\`, defeating the reuse the test is named for — the same pin assert_prebuilt_e2e_lanes_export_rocmd applies to the other lanes. - Give the contract test a descriptive fallback instead of an empty string, so a lane that stops invoking `cargo xtask e2e` fails with a message that says so rather than an assertion against "". Signed-off-by: Roman Inflianskas <Roman.Inflianskas@amd.com>
Delete the `e2e-test-hooks` cargo feature and both scripted-failure seams it gated. They existed to serve one scenario, `@id:serve-lemonade-preparation-recovery`, and cost the repo a CI-wide asymmetry: every lane running the full suite pre-built `rocm` with the feature, so the binary under test was not the binary released. The Linux `ci.yml` lifecycle lane packaged and installed a hook-carrying build through the real installer, and the contract tests encoded the split rather than removing it — one lane asserted hook-free, two asserted hook-ful, and Linux was covered by neither. The scenario needs Lemonade's llama.cpp backend install to fail twice so it can pin the retry count and the terminal recovery guidance. It now gets that from a planted runtime instead of a compiled-in branch: a fixture binary copied to `lemonade`/`lemond` in an isolated runtime dir, plus a hand-written install manifest. `resolve_runtime` accepts it because it checks only that the manifest parses and that `lemond` is a file — no checksum, version or signature is involved on this path, so nothing here weakens a verification the product performs. The CLI then fails where a genuinely broken install fails, at a non-zero child process, and `rocm` stays byte-identical to what ships. Seam #2 waived serve's no-GPU pre-flight so the scenario could reach the install phase on a GPU-less host. Without it the pre-flight is reached first, so the scenario is retagged `@requires-gpu`. That is the real cost of this change: it moves off the blocking every-PR lane onto a gated one. Tracked in the issue this closes. Because the scenario no longer runs on a GPU-less lane, a unit test pins the coupling that lane would otherwise silently lose: the fixture's `backends` table must still parse into a `llamacpp:rocm` install attempt. The two prebuilt-lane contract tests are repurposed rather than deleted. "Match the feature xtask uses" becomes "match the invocation xtask uses", which is the invariant that actually prevents the duplicate release build, asserted whole-line so a trailing flag cannot satisfy it. Closes #349. Signed-off-by: Roman Inflianskas <Roman.Inflianskas@amd.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Depends on #342 — this rewrites the contract test #342 adds, so it stays in draft until that merges. Closes #349.
Problem
e2e-test-hooksis a cargo feature that compiles two scripted-failure seams intorocm, and it exists to serve exactly one scenario:@id:serve-lemonade-preparation-recovery(serve-18).engines/lemonade/src/lib.rs— makes the llama.cpp backend install fail on demandapps/rocm/src/main.rs— waivesrocm serve's no-GPU pre-flight, so the scenario can reach the install phase on a GPU-less hostThe cost is spread across the repo: 2 feature declarations, 3
cfgsites, 10--featureslines acrossnightly.ymlande2e-selfhosted.yml,xtask's own build, and 3 contract tests.Two consequences follow from the feature existing at all.
Binaries under test are not the binaries shipped. Every lane that pre-builds with the feature installs a binary that differs from a release build. #342 fixed this for the Windows lifecycle lane; it was still true on Linux, where the release build happens inside
cargo xtask e2e— with the feature — so the Linux lifecycle lane packaged and installed a hook-carrying binary through the real installer.The contract tests encoded the asymmetry instead of removing it. One test asserted the Windows lane must be hook-free, two asserted self-hosted and nightly must be hook-ful, and
ci.yml's Linux lane was asserted by neither — the same blind spot that hid the duplicate-build drift #342 fixed, one lane over.Fix
Delete the feature and drive the failure from a planted runtime instead.
tests/e2e-cucumber/src/bin/fake-lemonade.rsis a fixture binary copied into an isolated runtime directory under bothlemonadeandlemond(it takes its role fromargv[0]), alongside a hand-written install manifest. It reproduces the three calls the engine makes:statusexits 0,backendsprints a table reportingllamacpp:rocmas installable, andbackends installexits non-zero.The CLI then fails where a genuinely broken install fails — at a non-zero child process — and
rocmstays byte-identical to what ships.A bin target of
e2e-cucumberrather than an xtask-built artifact, so the suite reaches it throughCARGO_BIN_EXE_fake-lemonadewith no path to keep in step. It has to be a real executable, not a script: the engine spawns it by exact path and WindowsCreateProcessneeds a PE image.Why this doesn't weaken verification
resolve_runtimeaccepts the planted manifest because it checks only that the manifest parses and thatlemondis a file. No checksum, version, or signature is involved on this path — the SHA-pinned archive download lives inprepare_embeddable, which the serve path does not go through. Nothing here bypasses a check the product performs.Approaches ruled out are recorded in #349 (mocking the request — the boundary is a subprocess, not HTTP; prewarming — ephemeral on hosted lanes, already installed on prewarmed ones; dropping the network; mocking
verify_sha256— a fail-open seam inside supply-chain verification).The tradeoff, explicitly
Seam #2 was what let serve-18 run without a GPU. Without it,
rocm servereaches its GPU-required pre-flight first, so the scenario is retagged@requires-gpuand moves off the blocking every-PR lane onto a gated one.That is a real loss of coverage cadence and it is the reviewer's call, not a cleanup side effect. The counter-argument is that what it covered on the every-PR lane was a code path that only existed in test builds.
To limit what that move gives up, a unit test in
engines/lemonadepins the coupling a GPU-less lane would otherwise silently lose: the fixture'sbackendstable must still parse into allamacpp:rocminstall attempt. If a parser change stopped accepting it, the scenario would quietly stop reaching the failure it asserts and only a GPU lane would notice. That test runs everywhere.Contract tests
assert_prebuilt_e2e_lanes_enable_test_hooksis repurposed, not deleted: "match the featurextaskuses" becomes "match the invocationxtaskuses", which is the invariant that actually prevents the duplicate release build. It asserts whole-line, so a trailing--features …cannot satisfy it — verified by re-adding the flag and watching it fail.#342's
!contains("e2e-test-hooks")assertion is dropped; its reuse assertions stay.Test plan
Verified locally:
backendstable,status→ 0,backends install→ non-zero,lemondstays alive until killed.--features rocm/e2e-test-hooksto a lane makesnightly_prebuilt_e2e_lanes_match_xtask_buildfail.cargo check --workspace --all-targets, pluscargo check -p e2e-cucumber --test e2eexplicitly (thee2etarget istest = false, so--all-targetsskips it).cargo clippy --workspace --all-targets -- -D warningsandcargo fmt --checkclean.cargo test --workspace --all-targets: two failures inrocm-dash-tui(chatgpt_oauth_client_*, "cannot create token cache dir: Permission denied"). Confirmed pre-existing — they fail identically onorigin/mainin this environment, and this diff does not touch that crate.Not verified locally, stated per AGENTS.md §8: serve-18 itself. This host has no usable AMD GPU, so
rocm servestops at the pre-flight — which is exactly why the scenario is now@requires-gpu. I confirmed the CLI reaches and reports that pre-flight against a planted runtime, but the retry-count and recovery-guidance assertions past it need a GPU host. The self-hosted GPU lane is what will exercise them, and this PR should not merge until that lane has run it green.