# BST-TIMEOUT: 1800 . "$BST_LIB" require dool dool/present # dool 1.3.1 -- the Python 3 fork of dstat. It reads /proc, computes a rate # between two samples and prints a table. It sits beside 85-nmon and 85-sysstat: # nmon has a batch mode that writes a CSV, sysstat has since-boot forms, and dool # has BOTH a fixed sample count (`dool ' terminates on its own) and # `--output ', which is why this suite can assert file CONTENT rather than # scraped screen text for everything that matters. # # THE ONE DESIGN CONSTRAINT IS THE PYTHON START COUNT, and it is not a style # preference. dool IS a python3 script, so every invocation is an interpreter # start, and a start on this arm is tens of seconds to minutes -- long enough that # a suite taking six of them cannot finish inside the 120 s window the host's # guest-growth watchdog measures over, and is killed before it reports a single # case (brintos/linux#21, brintos/hardwarejs#20; measured on batch-1826 for # 99-python3-pyproject-hooks, BRINTOS-1832). So this suite takes FOUR starts and # every one of them is made to earn several assertions at once: # # A --version identity and the platform facts it reports # B --list the plugin inventory, cross-checked against the package # C --output F --display 1 3 the DEFAULT report: CSV *and* screen form # D --output F --display --mem-adv --mem --battery 1 2 # the /proc/meminfo cross-check *and* the unavailable- # plugin case, in one run # # `--output F --display' together is what makes C and D each one start instead of # two: --output alone sets display=False, and --display anywhere on the line turns # it back on, so the same run leaves a machine-readable CSV on disk AND the screen # form on stdout. Everything checkable without a start -- packaging, the plugin # inventory on disk, the shebang -- is checked with no dool run at all. Adding an # assertion to an existing run is free; adding a run costs the suite minutes. # BST_DOOL_MORE=1 restores the three usage-error cases, which cost a start each; # without it they SKIP against that reason rather than red. # # WHAT `--nocolor' DOES AND DOES NOT DO, measured on an x86-64 control: it blanks # the colour table but NOT the ansi table, so the screen form still carries # `ESC[0;0m' between every field and box-drawing separators between the group # titles. Nothing here greps the screen form for a phrase containing a space -- # `total cpu usage' is rendered `totalcpuusage'. The CSV has no # escapes at all, so that is where the values are read from. # # AND WHAT A NON-TTY DOES, which is the shape a suite always sees: Options.__init__ # sets color=header=update=False when stdout is not a tty, and the per-sample line # break is emitted only when update is on. So the three data rows of `dool 1 3' # land CONCATENATED ON ONE PHYSICAL LINE, under one two-line header. That is not a # defect and it is not this arm -- it is measured identically on x86-64 -- but it # is the opposite of what "three rows plus a header" suggests, so it is asserted # explicitly (dool/display-lines) rather than left as a trap for the next reader. work=${TMPDIR:-/tmp}/bst-dool.$$ rm -rf "$work"; mkdir -p "$work" || { bst_notok dool/workdir "cannot create $work"; exit 0; } PDIR=/usr/share/dool LIST=/var/lib/dpkg/info/dool.list # --- 1. packaging: where the recipe's do_install puts things, no dool run ------ # The upstream Makefile install target is the whole of the packaging: one script # into ${bindir}, the same script again as ${datadir}/dool/dool.py, and # plugins/dool_*.py beside it. ${datadir}/dool is also the third entry of dool's # own `pluginpath', which is what makes the shipped plugins reachable at all. if [ -r "$LIST" ]; then bst_ok dool/pkg-list else bst_notok dool/pkg-list "no $LIST -- dool is not a dpkg-installed package on this image"; fi check_out dool/pkg-binary "/usr/bin/dool" cat "$LIST" check_out dool/pkg-dool-py "$PDIR/dool.py" cat "$LIST" check_out dool/pkg-plugin "$PDIR/dool_helloworld.py" cat "$LIST" if [ -x /usr/bin/dool ]; then bst_ok dool/bin-exec else bst_notok dool/bin-exec "/usr/bin/dool is not executable"; fi # The shebang is `#!/usr/bin/env python3' and the recipe does NOT mangle it -- # there is no inherit that would. So the port depends on env(1) being on the # image as well as on python3, and a missing env would fail every case below with # a message naming neither. _sb=$(head -n 1 /usr/bin/dool 2>/dev/null) check_eq dool/shebang "#!/usr/bin/env python3" "$_sb" if command -v env >/dev/null 2>&1; then bst_ok dool/env-present else bst_notok dool/env-present "no env(1) on PATH -- a #!/usr/bin/env script cannot start"; fi # 103 plugins in plugins/, plus the dool.py copy of the script = 104 *.py files. # The count is exact because the Makefile globs `plugins/dool_*.py' and this # SRCREV carries exactly 103 of them; an upstream bump that adds one becomes # visible here instead of silent. _np=$(ls "$PDIR"/dool_*.py 2>/dev/null | wc -l | tr -d ' ') check_eq dool/plugin-count 103 "$_np" _na=$(ls "$PDIR"/*.py 2>/dev/null | wc -l | tr -d ' ') check_eq dool/plugin-dir-files 104 "$_na" # dool.py is the SAME file as the executable (`install -Dp dool .../dool.py'), # not a stub, and it is the reason `dool_*.py' globbing does not pick it up. if cmp -s /usr/bin/dool "$PDIR/dool.py"; then bst_ok dool/dool-py-copy else bst_notok dool/dool-py-copy "$PDIR/dool.py differs from /usr/bin/dool"; fi # The package is pure Python. This image's python3 is one static binary with an # empty lib-dynload and no dynamic loader (BRINTOS-1476), so a compiled extension # shipped here could not be imported at all -- it would install cleanly and die at # `import'. Asserting the negative directly is what makes that visible. _so=$(ls "$PDIR"/*.so 2>/dev/null | wc -l | tr -d ' ') check_eq dool/no-extension 0 "$_so" # --- 2. run A: --version ------------------------------------------------------ # One start, ten assertions. Everything here is computed by dool itself from # os.uname(), resource.getpagesize() and os.sysconf(), so it also proves that the # `resource' extension is built into this static interpreter -- dool imports it at # module level and would not reach main() without it. vout=$(dool --version 2>&1); vrc=$? if [ "$vrc" -eq 0 ]; then bst_ok dool/version-rc elif bst_execfail "$vout"; then bst_unrun dool/version-rc "$vout" else bst_notok dool/version-rc "rc=$vrc want=0 $(_bst_1line "$vout")"; fi _vfield() { printf '%s\n' "$vout" | awk -F': *' -v k="$1" '$1==k {print $2; exit}'; } _vhas() { case "$vout" in *"$1"*) bst_ok "$2" ;; *) bst_notok "$2" "want:'$1' got:'$(_bst_1line "$vout")'" ;; esac; } _vhas "Dool 1.3.1" dool/version-name _vhas "Forked from Dstat" dool/version-lineage _vhas "Platform posix/linux" dool/version-platform _vhas "Kernel " dool/version-kernel _vhas "Python 3." dool/version-python _vhas "Terminal size: " dool/version-termsize # The three numbers dool reads out of the C library rather than out of /proc. # Values are not pinned -- pagesize and the processor count are properties of this # arm, not of the package -- but each must parse and be positive, which is what a # sysconf that returned None or raised would fail. for _k in Processors Pagesize; do _v=$(_vfield "$_k") case "$_v" in ''|*[!0-9]*) bst_notok "dool/version-$(echo "$_k" | tr 'A-Z' 'a-z')" "'$_k' did not report a plain integer: '$(_bst_1line "$_v")'" ;; *) if [ "$_v" -gt 0 ]; then bst_ok "dool/version-$(echo "$_k" | tr 'A-Z' 'a-z')" else bst_notok "dool/version-$(echo "$_k" | tr 'A-Z' 'a-z')" "$_k = $_v (want > 0)"; fi ;; esac done _hz=$(_vfield "Clock ticks per secs") case "$_hz" in ''|*[!0-9]*) bst_notok dool/version-clocktick "no plain integer for 'Clock ticks per secs': '$(_bst_1line "$_hz")'" ;; *) if [ "$_hz" -gt 0 ]; then bst_ok dool/version-clocktick else bst_notok dool/version-clocktick "SC_CLK_TCK = $_hz (want > 0)"; fi ;; esac # --- 3. run B: --list --------------------------------------------------------- # The inventory dool advertises, cross-checked against what the package actually # put on disk. `--list' prints the builtin classes first, then one block per # pluginpath entry that has files in it; ${datadir}/dool is the entry that matters # here, and its block must name every file the packaging installed -- so the # external count is compared with the ls count above rather than with a literal. lout=$(dool --list 2>&1); lrc=$? if [ "$lrc" -eq 0 ]; then bst_ok dool/list-rc elif bst_execfail "$lout"; then bst_unrun dool/list-rc "$lout" else bst_notok dool/list-rc "rc=$lrc want=0 $(_bst_1line "$lout")"; fi _lhas() { case "$lout" in *"$1"*) bst_ok "$2" ;; *) bst_notok "$2" "want:'$1' got:'$(_bst_1line "$lout")'" ;; esac; } _lhas "internal:" dool/list-internal _lhas "$PDIR:" dool/list-plugindir # Names, not the raw text: the printer wraps at the terminal width, so a name can # be split across lines only at a comma. Splitting on commas and whitespace is # therefore exact. printf '%s\n' "$lout" > "$work/list.txt" _names() { awk -v want="$1" ' /^internal:/ { sec="int"; next } /^\// { sec="ext"; next } { if (sec==want) print }' "$work/list.txt" | tr -d ' \t' | tr ',' '\n' | grep -c .; } check_eq dool/list-internal-count 28 "$(_names int)" check_eq dool/list-external-count "$_np" "$(_names ext)" for _p in cpu mem mem-adv net load page sys; do case "$lout" in *"$_p"*) bst_ok "dool/list-builtin-$_p" ;; *) bst_notok "dool/list-builtin-$_p" "builtin plugin '$_p' not enumerated by --list" ;; esac done for _p in battery helloworld top-cpu thermal; do case "$lout" in *"$_p"*) bst_ok "dool/list-external-$_p" ;; *) bst_notok "dool/list-external-$_p" "external plugin '$_p' installed but not enumerated by --list" ;; esac done # --- 4. run C: the default report, CSV and screen form in one start ----------- # `dool ' runs count samples and exits, so nothing here is wrapped # in timeout(1) -- which would not help on a kernel without the brintos/linux#77 # fix anyway, since its SIGALRM never fires there. The termination condition is # the package's own loop bound. csv=$work/def.csv rm -f "$csv" dool --nocolor --output "$csv" --display 1 3 > "$work/def.out" 2> "$work/def.err" crc=$? cerr=$(cat "$work/def.err" 2>/dev/null) cout=$(cat "$work/def.out" 2>/dev/null) if [ "$crc" -eq 0 ]; then bst_ok dool/report-rc elif bst_execfail "$cerr"; then bst_unrun dool/report-rc "$cerr" else bst_notok dool/report-rc "rc=$crc want=0 $(_bst_1line "$cerr")"; fi # The CSV's four metadata lines, then the two header lines, then the data. The # banner carries the version a second time, from a different code path than # --version, which is what makes it worth asserting rather than duplicating. if [ -s "$csv" ]; then bst_ok dool/csv-written else bst_notok dool/csv-written "--output wrote nothing to $csv"; fi check_eq dool/csv-banner '"dool 1.3.1 CSV output"' "$(sed -n 1p "$csv" 2>/dev/null)" check_out dool/csv-host '"Host:"' sed -n 3p "$csv" check_out dool/csv-cmdline '"Cmdline:"' sed -n 4p "$csv" _grp=$(sed -n 5p "$csv" 2>/dev/null) _col=$(sed -n 6p "$csv" 2>/dev/null) _ghas() { case "$_grp" in *"$1"*) bst_ok "$2" ;; *) bst_notok "$2" "group '$1' absent from CSV title row: '$(_bst_1line "$_grp")'" ;; esac; } _chas() { case "$_col" in *"$1"*) bst_ok "$2" ;; *) bst_notok "$2" "column '$1' absent from CSV subtitle row: '$(_bst_1line "$_col")'" ;; esac; } # The default plugin set is cpu, disk, net, load -- plus `time', which dool # appends for a defaulted run and titles "system". NOT the dstat-era # cpu/disk/net/paging/system: `default_plugins' in this source is exactly those # four, and the note dool prints on stdout says so in its own words. _ghas "total cpu usage" dool/csv-group-cpu _ghas "net/total" dool/csv-group-net _ghas "load avg" dool/csv-group-load _ghas "system" dool/csv-group-time _chas '"usr","sys","idl","wai","stl"' dool/csv-cols-cpu _chas '"recv","send"' dool/csv-cols-net _chas '"1m","5m","15m"' dool/csv-cols-load _chas '"time"' dool/csv-cols-time check_out dool/default-note "Using default plugins: cpu, disk, net, load" cat "$work/def.out" # THE DISK GROUP IS THE UNAVAILABLE-PLUGIN CASE, on a real one rather than a # contrived one. The kernel has a block layer (CONFIG_BLOCK=y from the rig's # common.fragment over wasm32_defconfig), so /proc/diskstats exists, but no disk # is attached and it lists nothing. dool_disk then refuses in __init__ with "No # suitable block devices found to monitor". A plugin that cannot find its source # must be REPORTED and the run must continue with the rest -- which is the whole # assertion: dool prints one line to stderr naming the module and the reason, # exits 0, and the other four groups are still in the CSV. A kernel with no # /proc/diskstats takes the same branch with "Cannot open file" as the reason; a # machine with a disk swaps the cases: the group is asserted present and the # stderr line absent. if [ -r /proc/diskstats ] && [ -n "$(awk 'NF' /proc/diskstats 2>/dev/null)" ]; then _ghas "dsk/total" dool/csv-group-disk _chas '"read","writ"' dool/csv-cols-disk case "$cerr" in *"dool_disk"*) bst_notok dool/disk-plugin-loads "/proc/diskstats lists a device but dool_disk still failed: $(_bst_1line "$cerr")" ;; *) bst_ok dool/disk-plugin-loads ;; esac else if [ -r /proc/diskstats ]; then _dwhy="/proc/diskstats lists no device (block layer, no disk attached)" _dpat="No suitable block devices found to monitor" else _dwhy="no /proc/diskstats on this kernel" _dpat="/proc/diskstats" fi bst_skip dool/csv-group-disk "$_dwhy, so dool_disk cannot load and dsk/total is absent by construction" bst_skip dool/csv-cols-disk "$_dwhy" case "$cerr" in *"Module dool_disk failed to load."*"$_dpat"*) bst_ok dool/disk-reported-unavailable ;; *) bst_notok dool/disk-reported-unavailable "want a 'Module dool_disk failed to load. (...$_dpat...)' line on stderr; got:'$(_bst_1line "$cerr")'" ;; esac if [ "$crc" -eq 0 ]; then bst_ok dool/unavailable-does-not-abort else bst_notok dool/unavailable-does-not-abort "an unloadable plugin ended the run: rc=$crc"; fi fi # Exactly three data rows for `1 3' -- the fixed sample count is what makes this # suite non-interactive, and it is asserted rather than assumed. _rows=$(awk 'NR>6' "$csv" 2>/dev/null | grep -c .) check_eq dool/csv-rows 3 "$_rows" # Every cell but the last parses as a number, on every row. A dool that prints a # column title it cannot fill emits an empty cell, and a rate computed over a zero # window emits `nan' -- neither is caught by an exit status. _bad=$(awk -F, 'NR>6 && NF>0 { for (i=1; i/dev/null) if [ -z "$_bad" ]; then bst_ok dool/csv-numeric else bst_notok dool/csv-numeric "non-numeric cells: $(_bst_1line "$_bad")"; fi # The five cpu columns are percentages of one interval and must sum to 100 on # EVERY row, the first included -- dool normalises the since-boot sample the same # way it normalises a delta. A 1% tolerance covers the printer's 3-decimal # rounding. This is the arithmetic assertion the port really turns on: it fails if # /proc/stat is read but the jiffy accounting is wrong. _cpu=$(awk -F, 'NR>6 && NF>0 { s=$1+$2+$3+$4+$5; if (s<99 || s>101) printf "row%d=%.3f ", NR-6, s }' "$csv" 2>/dev/null) if [ -z "$_cpu" ]; then bst_ok dool/csv-cpu-sums-100 else bst_notok dool/csv-cpu-sums-100 "cpu columns do not sum to 100: $(_bst_1line "$_cpu")"; fi # The `time' plugin's cell, on every row, and three DISTINCT stamps -- three rows # carrying one timestamp would mean the sample loop never advanced. _ts=$(awk -F, 'NR>6 && NF>0 && $NF !~ /^[A-Z][a-z][a-z]-[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9]$/ { printf "row%d=%s ", NR-6, $NF }' "$csv" 2>/dev/null) if [ -z "$_ts" ]; then bst_ok dool/csv-time-format else bst_notok dool/csv-time-format "bad time cells: $(_bst_1line "$_ts")"; fi _nts=$(awk -F, 'NR>6 && NF>0 {print $NF}' "$csv" 2>/dev/null | sort -u | grep -c .) check_eq dool/csv-time-distinct 3 "$_nts" # The screen form of the same run. Only substrings without spaces are matched -- # the group titles are separated by U+2504 in the rendered header, not by blanks. _ohas() { case "$cout" in *"$1"*) bst_ok "$2" ;; *) bst_notok "$2" "want:'$1' in the screen form; got:'$(_bst_1line "$cout")'" ;; esac; } _ohas "usage" dool/display-title-cpu _ohas "net/total" dool/display-title-net _ohas "time" dool/display-title-time # THE MEASURED SHAPE, and the reason it is asserted: on a non-tty dool emits no # per-sample line break, so the run is note + title + subtitle + ONE data line # holding all three samples, then a bare ESC[0;0m the exit handler writes with no # newline after it -- 5 non-empty lines however many samples were taken. Verified # identically on an x86-64 control, so this is upstream's non-tty behaviour and # not this arm. check_eq dool/display-lines 5 "$(grep -c . "$work/def.out" 2>/dev/null)" # ...and the three samples are all on that one line, which is where the "three # rows" of the screen form actually live. _dts=$(grep -o '[A-Z][a-z][a-z]-[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9]' "$work/def.out" 2>/dev/null | grep -c .) check_eq dool/display-samples 3 "$_dts" # --- 5. run D: --mem against /proc/meminfo, and a second unavailable plugin --- # `--mem-adv --mem' in one run because they read the same file through different # arithmetic and the cross-check needs both: mem-adv exposes `total', which is # MemTotal verbatim and is the only cell in either group that does not move # between samples, so it can be compared for EXACT equality against /proc/meminfo. # Everything else is bounded instead. `--battery' rides along as the second # unavailable-plugin case -- it needs /proc/acpi/battery or /sys/class/power_supply # and has no cost when it fails, which is the point. mcsv=$work/mem.csv rm -f "$mcsv" dool --nocolor --output "$mcsv" --display --mem-adv --mem --battery 1 2 > "$work/mem.out" 2> "$work/mem.err" mrc=$? merr=$(cat "$work/mem.err" 2>/dev/null) if [ "$mrc" -eq 0 ]; then bst_ok dool/mem-rc elif bst_execfail "$merr"; then bst_unrun dool/mem-rc "$merr" else bst_notok dool/mem-rc "rc=$mrc want=0 $(_bst_1line "$merr")"; fi _mgrp=$(sed -n 5p "$mcsv" 2>/dev/null) _mcol=$(sed -n 6p "$mcsv" 2>/dev/null) check_out dool/mem-group-adv '"advanced memory usage"' printf '%s\n' "$_mgrp" check_out dool/mem-group-plain '"memory usage"' printf '%s\n' "$_mgrp" check_eq dool/mem-cols '"total","used","free","buff","cach","dirty","shmem","recl","used","free","cach","avai"' "$_mcol" check_eq dool/mem-rows 2 "$(awk 'NR>6' "$mcsv" 2>/dev/null | grep -c .)" # A named plugin set suppresses the defaults, and with them the note dool prints # when it falls back to them. The absence is the assertion. case "$(cat "$work/mem.out" 2>/dev/null)" in *"Using default plugins"*) bst_notok dool/mem-no-default-note "dool fell back to the default plugins despite --mem-adv --mem" ;; *) bst_ok dool/mem-no-default-note ;; esac # THE CROSS-CHECK. dool_mem multiplies the kB figures in /proc/meminfo by 1024, so # `total' must equal MemTotal * 1024 exactly -- no tolerance, no timing window. _mt=$(awk '/^MemTotal:/ { print $2 * 1024; exit }' /proc/meminfo 2>/dev/null) case "$_mt" in ''|0) bst_notok dool/mem-total-exact "no MemTotal in /proc/meminfo -- nothing to cross-check against" ;; *) _mbad=$(awk -F, -v want="$_mt" 'NR>6 && NF>0 && $1 != want { printf "row%d=%s ", NR-6, $1 }' "$mcsv" 2>/dev/null) if [ -z "$_mbad" ]; then bst_ok dool/mem-total-exact else bst_notok dool/mem-total-exact "mem-adv total != MemTotal*1024 ($_mt): $(_bst_1line "$_mbad")"; fi ;; esac # dool's own arithmetic, asserted from the row it printed: the "used" it reports # is MemTotal - MemFree - Cached - SReclaimable, i.e. total - free - cach - recl # of the same row. Both operands come out of one read of /proc/meminfo, so this is # exact and immune to memory moving between samples -- which is what makes it a # better assertion than comparing `free' against a second read. _ibad=$(awk -F, 'NR>6 && NF>0 { e = $1 - $3 - $5 - $8; if (e != $2) printf "row%d: used=%s want=%s ", NR-6, $2, e }' "$mcsv" 2>/dev/null) if [ -z "$_ibad" ]; then bst_ok dool/mem-used-identity else bst_notok dool/mem-used-identity "$(_bst_1line "$_ibad")"; fi # free, cached and available are all real quantities of a real machine: strictly # positive and no larger than total. A plugin that read the file and parsed # nothing reports 0 here, which no exit status would show. # `buff' is NOT among them and is checked separately below -- see dool/mem-buff. _bbad=$(awk -F, 'NR>6 && NF>0 { for (i=3; i<=5; i++) if (i != 4 && ($i <= 0 || $i > $1)) printf "row%d.col%d=%s ", NR-6, i, $i if ($12 <= 0 || $12 > $1) printf "row%d.avai=%s ", NR-6, $12 }' "$mcsv" 2>/dev/null) if [ -z "$_bbad" ]; then bst_ok dool/mem-bounds else bst_notok dool/mem-bounds "out of range against total: $(_bst_1line "$_bbad")"; fi # `buff' is Buffers, which is nr_blockdev_pages() -- the BLOCK-DEVICE buffer # cache, and nothing else. It is the one memory column whose correct value here # can be exactly 0, so it cannot ride in the strictly-positive loop above. The # whole of block/ is obj-$(CONFIG_BLOCK), so a kernel with no /proc/diskstats # (block/genhd.c creates it) is a kernel where nr_blockdev_pages() is the inline # stub in blkdev.h returning a literal 0, and the exact 0 is asserted. This # kernel has the block layer with no disk attached, so /proc/diskstats exists # and the case is a bound: a block layer with no device read also legitimately # reports 0, so strictly positive would be wrong here too. if [ -r /proc/diskstats ]; then _fbad=$(awk -F, 'NR>6 && NF>0 && ($4 < 0 || $4 > $1) { printf "row%d.buff=%s ", NR-6, $4 }' "$mcsv" 2>/dev/null) if [ -z "$_fbad" ]; then bst_ok dool/mem-buff else bst_notok dool/mem-buff "buff out of range against total: $(_bst_1line "$_fbad")"; fi else _fbad=$(awk -F, 'NR>6 && NF>0 && $4 != 0 { printf "row%d.buff=%s ", NR-6, $4 }' "$mcsv" 2>/dev/null) if [ -z "$_fbad" ]; then bst_ok dool/mem-buff else bst_notok dool/mem-buff "no /proc/diskstats, so CONFIG_BLOCK=n and Buffers must be exactly 0; got: $(_bst_1line "$_fbad")"; fi fi _mnum=$(awk -F, 'NR>6 && NF>0 { for (i=1; i<=NF; i++) if ($i !~ /^-?[0-9]+(\.[0-9]+)?$/) printf "row%d.col%d=%s ", NR-6, i, $i }' "$mcsv" 2>/dev/null) if [ -z "$_mnum" ]; then bst_ok dool/mem-numeric else bst_notok dool/mem-numeric "non-numeric cells: $(_bst_1line "$_mnum")"; fi # The battery half. Guarded rather than assumed: on a machine that HAS a battery # the plugin loads and the run carries a thirteenth column, and asserting the # failure there would be asserting a property of the bench. if [ -d /proc/acpi/battery ] || [ -n "$(ls -d /sys/class/power_supply/BAT* 2>/dev/null)" ]; then bst_skip dool/battery-reported-unavailable "this machine reports a battery, so dool_battery loads -- the unavailable-plugin case is covered by dool_disk above" bst_skip dool/battery-adds-no-column "this machine reports a battery, so the column is expected" else case "$merr" in *"dool_battery.py failed to load."*) bst_ok dool/battery-reported-unavailable ;; *) bst_notok dool/battery-reported-unavailable "want a 'Module .../dool_battery.py failed to load.' line on stderr; got:'$(_bst_1line "$merr")'" ;; esac # 8 mem-adv columns + 4 mem columns and nothing else: a plugin that failed to # load must contribute no column, not an empty one. check_eq dool/battery-adds-no-column 12 "$(awk -F, 'NR==6 {print NF; exit}' "$mcsv" 2>/dev/null)" fi # --- 6. usage errors: one python3 start each, so they are opt-in -------------- # Each of these is a real assertion about a real surface, and each costs a start # this suite cannot afford by default (see the header). BST_DOOL_MORE=1 buys them. if [ "${BST_DOOL_MORE:-0}" = "1" ]; then _out=$(dool --no-such-option 1 1 2>&1); _rc=$? if [ "$_rc" -eq 1 ]; then bst_ok dool/badopt-rc; else bst_notok dool/badopt-rc "rc=$_rc want=1"; fi case "$_out" in *"not recognized"*) bst_ok dool/badopt-msg ;; *) bst_notok dool/badopt-msg "got:'$(_bst_1line "$_out")'" ;; esac _out=$(dool --nocolor 0 1 2>&1); _rc=$? if [ "$_rc" -eq 1 ]; then bst_ok dool/baddelay-rc; else bst_notok dool/baddelay-rc "rc=$_rc want=1"; fi case "$_out" in *"delay must be an integer, greater than zero"*) bst_ok dool/baddelay-msg ;; *) bst_notok dool/baddelay-msg "got:'$(_bst_1line "$_out")'" ;; esac # Every selected plugin unavailable is the one case where dool DOES abort, and # it aborts with a distinct status (8) rather than the 1 of a usage error. _out=$(dool --nocolor --battery 1 1 2>&1); _rc=$? if [ "$_rc" -eq 8 ]; then bst_ok dool/onlybad-rc; else bst_notok dool/onlybad-rc "rc=$_rc want=8"; fi case "$_out" in *"None of the stats you selected are available."*) bst_ok dool/onlybad-msg ;; *) bst_notok dool/onlybad-msg "got:'$(_bst_1line "$_out")'" ;; esac else for _c in badopt-rc badopt-msg baddelay-rc baddelay-msg onlybad-rc onlybad-msg; do bst_skip "dool/$_c" "costs a python3 interpreter start; set BST_DOOL_MORE=1 to run the usage-error cases (brintos/linux#21)" done fi rm -rf "$work"