. "$BST_LIB" # GNU time is invoked by ABSOLUTE PATH throughout: busybox ash (the target login # shell, and the shell each suite runs under) implements `time` as a builtin # keyword, so a bare `time` never reaches this port. /usr/bin/time is also the # update-alternatives name the recipe registers at priority 100, so asserting the # GNU banner through that path is what proves the alternative resolves to this # port and not to a busybox applet. require /usr/bin/time time/present tmp=${TMPDIR:-/tmp}/bst-time.$$ # --- identity --------------------------------------------------------------- # Note the exact banner: `time (GNU Time) ...`, not "GNU time ...". The version # field is deliberately NOT asserted: configure.ac derives it with # m4_esyscmd([build-aux/git-version-gen .tarball-version]), the release tarball # ships git-version-gen but no .tarball-version, and `inherit autotools` # regenerates configure -- so a correct Yocto build reports # `time (GNU Time) UNKNOWN` while the shipped configure would have said 1.9. # Pinning the digits reds a perfectly good port; the parenthesised product name # is what proves this is GNU time rather than a busybox applet. check_out time/version "time (GNU Time)" /usr/bin/time --version check_out time/help "Run COMMAND, then print system resource usage." /usr/bin/time --help # --- the fundamental path: fork, exec, wait4 the child ---------------------- # Everything below depends on this one working: time forks, execvp's COMMAND, # and wait4()s it for the rusage it reports. A substrate that cannot do that # fails here first, which is why it is asserted on its own before any format. check time/exec-true /usr/bin/time true # --- default report (stderr, two lines) ------------------------------------- check_out time/default-elapsed "elapsed" /usr/bin/time true check_out time/default-maxresident "maxresident)k" /usr/bin/time true check_out time/default-pagefaults "pagefaults" /usr/bin/time true # --- POSIX format (-p): three lines, real/user/sys --------------------------- check_out time/posix-real "real " /usr/bin/time -p true check_out time/posix-user "user " /usr/bin/time -p true check_out time/posix-sys "sys " /usr/bin/time -p true # --- custom format (-f) ------------------------------------------------------ # %C is the command line as invoked, including its arguments. check_out time/fmt-command "[/bin/echo hi]" /usr/bin/time -f "[%C]" /bin/echo hi check_out time/fmt-command-args "[sh -c exit 0]" /usr/bin/time -f "[%C]" sh -c "exit 0" check_out time/fmt-exit-zero "X=0" /usr/bin/time -f "X=%x" true # The timing fields are asserted for SHAPE, never for magnitude: userspace clock # reads are frozen on wasm32-hwjs (linux-in-the-browser#66), so %e/%U/%S can # legitimately read 0.00 on a machine where the port is perfectly correct. A # "nonzero elapsed" assertion would fail for a reason that has nothing to do # with time working. e=$(/usr/bin/time -f "%e" true 2>&1) case "$e" in *[0-9].[0-9][0-9]) bst_ok time/elapsed-shape ;; *) bst_notok time/elapsed-shape "want N.NN got:'$(_bst_1line "$e")'" ;; esac m=$(/usr/bin/time -f "%M" true 2>&1) case "$m" in ''|*[!0-9]*) bst_notok time/maxrss-shape "want integer got:'$(_bst_1line "$m")'" ;; *) bst_ok time/maxrss-shape ;; esac p=$(/usr/bin/time -f "%P" true 2>&1) case "$p" in *%) bst_ok time/cpu-pct-shape ;; *) bst_notok time/cpu-pct-shape "want N% got:'$(_bst_1line "$p")'" ;; esac # --- the child's own stdout is passed through untouched ---------------------- check_out time/child-stdout "PAYLOAD" /usr/bin/time /bin/echo PAYLOAD # ...and the report goes to STDERR, so stdout carries the child's output ALONE. # This is the half a combined-output assertion cannot see. out=$(/usr/bin/time /bin/echo PAYLOAD 2>/dev/null) check_eq time/report-on-stderr "PAYLOAD" "$out" # --- -o / -a: write the report to a file ------------------------------------- rm -f "$tmp.out" /usr/bin/time -o "$tmp.out" true 2>/dev/null check_out time/outfile-written "elapsed" cat "$tmp.out" check_eq time/outfile-lines "2" "$(wc -l < "$tmp.out" | tr -d ' ')" /usr/bin/time -a -o "$tmp.out" true 2>/dev/null check_eq time/append-lines "4" "$(wc -l < "$tmp.out" | tr -d ' ')" # --- -v: the long human-readable report -------------------------------------- check_out time/verbose-banner 'Command being timed: "true"' /usr/bin/time -v true check_out time/verbose-maxrss "Maximum resident set size" /usr/bin/time -v true check_out time/verbose-wall "Elapsed (wall clock) time" /usr/bin/time -v true # --- exit-status propagation ------------------------------------------------- # time exits with the CHILD's status, and (without -q) narrates a non-zero one on # stderr. Both halves are asserted: the status a caller branches on, and the # diagnostic a reader sees. check_out cannot express these -- it requires rc 0 -- # so the output is captured explicitly. check_rc time/rc-propagates 3 /usr/bin/time -f "X=%x" sh -c "exit 3" x=$(/usr/bin/time -f "X=%x" sh -c "exit 3" 2>&1) case "$x" in *X=3*) bst_ok time/fmt-exit-nonzero ;; *) bst_notok time/fmt-exit-nonzero "want X=3 got:'$(_bst_1line "$x")'" ;; esac case "$x" in *"Command exited with non-zero status 3"*) bst_ok time/nonzero-narrated ;; *) bst_notok time/nonzero-narrated "missing narration: '$(_bst_1line "$x")'" ;; esac # -q suppresses that narration while still reporting and still propagating. q=$(/usr/bin/time -q sh -c "exit 5" 2>&1) case "$q" in *"Command exited"*) bst_notok time/quiet-suppresses "narration present under -q: '$(_bst_1line "$q")'" ;; *) bst_ok time/quiet-suppresses ;; esac check_rc time/quiet-rc 5 /usr/bin/time -q sh -c "exit 5" # --- a command that cannot be run -------------------------------------------- # 127 is the shell's own not-found convention, which time adopts. check_rc time/notfound-rc 127 /usr/bin/time /nonexistent-brintos-125 n=$(/usr/bin/time /nonexistent-brintos-125 2>&1) case "$n" in *"cannot run"*) bst_ok time/notfound-msg ;; *) bst_notok time/notfound-msg "want 'cannot run' got:'$(_bst_1line "$n")'" ;; esac # --- signal termination (LAST, deliberately) --------------------------------- # time reports a signal-killed child and exits 128+signo. This is placed at the # END of the file on purpose: signal delivery is the weakest part of the # wasm32-hwjs substrate (the /proc EINTR class in brintos/linux#9, and the # SIGALRM+wait() path that hangs coreutils timeout), so if this case ever wedges, # it costs only itself -- every assertion above has already reported. # # Both cases below are KNOWN RED on wasm32-hwjs today, and the cause is the # substrate, not this port: a process cannot signal itself to death at all. # `sh -c 'kill -TERM $$'` run bare on target exits 0 instead of dying, so there # is no signal death for time to observe or report -- and `wait` on a child # killed by an external SIGTERM hangs outright. They are left FAILING rather # than skipped on purpose: they flip green by themselves the day signal # delivery lands, which is the regression signal this suite is buying. s=$(/usr/bin/time sh -c 'kill -TERM $$' 2>&1) case "$s" in *"Command terminated by signal 15"*) bst_ok time/signal-narrated ;; *) bst_notok time/signal-narrated "want signal 15 got:'$(_bst_1line "$s")'" ;; esac check_rc time/signal-rc 143 /usr/bin/time sh -c 'kill -TERM $$' rm -f "$tmp.out"