. "$BST_LIB" # apr (Apache Portable Runtime 1.7.5) -- the second port on this image whose # evidence is UPSTREAM'S OWN TEST SUITE rather than one we wrote, and the broadest # one this image has. OpenEmbedded packages APR's abts suite as a ptest, so what # executes on target is test/testall: the binary APR's authors run before a # release, walking forty-four subsystems in one process. # # WHY apr IS WORTH MORE THAN ITS OWN PORT. Every other library port here proves one # subsystem. testall proves the syscall surface in breadth, in one run: fork/exec # and wait (testproc), pipes blocking and non-blocking (testpipe), poll and epoll # (testpoll), UNIX and INET sockets (testsockets/testsock/testsockopt), mmap # (testmmap), POSIX and SysV shm (testshm), file locking (testflock) and both # mutex flavours (testprocmutex/testglobalmutex), threads and condition variables # (testthread/testcond), large files (testlfs), directories and stat # (testdir/testfileinfo), temp files (testtemp), atomics (testatomic), random # (testrand), env (testenv), users (testuser), DSO (testdso). A red here names the # subsystem directly. # # THE ORACLE IS A NATIVE x86_64 BUILD OF THE IDENTICAL 1.7.5 TARBALL (sha256 # cd0f5d52b9ab1704c72160c5ee3ed5d3d4ca2df4a7f8ab564e3cb352b67232f2, the sum the # recipe pins -- verified against SRC_URI[sha256sum], not taken on trust) # configured with the recipe's own flags plus the distro-wide --disable-shared, # then `make && cd test && make && ./testall`. Every expectation below was read off # that run. It scores 43 SUCCESS and 1 FAILED of 44 modules. # # THE ONE NATIVE FAILURE IS EXPECTED AND IS ASSERTED AS A FAILURE. testdso loads a # shared module at runtime; conf/distro/brintos.conf appends --disable-shared for # every target recipe, so libtool emits mod_test.a and no mod_test.so, and there is # nothing for apr_dso_load to open. The native control fails it 8 of 9 for exactly # the same reason on x86_64, which is what makes it a policy consequence rather # than a wasm32 finding -- so apr/mod-testdso wants FAILED. A SUCCESS there would # mean the distro's static-only rule stopped binding and is worth knowing too. # # SPAWN BUDGET. exec on this arm is a fresh wasm module instantiation, ~2-3 s, and # testall forks children of its own in five modules. So this file runs the suite # ONCE, flattens the archive once, joins everything in a SINGLE awk pass emitting a # pre-computed triple per case, and the reporting loop forks # nothing. Presence checks use check_rc with `test`, an ash builtin. ptestdir=/usr/lib/apr/ptest runner=/usr/bin/ptest-runner lib=/usr/lib/libapr-1.a exp=/usr/lib/apr.exp pc=/usr/lib/pkgconfig/apr-1.pc ver=/usr/include/apr_version.h tmp=${TMPDIR:-/tmp}/bst-apr.$$ mkdir -p "$tmp" 2>/dev/null || tmp=/tmp # ----------------------------------------------------------- what shipped ---- check_rc apr/ptest-dir 0 test -d "$ptestdir" check_rc apr/ptest-runptest 0 test -x "$ptestdir/run-ptest" check_rc apr/ptest-testbin 0 test -x "$ptestdir/test/testall" check_rc apr/ptest-data 0 test -d "$ptestdir/test/data" check_rc apr/ar-present 0 test -s "$lib" check_rc apr/exp-present 0 test -s "$exp" check_rc apr/pc-present 0 test -f "$pc" check_rc apr/config-present 0 test -x /usr/bin/apr-1-config check_rc apr/rules-present 0 test -s /usr/share/build-1/apr_rules.mk # The helper binaries testall EXECUTES. oe-core's do_install_ptest installs six of # the ten and leaves four behind; the bbappend installs all ten, because a module # that fails for want of a file measures our packaging and not the port. Each of # these is named by a specific module: proc_child/readchild/sockchild/tryread by # testproc, occhild by testoc, testshmproducer+testshmconsumer by testshm, # globalmutexchild by testglobalmutex, sockperf standalone. for b in proc_child readchild sockchild sockperf tryread \ occhild testshmproducer testshmconsumer globalmutexchild; do check_rc "apr/ptest-helper-$b" 0 test -x "$ptestdir/test/$b" done # The headers. Each is one public subsystem interface, so a missing one means a # configure result did not take rather than a lost file. They are FLAT in # /usr/include: oe_runconf passes --includedir=${includedir}, overriding the # include/apr-1 default APR's own configure would pick -- so the native control, # which is configured without that flag, is not the oracle for these paths. for h in apr apr_allocator apr_atomic apr_cstr apr_dso apr_encode apr_env \ apr_errno apr_escape apr_file_info apr_file_io apr_fnmatch apr_general \ apr_getopt apr_global_mutex apr_hash apr_inherit apr_lib apr_mmap \ apr_network_io apr_perms_set apr_poll apr_pools apr_portable \ apr_proc_mutex apr_random apr_ring apr_shm apr_signal apr_skiplist \ apr_strings apr_support apr_tables apr_thread_cond apr_thread_mutex \ apr_thread_proc apr_thread_rwlock apr_time apr_user apr_version \ apr_want; do check_rc "apr/hdr-$h" 0 test -s "/usr/include/$h.h" done # ------------------------------------------------------- run it, once --------- # TWO WAYS IN, AND THE HARNESS IS PREFERRED WHENEVER IT IS THERE, because the # ticket closes on the suite run UNDER ptest-runner. ptest-runner walks # /usr/lib/*/ptest, chdirs into apr/ptest and execs run-ptest under a pty; naming # the ptest confines the run to this one suite. # # THE FALLBACK IS NOT COSMETIC ON THIS IMAGE. ptest-runner traps in glibc's # __uflow on a non-tty stream (brintos.io/brintos/linux-in-the-browser#148), so on # a runtime carrying that defect it produces no suite output at all. Then the six # runner/* cases below red -- correctly, that IS the finding -- and the module # verdicts are recovered by running run-ptest's own testall directly, which is the # same binary the harness would have exec'd. apr/suite-source says which path the # numbers came from, so a reader never has to infer it. out=$tmp/run.out harness=0 source=direct if [ -x "$runner" ]; then harness=1 bst_ok apr/ptest-runner-present "$runner" apr > "$out" 2>&1 rc=$? check_eq apr/ptest-runner-rc 0 "$rc" else bst_skip apr/ptest-runner-present "this image ships no ptest-runner" bst_skip apr/ptest-runner-rc "no ptest-runner to run" for c in runner-begin runner-end runner-tally runner-no-error; do bst_skip "apr/$c" "harness cases have no subject without ptest-runner" done fi # Did the harness deliver module verdicts? grep -c is one process and settles it. got_mods=0 if [ -s "$out" ]; then got_mods=$(grep -c 'SUCCESS\|FAILED\|^PASS: \|^FAIL: ' "$out" 2>/dev/null) || got_mods=0 fi if [ "$got_mods" -lt 2 ]; then # Harness produced nothing usable (or is absent): run the suite directly, from # the ptest dir, because testall resolves ./data and its helper children # relative to the CWD. # # BACKGROUNDED AND POLLED, NOT WAITED ON, and that is the #148 workaround rather # than a style choice: a FOREGROUND child whose stdout is a redirected file # completes correctly on this image and is then never reaped -- the parent's # wait never returns -- so `./testall > file` would wedge this whole suite until # BST_SUITE_TIMEOUT killed it. Backgrounded, nothing foreground-waits, testall # runs to completion and lands its full output; the subshell is left wedged in # its own wait and is orphaned when this file exits, which costs nothing. The # end of the run is therefore read off the FILE, at abts's own terminal line # ("All tests passed." or the "Failed Tests" table), not off an exit status. # BRINTOS-WORKAROUND: brintos/linux-in-the-browser#148 remove -- delete the # poll and go back to a plain foreground redirect once wait(2) reaps again. ( cd "$ptestdir/test" && ./testall > "$tmp/direct.out" 2>&1 ) & i=0 while [ "$i" -lt 40 ]; do grep -q 'All tests passed\.\|Failed Tests' "$tmp/direct.out" 2>/dev/null && break sleep 10 i=$((i + 1)) done cat "$tmp/direct.out" >> "$out" 2>/dev/null else source=ptest-runner fi # The ticket closes on the suite run UNDER the harness, so this is asserted rather # than reported: a `direct` source is a red naming #148, not a quiet substitution. check_eq apr/suite-source ptest-runner "$source" # --------------------------------------------------- the archive, flattened --- idx=$tmp/ar.idx tr -c '\40-\176\n' ' ' < "$lib" > "$idx" 2>/dev/null # ------------------------------------------------------- the single join ------ awk -v OFS='\t' ' function emit(id, want, got) { print id, want, got } # ---- file 1: the suite run ---- # Two spellings reach this file. Raw abts, when testall ran directly: # testatomic : SUCCESS # testdso : FAILED 8 of 9 # and run-ptest sed-rewritten, under the harness: # PASS: testatomic # The rewrite is fragile upstream -- its \(.*\): SUCCESS anchor misses any # module that printed to stdout before its verdict (testfile emits "abcdef"), # which then arrives in the raw spelling on the SAME run. Both are accepted. FILENAME == RUNOUT { if ($1 == "PASS:") { verdict[$2] = "SUCCESS"; nmod++ } else if ($1 == "FAIL:") { verdict[$2] = "FAILED"; nmod++ } else if ($0 ~ /^[a-z][a-zA-Z0-9_]* *: /) { if ($0 ~ /SUCCESS/) { verdict[$1] = "SUCCESS"; nmod++ } else if ($0 ~ /FAILED/) { verdict[$1] = "FAILED"; nmod++ } } if ($0 ~ /^BEGIN: /) begin_line = $2 if ($0 ~ /^END: /) end_line = $2 if ($0 ~ /^TOTAL: /) tally = $0 if ($0 ~ /^ERROR: /) runner_error = runner_error $0 " " next } # ---- file 2: apr_version.h ---- FILENAME == VER { if ($0 ~ /^#define APR_MAJOR_VERSION/) major = $3 if ($0 ~ /^#define APR_MINOR_VERSION/) minor = $3 if ($0 ~ /^#define APR_PATCH_VERSION/) patch = $3 next } # ---- file 3: apr-1.pc ---- FILENAME == PC { if ($0 ~ /^Name: /) { sub(/^Name: */, ""); pcname = $0 } if ($0 ~ /^Version: /) { sub(/^Version: */, ""); pcver = $0 } if ($0 ~ /^prefix=/) { sub(/^prefix=/, ""); pcprefix = $0 } next } # ---- file 4: apr.exp, the autogenerated export list ---- FILENAME == EXP { if ($0 ~ /^apr_/) { nexp++; expsym[$1] = 1 } ; next } # ---- file 5: the flattened archive index ---- FILENAME == IDX { idx = idx " " $0; next } function has(tok) { return index(" " idx " ", " " tok " ") > 0 } END { # --- the suite, module by module. THESE ARE THE PORT EVIDENCE. --- # 43 want SUCCESS; testdso wants FAILED for the --disable-shared reason the # native control reproduces on x86_64. The order is the one testall reports in. n = split("testatomic testdir testdup testencode testenv testescape testfile " \ "testfilecopy testfileinfo testflock testfmt testfnmatch testargs " \ "testhash testipsub testlock testcond testlfs testmmap testnames " \ "testoc testpath testpipe testpoll testpools testproc testprocmutex " \ "testrand testsleep testshm testsock testsockets testsockopt teststr " \ "teststrnatcmp testtable testtemp testthread testtime testud " \ "testuser testvsn testskiplist", mods, " ") for (i = 1; i <= n; i++) emit("apr/mod-" mods[i], "SUCCESS", (mods[i] in verdict ? verdict[mods[i]] : "ABSENT")) emit("apr/mod-testdso", "FAILED", ("testdso" in verdict ? verdict["testdso"] : "ABSENT")) # All forty-four ran. A truncated run whose reported modules each pass is not # a pass, and this is the case that says so. emit("apr/suite-modules-run", "44", nmod "") # --- the harness. Only when ptest-runner actually ran. --- if (HARNESS == "1") { emit("apr/runner-begin", PTESTDIR, begin_line "") emit("apr/runner-end", PTESTDIR, end_line "") # run-ptest exits on the sed at the end of its pipeline, so ptest-runner # reports one ptest and zero failures whatever testall did -- which is why # the tally is asserted BESIDE the module verdicts and never instead of them. emit("apr/runner-tally", "TOTAL: 1 FAIL: 0", tally "") # ptest-runner reports its own fork/execv/openpty failures as ERROR lines # and still exits 0 for some of them, so absence is asserted directly. emit("apr/runner-no-error", "", runner_error "") } # --- the payload --- emit("apr/hdr-major", "1", major "") emit("apr/hdr-minor", "7", minor "") emit("apr/hdr-patch", "5", patch "") emit("apr/pc-name", "APR", pcname "") emit("apr/pc-version", "1.7.5", pcver "") emit("apr/pc-prefix", "/usr", pcprefix "") # apr.exp is generated from the compiled objects by make_exports.awk, so its # size is a direct measure of how much of APR actually compiled. The native # control emits 561 lines, 558 of them apr_ symbols. emit("apr/exp-symbols", "558", nexp "") ne = split("apr_pool_create_ex apr_pool_destroy apr_palloc " \ "apr_file_open apr_file_read apr_file_write apr_file_close " \ "apr_socket_create apr_socket_connect apr_socket_send " \ "apr_thread_create apr_thread_join apr_proc_create apr_proc_wait " \ "apr_shm_create apr_mmap_create apr_poll apr_pollset_create " \ "apr_global_mutex_create apr_proc_mutex_create apr_dso_load " \ "apr_hash_make apr_table_make apr_time_now apr_initialize", want_e, " ") for (i = 1; i <= ne; i++) emit("apr/exp-sym-" want_e[i], want_e[i], (want_e[i] in expsym ? want_e[i] : "MISSING")) # A representative member per subsystem directory in libapr-1.a. ar stores a # short member name slash-terminated in its own header, which is the token to # look for; a bare "proc.o" is not in the file at all. no = split("apr_pools.o open.o sockets.o proc.o thread.o shm.o mmap.o poll.o " \ "apr_hash.o apr_tables.o time.o dso.o global_mutex.o apr_random.o " \ "apr_strings.o apr_encode.o flock.o", want_o, " ") for (i = 1; i <= no; i++) emit("apr/ar-member-" want_o[i], want_o[i], (has(want_o[i] "/") ? want_o[i] : "MISSING")) } ' RUNOUT="$out" VER="$ver" PC="$pc" EXP="$exp" IDX="$idx" PTESTDIR="$ptestdir" HARNESS="$harness" \ "$out" "$ver" "$pc" "$exp" "$idx" > "$tmp/verdicts" 2>/dev/null # ------------------------------------------------------ report, fork-free ----- # A join that produced nothing is itself a failure, and one that silently emitted # half its cases would otherwise read as a short but green suite. n=0 while IFS=' ' read -r id want got; do [ -n "$id" ] || continue check_eq "$id" "$want" "$got" n=$((n + 1)) done < "$tmp/verdicts" if [ "$harness" = 1 ]; then check_eq apr/join-complete 98 "$n" else check_eq apr/join-complete 94 "$n"; fi rm -rf "$tmp"