. "$BST_LIB" # BST-TIMEOUT: 300 # # 72 cases (68 if /var/lib/dpkg/info/digitemp.list is absent, which is itself one # of them). Every one is a single exec that returns before any adapter is # touched, and the five that could in principle block are individually bounded # at 30 s by bst_probe, so the suite's own bound only has to cover the sum. # # THE WHOLE FILE WAS RUN UNDER busybox ash AGAINST A NATIVE x86-64 BUILD of this # SRCREV before it shipped (2026-09-01): 68 of 68 cases, with the only reds being # the four install-path/which cases and the dpkg-list case, all of which are # statements about the image rather than about digitemp. So the shell is known to # execute end to end and every expectation below is known to hold for the program; # what the on-target run adds is whether the wasm32 port agrees. # # digitemp 3.7.2 on wasm32-hwjs glibc -- the 1-Wire temperature-sensor reader # (meta-openembedded/meta-oe, recipes-support/digitemp). No bbappend and no # patch: meta-oe's recipe cross-compiles unmodified on this arm, 905 of 905 # tasks with zero ERROR: lines, measured 2026-09-01. # # --------------------------------------------------------------------------- # TWO BINARIES SHIP, NOT THREE, AND THAT IS THE PORT'S ONE SCOPE DECISION. # # digitemp builds one program per adapter family and meta-oe's recipe names two # of the three upstream targets: `EXTRA_OEMAKE = "ds9097 ds9097u ..."`, giving # /usr/sbin/digitemp_DS9097 (passive DS9097 dongle) and /usr/sbin/digitemp_DS9097U # (DS2480B-based DS9097U). digitemp_DS2490 -- the DS2490 USB adapter -- is NOT # built, for three reasons that agree: # # * it links against libusb-0.1, not libusb1. userial/ds2490/usbses.c includes # and the Makefile's ds2490 rule adds `-lusb`. libusb1 is what this # image carries and what the recipe's DEPENDS names; the compat shim is a # separate package (BRINTOS-1892 is porting it in the same round, which is # exactly why this port does not take a build dependency on it -- a member # that couples itself to a sibling's in-flight layer breaks the shared image # if that sibling is dropped); # * upstream calls it broken. The Makefile's own help text reads # `make ds2490 - Build version for DS2490 (USB) (edit Makefile) (BROKEN)`, # and README line 423 says "DS2490/9490 support still doesn't work well for # me"; # * it would reach nothing here in any case. This machine has a USB core and # no host controller, so /sys/bus/usb/devices is empty and /dev/bus does not # exist at all (BRINTOS-1338, and libusb1 is built --disable-udev for it, # BRINTOS-1576). A DS2490 binary could only report the same absence the two # serial binaries report, by a path upstream disowns. # # A03 asserts that absence positively rather than leaving it to be noticed, so # the day a recipe bump adds the target this suite says so. # # --------------------------------------------------------------------------- # THERE IS NO 1-WIRE ADAPTER BEHIND THIS MACHINE, AND THE POINT IS THAT THE # ANSWER IS A stat(2) RATHER THAN A READ. # # digitemp's main() is ordered so that every failure this suite asserts is # reached BEFORE owAcquire() -- that is, before a single byte is written to a # serial line. In source order (src/digitemp.c): # # getopt loop -h / -? -> usage(), exit(EXIT_HELP=3) # action-command check no -w/-a/-i/-t -> "You need 1 of the following # action commands", exit(EXIT_HELP=3) # read_rcfile() absent file is NOT fatal (returns 1, so -i can # create one); a malformed one returns -1 -> # exit(EXIT_NORC=2) # file_exists(serial_port) -> "Error, serial port '%s' does not exist!", # exit(EXIT_NOPORT=7) # access(serial_port, R_OK|W_OK) -> exit(EXIT_NOPERM=4) # owAcquire() the first thing that opens the line # # So "no adapter" is answered by stat(2) on a path that is not there, and it is # answered the same way whichever action command was asked for. That is a # deterministic, documented result and not a degraded one -- which is why the # B section asserts a STATUS and a MESSAGE rather than skipping. The four # B cases are nonetheless run under bst_probe: a regression that made any of # them block on a device would otherwise take the whole suite down and the # runner would report every case above it as a pass. # # --------------------------------------------------------------------------- # WHAT THE CONFIGURATION-PARSER SECTION CAN AND CANNOT ASSERT. # # read_rcfile() is pure userland -- fopen, fgets, strtok, strncasecmp, atoi, # strtol -- and section D exercises it over fixtures with known contents. It # asserts three OBSERVABLE consequences of the parse: # # the TTY directive a literal path from the fixture comes back out in # the port-does-not-exist diagnostic, which is the # only place a parsed string is echoed; # the SENSORS/ROM bound `sensors >= sensor_list->max` -> "Error, too many # ROM entries. Check SENSORS value." with EXIT_NORC. # Both operands come from literals in the fixture, so # this pins that the ROM index and the SENSORS count # were both read and compared; # the absent-file path a missing rc file is not an error, because -i is # meant to create one. # # IT CANNOT ASSERT THE PARSED SERIAL NUMBERS AGAINST LITERALS, and that is a # property of digitemp rather than of this arm: every code path that prints a # ROM address -- the -a/-t read loops, the -w walk, write_rcfile() under -i -- # runs after owAcquire(), so there is no adapter-free path in the program that # echoes a sensor's 8 bytes back. The bound check above is what is available, # and it is a real assertion about the literals rather than a proxy for one. # # --------------------------------------------------------------------------- # WHY -h EXITS 3 AND NOT 0. `case 'h'` falls into `usage(); exit(EXIT_HELP)`, # and EXIT_HELP is 3 (src/digitemp.h:63). Every getopt error, and an unknown # option, take the same exit. That is upstream's convention here and is asserted # as it is; a suite that asserted 0 would be asserting a change nobody made. bst_ok digitemp/suite-canary # first line: absorbs brintos/linux#96, which drops it # The binaries land in ${sbindir}. `require` resolves through PATH, so a PATH # without /usr/sbin would report a shipped port as ABSENT -- a failure that # looks like coverage. Extended before the first require, as 60-tcpslice does. PATH=$PATH:/usr/sbin:/sbin export PATH require digitemp_DS9097 digitemp/present require digitemp_DS9097U digitemp/present-u W=${TMPDIR:-/tmp}/bst-digitemp.$$ rm -rf "$W" if mkdir -p "$W" 2>/dev/null; then bst_ok digitemp/workdir else bst_notok digitemp/workdir "cannot create working directory $W" exit 0 fi export W nl=' ' # has : ok iff contains the literal . has() { case "$3" in *"$2"*) bst_ok "$1" ;; *) bst_notok "$1" "want:'$(_bst_1line "$2")' got:'$(_bst_1line "$3")'" ;; esac; } # hasnt : the negative, for a split that must hold. hasnt() { case "$3" in *"$2"*) bst_notok "$1" "present and must not be: $(_bst_1line "$2")" ;; *) bst_ok "$1" ;; esac; } # run : keep a command's stdout, stderr and status as # $W/.out, .err and .rc, and always exit 0 itself -- so bst_probe can bound # it while the command's own non-zero status stays readable. Every B case here # is EXPECTED to fail, and bst_probe scores a non-zero child as a red. cat > "$W/run" <<'EOS' #!/bin/sh tag=$1; shift "$@" >"$W/$tag.out" 2>"$W/$tag.err" printf '%s\n' $? >"$W/$tag.rc" exit 0 EOS chmod +x "$W/run" # --- A. the packaging --------------------------------------------------------- # Asserted by stat AND against dpkg's own file list: a binary that reached the # image by some other route satisfies the stat and does not satisfy the claim. check digitemp/install-path-9097 test -x /usr/sbin/digitemp_DS9097 check digitemp/install-path-9097u test -x /usr/sbin/digitemp_DS9097U check_eq digitemp/which-9097 "/usr/sbin/digitemp_DS9097" "$(command -v digitemp_DS9097)" check_eq digitemp/which-9097u "/usr/sbin/digitemp_DS9097U" "$(command -v digitemp_DS9097U)" # The scope decision, asserted rather than assumed -- see the header. check digitemp/no-ds2490-binary sh -c '[ ! -e /usr/sbin/digitemp_DS2490 ]' pkglist=$(cat /var/lib/dpkg/info/digitemp.list 2>/dev/null) if [ -n "$pkglist" ]; then bst_ok digitemp/pkg-list hasline() { case "$nl$3$nl" in *"$nl$2$nl"*) bst_ok "$1" ;; *) bst_notok "$1" "not listed: $2" ;; esac; } hasline digitemp/pkg-9097 /usr/sbin/digitemp_DS9097 "$pkglist" hasline digitemp/pkg-9097u /usr/sbin/digitemp_DS9097U "$pkglist" hasnt digitemp/pkg-no-ds2490 "/usr/sbin/digitemp_DS2490" "$pkglist" # do_install is `install -m 0755 digitemp_* ${D}${sbindir}` and nothing else, # so a stray file in the package means the build left something in $S. hasnt digitemp/pkg-no-bindir "/usr/bin/digitemp" "$pkglist" else bst_notok digitemp/pkg-list "no /var/lib/dpkg/info/digitemp.list -- the packaging cases below cannot run" fi # --- B. usage and identity ---------------------------------------------------- # usage() prints BANNER_1..3 and the option table on STDOUT and then exits # EXIT_HELP. The version string is pinned because it is the only place the # built binary states which source it came from. h_out=$(digitemp_DS9097 -h 2>&1); h_rc=$? hu_out=$(digitemp_DS9097U -h 2>&1); hu_rc=$? check_eq digitemp/help-rc-9097 "3" "$h_rc" check_eq digitemp/help-rc-9097u "3" "$hu_rc" has digitemp/help-version "DigiTemp v3.7.2 Copyright 1996-2018 by Brian C. Lane" "$h_out" has digitemp/help-usage "Usage: digitemp [-s -i -I -U -l -r -v -t -a -d -n -o -c]" "$h_out" has digitemp/help-opt-s "-s /dev/ttyS0 Set serial port" "$h_out" has digitemp/help-opt-c "-c digitemp.conf Configuration File" "$h_out" has digitemp/help-opt-i "-i Initialize .digitemprc file" "$h_out" has digitemp/help-opt-w "-w Walk the full device tree" "$h_out" has digitemp/help-opt-a "-a Read all Sensors" "$h_out" # BANNER_3 is "Compiled for %s\n\n" and %s is `dtlib`, a const char[] defined # once per adapter object -- "DS9097" in src/ds9097.c, "DS9097U" in src/ds9097u.c. # It is the ONLY thing that differs between the two binaries' output, and it is # what tells an operator which dongle a given binary drives. Asserting both, and # asserting each does not carry the other's, is what makes the two-target build # visible from the image rather than only from the recipe. has digitemp/help-lib-9097 "Compiled for DS9097" "$h_out" has digitemp/help-lib-9097u "Compiled for DS9097U" "$hu_out" hasnt digitemp/help-lib-9097-not-u "Compiled for DS9097U" "$h_out" check_eq digitemp/help-tables-differ "differ" \ "$(if [ "$h_out" = "$hu_out" ]; then echo same; else echo differ; fi)" # An unknown option takes the same exit as -h and prints the same table, so a # typo in any case below cannot quietly become "digitemp with no options". check_rc digitemp/bad-option-rc 3 digitemp_DS9097 -Z has digitemp/bad-option-usage "Usage: digitemp" "$(digitemp_DS9097 -Z 2>&1)" # No action command is refused BEFORE the rc file is read and before any device # path is looked at -- the earliest of the four refusals. noact_out=$(digitemp_DS9097 -q 2>&1); noact_rc=$? check_eq digitemp/no-action-rc "3" "$noact_rc" has digitemp/no-action-msg "Error! You need 1 of the following action commands, -w -a -i -t" "$noact_out" # --- C. the absent adapter, bounded ------------------------------------------ # The four action commands against a serial device that is not there. Each is # expected to answer EXIT_NOPORT (7) with the named path in the diagnostic, and # each is bounded because a regression that blocked on a read would otherwise # hang the suite. bst_probe scores the WRAPPER, which always exits 0; digitemp's # own status is read from the .rc file afterwards. DEV=/dev/digitemp-absent-adapter check digitemp/dev-really-absent sh -c '[ ! -e /dev/digitemp-absent-adapter ]' probe_absent() { _pid=$1; _ptag=$2; shift 2 bst_probe "digitemp/$_pid-bounded" "$W/$_ptag.probe" 30 "$W/run" "$_ptag" "$@" _prc=$(cat "$W/$_ptag.rc" 2>/dev/null) _perr=$(cat "$W/$_ptag.err" 2>/dev/null) check_eq "digitemp/$_pid-status" "7" "$_prc" has "digitemp/$_pid-names-port" "Error, serial port '$DEV' does not exist!" "$_perr" } probe_absent read-all all digitemp_DS9097 -q -s "$DEV" -a probe_absent walk walk digitemp_DS9097 -q -s "$DEV" -w probe_absent init init digitemp_DS9097 -q -s "$DEV" -i probe_absent single one digitemp_DS9097 -q -s "$DEV" -t 0 probe_absent read-all-u allu digitemp_DS9097U -q -s "$DEV" -a # -i is the command that WRITES .digitemprc, and it must not have written one: # the port check precedes owAcquire(), which precedes write_rcfile(). check digitemp/init-wrote-nothing sh -c '[ ! -e .digitemprc ]' # With no -s and no rc file the serial port is the empty string (main() sets # `serial_port[0] = 0` and there is no default), so the same refusal names it as # ''. This is the case an operator hits first, and it must not be a crash. noport_out=$(digitemp_DS9097 -q -c "$W/absent.conf" -a 2>&1); noport_rc=$? check_eq digitemp/no-port-rc "7" "$noport_rc" has digitemp/no-port-msg "Error, serial port '' does not exist!" "$noport_out" # --- D. the configuration-file parser ---------------------------------------- # See the header for what this section can and cannot assert. # D1 -- an absent rc file is not an error. read_rcfile() returns 1 so that -i can # go on to create one; the run proceeds and stops at the port check instead. # Established above by digitemp/no-port-rc; asserted here as its own statement, # with a path that provably does not exist. check digitemp/rc-absent-file-gone sh -c '[ ! -e "$W/absent.conf" ]' d1_out=$(digitemp_DS9097 -q -c "$W/absent.conf" -w 2>&1); d1_rc=$? check_eq digitemp/rc-absent-not-fatal "7" "$d1_rc" has digitemp/rc-absent-reached-port "Error, serial port '' does not exist!" "$d1_out" # D2 -- a well-formed fixture with a known sensor list. TTY names a path that is # not there, so the parse is observable through the port diagnostic: the literal # below comes out of the file and nowhere else. FIXTTY=$W/fixture-tty-9097 cat > "$W/good.conf" <&1); d2_rc=$? # EXIT_NOPORT, not EXIT_NORC: the file parsed cleanly and the run got as far as # the device check. A parse error would be 2 here and would name itself instead. check_eq digitemp/rc-good-rc "7" "$d2_rc" has digitemp/rc-tty-directive-parsed "Error, serial port '$FIXTTY' does not exist!" "$d2_out" hasnt digitemp/rc-good-no-rom-error "too many ROM entries" "$d2_out" # The command line overrides the file, which is main()'s documented order: # read_rcfile() first, then the -s value is copied over serial_port. d3_out=$(digitemp_DS9097 -q -c "$W/good.conf" -s "$DEV" -a 2>&1); d3_rc=$? check_eq digitemp/rc-cmdline-overrides-rc "7" "$d3_rc" has digitemp/rc-cmdline-overrides "Error, serial port '$DEV' does not exist!" "$d3_out" hasnt digitemp/rc-cmdline-not-overridden "$FIXTTY" "$d3_out" # D3 -- the SENSORS/ROM bound, which is the assertion about the literals. The # file declares 2 sensors and then a ROM at index 2; `sensors >= max` refuses it. cat > "$W/toomany.conf" <&1); d4_rc=$? check_eq digitemp/rc-toomany-rc "2" "$d4_rc" # EXIT_NORC has digitemp/rc-toomany-msg "Error, too many ROM entries. Check SENSORS value." "$d4_out" # It refused at the PARSE, so it never reached the device check -- which is what # separates this from every other red in this suite. hasnt digitemp/rc-toomany-stopped-early "does not exist" "$d4_out" # The same bound with the count omitted entirely: sensor_list->max stays 0, so # the first ROM line is already out of range. A file whose SENSORS line is # missing is rejected rather than silently read as empty. cat > "$W/nosensors.conf" <&1); d5_rc=$? check_eq digitemp/rc-nosensors-rc "2" "$d5_rc" has digitemp/rc-nosensors-msg "Error, too many ROM entries. Check SENSORS value." "$d5_out" # D4 -- comments and blank lines. `temp[0] == '#'` and `temp[0] == '\n'` are the # two skips at the top of the loop; a file that is nothing else must parse to # the same state as an empty one, i.e. the port stays ''. printf '# nothing but a comment\n\n#\n\n' > "$W/comments.conf" d6_out=$(digitemp_DS9097 -q -c "$W/comments.conf" -a 2>&1); d6_rc=$? check_eq digitemp/rc-comments-rc "7" "$d6_rc" has digitemp/rc-comments-msg "Error, serial port '' does not exist!" "$d6_out" # D5 -- directive matching is case-insensitive (strncasecmp) and prefix-based on # a fixed length. A lower-case `tty` must set the port exactly as `TTY` does. printf 'tty %s\nSENSORS 1\nROM 0 0x10 0x11 0x22 0x33 0x44 0x55 0x66 0x77\n' "$FIXTTY" > "$W/lower.conf" d7_out=$(digitemp_DS9097 -q -c "$W/lower.conf" -a 2>&1); d7_rc=$? check_eq digitemp/rc-case-insensitive-rc "7" "$d7_rc" has digitemp/rc-case-insensitive "Error, serial port '$FIXTTY' does not exist!" "$d7_out" # D6 -- the two binaries share one parser (src/digitemp.c is in $(OBJS) for both # targets), so the same fixture must produce the same answer from each. A # divergence here would mean the adapter object leaked into the parse. d8_out=$(digitemp_DS9097U -q -c "$W/good.conf" -a 2>&1); d8_rc=$? check_eq digitemp/rc-parser-shared-rc "$d2_rc" "$d8_rc" check_eq digitemp/rc-parser-shared "$d2_out" "$d8_out" # --- E. the banner suppression switch ---------------------------------------- # -q clears the copyright banner that main() otherwise prints before the device # check. Every case above passes -q, so this is the control that says the flag # was doing something and the diagnostics asserted above were not merely the # first two lines of a banner. e1_out=$(digitemp_DS9097 -c "$W/comments.conf" -a 2>&1) has digitemp/banner-without-q "GNU General Public License v2.0" "$e1_out" hasnt digitemp/banner-with-q "GNU General Public License v2.0" "$d6_out" rm -rf "$W"