# BST-TIMEOUT: 300 . "$BST_LIB" # Catch2 3.6.0 (upstream catchorg/Catch2 at SRCREV 4e8d92bf, meta-brintos # recipes-test/catch2) -- the C++ xUnit framework. Numbered with 77-cunit and # 77-cmocka, the two test frameworks already on this image. # # WHY THIS SUITE IS ABOUT BEHAVIOUR AND NOT ABOUT FILES. Catch2 v3 is a compiled # library, so `it builds` is already established by do_compile on the build host # -- 107 translation units through this distro's C++ driver into libCatch2.a. # What that does NOT establish, and what a consumer actually needs to know, is # WHICH HALF OF CATCH2 STILL WORKS on an arm with no unwinder. # # THE ANSWER, AND IT IS NOT A DEFECT. Catch2 auto-detects the exceptions-off # driver (catch_compiler_capabilities.hpp:241) and sets # CATCH_CONFIG_DISABLE_EXCEPTIONS itself -- correctly, and note the recipe does # NOT force that macro, so what this suite measures is a property of the ARM and # not of our configuration. In that mode Catch2's two ABORTING control paths # have no way to reach the runner: # # a failing REQUIRE throw_test_failure_exception() -> CATCH_ERROR -> # throw_exception() -> prints "Catch will terminate # because it needed to throw an exception." -> terminate() # a run-time SKIP() throw_test_skip_exception(), same route # # while a failing CHECK, which does not abort, behaves exactly as it does # anywhere else. So Catch2 works here for a suite that PASSES and for one that # fails a non-aborting assertion; a REQUIRE-heavy suite gets a runner that dies # on its first real failure instead of reporting it. That is invisible until # something breaks, which is why it is measured here rather than left in a doc. # # THE FOUR PROBES ARE THE MEASUREMENT, and section 5 is what makes it one. # catch2-probe-check is the CONTROL: without it, "REQUIRE aborts the process" # would be equally consistent with "no failure of any kind is reportable on this # arm", and the suite would be asserting far less than it appears to. # # MEASURED ON BOTH ARMS OF X86-64 FIRST, then on wasm32 (2026-08-22), which is # why these are attributed to the exceptions state rather than to Catch2 -- and # why the wasm32 column is a confirmation rather than the only evidence: # # x86-64 exc ON x86-64 exc OFF wasm32 (ships) # all pass 0, 11 assertions same same # failing CHECK 1, 1 failed same same # failing REQUIRE 1, 1 failed 134 SIGABRT 134 SIGABRT # SKIP() 0, 1 SKIPPED 134 SIGABRT 134 SIGABRT # # ONE CROSS-ARM DIFFERENCE, and it sharpens the finding rather than softening it. # On x86-64 the abort still ended in a Catch2 report -- its fatal-condition # handler caught the SIGABRT and printed `test cases: 6 | 5 passed | 1 failed`. # On wasm32 there is NO SUMMARY AT ALL: the output ends at # `libc++abi: terminating` / `Aborted`. So the consumer consequence here is not # merely "the run dies on the first REQUIRE failure" but "it dies leaving no # report of what had passed up to that point". Sections 3 and 4 assert that # absence, because it is the half a reader would otherwise assume they still had. # # NO COMPILE HAPPENS ON TARGET. This image ships no compiler, so nothing here # can write a Catch2 suite; the probes were cross-linked in the same build that # installed the headers, which makes do_compile the port's first consumer. PT=/usr/libexec/catch2-tests INC=/usr/include/catch2 CFG=$INC/catch_user_config.hpp # ----------------------------------------------------------- what shipped ---- # All builtin `test`, so this block costs no process. Exec is a fresh wasm # module instantiation here (~2-3 s apiece), so the suite spends its budget on # the four probe runs and nothing else. check_rc catch2/inc-dir 0 test -d "$INC" check_rc catch2/hdr-all 0 test -s "$INC/catch_all.hpp" check_rc catch2/hdr-test-macros 0 test -s "$INC/catch_test_macros.hpp" check_rc catch2/hdr-user-config 0 test -s "$CFG" check_rc catch2/hdr-version 0 test -s "$INC/catch_version_macros.hpp" check_rc catch2/ar-catch2 0 test -s /usr/lib/libCatch2.a check_rc catch2/ar-catch2main 0 test -s /usr/lib/libCatch2Main.a check_rc catch2/pc-catch2 0 test -f /usr/share/pkgconfig/catch2.pc check_rc catch2/pc-with-main 0 test -f /usr/share/pkgconfig/catch2-with-main.pc check_rc catch2/cmake-config 0 test -f /usr/lib/cmake/Catch2/Catch2Config.cmake check_rc catch2/cmake-targets 0 test -f /usr/lib/cmake/Catch2/Catch2Targets.cmake check_rc catch2/cmake-catch 0 test -f /usr/lib/cmake/Catch2/Catch.cmake # NO shared object, and asserted as an ABSENCE so a distro change that starts # emitting one is a red rather than a silent change. This arm links whole-program # bitcode; BUILD_SHARED_LIBS=OFF is the recipe's half of the same statement. check_eq catch2/no-so "0" "$(find /usr/lib -maxdepth 1 -name 'libCatch2*.so*' 2>/dev/null | grep -c .)" # The debugger rc files upstream's CATCH_INSTALL_EXTRAS ships. Removed by the # recipe -- there is no debugger on this image, and no default FILES rule matches # ${datadir}/Catch2, so leaving them would have failed do_package_qa. Pinned as # an absence for the same reason as the .so above. check_eq catch2/no-debugger-rc "0" "$(find /usr/share/Catch2 -maxdepth 1 -type f 2>/dev/null | grep -c .)" # The three version numbers, read from the header rather than from the recipe. check_eq catch2/ver-major "#define CATCH_VERSION_MAJOR 3" "$(grep '^#define CATCH_VERSION_MAJOR' "$INC/catch_version_macros.hpp" 2>/dev/null)" check_eq catch2/ver-minor "#define CATCH_VERSION_MINOR 6" "$(grep '^#define CATCH_VERSION_MINOR' "$INC/catch_version_macros.hpp" 2>/dev/null)" check_eq catch2/ver-patch "#define CATCH_VERSION_PATCH 0" "$(grep '^#define CATCH_VERSION_PATCH' "$INC/catch_version_macros.hpp" 2>/dev/null)" # The .pc a cross-compiling consumer reads. Unlike asio (BRINTOS-1267) this port # does NOT rewrite it, and that is a decision worth pinning: Catch2 materialises # its configuration into the INSTALLED catch_user_config.hpp, so a consumer # inherits the arm's decisions from the header set itself and needs nothing in # the .pc. Upstream's `Libs:` is correct here (there IS an archive to link). check_out catch2/pc-version "Version: 3.6.0" grep '^Version:' /usr/share/pkgconfig/catch2.pc check_out catch2/pc-libs "-lCatch2" grep '^Libs:' /usr/share/pkgconfig/catch2.pc # --------------------------------------------- the configuration it shipped --- # THIS IS THE TRIPWIRE for everything below it. These two lines in the shipped # header are what every measurement in sections 3-6 was made under; a red here # means the recipe's flags or the driver moved, and the rest of the file would # have to be re-measured rather than merely re-read. # Pinned ON by the recipe (-DCATCH_CONFIG_NO_USE_ASYNC=ON), and MATERIALISED -- # which is the point: catch_stats.cpp reaches and std::async under # CATCH_CONFIG_USE_ASYNC, this libc++ is built threads-off so its # declares nothing (linux-in-the-browser#85), and a consumer that passes -pthread # would otherwise flip _REENTRANT and turn a working build into asio's # `no member named 'promise' in namespace 'std'`. check_eq catch2/cfg-no-use-async "#define CATCH_CONFIG_NO_USE_ASYNC" \ "$(grep '^#define CATCH_CONFIG_NO_USE_ASYNC' "$CFG" 2>/dev/null)" # NOT forced by the recipe, deliberately -- the header discovers the driver's # exceptions state on its own at consumer compile time. If this ever became a # #define, the port's headline finding would be an artifact of our configuration # rather than a property of the arm, and the probes below would stop measuring it. check_eq catch2/cfg-exceptions-not-forced "/* #undef CATCH_CONFIG_DISABLE_EXCEPTIONS */" \ "$(grep 'CATCH_CONFIG_DISABLE_EXCEPTIONS \*/' "$CFG" 2>/dev/null | head -1)" # ------------------------------------------------------- the probes exist ----- if [ ! -x "$PT/catch2-probe" ]; then bst_skip catch2/probe-present "catch2-probe not installed on this image" exit 0 fi bst_ok catch2/probe-present # Probe size is a functional property here, not trivia. Read against the real # geometry rather than the figure in circulation: binfmt_wasm.c lets one image # span both exec-staging slots, ONE SLOT IS 32 MiB, and below it an exec takes # the kmalloc path and claims no slot at all (measured under flatbuffers, which # also established that Yocto's packaging strip cannot touch a wasm module -- # runstrip selects with oe.package.is_elf()). Catch2 links its whole reporter, # matcher and benchmarking surface statically, so these are large: 17.5 MB # unstripped, of which 10.6 MB was DWARF carried in by libCatch2.a, and # 6.06 MB after the recipe's explicit ${STRIP}. The 12 MiB ceiling here is well # under one slot and leaves real headroom for a compiler change. for p in catch2-probe catch2-probe-check catch2-probe-require catch2-probe-skip; do if [ -x "$PT/$p" ]; then sz=$(wc -c < "$PT/$p") if [ "$sz" -lt 12582912 ]; then bst_ok "catch2/size-$p" else bst_notok "catch2/size-$p" "$p is $sz bytes, over the 12582912 ceiling"; fi else bst_notok "catch2/size-$p" "$p not installed" fi done # A shipped binary must carry no build-tree path. Catch2's assertion macros # expand __FILE__ into a DATA-section string literal that no strip removes, so # this asserts the recipe's -fmacro-prefix-map where it LANDS rather than where # it was passed -- do_package_qa flagged all four probes for `buildpaths` before # it was added, and the leak was measured to be exactly one string in the `data` # section with none in DWARF. # # TWO CASES, BECAUSE THE SECOND ONE ALONE WOULD BE A FALSE GREEN. `strings` # happens to be on this image, but a case that reports "no build paths found" # when its reader produced nothing at all asserts precisely as much as no case # -- and it would go green the day binutils left the image, which is the day it # stops being true that anyone checked. So the reader is proved first, against a # string the binary is known to contain (__FILE__ after the prefix-map, i.e. the # bare basename), and only then is the absence asserted. # # `|| echo 0` is deliberately NOT written here: grep -c exits 1 on zero matches, # so the fallback fires on the SUCCESS path and appends a second 0. That is what # this case's first run reported (`want:'0' got:'0 0'`) -- the guard failing, not # the port. check_out catch2/strings-usable "catch2_probe.cpp" \ sh -c 'strings "$1" | grep -x catch2_probe.cpp' _ "$PT/catch2-probe" check_eq catch2/no-buildpaths "0" \ "$(strings "$PT/catch2-probe" 2>/dev/null | grep -c '/tmp-glibc/work/')" # ------------------------------------------- 1. the acceptance gate (arm 0) --- # Every assertion passes, so Catch2's own summary line is the port's pass # criterion -- which is what BRINTOS-1269 asked the port to close on. out=$("$PT/catch2-probe" 2>&1); rc=$? P_DISEXC=; P_ASYNC=; P_ARM=; P_VOIDP=; P_SIZET= oIFS=$IFS; IFS=' ' for line in $out; do tag=${line%% *}; val=${line#* } case "$tag" in P-DISABLE-EXCEPTIONS) P_DISEXC=$val ;; P-USE-ASYNC) P_ASYNC=$val ;; P-ARM) P_ARM=$val ;; P-SIZEOF-VOIDP) P_VOIDP=$val ;; P-SIZEOF-SIZET) P_SIZET=$val ;; esac done IFS=$oIFS if bst_execfail "$out"; then bst_unrun catch2/probe-rc "$out" bst_unrun catch2/probe-summary "$out" exit 0 fi check_eq catch2/probe-rc "0" "$rc" # Catch2's own summary, verbatim. The assertion and test-case counts are fixed # by the probe's control flow -- no pointer width, no sizeof, no address, no # timing -- so nothing in them is ALLOWED to differ from the x86-64 oracle, and a # disagreement is a defect on this arm rather than a platform difference. check_out catch2/probe-summary "All tests passed (11 assertions in 5 test cases)" \ printf '%s' "$out" # The compile-time state the binary was actually built under, reported by the # probe itself rather than inferred from the recipe. check_eq catch2/probe-disable-exceptions "1" "${P_DISEXC:-absent}" check_eq catch2/probe-use-async "0" "${P_ASYNC:-absent}" check_eq catch2/probe-arm "0" "${P_ARM:-absent}" # Word size, which DOES differ from the x86-64 oracle and is pinned here as the # wasm32 value rather than reported: 4 and 4 against x86-64's 8 and 8. check_eq catch2/probe-sizeof-voidp "4" "${P_VOIDP:-absent}" check_eq catch2/probe-sizeof-sizet "4" "${P_SIZET:-absent}" # ------------------------------- 2. a failing CHECK is reportable (arm 1) ----- # THE CONTROL. Non-aborting, so the run must continue past the failure, print the # line after it, report 1 failed and exit 1 -- identical to x86-64 with # exceptions on. Without this case, section 3's abort would be equally # consistent with "no failure is reportable on this arm at all". cout=$("$PT/catch2-probe-check" 2>&1); crc=$? if bst_execfail "$cout"; then bst_unrun catch2/check-rc "$cout" else check_eq catch2/check-rc "1" "$crc" check_out catch2/check-continued "P-AFTER-FAILED-CHECK 1" printf '%s' "$cout" check_out catch2/check-reported "1 failed" printf '%s' "$cout" check_out catch2/check-assertion-count "assertions: 12" printf '%s' "$cout" # It must NOT have taken the terminate path -- that is what makes it a # control for section 3 rather than a second copy of it. case "$cout" in *"needed to throw an exception"*) bst_notok catch2/check-no-terminate "a failing CHECK reached the terminate path" ;; *) bst_ok catch2/check-no-terminate ;; esac fi # --------------------------- 3. a failing REQUIRE aborts the run (arm 2) ------ # THE HEADLINE. Upstream's documented behaviour for CATCH_CONFIG_DISABLE_EXCEPTIONS # and a property of this arm's missing unwinder (brintos/hwjs-cc#10) -- not a # defect, not a workaround, and nothing filed. What it costs a consumer is the # thing to know: a REQUIRE-heavy suite dies on its first real failure. rout=$("$PT/catch2-probe-require" 2>&1); rrc=$? if bst_execfail "$rout"; then bst_unrun catch2/require-aborts "$rout" else # The message Catch2 prints on the way out. This is the discriminating case: # it is emitted ONLY from throw_exception() under CATCH_CONFIG_DISABLE_EXCEPTIONS. check_out catch2/require-terminate-msg "Catch will terminate because it needed to throw an exception" \ printf '%s' "$rout" check_out catch2/require-message "Test failure requires aborting test" printf '%s' "$rout" # The statement AFTER the failing REQUIRE must never have run. case "$rout" in *"P-AFTER-FAILED-REQUIRE"*) bst_notok catch2/require-did-abort "the statement after a failing REQUIRE ran; the abort path did not fire" ;; *) bst_ok catch2/require-did-abort ;; esac # 134 is 128+SIGABRT, measured here and identical on x86-64. Pinned as the # exact value rather than as "non-zero": the number is a POSIX constant, not # a guest quirk, and what makes it the finding is that arm 1 -- the same # binary one assertion apart -- exits 1. A change here means the abort route # itself moved and everything in this section needs re-measuring. check_eq catch2/require-rc "134" "$rrc" # AND NO SUMMARY SURVIVES. This is the wasm32-specific half: on x86-64 # Catch2's fatal-condition handler still printed `test cases: ...`; here the # output ends at libc++abi. A consumer therefore loses the record of what # passed before the failure, not just the failing case. case "$rout" in *"test cases:"*) bst_notok catch2/require-no-summary "a Catch2 summary survived the abort; the fatal-condition handler reported after all" ;; *) bst_ok catch2/require-no-summary ;; esac fi # ------------------------------- 4. a run-time SKIP aborts too (arm 3) -------- # The second aborting path, and the worse of the two: on x86-64 with exceptions # this exits 0 reporting `1 skipped`; here it takes the same terminate route AND # is counted as a FAILURE. A port suite that skips a case on an absent feature # would therefore report a failure and die -- worth knowing before writing one. sout=$("$PT/catch2-probe-skip" 2>&1); src=$? if bst_execfail "$sout"; then bst_unrun catch2/skip-aborts "$sout" else check_out catch2/skip-terminate-msg "Catch will terminate because it needed to throw an exception" \ printf '%s' "$sout" check_out catch2/skip-message "Explicitly skipping tests during runtime requires exceptions" \ printf '%s' "$sout" case "$sout" in *"P-AFTER-SKIP"*) bst_notok catch2/skip-did-abort "the statement after SKIP() ran; the abort path did not fire" ;; *) bst_ok catch2/skip-did-abort ;; esac # 134 again, against exit 0 on an exceptions-enabled build -- SKIP() is the # path where the two arms disagree most, because there the exceptions-on # answer is not merely "reported differently" but "not a failure at all". check_eq catch2/skip-rc "134" "$src" case "$sout" in *"test cases:"*) bst_notok catch2/skip-no-summary "a Catch2 summary survived the abort" ;; *) bst_ok catch2/skip-no-summary ;; esac fi