Skip to content

Commit 0389b9e

Browse files
committed
fix(e2e): drop requires-hard — a token cannot know which runner it is on
Shipped it, used it once, and CI proved it wrong within the hour: FAIL: 130_freestanding_riscv_build_and_run.sh (REQUIRED capability missing: llvm) ← the macOS e2e suite `llvm` and `qemu-riscv` are absent on the macOS and Windows runners BY DESIGN, so a token whose absence fails makes those jobs structurally red — a worse outcome than the silent skip it was meant to prevent. The same word has to mean both "this platform legitimately lacks it" and "this runner is misconfigured", and nothing in the token can tell them apart. The guard that works has to know WHICH runner it is talking about, so it lives in the job. ci-linux-e2e.yml's `baremetal` job installs qemu and the sysroot (into the home MCPP uses, or 131 skips), runs the two scripts DIRECTLY — they are standalone, run_all.sh takes no filter and would run all 250 tests for two — and then asserts each script's PASS line appeared. Both scripts can exit 0 without running, so the exit code alone cannot answer the question. run_all.sh keeps the qemu-riscv capability probe and gains a comment saying why the hard form is not there, so the next person does not re-derive it.
1 parent 720690c commit 0389b9e

3 files changed

Lines changed: 59 additions & 41 deletions

File tree

.github/workflows/ci-linux-e2e.yml

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -158,16 +158,28 @@ jobs:
158158
# llvm is the toolchain a freestanding target pins; install it
159159
# explicitly rather than relying on whatever the sandbox cache holds.
160160
"$MCPP" toolchain install llvm 22.1.8
161-
bash tests/e2e/run_all.sh 13 2>&1 | tee e2e.log
162-
# The assertion this job exists for: the tests RAN. `run_all.sh`
163-
# exits 0 on a skip, so its exit code cannot answer this.
164-
grep -q 'PASS: freestanding riscv64 build + run' e2e.log || {
165-
echo "130 (engine chain) did not run — see above"; exit 1; }
166-
grep -q 'PASS: BSP supplies the sysroot' e2e.log || {
167-
echo "131 (ecosystem chain) did not run — see above"; exit 1; }
168-
if grep -qE '^SKIP: 13[01]' e2e.log; then
169-
echo "a bare-metal e2e SKIPPED on the runner that must run it"; exit 1
170-
fi
161+
# Run the two scripts DIRECTLY rather than through run_all.sh.
162+
# They are standalone (they take $MCPP and nothing else), run_all.sh
163+
# accepts no filter — it would run the whole 250-test suite here for
164+
# two tests — and, more to the point, run_all.sh exits 0 on a skip.
165+
# Invoked directly, a skip is visible: the script either prints its
166+
# PASS line or it does not.
167+
for t in tests/e2e/130_freestanding_riscv_build_and_run.sh \
168+
tests/e2e/131_freestanding_bsp_supplies_everything.sh; do
169+
echo "=== $t ==="
170+
bash "$t" 2>&1 | tee "$(basename "$t").log"
171+
rc=${PIPESTATUS[0]}
172+
[ "$rc" = "0" ] || { echo "$t failed (exit $rc)"; exit 1; }
173+
done
174+
# The assertion this job exists for: both tests RAN. Each has an
175+
# early `exit 0` for a missing capability, so a zero exit code alone
176+
# does not distinguish "passed" from "skipped".
177+
grep -q 'PASS: freestanding riscv64 build + run' \
178+
130_freestanding_riscv_build_and_run.sh.log || {
179+
echo "130 (engine chain) skipped on the runner that must run it"; exit 1; }
180+
grep -q 'PASS: BSP supplies the sysroot' \
181+
131_freestanding_bsp_supplies_everything.sh.log || {
182+
echo "131 (ecosystem chain) skipped on the runner that must run it"; exit 1; }
171183
172184
# ──────────────────────────────────────────────────────────────────
173185
# Hermetic (no host toolchain): the ONLY environment class that

tests/e2e/130_freestanding_riscv_build_and_run.sh

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,28 @@
11
#!/usr/bin/env bash
2-
# requires-hard: llvm qemu-riscv unix-shell
2+
# requires: llvm qemu-riscv unix-shell
33
# Bare metal, end to end: `mcpp build --target riscv64-none-elf` produces a
44
# RISC-V firmware image from a C++20 MODULE interface unit plus assembly, and
55
# `mcpp run --target-triple` boots it in qemu.
66
#
7-
# ⚠️ `requires-hard`, not `requires`. Every capability here is one a runner
8-
# that runs this file is supposed to have: llvm is mcpp's own payload and
9-
# qemu-riscv is an xim package. Declared the soft way, a runner that lost
10-
# either would report SKIP — indistinguishable from "inapplicable on this
11-
# platform" — and the one test that proves the bare-metal chain works would
12-
# stop running while the job stayed green. That has happened twice in this
13-
# repository already.
7+
# ⚠️ SOFT `requires:`, and the guard lives elsewhere on purpose.
8+
#
9+
# `requires-hard:` (missing capability FAILS instead of skipping) exists and is
10+
# the right tool for "this runner is misconfigured" — but it cannot express
11+
# THIS test's condition. Neither `llvm` nor `qemu-riscv` is present on the
12+
# macOS and Windows e2e runners, so a hard token here makes those jobs
13+
# structurally red forever — measured, not predicted: this file shipped with
14+
# `requires-hard` for one round and the macOS suite reported
15+
#
16+
# FAIL: 130_freestanding_riscv_build_and_run.sh
17+
# (REQUIRED capability missing: llvm)
18+
#
19+
# which is a worse failure than the one it was meant to prevent.
20+
#
21+
# The thing that must not happen — this test silently skipping on the runner
22+
# that is supposed to run it, leaving the bare-metal chain unexercised while
23+
# the job stays green — is guarded in ci-linux-e2e.yml, which installs the
24+
# capabilities and then asserts this test's PASS line actually appeared. A
25+
# guard belongs where it can be exact about which runner it is talking about.
1426
#
1527
# What each assertion is FOR (none of them is decoration):
1628
#

tests/e2e/run_all.sh

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,8 @@ bad_tokens=0
186186
for tf in "$HERE"/[0-9]*.sh; do
187187
base="$(basename "$tf")"
188188
req="$(sed -n '2p' "$tf")"
189-
[[ "$req" =~ ^#\ requires(-hard)?: ]] || continue
189+
[[ "$req" =~ ^#\ requires: ]] || continue
190190
toks="${req#\# requires:}"
191-
toks="${toks#\# requires-hard:}"
192191
for tok in $toks; do
193192
if [[ " ${KNOWN_CAPS[*]} " != *" $tok "* ]]; then
194193
echo "ERROR: $base declares unknown capability '$tok' — it would be"
@@ -206,18 +205,22 @@ done
206205
# Returns 0 (true) if the test should be skipped, prints reason.
207206
# Returns 1 (false) if all requirements are met.
208207

209-
# `# requires-hard:` — a missing capability FAILS instead of skipping.
208+
# ⚠️ THERE IS DELIBERATELY NO "requires-hard" FORM.
210209
#
211-
# The ordinary form is right for a test that is genuinely inapplicable here
212-
# (msvc on Linux). It is wrong for a test whose capability the runner is
213-
# SUPPOSED to have: the skip line is indistinguishable from a legitimate one,
214-
# so a job that silently stopped exercising the thing it exists to exercise
215-
# stays green forever. This repository has hit that twice (65_* never ran at
216-
# all; ten pack e2e skipped on two platforms). A test that declares its needs
217-
# with the hard form says "if this is missing, the runner is misconfigured".
218-
requires_is_hard() {
219-
[[ "$(sed -n '2p' "$1")" =~ ^#\ requires-hard: ]]
220-
}
210+
# The obvious answer to "a test silently skipped on the runner that was
211+
# supposed to run it" is a token whose absence FAILS. It was implemented here,
212+
# used once, and measured wrong: a token cannot tell "this runner is
213+
# misconfigured" from "this platform legitimately lacks the capability",
214+
# because the same word means both. `llvm` and `qemu-riscv` are absent on the
215+
# macOS runner by design, so the one test that declared them the hard way made
216+
# the macOS suite fail — a worse outcome than the silent skip it was meant to
217+
# prevent, and one CI reported within the hour.
218+
#
219+
# The guard that works has to know WHICH runner it is talking about, so it
220+
# lives in the job: ci-linux-e2e.yml's `baremetal` job installs the
221+
# capabilities and then asserts the tests' PASS lines actually appeared.
222+
# `run_all.sh` exits 0 on a skip, so its exit code cannot answer that question
223+
# and no token can either.
221224

222225
check_requires() {
223226
local test_file="$1"
@@ -226,10 +229,9 @@ check_requires() {
226229
req_line="$(sed -n '2p' "$test_file")"
227230

228231
# If there's no requires comment at all, run the test
229-
[[ "$req_line" =~ ^#\ requires(-hard)?: ]] || return 1
232+
[[ "$req_line" =~ ^#\ requires: ]] || return 1
230233

231234
local caps_needed="${req_line#\# requires:}"
232-
caps_needed="${caps_needed#\# requires-hard:}"
233235
caps_needed="${caps_needed# }" # strip leading space
234236

235237
# Empty requirements → runs everywhere
@@ -331,14 +333,6 @@ for test in "$HERE"/[0-9]*.sh; do
331333
echo
332334
missing_cap="$(check_requires "$test")"
333335
if [[ -n "$missing_cap" ]]; then
334-
if requires_is_hard "$test"; then
335-
echo "FAIL: $name (REQUIRED capability missing: $missing_cap)"
336-
echo " declared with '# requires-hard:', so this runner is"
337-
echo " misconfigured rather than merely inapplicable."
338-
FAILED_TESTS+=("$name (REQUIRED capability missing: $missing_cap)")
339-
((FAIL++))
340-
continue
341-
fi
342336
echo "SKIP: $name (missing capability: $missing_cap)"
343337
((SKIP++))
344338
continue

0 commit comments

Comments
 (0)