Found while fixing #690, and deliberately not folded into that fix: the same trap is still armed in three other jobs.
The shape of it
check_sanitizer_instrumentation.sh begins with ctest --show-only=json-v1. For any suite registered with DISCOVERY_MODE PRE_TEST, listing the tests runs the test binary (<binary> --list-tests), inside the sweep step, not inside the build. If that binary cannot start, Catch2's CatchAddTests.cmake raises message(FATAL_ERROR ...), and ctest exits nonzero having printed no JSON at all — the whole listing, not just that target's share of it. The sweep then correctly refuses to pass having examined nothing, and the job is red for a reason that has nothing to do with instrumentation. That is #690 exactly.
The sweep step therefore has an implicit requirement: it must run in the environment its own Test step declares. Four jobs run the sweep, and after #690 only one of them does:
| job |
sweep step env |
its Test step env |
bank-sanitizers (ci.yml:1212, on this branch) |
QT_QPA_PLATFORM: offscreen (added by the #690 fix) |
QT_QPA_PLATFORM: offscreen |
linux-sanitizers (ci.yml:567) |
none |
QT_QPA_PLATFORM not set either |
kanban-tsan (ci.yml:960) |
none |
QT_QPA_PLATFORM: offscreen |
ladder-asan (ci.yml:1961, on this branch) |
none |
QT_QPA_PLATFORM: offscreen |
kanban-tsan and ladder-asan both declare QT_QPA_PLATFORM: offscreen on their Build and Test steps and omit it on the sweep step sitting between them. They are green today only because of a fact nobody wrote down: every Qt-linked suite they build uses DISCOVERY_MODE POST_BUILD (cmake/morph_add_rung.cmake:532, examples/common/CMakeLists.txt:307, tests/qt/CMakeLists.txt:91), so their enumeration already happened during the Build step, under that step's offscreen. The PRE_TEST suites they build (morph_tests at tests/CMakeLists.txt:222, morph_concepts_tests, morph_vetted_hmac_*) link no Qt.
Changing one word — POST_BUILD to PRE_TEST on any Qt-linked rung suite — turns two green jobs red at a step that will report only "ctest listed no tests".
A second, sharper edge of the same thing
catch_discover_tests(... PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen") reads exactly like "this suite is headless-safe". It is not. PROPERTIES are applied to the tests Catch2 registers; the run that discovers what those tests are happens before that, and CatchAddTests.cmake's execute_process() forwards only DL_PATHS and DL_FRAMEWORK_PATHS into it — never ENVIRONMENT. examples/bank/CMakeLists.txt:402 carries that exact combination, and it is what made #690 look impossible for two sessions. The #690 fix adds a comment there saying so; no other call site in the tree pairs PRE_TEST with an ENVIRONMENT property today, but nothing stops one.
Verification status
Mechanism reproduced; these three jobs' exposure inferred from reading, not reproduced.
Reproduced (local, 24a470c, clang 22.1.8, Catch2 3.16.0, Qt 6.11.2): on the real clang-ubsan bank configure, headless, the sweep fails with ctest --show-only=json-v1 exiting 8 and zero bytes of stdout, and succeeds with 9 ctest binaries all carry __ubsan_ symbols when QT_QPA_PLATFORM=offscreen is set. Full output is in the #690 fix's commit message.
Also reproduced on a two-target fixture (one Catch2 binary owning a QGuiApplication, one plain, both PRE_TEST): one failed discovery removes all three tests from the listing, not two — which is the part that makes the symptom so uninformative.
Not verified: I did not build the linux-sanitizers, kanban-tsan or ladder-asan configures. Their safety is inferred from grepping every catch_discover_tests call in the tree for DISCOVERY_MODE and checking which targets link Qt. No currently-failing job is claimed here — this is a latent hazard, not an outage.
What would resolve it
Either of, not both:
- Give the three remaining sweep steps the same
env: block their Test step has. Three lines, inert where nothing needs a platform.
- Or state the requirement once, in
scripts/check_sanitizer_instrumentation.sh's header, that a caller whose tree contains a Qt-linked PRE_TEST suite must invoke it under the Test step's environment — and leave the workflows alone.
(1) is cheaper and does not depend on anyone reading a header. Deliberately not doing it as part of #690: that fix is about one measured failure, and three unmeasured jobs do not belong in it.
What would change the verdict
Close as invalid if PRE_TEST discovery turns out not to run under ctest --show-only in the Catch2 version CI resolves (v3.8.1 via morph_cache_dep, or the distro catch2 the bank job installs from apt), rather than the 3.16.0 this was measured against. The execute_process + FATAL_ERROR path in CatchAddTests.cmake is long-standing, so I do not expect that, but I did not check 3.8.1's copy.
Related
Found while fixing #690, and deliberately not folded into that fix: the same trap is still armed in three other jobs.
The shape of it
check_sanitizer_instrumentation.shbegins withctest --show-only=json-v1. For any suite registered withDISCOVERY_MODE PRE_TEST, listing the tests runs the test binary (<binary> --list-tests), inside the sweep step, not inside the build. If that binary cannot start, Catch2'sCatchAddTests.cmakeraisesmessage(FATAL_ERROR ...), and ctest exits nonzero having printed no JSON at all — the whole listing, not just that target's share of it. The sweep then correctly refuses to pass having examined nothing, and the job is red for a reason that has nothing to do with instrumentation. That is #690 exactly.The sweep step therefore has an implicit requirement: it must run in the environment its own
Teststep declares. Four jobs run the sweep, and after #690 only one of them does:bank-sanitizers(ci.yml:1212, on this branch)QT_QPA_PLATFORM: offscreen(added by the #690 fix)QT_QPA_PLATFORM: offscreenlinux-sanitizers(ci.yml:567)QT_QPA_PLATFORMnot set eitherkanban-tsan(ci.yml:960)QT_QPA_PLATFORM: offscreenladder-asan(ci.yml:1961, on this branch)QT_QPA_PLATFORM: offscreenkanban-tsanandladder-asanboth declareQT_QPA_PLATFORM: offscreenon their Build and Test steps and omit it on the sweep step sitting between them. They are green today only because of a fact nobody wrote down: every Qt-linked suite they build usesDISCOVERY_MODE POST_BUILD(cmake/morph_add_rung.cmake:532,examples/common/CMakeLists.txt:307,tests/qt/CMakeLists.txt:91), so their enumeration already happened during the Build step, under that step'soffscreen. The PRE_TEST suites they build (morph_testsattests/CMakeLists.txt:222,morph_concepts_tests,morph_vetted_hmac_*) link no Qt.Changing one word —
POST_BUILDtoPRE_TESTon any Qt-linked rung suite — turns two green jobs red at a step that will report only "ctest listed no tests".A second, sharper edge of the same thing
catch_discover_tests(... PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen")reads exactly like "this suite is headless-safe". It is not.PROPERTIESare applied to the tests Catch2 registers; the run that discovers what those tests are happens before that, andCatchAddTests.cmake'sexecute_process()forwards onlyDL_PATHSandDL_FRAMEWORK_PATHSinto it — neverENVIRONMENT.examples/bank/CMakeLists.txt:402carries that exact combination, and it is what made #690 look impossible for two sessions. The #690 fix adds a comment there saying so; no other call site in the tree pairsPRE_TESTwith anENVIRONMENTproperty today, but nothing stops one.Verification status
Mechanism reproduced; these three jobs' exposure inferred from reading, not reproduced.
Reproduced (local, 24a470c, clang 22.1.8, Catch2 3.16.0, Qt 6.11.2): on the real
clang-ubsanbank configure, headless, the sweep fails withctest --show-only=json-v1exiting 8 and zero bytes of stdout, and succeeds with9 ctest binaries all carry __ubsan_ symbolswhenQT_QPA_PLATFORM=offscreenis set. Full output is in the #690 fix's commit message.Also reproduced on a two-target fixture (one Catch2 binary owning a
QGuiApplication, one plain, bothPRE_TEST): one failed discovery removes all three tests from the listing, not two — which is the part that makes the symptom so uninformative.Not verified: I did not build the
linux-sanitizers,kanban-tsanorladder-asanconfigures. Their safety is inferred from grepping everycatch_discover_testscall in the tree forDISCOVERY_MODEand checking which targets link Qt. No currently-failing job is claimed here — this is a latent hazard, not an outage.What would resolve it
Either of, not both:
env:block their Test step has. Three lines, inert where nothing needs a platform.scripts/check_sanitizer_instrumentation.sh's header, that a caller whose tree contains a Qt-linkedPRE_TESTsuite must invoke it under the Test step's environment — and leave the workflows alone.(1) is cheaper and does not depend on anyone reading a header. Deliberately not doing it as part of #690: that fix is about one measured failure, and three unmeasured jobs do not belong in it.
What would change the verdict
Close as
invalidifPRE_TESTdiscovery turns out not to run underctest --show-onlyin the Catch2 version CI resolves (v3.8.1viamorph_cache_dep, or the distrocatch2the bank job installs from apt), rather than the 3.16.0 this was measured against. Theexecute_process+FATAL_ERRORpath inCatchAddTests.cmakeis long-standing, so I do not expect that, but I did not check 3.8.1's copy.Related
bank-sanitizers.