. "$BST_LIB" # BST-TIMEOUT: 20000 # NOT the default run's cost -- see THE BOUND below # quilt 0.67 -- and, like 15-bash.test and 77-cunit.test, a suite whose evidence # is UPSTREAM'S OWN TEST SUITE rather than one we wrote. # # WHY UPSTREAM'S SUITE IS THE RIGHT ORACLE HERE. quilt is not a program that # computes something we could check an answer for; it is a workflow over a # directory of patches, and every one of its ~30 subcommands is a shell script # whose correctness is a sequence of side effects on ./patches, ./.pc and the # working tree. Andrew Morton's suite ships 64 literate tests -- a transcript of # commands and the exact output each must produce -- which is an oracle at a # resolution nothing we wrote by hand would reach. oe-core drops two of them # (mail.test needs an MTA, patch-wrapper.test needs the disabled wrapper), so 62 # ship here. # # WHAT A quilt PORT ACTUALLY TESTS ON THIS ARM. There is no compiled code in # quilt at all, so nothing here is about codegen. Every test is really a probe of # the RUNTIME: fork/exec of a dozen helpers per command, $(command substitution), # here-documents, temporary directories, file modes, symlinks, hard links, # timestamps, and the exit statuses of GNU patch/diff/diffstat/sed/awk/perl. That # makes this suite one of the broader userland integration tests on the image, # and its reds are almost always statements about the platform rather than about # quilt. # # THE BOUND, AND WHY THE 62 ARE OPT-IN. exec on this arm is a fresh wasm module # instantiation, and a quilt test is a transcript of dozens of commands -- # backup-files.test alone runs 136 -- so the suite pays that cost hundreds of # times over. Measured on fra2 under tools/brintos_local_verify.py (the # BRINTOS-169 proxy, node --liftoff-only, host load ~45 on 32 cores): about five # minutes per test file, against 188 s for all 62 on a native x86_64 control of # the same source. That is upwards of five hours for one suite, and it is why the # 62 do NOT run by default: the nightly sweep covers 160+ suites in ~45 minutes, # and a suite that alone costs seven times the whole sweep would either never # finish or turn every sweep red on a bound rather than on a defect. # # So the default run asserts the port and drives quilt through a real series # lifecycle below (new -> add -> refresh -> pop -> push, checking the file # contents at each step), and the upstream 62 are one environment variable away: # # BST_QUILT_PTEST=1 BST_SUITE_TIMEOUT_MAX=20000 brintos-selftest 53-quilt.test # # BOTH variables are needed, and passing only the first is the trap. The runner # takes a suite's `# BST-TIMEOUT:` marker as its bound but then CLAMPS it to # $BST_SUITE_TIMEOUT_MAX, whose default is 2400 -- so the 20000 declared above is # not in force until the caller raises that ceiling too. A run that hits the # clamp reports `bound=2400s ... suite-clamped` and no per-test verdict at all, # which reads like a hung suite rather than an un-raised ceiling. (Setting # BST_SUITE_TIMEOUT alone does nothing here: the file marker overrides it.) # # Two things should bring the cost down and neither is this suite's to do: the # real browser gate returning (BRINTOS-169) so the run is not on Liftoff-tier # wasm, and a cheaper exec. The recorded full-suite result, and the sharded way # it was actually obtained, are in docs/brintos-test-plan.md under quilt. require quilt quilt/present ptestdir=/usr/lib/quilt/ptest runner=/usr/bin/ptest-runner tmp=${TMPDIR:-/tmp}/bst-quilt.$$ mkdir -p "$tmp" 2>/dev/null || tmp=/tmp # ------------------------------------------------------------ what shipped ---- check_rc quilt/bin 0 test -x /usr/bin/quilt check_rc quilt/quiltrc 0 test -s /etc/quilt.quiltrc check_rc quilt/sharedir 0 test -d /usr/share/quilt check_rc quilt/ptest-dir 0 test -d "$ptestdir" check_rc quilt/ptest-runptest 0 test -x "$ptestdir/run-ptest" check_rc quilt/ptest-testsh 0 test -x "$ptestdir/test.sh" check_rc quilt/ptest-run 0 test -x "$ptestdir/test/run" check_rc quilt/ptest-quiltrc 0 test -s "$ptestdir/test/test.quiltrc" # oe-core removes exactly two of upstream's 64 tests, and for reasons that hold # here too: mail.test needs an MTA and patch-wrapper.test needs the wrapper this # recipe disables. Asserting their ABSENCE is what makes the count below a # statement rather than an accident. check_rc quilt/ptest-no-mail 1 test -e "$ptestdir/test/mail.test" check_rc quilt/ptest-no-wrapper 1 test -e "$ptestdir/test/patch-wrapper.test" check_eq quilt/ptest-test-count 62 "$(ls "$ptestdir"/test/*.test 2>/dev/null | wc -l | tr -d ' ')" # quilt's runtime closure outside its own package. configure.ac probes both of # these unconditionally -- QUILT_COMPAT_PROG_PATH(GETOPT, getopt) and # QUILT_COMPAT_PROG_PATH(COLUMN, column) -- and the installed scripts call them # by bare name off $PATH: every subcommand's argument parse is a `getopt --long` # and the usage table pipes through `column`. quilt_%.bbappend narrows the # recipe's util-linux RDEPENDS to exactly these two, because the umbrella # util-linux package cannot be installed on a static-only arm; asserting them # here is what keeps that narrowing honest. check_rc quilt/getopt-present 0 sh -c 'command -v getopt >/dev/null' check_rc quilt/column-present 0 sh -c 'command -v column >/dev/null' check_rc quilt/getopt-long 0 sh -c 'getopt -o a --long alpha -- --alpha >/dev/null' # The privilege drop upstream's run-ptest performs. Both pieces it needs are # here -- and it still cannot happen, because this arm wires no credential # setter (brintos/linux#19): setuid/setgid/setgroups are NULL rows in the wasm32 # syscall table and return ENOSYS, so su dies before test.sh is reached. # quilt/run-ptest carries the workaround; the case below is INVERTED -- it is # green while the gap exists and goes RED the day #19 lands, which is the signal # to delete that file and let poky's run-ptest win again. # # THE NEEDLE IS THE PLATFORM'S strerror, NOT ANY ONE TOOL'S WORDING, and it was # narrowed to that when shadow was ported. /bin/su was the busybox applet, which # dies inside initgroups(3) saying "can't set groups"; shadow's update-alternatives # priority 200 now owns the name, and its su dies one call earlier at setgid # (lib/setugid.c) saying "setgid". Both append strerror(ENOSYS), and the ENOSYS is # the whole of what this case reports -- keying on either applet's phrasing would # make it go red when the tool behind /bin/su changes, which is not the event it # exists to signal. The id still says "setgroups" because two files in this recipe # cite it by name; the syscall it names is one of the family, not the assertion. check_rc quilt/su-present 0 test -x /bin/su check_rc quilt/ptest-user 0 grep -q "^ptest:" /etc/passwd check_out quilt/su-setgroups-enosys "Function not implemented" sh -c 'su -c true ptest 2>&1; true' # ------------------------------------------------- quilt runs, on its own ----- # This is the default run's functional evidence, and it is a real quilt workflow # rather than a smoke test: one series driven through the whole cycle, checking # the working tree's CONTENT at each step, not just exit statuses. It is what # makes the opt-in above an addition to the coverage rather than the whole of it. check_out quilt/version "0.67" quilt --version w=$tmp/w mkdir -p "$w/patches" printf 'one\ntwo\nthree\n' > "$w/f.txt" ( cd "$w" || exit 1 QUILT_PATCHES=patches quilt new fix.patch >/dev/null 2>&1 || exit 1 QUILT_PATCHES=patches quilt add f.txt >/dev/null 2>&1 || exit 1 sed -i 's/two/TWO/' f.txt || exit 1 QUILT_PATCHES=patches quilt refresh >/dev/null 2>&1 || exit 1 ) 2>/dev/null check_rc quilt/new-add-refresh 0 test -s "$w/patches/fix.patch" check_rc quilt/refresh-is-diff 0 grep -q '^+TWO$' "$w/patches/fix.patch" check_eq quilt/applied patches/fix.patch "$(cd "$w" && QUILT_PATCHES=patches quilt applied 2>&1 | tail -1)" check_eq quilt/pop one "$(cd "$w" && QUILT_PATCHES=patches quilt pop -a >/dev/null 2>&1; sed -n 1p f.txt)" check_eq quilt/pop-content two "$(sed -n 2p "$w/f.txt")" check_eq quilt/push TWO "$(cd "$w" && QUILT_PATCHES=patches quilt push -a >/dev/null 2>&1; sed -n 2p f.txt)" # ------------------------------------------------- the 62, on explicit ask ---- # ptest-runner resolves /usr/lib itself, discovers quilt/ptest, chdirs in and # execs run-ptest under a pty; naming the ptest confines the run to this one # suite. -t is set high enough that it cannot bite: on this arm ptest-runner's # -t rides the relative-timer path, which never fires (brintos/linux#48), so a # tight bound would be an assertion about nothing. The bound that actually holds # this run is the brintos-selftest runner's own, declared at the top of this file # -- and clamped unless the caller raises BST_SUITE_TIMEOUT_MAX with it. if [ ! -x "$runner" ]; then bst_skip quilt/ptest-runner-present "this image ships no ptest-runner" for c in ptest-runner-rc runner-begin runner-end runner-tally runner-no-error \ suite-tests-run join-nonempty; do bst_skip "quilt/$c" "no ptest-runner to drive the suite" done rm -rf "$tmp" return 2>/dev/null || exit 0 fi bst_ok quilt/ptest-runner-present if [ -z "${BST_QUILT_PTEST:-}" ]; then for c in ptest-runner-rc runner-begin runner-end runner-tally runner-no-error \ suite-tests-run join-nonempty; do bst_skip "quilt/$c" \ "upstream's 62 are opt-in: ~5 min/test here (over 5 h) against 188 s for all 62 natively, so a default run would cost seven times the whole nightly sweep -- BST_QUILT_PTEST=1 BST_SUITE_TIMEOUT_MAX=20000 brintos-selftest 53-quilt.test" done rm -rf "$tmp" return 2>/dev/null || exit 0 fi out=$tmp/runner.out "$runner" -t 20000 quilt > "$out" 2>&1 rc=$? check_eq quilt/ptest-runner-rc 0 "$rc" # ------------------------------------------------------------- the join ------- # One awk pass over the transcript emitting "\t\t" per case, so # the reporting loop below forks nothing. test.sh prints "PASS: " or # "FAIL: " per test, in series order. awk -v OFS='\t' ' function emit(id, want, got) { print id, want, got } $0 ~ /^PASS: / { name = $2; verdict[name] = "PASS"; order[++n] = name; next } $0 ~ /^FAIL: / { name = $2; verdict[name] = "FAIL"; order[++n] = name; next } $0 ~ /^BEGIN: / { begin_line = $2; next } $0 ~ /^END: / { end_line = $2; next } $0 ~ /^TOTAL: / { tally = $0; next } $0 ~ /^TIMEOUT: / { runner_error = runner_error $0 " "; next } $0 ~ /^ERROR: / { runner_error = runner_error $0 " "; next } END { for (i = 1; i <= n; i++) emit("quilt/t-" order[i], "PASS", verdict[order[i]]) # A run cut off a third of the way through leaves every test it DID reach # green, so the COUNT is asserted separately from the verdicts. emit("quilt/suite-tests-run", TESTS, n + 0) emit("quilt/runner-begin", PTESTDIR, begin_line "") emit("quilt/runner-end", PTESTDIR, end_line "") # ptest-runner counts PTESTS, not cases: run-ptest exits on test.sh status, # and test.sh now always exits 0 (it reports per test instead of dying), so # this is "the harness completed one ptest" and NOT the suite verdict. The # quilt/t-* cases above are the verdict. emit("quilt/runner-tally", "TOTAL: 1 FAIL: 0", tally "") # "none" rather than "" -- an empty middle field collapses under the # IFS= of the loop that reads this back, which swaps want and got and # makes the red unreadable. Note there is no apostrophe anywhere in this awk # block, and there must not be: the program is one single-quoted shell word, # so an apostrophe closes it and the whole FILE stops parsing. emit("quilt/runner-no-error", "none", runner_error == "" ? "none" : runner_error) } ' TESTS=62 PTESTDIR="$ptestdir" "$out" > "$tmp/verdicts" 2>/dev/null n=0 while IFS=' ' read -r id want got; do [ -n "$id" ] || continue check_eq "$id" "$want" "$got" n=$((n + 1)) done < "$tmp/verdicts" # A join that emitted nothing at all is itself a failure, and one that emitted # half its cases would otherwise read as a short but green suite. check_rc quilt/join-nonempty 0 test "$n" -gt 1 rm -rf "$tmp"