. "$BST_LIB" # BST-TIMEOUT: 300 # ~105 cases, ~70 execs, no loops, no corpus and no payload. # # Nothing here waits: every run either returns inside the # # getopt loop or is refused at open(2)/ioctl(2) and returns # # at once. Declared at the same bound as 99-i2c-tools.test, # # whose 114 cases of the same shape MEASURED 72s. # ssd1306 -- the armlabs SSD1306 OLED utility on a machine with no I2C adapter # and no display. # # ONE STATIC BINARY, ONE JOB. The package is a single program, /usr/bin/ # ssd1306_bin, built from four files (main.c, ssd1306.c, linux_i2c.c, font.h). # It parses argv, opens /dev/i2c-, sets the slave address to the SSD1306's # fixed 0x3c and writes command bytes at it. There is no library, no data file, # no man page and no second program. # # WHAT THIS MACHINE CAN AND CANNOT TEST, EXACTLY. Look at main() and the # division is sharp. Everything up to and including the getopt loop is # arithmetic on argv and calls to printf; then line 145 calls ssd1306_init(), # which is open("/dev/i2c-%d", O_RDWR) followed by ioctl(I2C_SLAVE, 0x3c), and # EVERY REMAINING LINE OF THE PROGRAM IS BEHIND A SUCCESSFUL RETURN FROM IT. # With no adapter that call cannot succeed -- open fails ENOENT, or, on a path # that is not an adapter, the ioctl fails ENOTTY -- so ssd1306.c's whole command # vocabulary, the font renderer, the resolution file and the cursor arithmetic # are UNREACHABLE HERE and this suite does not pretend otherwise. # # WHAT IS THEREFORE ASSERTED FOR REAL, and it is the whole of the reachable # program: the help text and its exit status; the ten options that take a # required argument, each with the message it prints when the argument is # missing; the one value the program validates; the bus-number arithmetic, # INCLUDING ITS TRUNCATION, which is observable in the diagnostic; the refusal # at the absent bus, naming the path; the fact that the refusal happened at # init rather than later; and the packaging. Those are argv arithmetic and # fprintf, they contain no device, and a wasm32 build that got them wrong is a # wasm32 build that is wrong with no environmental excuse available. # # EVERY EXPECTATION HERE WAS READ OFF A NATIVE x86-64 BUILD of this exact # SRCREV (34b42992, the one the recipe pins) during the port -- the exit # statuses, the message wording, and which stream each goes to -- rather than # transcribed from the source. Where the source and the binary disagreed, the # binary won. # # FOUR OF THE TICKET'S EXPECTATIONS ARE WRONG, AND THE SUITE RECORDS WHAT IS # ACTUALLY THERE: # # * THERE IS NO VERSION. The program has no -V, no --version and no banner; # the recipe pins a SRCREV and upstream ships no version string anywhere in # the binary. Asserted as an ABSENCE below, so upstream adding one is # visible rather than silent. # * THERE IS NO I2C ADDRESS ARGUMENT, so no address can be out of range. The # slave address is the compile-time constant SSD1306_I2C_ADDR (0x3c) in # ssd1306.h and nothing on the command line reaches it. Also asserted as an # absence. # * THERE IS NO BUS-NUMBER VALIDATION, and no bus number can be rejected. # `-n' is atoi() into a uint8_t, so the value WRAPS instead of being # refused: `-n 300' opens /dev/i2c-44, `-n 256' opens /dev/i2c-0 and # `-n -1' opens /dev/i2c-255. That is not inferred -- the truncated number # is printed back in the diagnostic, which is what makes it assertable # here. `-n abc' is atoi's 0, so it opens /dev/i2c-0, indistinguishable # from no -n at all. # * THE ERRNO IS DISCARDED. The refusal is printf("no oled attached to # /dev/i2c-%d\n", n) with no strerror and no perror anywhere in the # program, so the message CANNOT name what the kernel returned. MEASURED # both ways under a user namespace with a private /dev: a bus with no node # (ENOENT from open) and a bus where an ordinary file is staged (ENOTTY # from ioctl) produce BYTE-IDENTICAL output and the same status. The # collapse is pinned below as the fact it is. # # AND ONE BEHAVIOUR THAT SURPRISES EVERY READER: AN UNKNOWN OPTION IS NOT AN # ERROR. getopt returns '?', and main's '?' arm tests optopt against the ten # options that take arguments and falls through the bottom of the chain for # anything else -- no message, no return. So `-Z' produces getopt's OWN # diagnostic on stderr and then the program carries on to open the bus and # fails there, for the ordinary reason, with the ordinary message. The # `default:' arm that would have printed the help and returned 1 is dead code: # getopt never returns anything but a listed option, '?' or -1. Pinned rather # than reported -- ssd1306_linux is a third-party public repository (see # no-public-upstream-reports.md). # # THE STREAMS SPLIT, AND THE SPLIT IS ASSERTED. Everything the PROGRAM prints # -- the help, the ten "prams" messages, the orientation refusal and the "no # oled attached" line -- goes to STDOUT, because every one of them is printf. # The only thing on STDERR is getopt's own opterr diagnostic. A build that # moved either would still pass a naive combined-output check, so both are # posed separately. require ssd1306_bin ssd1306/present W=${TMPDIR:-/tmp}/bst-ssd1306.$$ out=$W.out : > "$out" TAB=$(printf '\t') # ------------------------------------------------------------------ probe ---- # Read the live system for an I2C adapter, by the routes the kernel would have # to provide first. Nothing is hardcoded: `adapter' is set only from what this # machine actually reports, so the gated cases at the end run their real # assertion the moment an adapter appears. adapter=""; adapter_src="" if [ -r /proc/bus/i2c ]; then _n=$(sed -n 's/^i2c-\([0-9][0-9]*\).*/\1/p' /proc/bus/i2c 2>/dev/null | head -1) [ -n "$_n" ] && { adapter=$_n; adapter_src=/proc/bus/i2c; } fi if [ -z "$adapter" ]; then for d in /sys/class/i2c-dev/i2c-*; do [ -e "$d" ] || continue adapter=${d##*/i2c-}; adapter_src=/sys/class/i2c-dev; break done fi if [ -z "$adapter" ]; then for d in /dev/i2c-*; do [ -c "$d" ] || continue adapter=${d##*i2c-}; adapter_src=/dev; break done fi if grep -q '[0-9] i2c$' /proc/devices 2>/dev/null; then chardev=yes; else chardev=no; fi [ -d /sys/bus/i2c ] && sysbus=yes || sysbus=no printf '#diag: ssd1306 probe: adapter=%s src=%s i2c-dev-chardev=%s /sys/bus/i2c=%s\n' \ "${adapter:-none}" "${adapter_src:-none}" "$chardev" "$sysbus" # A bus number nothing resolves to, chosen by looking rather than by assuming. # It must be <= 255 or the uint8_t would wrap it into a bus that might exist, # which is the very arithmetic asserted further down. free="" for n in 97 96 95 94 93 92 91 90 89 88; do [ -e "/dev/i2c-$n" ] && continue free=$n; break done if [ -z "$free" ]; then bst_notok ssd1306/free-bus "every candidate bus number 88-97 already exists in /dev; the absent-bus assertions cannot be posed" free=97 else bst_ok ssd1306/free-bus fi # ...and a second one, one lower, to serve as the control for the staged-node # cases. Kept distinct so neither case can be satisfied by the other's node. ctl=$((free - 1)) # --------------------------------------------------------------- packaging --- # do_install installs exactly one file: ${B}/ssd1306_bin into ${bindir}. The # recipe has no other install rule, so "any data files the recipe installs" is # the empty set -- and that is asserted, not assumed, because a packaging change # that started shipping something would otherwise be invisible. if [ -f /usr/bin/ssd1306_bin ]; then bst_ok ssd1306/binary else bst_notok ssd1306/binary "/usr/bin/ssd1306_bin is not installed"; fi if [ -x /usr/bin/ssd1306_bin ]; then bst_ok ssd1306/binary-mode else bst_notok ssd1306/binary-mode "/usr/bin/ssd1306_bin is not executable"; fi # It must be the thing PATH resolves, and it must resolve to ITSELF: this # package is not under update-alternatives (the recipe declares none), unlike # the five i2c-tools names beside it. p=$(command -v ssd1306_bin 2>/dev/null) check_eq ssd1306/path /usr/bin/ssd1306_bin "$p" check_eq ssd1306/no-alternative "" "$(readlink /usr/bin/ssd1306_bin 2>/dev/null)" # The font is font.h, compiled in -- there is no runtime font file to ship, and # a build that started needing one would be a different program. extra="" for f in /usr/share/ssd1306 /usr/share/ssd1306_linux /usr/lib/libssd1306.a \ /usr/lib/libssd1306.so /usr/include/ssd1306.h /usr/bin/ssd1306; do [ -e "$f" ] && extra="$extra $f" done if [ -n "$extra" ]; then bst_notok ssd1306/no-extra-files "the recipe installs only ssd1306_bin, but these are present:$extra" else bst_ok ssd1306/no-extra-files fi # Shape, not absence: this arm converts to shared libraries per recipe # (BRINTOS-1740), so a .so here would be a conversion rather than a breach -- # but this recipe builds no library at all, so the glob must simply stay empty. bst_lib_shape ssd1306/lib-shape "/usr/lib/libssd1306.so" "/usr/lib/libssd1306.so.*" # Upstream ships a Readme.md and no man page, and the recipe installs neither. if [ -f /usr/share/man/man1/ssd1306_bin.1 ] || [ -f /usr/share/man/man1/ssd1306_bin.1.gz ]; then bst_notok ssd1306/no-man "a man page is installed; upstream ships none and the recipe installs none" else bst_ok ssd1306/no-man fi # -------------------------------------------------------------------- help --- # -h prints the help and returns 0, and it does so BEFORE ssd1306_init -- it is # the only exit-0 path in the whole program and the only one that touches no # device. Captured once; the twelve option lines below are twelve cases over # that one exec. check_rc ssd1306/help-rc 0 sh -c "ssd1306_bin -h > '$out' 2>&1" check_out ssd1306/help-banner "help message" cat "$out" check_out ssd1306/help-init "init oled (128x32 or 128x64 or 64x48)" cat "$out" check_out ssd1306/help-clear "clear (line number or all)" cat "$out" check_out ssd1306/help-display "0/display off 1/display on" cat "$out" check_out ssd1306/help-font "0/small font 5x7 1/normal font 8x8 (default normal font)" cat "$out" check_out ssd1306/help-help "help message" cat "$out" check_out ssd1306/help-invert "0/normal oled 1/invert oled" cat "$out" check_out ssd1306/help-line "put your line to display" cat "$out" check_out ssd1306/help-msg "put your strings to oled" cat "$out" check_out ssd1306/help-node "I2C device node address (0,1,2..., default 0)" cat "$out" check_out ssd1306/help-rotate "0/normal 180/rotate" cat "$out" check_out ssd1306/help-x "x position" cat "$out" check_out ssd1306/help-y "y position" cat "$out" # The layout is two literal tabs between the letter and its text. Asserted on # one line, with the tabs built by printf rather than typed, so an editor that # converted them cannot make this pass by accident. check_out ssd1306/help-layout "-I$TAB${TAB}init oled" cat "$out" # The help is printf, so it is on STDOUT and stderr carries nothing. check_eq ssd1306/help-stream "" "$(ssd1306_bin -h 2>&1 >/dev/null)" # -h wins wherever it appears, because the getopt loop returns the moment it # reaches it -- even after an option that would have opened a bus. check_rc ssd1306/help-after-n 0 sh -c "ssd1306_bin -n $free -h > /dev/null 2>&1" # ...but NOT after an option that already returned. -r's validation is upstream # of -h in argv order, and argv order is what decides. The pair is a # DIFFERENTIAL on position alone: same two options, opposite outcomes. check_rc ssd1306/help-after-bad-r 1 sh -c "ssd1306_bin -r 5 -h > /dev/null 2>&1" check_rc ssd1306/help-before-bad-r 0 sh -c "ssd1306_bin -h -r 5 > /dev/null 2>&1" # ------------------------------------------- the missing-argument lattice ---- # Ten options take a required argument. Each one, given none, produces TWO # lines -- getopt's own on stderr and main's own on stdout -- and returns 1 # without touching a device. Three cases apiece: the status, the program's # message, and the stream split. for c in "I:missing oled type (128x64/128x32/64x48)" \ "d:missing 0 or 1 fields" \ "f:missing 0 or 1 fields" \ "i:missing 0 or 1 fields" \ "l:missing string" \ "m:missing string" \ "n:missing 0,1,2... I2C device node number" \ "r:missing 0 or 180 fields" \ "x:missing coordinate values" \ "y:missing coordinate values"; do o=${c%%:*}; want=${c#*:} sh -c "ssd1306_bin -$o > '$out' 2>&1"; rc=$? check_eq ssd1306/noarg-$o-rc 1 "$rc" check_out ssd1306/noarg-$o "prams -$o $want" cat "$out" # getopt's half, on stderr, naming the same letter. check_out ssd1306/noarg-$o-getopt "option requires an argument -- '$o'" cat "$out" done # The split itself, posed once: the program's message is on stdout and getopt's # is on stderr, and neither is on the other. check_eq ssd1306/noarg-stdout "prams -n missing 0,1,2... I2C device node number" \ "$(ssd1306_bin -n 2>/dev/null)" sh -c "ssd1306_bin -n 2>'$out' >/dev/null" check_out ssd1306/noarg-stderr "option requires an argument -- 'n'" cat "$out" # ...and argv[0] reaches the tool intact, which is what getopt prefixes its # line with. On this image it is the bare name, because the runner resolves the # tool through PATH. check_out ssd1306/argv0 "ssd1306_bin:" cat "$out" # -c IS THE ONE OPTION WITH AN OPTIONAL ARGUMENT ("c::"), so a bare -c is not a # missing argument and never produces a "prams" line. It falls through to the # open like any ordinary run. Whether an attached `-c3' is parsed as a line # number rather than as clear-all is NOT observable here -- both branches only # take effect after a successful init -- so it is not claimed. sh -c "ssd1306_bin -c -n $free > '$out' 2>&1"; rc=$? check_eq ssd1306/copt-rc 1 "$rc" check_out ssd1306/copt-no-prams "no oled attached to /dev/i2c-$free" cat "$out" if grep -q 'prams -c' "$out"; then bst_notok ssd1306/copt-optional "a bare -c produced a missing-argument message; c:: declares the argument optional" else bst_ok ssd1306/copt-optional fi check_rc ssd1306/copt-attached 1 sh -c "ssd1306_bin -c3 -n $free > /dev/null 2>&1" # ------------------------------------------------- an unknown option is not -- # ...an error. See the header. getopt complains on stderr, main's '?' arm # recognises none of its cases, and the run proceeds to the open. sh -c "ssd1306_bin -Z -n $free > '$out' 2>&1"; rc=$? check_eq ssd1306/badopt-rc 1 "$rc" check_out ssd1306/badopt-getopt "invalid option -- 'Z'" cat "$out" # The refusal it ends on is the ORDINARY one, not a usage error -- which is the # whole of the surprise, and the thing that would change if upstream ever wired # the '?' arm's fall-through to the help. check_out ssd1306/badopt-falls-through "no oled attached to /dev/i2c-$free" cat "$out" # And it prints no help: the `default:' arm is dead code. if grep -q 'help message' "$out"; then bst_notok ssd1306/badopt-no-help "an unknown option printed the help text; upstream's default: arm is unreachable from getopt" else bst_ok ssd1306/badopt-no-help fi # getopt's line is on stderr and the program's is on stdout, here too. check_eq ssd1306/badopt-stdout "no oled attached to /dev/i2c-$free" \ "$(ssd1306_bin -Z -n $free 2>/dev/null)" # --------------------------------------- the one value the program checks ---- # -r is validated in the getopt loop and refused BEFORE any device is touched: # the only pre-device value refusal in the program, and the only case where the # ticket's "rejected before any device is touched" is literally true. The pair # below is a differential on the value alone -- 0 and 180 carry on to the open, # anything else returns at once. sh -c "ssd1306_bin -r 5 -n $free > '$out' 2>&1"; rc=$? check_eq ssd1306/rotate-bad-rc 1 "$rc" check_out ssd1306/rotate-bad "orientation value must be 0 or 180" cat "$out" # ...and it really did stop before the open, which is what the ABSENCE of the # device message proves. if grep -q 'no oled attached' "$out"; then bst_notok ssd1306/rotate-bad-early "an invalid orientation reached the device open; upstream returns 1 from the getopt loop" else bst_ok ssd1306/rotate-bad-early fi for v in 0 180; do sh -c "ssd1306_bin -r $v -n $free > '$out' 2>&1"; rc=$? check_eq ssd1306/rotate-$v-rc 1 "$rc" check_out ssd1306/rotate-$v "no oled attached to /dev/i2c-$free" cat "$out" done # 90 and 360 are the two values a reader expects to be legal and are not. for v in 90 360 -180; do check_out ssd1306/rotate-reject-$v "orientation value must be 0 or 180" \ sh -c "ssd1306_bin -r $v -n $free 2>&1; true" done # -------------------------------------------- the bus number, and its wrap --- # There is no range check. `-n' is atoi() into a uint8_t and the TRUNCATED value # is what gets opened -- and, decisively, what gets printed back. Each pair # below is one exec whose output names the bus the program actually reached, so # the arithmetic is measured rather than inferred. for c in "$free:$free" "300:44" "256:0" "255:255" "-1:255" "abc:0" "0:0" "1:1"; do given=${c%%:*}; want=${c#*:} sh -c "ssd1306_bin -n $given > '$out' 2>&1"; rc=$? check_eq ssd1306/bus-$given-rc 1 "$rc" check_out ssd1306/bus-$given "no oled attached to /dev/i2c-$want" cat "$out" done # With no -n at all the default is 0, which is the same answer `-n abc' and # `-n 256' give -- three spellings the program cannot tell apart. check_out ssd1306/bus-default "no oled attached to /dev/i2c-0" \ sh -c "ssd1306_bin 2>&1; true" # ------------------------------------------- the absent bus, and the depth --- # The ticket's central ask: the one path this machine can take ends in the # documented diagnostic, naming the device path, with a non-zero status and # inside a bounded run. Posed on its own, on the bus the probe found free. sh -c "ssd1306_bin -n $free > '$out' 2>&1"; rc=$? check_eq ssd1306/noent-rc 1 "$rc" check_out ssd1306/noent "no oled attached to /dev/i2c-$free" cat "$out" # The message is the WHOLE of stdout -- one line, nothing before it and nothing # after. A build that also emitted a libc diagnostic, a wasm trap note or a # partial help would still pass the substring case above. check_eq ssd1306/noent-only "no oled attached to /dev/i2c-$free" \ "$(ssd1306_bin -n $free 2>/dev/null)" # ...and stderr is empty, because there is no perror anywhere in the program. check_eq ssd1306/noent-stderr "" "$(ssd1306_bin -n $free 2>&1 >/dev/null)" # THE FAILURE HAPPENED AT init, NOT LATER, and here is the control that shows # it. ssd1306_oled_default_config() writes /tmp/.ssd1306_oled_type as its FIRST # act, before it sends a single command byte -- so a run given -I that got past # init would leave that file behind. It must not exist afterwards. MEASURED on # the oracle: absent after `-I 128x64'. rm -f /tmp/.ssd1306_oled_type 2>/dev/null sh -c "ssd1306_bin -I 128x64 -n $free > '$out' 2>&1"; rc=$? check_eq ssd1306/init-rc 1 "$rc" check_out ssd1306/init-refused "no oled attached to /dev/i2c-$free" cat "$out" if [ -e /tmp/.ssd1306_oled_type ]; then bst_notok ssd1306/init-stopped-early "/tmp/.ssd1306_oled_type was created, so the run reached ssd1306_oled_default_config despite reporting no oled attached" rm -f /tmp/.ssd1306_oled_type 2>/dev/null else bst_ok ssd1306/init-stopped-early fi # THE DEEPEST REACHABLE POINT, AND THE COLLAPSE IT EXPOSES. open(2) SUCCEEDS on # an ordinary file, so staging one at /dev/i2c- carries the program past # the open and into a real ioctl(I2C_SLAVE) on a real descriptor, which this # kernel answers ENOTTY. Upstream reports that with the SAME line it prints for # a missing node, so the pair below asserts the collapse itself: two runs, two # different kernel errors, byte-identical output. The staging is probed, not # assumed -- `touch', never a redirect, because a failing redirect at the top # level of a suite is fatal to the suite shell (brintos/linux#73). node=/dev/i2c-$free staged=no if [ ! -e "$node" ] && touch "$node" 2>/dev/null && [ -f "$node" ]; then staged=yes; fi if [ "$staged" = yes ]; then bst_ok ssd1306/stage-node deep=$(ssd1306_bin -n $free 2>/dev/null); deep_rc=$? check_eq ssd1306/enotty-rc 1 "$deep_rc" check_eq ssd1306/enotty "no oled attached to /dev/i2c-$free" "$deep" # THE CONTROL. The same argv shape against a bus with no node staged fails # one syscall earlier, with a different errno -- and says exactly the same # thing. Without this pair the case above is consistent with a program that # never opened anything. if [ ! -e "/dev/i2c-$ctl" ]; then shallow=$(ssd1306_bin -n $ctl 2>/dev/null) check_eq ssd1306/enoent-control "no oled attached to /dev/i2c-$ctl" "$shallow" # The collapse, stated as the equality it is: strip the bus number and # the two are the same string. check_eq ssd1306/errno-collapse "${deep#no oled attached to /dev/i2c-$free}" \ "${shallow#no oled attached to /dev/i2c-$ctl}" else bst_skip ssd1306/enoent-control "bus $ctl also resolves, so it cannot serve as the no-node control" bst_skip ssd1306/errno-collapse "no usable control bus; see ssd1306/enoent-control" fi rm -f "$node" 2>/dev/null else bst_skip ssd1306/stage-node "cannot create $node (/dev is not writable here), so the ioctl depth has no vehicle" bst_skip ssd1306/enotty-rc "no node could be staged at $node; see ssd1306/stage-node" bst_skip ssd1306/enotty "no node could be staged at $node; see ssd1306/stage-node" bst_skip ssd1306/enoent-control "no node could be staged, so there is nothing to control against" bst_skip ssd1306/errno-collapse "no node could be staged; see ssd1306/stage-node" fi # ------------------------------------------------------------- robustness ---- # The ticket's other half: the refusal must be a refusal, not a crash, a garbage # errno or a wedge. Each case below is an argv the program has no defence # against by inspection -- an argument longer than the fixed buffer it is # strncpy'd into, every option at once, an operand it never reads -- and each # must still end in the one documented line with status 1. # # THE BUFFERS ARE REAL BUT THE OVERRUN IS NOT REACHABLE HERE. oled_type[10], # line[25] and msg[200] are each filled with strncpy(dst, optarg, sizeof dst), # which leaves them UNTERMINATED when optarg is at least as long as the buffer. # Every read of them (the strcmp on oled_type, the two write_* calls) is behind # a successful init, so the unterminated string is never consumed on this # machine -- but the strncpy itself runs, and these cases assert it is survived. # Argv is kept small on purpose: the guest's argument buffer is 8192 bytes and # an exec past it WEDGES rather than errors (brintos/hardwarejs#15). The longest # case here is under 400. long10=0123456789 long40=0123456789012345678901234567890123456789 msg300=$(awk 'BEGIN{ s=""; while (length(s) < 300) s = s "x"; print s }') line80=$(awk 'BEGIN{ s=""; while (length(s) < 80) s = s "y"; print s }') for c in "I-exact:-I $long10" \ "I-long:-I $long40" \ "l-long:-l $line80" \ "m-long:-m $msg300" \ "combined:-I 128x64 -c -d 1 -i 0 -f 1 -x 0 -y 0 -m hi" \ "dashdash:--" \ "operand:extra-operand" \ "repeated:-n 3 -n 4 -n $free"; do id=${c%%:*}; args=${c#*:} sh -c "ssd1306_bin -n $free $args > '$out' 2>&1"; rc=$? check_eq ssd1306/robust-$id-rc 1 "$rc" check_out ssd1306/robust-$id "no oled attached to /dev/i2c-" cat "$out" done # `-n' repeated: the LAST one wins, which is what shows the loop keeps assigning # rather than stopping at the first. Posed as a value, not as a status. check_out ssd1306/repeated-last-wins "no oled attached to /dev/i2c-$free" \ sh -c "ssd1306_bin -n 3 -n 4 -n $free 2>&1; true" # ---------------------------------------------- what the program does NOT have # Asserted as absences so that upstream growing any of them is visible here # rather than silent. Each is a documented gap, not a defect: see the header. # A version option would be a NEW option, so getopt would refuse it and the run # would fall through to the open -- which is exactly what an absent option looks # like, and is what these three assert. for o in V version-long A; do case $o in version-long) argv="--version" ;; *) argv="-$o" ;; esac sh -c "ssd1306_bin $argv -n $free > '$out' 2>&1"; rc=$? check_eq ssd1306/absent-$o-rc 1 "$rc" if grep -qi 'version\|[0-9]\.[0-9]' "$out" && ! grep -q 'no oled attached' "$out"; then bst_notok ssd1306/absent-$o "$argv produced something other than the ordinary refusal: $(_bst_1line "$(cat "$out")")" else bst_ok ssd1306/absent-$o fi done # ------------------------------------------------------- adapter-gated cases -- # These run the real assertion the moment an adapter exists. Until then they say # what they saw, not merely that they looked. if [ -n "$adapter" ]; then # AN ADAPTER IS NOT A DISPLAY, and the three outcomes are distinguished. # ssd1306_init opens the bus, sets the slave address -- neither of which # probes anything -- and only then writes a control byte and reads it back; # an adapter with nothing at 0x3c reads 0 and is reported with the SAME # "no oled attached" line as a bus that does not exist. So rc=0 is a display # that answered, the documented line is the ticket's own premise (an adapter # but no display) and is a skip rather than a red, and anything else is a # genuine failure. MEASURED on the build-host oracle, which has a real x86 # SMBus at i2c-0 and no OLED on it. sh -c "ssd1306_bin -n $adapter > '$out' 2>&1"; rc=$? if [ "$rc" -eq 0 ]; then bst_ok ssd1306/adapter-init elif [ "$rc" -eq 1 ] && grep -q "no oled attached to /dev/i2c-$adapter" "$out"; then bst_skip ssd1306/adapter-init "adapter $adapter is present (via $adapter_src) but nothing answers at 0x3c, so ssd1306_init reports the documented refusal -- this machine has a bus and no display" else bst_notok ssd1306/adapter-init "adapter $adapter is present (via $adapter_src) and ssd1306_init failed in an undocumented way: rc=$rc $(_bst_1line "$(cat "$out")")" fi if [ "$rc" -eq 0 ]; then check_rc ssd1306/adapter-config 0 sh -c "ssd1306_bin -n $adapter -I 128x64 > /dev/null 2>&1" else bst_skip ssd1306/adapter-config "no display answered on adapter $adapter, so no resolution can be configured; see ssd1306/adapter-init" fi else bst_skip ssd1306/adapter-init "no I2C adapter on this machine: /proc/bus/i2c absent, /sys/class/i2c-dev empty, no /dev/i2c-* character device (i2c-dev chardev in /proc/devices: $chardev, /sys/bus/i2c: $sysbus) -- ssd1306_init cannot succeed and everything behind it is unreachable; the reachable half is covered by the cases above" bst_skip ssd1306/adapter-config "no I2C adapter, so no resolution can be configured; see ssd1306/adapter-init" fi rm -f "$out" "$W".* 2>/dev/null