#!/bin/sh # Run nghttp2's upstream test suite -- the programmes tests/CMakeLists.txt declares # to ctest with add_test() -- and report one PASS:/FAIL:/SKIP: line per test case, # which is the protocol ptest-runner reads. # # THIS IS NOT ctest, AND IT DOES NOT NEED TO BE. ctest's whole contribution here is # to exec each programme and score its exit status; the programmes are munit # suites that already report per-case results on stdout and exit non-zero if any # case failed. Running them directly therefore preserves the suite's semantics # exactly while replacing a harness the image does not ship -- and it reports 218 # cases where ctest would have reported 2, which is the difference between naming # the entry point that broke and naming the binary it lived in. # # WHAT IS PARSED, AND THE ONE TRAP IN IT. munit prints # # /pq/test_nghttp2_pq [ OK ] [ 0.00061 / 0.00061 CPU ] # # padding the name to a fixed width -- but a name LONGER than that width is # followed by the bracket with NO separating space: # # /http/test_nghttp2_http_parse_priority[ OK ] [ ... ] # # so a parser that reads the verdict from one fixed field number silently drops # every long-named case. Both shapes are handled below; nghttp2 1.61.0 has one # case in the second shape today, and a version bump can add more without notice. # # THE FIELDS ARE SPLIT BY `read`, NOT BY `set -- $line`, because the result lines # contain `[` and `]` as whole words: `set --` would submit them to pathname # expansion, where an unterminated bracket is only harmless until the working # directory happens to hold a file that matches. `read` splits on IFS and never # globs. # # THE SEED IS RECORDED BECAUSE IT IS THE ONLY WAY BACK TO A RED. munit seeds its # PRNG differently on every run and prints the seed it chose; a case that fails # only under one seed is reproducible with `main --seed ` and not otherwise. # The seed is therefore reported per programme rather than pinned here -- pinning # it would make the suite deterministic by making it explore one arrangement # forever, which is the opposite of what upstream's randomisation is for. # # $NGHTTP2_PTEST_ARGS is passed through to each programme, unset by default so # what runs is upstream's own default: one fork per case, which is what gives a # crashing case a report line instead of taking the rest of the suite with it. # The hook exists so a diagnostic run can ask for --no-fork on a target where # that matters, without rebuilding the image to find out. set -u pdir=$(cd "$(dirname "$0")" && pwd) args=${NGHTTP2_PTEST_ARGS:-} # munit buffers each case's stderr through tmpfile(), which resolves under /tmp -- # the rootfs is read-only at browser runtime, so a run with no writable /tmp would # report every case as an ERROR for a reason that has nothing to do with nghttp2. # Checked once, here, so that condition is one loud line instead of 218 confusing # ones. # The probe runs in a SUBSHELL, which is not a stylistic choice: `:` is a POSIX # special built-in, and a redirection error on one makes a non-interactive shell # EXIT. Written as a plain `if : > "$probe"`, the very condition this checks for # would kill run-ptest before it could report it -- an unwritable /tmp would # produce no output at all, which the suite reads as a suite that never ran # rather than as the one-line environment finding it is. Measured, not reasoned: # under dash the plain form dies at the redirect. probe=${TMPDIR:-/tmp}/nghttp2-ptest.$$ if ( : > "$probe" ) 2>/dev/null; then rm -f "$probe" else echo "FAIL: nghttp2-ptest-setup (no writable ${TMPDIR:-/tmp}; munit needs tmpfile())" exit 0 fi out=${TMPDIR:-/tmp}/nghttp2-ptest-out.$$ for prog in "$pdir"/progs/*; do [ -f "$prog" ] && [ -x "$prog" ] || continue pname=${prog##*/} # What the shipped binary itself says its suite is. This is the count the # report is measured against on target, and it costs one extra run of a # programme that does nothing but print names. listed=0 "$prog" --list > "$out" 2>/dev/null while read -r name _rest; do case "$name" in /*) listed=$((listed + 1)) ;; esac done < "$out" "$prog" $args > "$out" 2>&1 seed=none msucc=none mtotal=none npass=0 nfail=0 nskip=0 while read -r f1 f2 f3 f4 f5 f6 _rest; do case "$f1" in /*) ;; Running) # "Running test suite with seed 0x09f53e75..." if [ "$f5" = "seed" ]; then seed=${f6%...}; fi continue ;; *) # "213 of 213 (100%) tests successful, 0 (0%) test skipped." if [ "$f2" = "of" ]; then msucc=$f1; mtotal=$f3; fi continue ;; esac case "$f1" in *'[') name=${f1%'['}; verdict=$f2 ;; *) if [ "$f2" != "[" ]; then continue; fi name=$f1; verdict=$f3 ;; esac # The report drops munit's leading slash so the case id reads # nghttp2// on target rather than carrying an empty component. name=${name#/} case "$verdict" in OK) echo "PASS: $name"; npass=$((npass + 1)) ;; SKIP) echo "SKIP: $name"; nskip=$((nskip + 1)) ;; # TODO is munit's "expected to fail, and did": it counts as neither a # success nor a failure in munit's own tally, so reporting it as a pass # would put the per-case count and the summary count into disagreement. # nghttp2 declares no TODO case today; this is here so that one arriving # is visible rather than arithmetic. TODO) echo "SKIP: $name"; nskip=$((nskip + 1)) ;; FAIL|ERROR) echo "FAIL: $name"; nfail=$((nfail + 1)) ;; *) continue ;; esac done < "$out" echo "INFO: nghttp2-ptest prog=$pname listed=$listed reported=$((npass + nfail + nskip)) pass=$npass fail=$nfail skip=$nskip msucc=$msucc mtotal=$mtotal seed=$seed" done rm -f "$out" # The per-case verdicts above are the report; ptest-runner tallies them itself and # a non-zero exit here would be counted a second time as a harness failure. exit 0