# 99-debianutils.test -- debianutils 5.16 (poky, recipes-support). # # The small Debian helper set the installation scripts of Debian packages lean # on: run-parts executes every script in a directory, tempfile mints a unique # name, savelog rotates a log, which.debianutils resolves a name against PATH, # ischroot answers whether the caller is inside a chroot, and add-shell / # remove-shell / installkernel edit system state. # # Two of these are C programs (run-parts, tempfile; ischroot is a third) and the # rest are POSIX shell. What makes the set interesting on this arm is run-parts: # it is the only tool on this image whose whole job is fork(2) + exec(2) + # waitpid(2) around a SIGCHLD handler, so it measures the platform's child-reaping # path rather than its file or string handling. # # Calibrated against a NATIVE x86_64 build of the SAME upstream commit # (9e0facf19b17), which scores all-green on every functional case -- so a red # here is a target-only divergence, not a miscalibrated assertion. The two # alternatives/ cases assert this IMAGE's update-alternatives wiring and have no # native counterpart by construction. . "$BST_LIB" require run-parts debianutils/present W=${TMPDIR:-/tmp}/bst-dbu.$$ mkdir -p "$W" 2>/dev/null || { bst_notok debianutils/workdir "cannot mkdir $W"; exit 0; } # WHICH is the debianutils `which`, which is NOT /usr/bin/which on this image: # GNU which carries a higher update-alternatives priority and owns that name, so # debianutils' copy ships under its unsuffixed alternative target. Resolve it # rather than assume either spelling. if command -v which.debianutils >/dev/null 2>&1; then WHICH=which.debianutils elif [ -x /usr/bin/which.debianutils ]; then WHICH=/usr/bin/which.debianutils else WHICH=""; fi # has : ok iff contains the literal . has() { case "$3" in *"$2"*) bst_ok "$1" ;; *) bst_notok "$1" "want:'$2' got:'$(_bst_1line "$3")'" ;; esac } # hasnt : ok iff does NOT contain . hasnt() { case "$3" in *"$2"*) bst_notok "$1" "unwanted:'$2' in '$(_bst_1line "$3")'" ;; *) bst_ok "$1" ;; esac } # --------------------------------------------------------------------------- # run-parts -- directory listing (no fork: these exercise the scan/sort/filter) # --------------------------------------------------------------------------- D=$W/parts.d mkdir -p "$D" printf '#!/bin/sh\necho SAW-one\ntouch %s/ran-one\n' "$W" > "$D/10one" printf '#!/bin/sh\necho SAW-two\n' > "$D/20two" printf '#!/bin/sh\necho SAW-three\necho "ARG=[$1]"\n' > "$D/30three" printf '#!/bin/sh\necho SAW-old\n' > "$D/40old.dpkg-old" chmod +x "$D/10one" "$D/20two" "$D/30three" "$D/40old.dpkg-old" check_out run-parts/version "Debian run-parts program" run-parts --version out=$(run-parts --list "$D" 2>&1) has run-parts/list-one "$D/10one" "$out" has run-parts/list-three "$D/30three" "$out" # a .dpkg-old suffix is one of run-parts' built-in exclusions hasnt run-parts/list-skips-dpkg-old "40old.dpkg-old" "$out" # --list emits them in lexical order, which is the order they would execute in check_eq run-parts/list-order "10one 20two 30three" \ "$(run-parts --list "$D" 2>/dev/null | sed 's:.*/::' | tr '\n' ' ' | sed 's/ $//')" check_out run-parts/regex "$D/10one" run-parts --list --regex '^10' "$D" out=$(run-parts --list --regex '^10' "$D" 2>&1) hasnt run-parts/regex-excludes "20two" "$out" check_eq run-parts/reverse "30three 20two 10one" \ "$(run-parts --list --reverse "$D" 2>/dev/null | sed 's:.*/::' | tr '\n' ' ' | sed 's/ $//')" # --test names what WOULD run and must not run it rm -f "$W/ran-one" out=$(run-parts --test "$D" 2>&1) has run-parts/test-lists "$D/10one" "$out" check_rc run-parts/test-does-not-execute 1 test -e "$W/ran-one" check_rc run-parts/missing-dir-rc 1 run-parts "$W/nosuch.d" out=$(run-parts "$W/nosuch.d" 2>&1) has run-parts/missing-dir-msg "failed to open directory" "$out" # --------------------------------------------------------------------------- # run-parts -- execution (fork + exec + waitpid). --report drives the children # through pselect(2) + waitpid(WNOHANG); the plain path uses a BLOCKING # waitpid(2), which is where this arm diverges. # --------------------------------------------------------------------------- out=$(run-parts --report "$D" 2>&1); rc=$? check_eq run-parts/report-rc 0 "$rc" has run-parts/report-runs-first "SAW-one" "$out" has run-parts/report-runs-last "SAW-three" "$out" has run-parts/report-header "$D/20two:" "$out" # an argument passed through to every script out=$(run-parts --report --arg=HELLO "$D" 2>&1) has run-parts/arg-passthrough "ARG=[HELLO]" "$out" # a child's non-zero exit is reported and makes run-parts exit 1 B=$W/bad.d mkdir -p "$B" printf '#!/bin/sh\necho FAILING\nexit 3\n' > "$B/10bad"; chmod +x "$B/10bad" check_rc run-parts/child-failure-rc 1 run-parts --report "$B" out=$(run-parts --report "$B" 2>&1) has run-parts/child-failure-msg "exited with return code 3" "$out" # The plain (non---report) execute path. On a working kernel this runs every # script and exits 0; here the blocking waitpid(2) for the FIRST child returns # EINTR instead of reaping it, run-parts treats that as fatal, and the remaining # scripts never run. Left FAILING deliberately -- it flips green on its own when # the kernel stops interrupting a blocking wait. out=$(run-parts "$D" 2>&1); rc=$? check_eq run-parts/exec-rc 0 "$rc" has run-parts/exec-runs-first "SAW-one" "$out" has run-parts/exec-runs-last "SAW-three" "$out" # --------------------------------------------------------------------------- # tempfile # --------------------------------------------------------------------------- t=$(tempfile 2>/dev/null) if [ -n "$t" ] && [ -f "$t" ]; then bst_ok tempfile/creates else bst_notok tempfile/creates "no file at '$(_bst_1line "$t")'"; fi [ -n "$t" ] && rm -f "$t" # --directory is only consulted when TMPDIR is UNSET -- upstream documents that # precedence (tempfile.1 (a) TMPDIR, (b) --directory), so the env is cleared here # to make -d unambiguous rather than assert a shape the environment decides. t=$(env -u TMPDIR tempfile -d "$W" -p bst -s .txt 2>/dev/null) if [ -n "$t" ] && [ -f "$t" ]; then bst_ok tempfile/dir-prefix-suffix-creates else bst_notok tempfile/dir-prefix-suffix-creates "no file at '$(_bst_1line "$t")'"; fi case "$t" in "$W"/bst*.txt) bst_ok tempfile/dir-prefix-suffix-name ;; *) bst_notok tempfile/dir-prefix-suffix-name "want $W/bst*.txt got '$(_bst_1line "$t")'" ;; esac # and TMPDIR wins over -d when both are given -- the documented precedence E=$W/envdir; mkdir -p "$E" t2=$(TMPDIR=$E tempfile -d "$W" -p bst 2>/dev/null) case "$t2" in "$E"/bst*) bst_ok tempfile/tmpdir-outranks-d ;; *) bst_notok tempfile/tmpdir-outranks-d "want $E/bst* got '$(_bst_1line "$t2")'" ;; esac [ -n "$t2" ] && rm -f "$t2" # two calls never collide a=$(tempfile -d "$W" 2>/dev/null); b=$(tempfile -d "$W" 2>/dev/null) if [ -n "$a" ] && [ -n "$b" ] && [ "$a" != "$b" ]; then bst_ok tempfile/unique else bst_notok tempfile/unique "a='$a' b='$b'"; fi rm -f "$t" "$a" "$b" # --------------------------------------------------------------------------- # which.debianutils (shipped, but NOT the /usr/bin/which alternative here) # --------------------------------------------------------------------------- if [ -z "$WHICH" ]; then bst_skip which-debianutils/found "which.debianutils not installed on this image" else # assert it resolves sh to an EXECUTABLE path ending in /sh -- which directory # that is depends on PATH order and differs between the target and the native # control box, so pinning a literal would test the box, not the tool s=$($WHICH sh 2>/dev/null) case "$s" in */sh) [ -x "$s" ] && bst_ok which-debianutils/found \ || bst_notok which-debianutils/found "not executable: '$s'" ;; *) bst_notok which-debianutils/found "want */sh got '$(_bst_1line "$s")'" ;; esac check_rc which-debianutils/missing-rc 1 $WHICH bstnosuchcommand out=$($WHICH bstnosuchcommand 2>/dev/null) check_eq which-debianutils/missing-silent "" "$out" out=$($WHICH -a sh 2>/dev/null) has which-debianutils/all "/bin/sh" "$out" fi # --------------------------------------------------------------------------- # savelog # --------------------------------------------------------------------------- L=$W/logs mkdir -p "$L" echo first > "$L/app.log" ( cd "$L" && savelog app.log >/dev/null 2>&1 ) check_rc savelog/rotates-to-0 0 test -f "$L/app.log.0" check_eq savelog/rotated-content "first" "$(cat "$L/app.log.0" 2>/dev/null)" echo second > "$L/app.log" ( cd "$L" && savelog app.log >/dev/null 2>&1 ) # the previous .0 is pushed down and compressed check_rc savelog/compresses-older 0 test -f "$L/app.log.1.gz" # --------------------------------------------------------------------------- # ischroot -- exit 0 in a chroot, 1 outside one, 2 when it cannot tell # --------------------------------------------------------------------------- check_rc ischroot/not-in-chroot 1 ischroot check_rc ischroot/quiet-rc 1 ischroot -t check_eq ischroot/quiet-silent "" "$(ischroot -t 2>&1)" check_rc ischroot/fake 1 ischroot -f # --------------------------------------------------------------------------- # add-shell / remove-shell -- must round-trip /etc/shells # --------------------------------------------------------------------------- if [ -w /etc/shells ] || [ "$(id -u 2>/dev/null)" = 0 ]; then add-shell /bin/bstfakeshell >/dev/null 2>&1 out=$(cat /etc/shells 2>/dev/null) has add-shell/adds "/bin/bstfakeshell" "$out" remove-shell /bin/bstfakeshell >/dev/null 2>&1 out=$(cat /etc/shells 2>/dev/null) hasnt remove-shell/removes "/bin/bstfakeshell" "$out" has remove-shell/keeps-others "/bin/sh" "$out" else bst_skip add-shell/adds "/etc/shells not writable" bst_skip remove-shell/removes "/etc/shells not writable" bst_skip remove-shell/keeps-others "/etc/shells not writable" fi # --------------------------------------------------------------------------- # installkernel -- refuses without its four arguments # --------------------------------------------------------------------------- check_rc installkernel/usage-rc 1 installkernel out=$(installkernel 2>&1) has installkernel/usage-msg "Usage: installkernel" "$out" # --------------------------------------------------------------------------- # update-alternatives wiring on THIS image (no native counterpart) # --------------------------------------------------------------------------- if [ -L /bin/run-parts ]; then check_eq alternatives/run-parts-is-debianutils "run-parts.debianutils" \ "$(readlink /bin/run-parts 2>/dev/null)" else bst_skip alternatives/run-parts-is-debianutils "/bin/run-parts is not a symlink here" fi # debianutils also ships a `which`, and update-alternatives decides who owns the # unsuffixed name. WHICHEVER wins, the image's `which` must still work -- this is # the regression this port could plausibly cause, so it is the thing asserted. # (Which implementation actually wins is image topology, not a functional # invariant: GNU which outranks debianutils here, while on a stock Debian box # debianutils' copy IS the system which. It is recorded in the image's recipe # comment block rather than pinned as a test case that a priority change would # turn red for no functional reason.) s=$(/usr/bin/which sh 2>/dev/null) case "$s" in */sh) [ -x "$s" ] && bst_ok alternatives/which-still-resolves \ || bst_notok alternatives/which-still-resolves "not executable: '$s'" ;; *) bst_notok alternatives/which-still-resolves "want */sh got '$(_bst_1line "$s")'" ;; esac rm -rf "$W"