# BST-TIMEOUT: 300 # 56 cases; measured elapsed=13s on an idle local guest # # (2026-08-23). The payload is 2 kB and the read of it is # # three programs -- the 13 s is almost entirely the FIVE # # execs in the consumer block, at roughly 1.5 s apiece on # # this arm. That is 4.6x under the 60 s default on the # # host's BEST day, which is not the day a bound has to # # hold on, so one is declared. Same value and same reason # # as 99-libjs-sizzle. # # fbset-modes 0.1.0 -- /etc/fb.modes, the framebuffer video-mode database # fbset(8) reads to translate a named mode into geometry and timings. # meta-openembedded/meta-oe, recipes-support/fbset. # # WHAT THIS IMAGE ACTUALLY INSTALLS, MEASURED, NOT ASSUMED: 89 bytes, three # lines, TWO COMMENTS AND A BLANK -- and NO MODE STANZAS AT ALL. That is # upstream's own default and it is the correct file for this machine, not a # truncated payload. The recipe's SRC_URI is a bare `file://fb.modes` resolved # through FILESPATH, and its fbset-modes/ directory carries machine overrides # for exactly four machines (qemuarm, qemumips, om-gta01, omap3-pandora). # brintos-wasm32 is not one of them and conf/machine/brintos-wasm32.conf sets no # MACHINEOVERRIDES, so the top-level default is selected -- the same file an # x86-64 build with no override selects, which is why byte identity with the # reference is expected here BY CONSTRUCTION. # # SO WHAT IS THIS SUITE A STATEMENT ABOUT? Not the compiler: the recipe compiles # nothing, its do_install is one `install -m 0644`. It is a statement about the # INSTALL AND PACKAGING PATH, which is the part that can break on this arm -- # that ${sysconfdir} resolved to /etc, that the payload landed in the package # the image installs rather than in the -dev or -dbg package it does not, that # nothing truncated or re-encoded it on the way to a virtiofs root, and that no # other recipe claims the same path. That was the ticket's stated risk and it is # what the cases below are arranged around. # # ZERO IS THE RIGHT ANSWER, AND ZERO IS ALSO WHAT A BROKEN READER SAYS. # `modes 0` on the shipped file is the assertion no wrong answer could fail, so # it is not left standing alone: data/fbset-modes-parse.awk reads the shipped # file AND data/fbset-modes-fbmodes.fixture on ONE pass, and the fixture's four # stanzas -- three of them verbatim from this recipe's own machine variants -- # are what make the zero a measurement. The parser was mutation-tested against # seven deliberate defects (a dropped endmode, an unknown keyword, a wrong-arity # geometry, a stray endmode, a keyword outside any stanza, a duplicate name, a # dropped alias); each fires its own counter and only its own. # # WRITTEN TO EXEC AS LITTLE AS POSSIBLE -- one find, one sha256sum and one gawk # cover the whole payload, and the five remaining execs all belong to the # consumer block, where each one IS the measurement. On this arm a process's # memory is not returned when it exits and /bin/sh is a 16 MB wasm module, so a # suite's cost is dominated by how many programs it starts. . "$BST_LIB" S=fbset-modes # THE CANARY, which asserts nothing about fbset-modes. # # A suite's FIRST BST result line is lost between the guest's printf and the # host's read -- per-suite, deterministic, and NOT a defect in the host tool # (BRINTOS-1149 established that and filed it as linux-in-the-browser#177). # The harness reports the deficit but computes its pass count as # total-minus-failures, so THE DROPPED LINE IS COUNTED AS A PASS. A nothing-case # therefore stands in that position and the eaten line costs nothing; every real # case below is still counted. It sits ahead of the `require` guards # deliberately, because a `require` that skips would emit its skip as the first # line and that line would be eaten too. Same idiom and reason as 99-mailcap # and 99-libjs-sizzle. # # Remove this case when linux-in-the-browser#177 is fixed. bst_ok $S/suite-canary F=/etc/fb.modes D=$BST_DATA FIX=$D/fbset-modes-fbmodes.fixture tmp=${TMPDIR:-/tmp} W=$tmp/bst-fbset-modes.$$ require gawk $S/gawk-present require sha256sum $S/sha256sum-present require find $S/find-present # ------------------------------------------------------------- payload shape # -maxdepth 0 over the two paths this suite reads, plus the three the package # must NOT have produced. %y catches a symlink where a regular file is meant, # %m the mode, %l a link target -- none of which `test -f` can see. find "$F" "$FIX" /etc/fb.modes.dpkg-new /etc/fb.modes.dpkg-old \ /usr/share/doc/fbset-modes \ -maxdepth 0 -printf '%p %y %s %m %l\n' 2>/dev/null | LC_ALL=C sort > "$W.inv" sha256sum "$F" > "$W.sums" 2>&1 for pair in "installed:$F" "fixture:$FIX"; do nm=${pair%%:*}; f=${pair#*:} if [ -f "$f" ]; then bst_ok "$S/present-$nm" else bst_notok "$S/present-$nm" "$f is not a regular file"; fi done if [ ! -f "$F" ] || [ ! -f "$FIX" ]; then bst_notok $S/parse-prereq "a payload file is missing; the parse batch cannot run" rm -f "$W.inv" "$W.sums" return 2>/dev/null || exit 0 fi LC_ALL=C gawk -v PFX1=FBS -v PFX2=FBF -v INV="$W.inv" -v SUMS="$W.sums" \ -f "$D/fbset-modes-parse.awk" "$F" "$FIX" > "$W.f1" . "$W.f1" # THE SCAN RAN AT ALL. Several cases below assert an empty value or a zero, and # both are exactly what a gawk that died before END hands back -- which is what # an unreadable input produces, gawk being fatal on one. Asserted first, so # those cases rest on something. check_eq $S/scan-ran 'ok' "$FBS_SCAN" check_eq $S/fix-scan-ran 'ok' "$FBF_SCAN" # The find saw exactly the paths it should: the installed file and the fixture, # and NEITHER of the two dpkg conffile leftovers nor a -doc directory. Asserting # the total is what makes the three absences provable from one measurement -- # a find without -printf produces nothing, silently, and every size and type # case below would then read ''. check_eq $S/inventory-n 2 "$FB_INV_N" check_eq $S/type 'f' "$FB_TY__etc_fb_modes" check_eq $S/mode '644' "$FB_MO__etc_fb_modes" check_eq $S/not-symlink '' "$FB_LN__etc_fb_modes" # ------------------------------------------ content, against x86-64 reference # The sum is of meta-openembedded's own fbset-modes/fb.modes blob at the pin # this image builds from (bec75506), put through the recipe's do_install -- # which is a single `install -m 0644`, i.e. a copy. That is the upstream # artifact by its own route on x86-64, not a re-hash of the wasm32 .deb, which # would have made the comparison tautological. check_eq $S/size 89 "$FB_SZ__etc_fb_modes" check_eq $S/sha256 \ f63772c038b0d2e9c2dd92db68d2be7228cdccbe16610b9e04f67dab8f8e052d \ "$FB_SHA__etc_fb_modes" # --------------------------------------------- read back through the target # An INDEPENDENT way of saying the file is complete: these come from gawk's own # reader rather than from sha256sum's, and a truncation shows up as a short byte # or line count rather than only as a different digest. check_eq $S/readback-lines 3 "$FBS_LINES" check_eq $S/readback-bytes 89 "$FBS_BYTES" check_eq $S/comments 2 "$FBS_COMMENTS" check_eq $S/blanks 1 "$FBS_BLANKS" check_eq $S/final-newline 'yes' "$FBS_FINAL_NL" # Every byte is ASCII and none is a CR. Asserted rather than left implicit: both # would be NEW facts about the transfer to the virtiofs root rather than about # the package, and the byte counts above are only locale-independent while the # first holds. The recipe runs no CRLF strip, so a CR here would have to have # come from somewhere else entirely. check_eq $S/no-cr-bytes 0 "$FBS_CR" check_eq $S/no-high-bytes 0 "$FBS_HI" # ------------------------------------------------ the database, as a database # THE HEADLINE, and the reason the fixture exists. The shipped file declares no # modes, which is correct for a machine with no override -- and is also what a # reader that cannot parse fb.modes(5) reports. The fixture cases below are run # by the same program on the same pass, so this zero is only produced by a # reader that found four stanzas. check_eq $S/modes 0 "$FBS_MODES" check_eq $S/mode-names '' "$FBS_NAMES" # The five ways an fb.modes file can be malformed, all zero on a file that is # nothing but comments. They are named rather than folded into `modes 0` # because a payload that acquired content would fail these first and by cause. check_eq $S/no-bad-keyword 0 "$FBS_BADKEY" check_eq $S/no-bad-arity 0 "$FBS_BADARG" check_eq $S/no-unclosed-mode 0 "$FBS_UNCLOSED" check_eq $S/no-stray-endmode 0 "$FBS_STRAY_END" check_eq $S/no-keyword-loose 0 "$FBS_OUTSIDE" # --------------------------------------------- the parser, on real fb.modes # Three of these four stanzas are verbatim from THIS RECIPE's own machine # variants; the fourth is composed to reach the ten fb.modes(5) keywords none of # the three uses. Together they are what the zero above rests on. check_eq $S/fix-modes 4 "$FBF_MODES" check_eq $S/fix-names 'vga,480x640,800x480-65,1024x768-60' "$FBF_NAMES" # The two-name form: qemuarm's line is `mode "vga" "640x480"`, and a reader that # takes field 2 as the whole of the name loses the alias silently. This is the # case that catches it -- fix-names above passes either way. check_eq $S/fix-aliases \ 'vga|640x480,480x640,800x480-65,1024x768-60' "$FBF_ALLNAMES" check_eq $S/fix-geometry-1 '640 480 640 480 16' "$FBF_GEOM_1" check_eq $S/fix-timings-1 '' "$FBF_TIM_1" check_eq $S/fix-geometry-2 '480 640 480 640 16' "$FBF_GEOM_2" check_eq $S/fix-timings-2 '38461 104 8 2 16 8 2' "$FBF_TIM_2" check_eq $S/fix-opts-2 'accel=false' "$FBF_OPTS_2" check_eq $S/fix-geometry-3 '800 480 800 480 16' "$FBF_GEOM_3" check_eq $S/fix-timings-3 '27777 40 214 10 34 1 1' "$FBF_TIM_3" # rgba carries commas and slashes inside one argument; a reader that splits the # option list on ',' before the keyword reports four bogus options here. check_eq $S/fix-opts-3 'rgba=5/11,6/5,5/0,0/0' "$FBF_OPTS_3" check_eq $S/fix-geometry-4 '1024 768 1024 768 16' "$FBF_GEOM_4" check_eq $S/fix-timings-4 '15384 160 24 29 3 136 6' "$FBF_TIM_4" check_eq $S/fix-opts-4 \ 'hsync=low,vsync=low,csync=low,gsync=false,extsync=false,bcast=false,laced=false,double=false,nonstd=0,grayscale=false' \ "$FBF_OPTS_4" # The inline comments inside two of the stanzas are dropped rather than read as # keywords -- which is what no-bad-keyword would catch on the fixture side. check_eq $S/fix-no-bad-keyword 0 "$FBF_BADKEY" check_eq $S/fix-no-bad-arity 0 "$FBF_BADARG" check_eq $S/fix-no-unclosed 0 "$FBF_UNCLOSED" check_eq $S/fix-no-stray-end 0 "$FBF_STRAY_END" check_eq $S/fix-no-loose-kw 0 "$FBF_OUTSIDE" check_eq $S/fix-no-dup-name 0 "$FBF_DUPNAME" # ------------------------------------------------------------------ packaging # THE EXPECTATION IS DERIVED, NOT WRITTEN DOWN, and this is the ticket's stated # risk. Measured off the three .debs the recipe builds: fbset-modes carries # ./etc/fb.modes and nothing else, and fbset-modes-dev and fbset-modes-dbg are # BOTH EMPTY. No -doc .deb is produced at all -- the recipe installs nothing to # ${docdir} -- so that directory must be absent while the payload above is # present. Saying it that way round is the point: this is the case that fails if # the file ever lands in a package the image does not install. if [ -d /usr/share/doc/fbset-modes ]; then bst_notok $S/doc-not-installed \ "/usr/share/doc/fbset-modes exists; the recipe installs nothing to \${docdir}" else bst_ok $S/doc-not-installed fi # CONFFILES:${PN} names /etc/fb.modes, so the package manager owns it as a # conffile. A .dpkg-new or .dpkg-old beside it would mean the install resolved a # conflict rather than placing the file -- which is also the only way the sha # above could be right while the file anything reads is a different one. # Two cases rather than a loop: a loop would have to derive each case id from # its path, and a case id built by an exec is one the suite pays for twice. if [ -e /etc/fb.modes.dpkg-new ]; then bst_notok $S/conffile-no-dpkg-new "/etc/fb.modes.dpkg-new exists beside the conffile" else bst_ok $S/conffile-no-dpkg-new fi if [ -e /etc/fb.modes.dpkg-old ]; then bst_notok $S/conffile-no-dpkg-old "/etc/fb.modes.dpkg-old exists beside the conffile" else bst_ok $S/conffile-no-dpkg-old fi # --------------------------------------------------------------- the consumer # THE TICKET SAID THERE IS NO END-TO-END READ TO ADD BECAUSE "fbset itself is # not in this image". Half of that is wrong, and the half that is right is right # for a different reason -- so both were measured on target (2026-08-23) rather # than assumed, and the cases below are what came back. # # THERE IS AN fbset HERE. /usr/sbin/fbset is BUSYBOX 1.36.1's applet, a symlink # to /bin/busybox.nosuid held on the update-alternatives link at priority 50 # (/usr/lib/opkg/alternatives/fbset). What is absent is meta-oe's fbset PACKAGE, # which installs ${sbindir}/fbset.real and declares priority 55 -- so when that # port lands it takes this link from busybox, and the fbset-real case below # turns green on its own. # # AND IT STILL CANNOT READ THIS DATABASE -- for a reason that has nothing to do # with the file declaring no modes. busybox's fbset opens /dev/fb0 and issues # FBIOGET_VSCREENINFO on it BEFORE it consults the mode database, and this # platform has no framebuffer device at all: /dev/fb0 absent, /proc/fb empty, # /sys/class/graphics carrying only fbcon. Every spelling that would reach the # database dies at that open first, INCLUDING `fbset -db /etc/fb.modes vga`, and # including with FRAMEBUFFER=/dev/null set -- busybox hardcodes /dev/fb0 and # ignores that variable. # # THE ORDERING CONTROL IS `fbset -fb /dev/null vga`. Without it, "the lookup # fails" is compatible with the database being at fault. With it, the call gets # PAST the open and dies at `FBIOGET_VSCREENINFO: Inappropriate ioctl for # device` -- still before any read of /etc/fb.modes. That is what establishes # the order rather than merely the failure, and it is why the file comparison # above is the whole criterion for this port: the database is unreachable # through this consumer by construction, not by accident of the payload. # # EVERY EXPECTATION HERE IS DERIVED from what is installed, so none of it goes # false the day either half changes. require readlink $S/readlink-present fbset_path=$(command -v fbset 2>/dev/null) if [ -n "$fbset_path" ]; then bst_ok $S/consumer-present else bst_notok $S/consumer-present "no fbset on PATH; busybox provides the applet and should"; fi # Which fbset holds the alternatives link. The meta-oe package installs # ${sbindir}/fbset.real; busybox's applet resolves into the busybox binary. # `readlink -f` prints a path it could not resolve rather than failing, so the # existence test comes first -- without it an absent link reads as "the package # landed", which is the one answer that must not be guessed. if [ ! -e /usr/sbin/fbset ]; then bst_notok $S/consumer-is-busybox "/usr/sbin/fbset does not exist; busybox should hold the alternatives link" else fbset_real=$(readlink -f /usr/sbin/fbset 2>/dev/null) case "$fbset_real" in */busybox*) FBIMPL=busybox; bst_ok $S/consumer-is-busybox ;; *) FBIMPL=metaoe; bst_skip $S/consumer-is-busybox \ "the link now resolves to $fbset_real -- meta-oe's fbset package has landed and taken it from busybox at priority 55" ;; esac fi # WHICH IMPLEMENTATION ANSWERS `fbset' DECIDES TWO OF THE CASES BELOW, and the # two behave differently for reasons that are both correct. This was written # when busybox was the only answer; BRINTOS-1395 landed meta-oe's fbset at # update-alternatives priority 55, which takes the link from busybox's 50 -- # exactly as the comment above predicted. What the comment did not predict is # that the two disagree on more than their provenance: # # --help busybox's applet prints usage and exits 0. fbset 2.1's Usage() # is `puts(VERSION); Die(...)', and Die() exits 1 -- so a # non-zero status IS its documented success here. # -fb /dev/null # busybox opens /dev/fb0 and issues FBIOGET_VSCREENINFO BEFORE it # consults the database, so it never reads the file. fbset 2.1 # calls ReadModeDB() immediately after the open, so it DOES read # it and reports `Unknown video mode' -- which is the right # answer, because the shipped /etc/fb.modes declares no modes. # # So the expectation is derived from FBIMPL rather than pinned to either. Both # branches stay live: this suite must keep passing on an image built without # the fbset package, which is every image before that port and any image that # drops it. [ -n "$FBIMPL" ] || FBIMPL=busybox if [ -e /usr/sbin/fbset.real ]; then bst_skip $S/fbset-package-absent \ "meta-oe's fbset package has landed; an end-to-end read through it is now possible and is not covered here" else bst_ok $S/fbset-package-absent fi # The binary runs on this arm at all. Asserted before the failures below, so a # busybox that could not start is not mistaken for the platform having no # framebuffer. # busybox exits 0 from --help; fbset 2.1's Usage() ends in Die(), which exits 1. if [ "$FBIMPL" = metaoe ]; then want_help_rc=1; else want_help_rc=0; fi out=$(fbset --help 2>&1); rc=$? case "$out" in *BusyBox*|*fbset*) [ "$rc" -eq "$want_help_rc" ] && bst_ok $S/consumer-runs \ || bst_notok $S/consumer-runs "rc=$rc want=$want_help_rc ($FBIMPL) $(_bst_1line "$out")" ;; *) bst_notok $S/consumer-runs "rc=$rc $(_bst_1line "$out")" ;; esac if [ -e /dev/fb0 ]; then # A framebuffer appeared. Everything below was written for a platform with # none, so say so rather than reporting its expectations as met. bst_skip $S/no-framebuffer \ "/dev/fb0 now exists -- this image has a framebuffer, and the three cases below need rewriting as real lookups" bst_skip $S/db-blocked-at-open "/dev/fb0 exists" bst_skip $S/db-blocked-with-db-flag "/dev/fb0 exists" bst_skip $S/db-blocked-before-read "/dev/fb0 exists" else bst_ok $S/no-framebuffer # The lookup, and the same lookup pointed explicitly at this database. Both # die at the open, which is the statement that the database is never reached. out=$(fbset vga 2>&1); rc=$? case "$out" in *"/dev/fb0"*) [ "$rc" -ne 0 ] && bst_ok $S/db-blocked-at-open \ || bst_notok $S/db-blocked-at-open "rc=0 from a lookup with no framebuffer" ;; *) bst_notok $S/db-blocked-at-open "rc=$rc want the /dev/fb0 open to fail first, got: $(_bst_1line "$out")" ;; esac out=$(fbset -db "$F" vga 2>&1); rc=$? case "$out" in *"/dev/fb0"*) [ "$rc" -ne 0 ] && bst_ok $S/db-blocked-with-db-flag \ || bst_notok $S/db-blocked-with-db-flag "rc=0 from a lookup with no framebuffer" ;; *) bst_notok $S/db-blocked-with-db-flag "rc=$rc want the /dev/fb0 open to fail first, got: $(_bst_1line "$out")" ;; esac # THE ORDERING CONTROL. -fb /dev/null gets past the open; the failure moves to # the ioctl, and is still not a complaint about the database. A run that # reported an unknown video mode here would mean the database IS consulted and # the two cases above were measuring the wrong thing. # THE ORDERING CONTROL, and the one case whose expected answer is the # OPPOSITE for the two implementations -- which is the whole finding, not a # tolerance. -fb /dev/null gets past the open in both. busybox then issues # the ioctl and dies there, having never read the file; fbset 2.1 reads the # file first and reports that `vga' is not in it. A run that reported an # unknown video mode under BUSYBOX would mean the two cases above were # measuring the wrong thing, and one that reported FBIOGET_VSCREENINFO under # META-OE would mean the same in reverse -- so each branch still refuses the # other's answer rather than accepting either. out=$(fbset -fb /dev/null vga 2>&1); rc=$? if [ "$FBIMPL" = metaoe ]; then case "$out" in *"Unknown video mode"*) [ "$rc" -ne 0 ] && bst_ok $S/db-blocked-before-read \ || bst_notok $S/db-blocked-before-read "rc=0 for a mode the shipped database does not declare" ;; *FBIOGET_VSCREENINFO*) bst_notok $S/db-blocked-before-read \ "fbset 2.1 reached the ioctl before reading the database: $(_bst_1line "$out")" ;; *) bst_notok $S/db-blocked-before-read "rc=$rc want the empty database to be consulted, got: $(_bst_1line "$out")" ;; esac else case "$out" in *FBIOGET_VSCREENINFO*) [ "$rc" -ne 0 ] && bst_ok $S/db-blocked-before-read \ || bst_notok $S/db-blocked-before-read "rc=0 on a non-framebuffer fd" ;; *"video mode"*|*"unknown"*) bst_notok $S/db-blocked-before-read \ "the database IS consulted past the open: $(_bst_1line "$out")" ;; *) bst_notok $S/db-blocked-before-read "rc=$rc want FBIOGET_VSCREENINFO, got: $(_bst_1line "$out")" ;; esac fi fi rm -f "$W.inv" "$W.sums" "$W.f1"