# BST-TIMEOUT: 600 # 81 cases, TEN of them runs of the 7.8 MB # # data/freetype-probe. MEASURED at elapsed=37s on a fresh # # guest (2026-08-25, local, unloaded), which is the same # # order as its three neighbours on the same image -- # # 99-liberation-fonts 25s, 99-terminus-font 28s, # # 57-freetype 23s -- so the ten probe runs cost far less # # than their size suggests. The bound is 57-freetype's: # # that suite drives the SAME probe and declares 600, and a # # suite whose cost is dominated by one binary should not # # claim a different bound for it. ~16x the measurement, and # # a hung suite costs ten minutes of a sweep rather than # # thirty. Set from the ==BST-SUITE elapsed= line, not # # guessed: the first draft said 1800 on an estimate, which # # was 48x the real figure. # # ttf-bitstream-vera 1.10 -- the Bitstream Vera family: four sans faces, four # monospace faces and two serif faces, the reference font set most of the free # desktop stack falls back to. poky # meta/recipes-graphics/ttf-fonts/ttf-bitstream-vera_1.10.bb. # # WHAT THIS IMAGE ACTUALLY INSTALLS, MEASURED, NOT ASSUMED: THIRTEEN files in # TWO packages. Ten TTFs (578,372 bytes) under /usr/share/fonts/ttf from # ttf-bitstream-vera, and three .TXT files under # /usr/share/doc/ttf-bitstream-vera from ttf-bitstream-vera-doc. The split is # the recipe's: it sets `FILES:${PN} = "${datadir}/fonts"` -- an assignment, not # an append -- so the ${docdir}/${BPN} half of its own do_install falls to the # default -doc package. liberation-fonts, the sibling font port on this image, # instead sets `PACKAGES = "${PN}"` and has no such split; reading one recipe's # packaging off the other's is exactly the assumption this suite exists to # refuse. The image names BOTH packages, deliberately: the ticket's criterion is # every file the recipe installs, and the Bitstream Vera licence asks that its # notice travel with the fonts. # # THE FONT DIRECTORY IS SHARED WITH liberation-fonts, AND THAT IS THE ONE NEW # RISK THIS PORT CARRIES. Both recipes install into /usr/share/fonts/ttf. No # filename collides (Vera* against Liberation*), but an install that clobbered # the directory rather than adding to it would leave every per-file case here # green, so `cotenant-liberation` counts the twelve Liberation files from the # same inventory. The Vera census is scoped to ^Vera for the mirror-image # reason: a case counting "the files in the directory" would be counting two # packages and would redden the day a third font lands. 99-liberation-fonts was # amended by this ticket for the same reason -- its own census had been written # when it was the only font on the image. # # FOUR LAYERS, AND A RED IN AN EARLIER ONE LOCALISES A RED IN A LATER ONE: # # file-*, ttf-*, the payload arrived intact -- size, bytes, mode, type, # pkg-*, docdir-* count -- and in the packages the image really installs. # No font machinery involved at all. # ft-* the image's OWN freetype PARSES each face and agrees # about num_faces, num_glyphs, units_per_EM, family and # style. This is the layer the ticket asks for by name, and # nothing else on the image can reach it: freetype installs # no executable, and fontconfig -- which parses the same # files -- has no notion of a face or glyph count. # fcq-* the SECOND, INDEPENDENT parse: one fc-scan reads what # FONTCONFIG makes of each file, compared field for field # against a control captured natively on x86-64 with the # same fontconfig 2.15.0 over the same freetype 2.13.2. # It covers what the freetype API does not name -- the # PostScript name, the derived slant/weight/spacing, and # the charset -- and costs one exec for all ten. # fc-* fontconfig can SEE and RESOLVE them through the CACHE, # which is a different path again: the cache rebuilds, # fc-list finds all ten faces under their three families, # and fc-match resolves each family to a Vera file rather # than falling through to Liberation. # # ft-* AND fcq-* ARE NOT THE SAME MEASUREMENT MADE TWICE. They read the same # ten files through two different consumers of one library, and they disagree # about different things: a defect in the sfnt table parsing moves both, a # defect in fontconfig's derivation of slant or spacing moves only fcq-*, and a # truncated glyph table moves only ft-*. The split is what says which. # # THE REFERENCE IS UPSTREAM'S OWN BYTES, WHICH IS STRONGER THAN THE x86-64 BUILD # THE TICKET ASKS FOR. `inherit allarch` plus a do_install that is two loops of # `install -m 644` over the extracted tarball means nothing arch-dependent # touches a byte, so a native build of this version produces these same files by # construction. All thirteen were verified byte-identical to # ttf-bitstream-vera-1.10.tar.bz2 -- whose sha256 the recipe itself pins as # db5b27df...78dc -- and data/ttf-bitstream-vera-manifest.txt was GENERATED from # the built .debs, not typed. # # THE fontcache POSTINST IS THE THING THE TICKET SAYS TO CHECK RATHER THAN # ASSUME, AND IT IS CHECKED HERE IN THE ONE FORM THAT IS BOOT-MODE INDEPENDENT. # fontcache.bbclass appends fontconfig-utils to RDEPENDS and registers an # update_font_cache intercept that runs fc-cache under qemu-user; there is no # wasm32 qemu-user, so it is deferred to run-postinsts at first boot, which # /etc/inittab does run (BRINTOS-53). Whether it HAS run depends on the pid 1 the # harness booted -- run-machine.mjs goes straight to a shell by default, which is # why 79-run-postinsts has to branch on the boot mode -- so asserting the # configured state here would report the runner's choice as a package red. What # is asserted instead is boot-mode independent and is the actual risk: the # scriptlet is on the image, it calls fc-cache, and fc-cache is present, so the # deferred run cannot fail for want of the tool. Image-wide postinst SUCCESS is # 79-run-postinsts' `no-unpacked` case and is not re-litigated here. The fc-* # block below forces `fc-cache -f` itself so it is independent of all of this. # # WRITTEN TO EXEC AS LITTLE AS POSSIBLE AROUND THE PROBES. One find, one # sha256sum, one fc-scan, one sed and two gawks cover every payload, packaging # and fontconfig case; the ten probe runs are appended to a single fenced file # rather than parsed one at a time, because twenty greps on top of ten 7.8 MB # execs would roughly double the suite for nothing. The ten probe runs ARE the # cost of this suite and are what its BST-TIMEOUT is set from. . "$BST_LIB" S=ttf-bitstream-vera # THE CANARY, which asserts nothing about ttf-bitstream-vera. # # 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. # # Remove this case when linux-in-the-browser#177 is fixed. bst_ok $S/suite-canary D=$BST_DATA REF=$D/ttf-bitstream-vera-manifest.txt FACTS=$D/ttf-bitstream-vera-fontfacts.txt PROBE_SRC=$D/freetype-probe FACES=$D/ttf-bitstream-vera-fcfaces.txt FIX=$D/freetype-vera.ttf TTFDIR=/usr/share/fonts/ttf DOCDIR=/usr/share/doc/ttf-bitstream-vera POSTINST=/var/lib/dpkg/info/ttf-bitstream-vera.postinst tmp=${TMPDIR:-/tmp} W=$tmp/bst-ttf-bitstream-vera.$$ require gawk $S/gawk-present require sha256sum $S/sha256sum-present require find $S/find-present # fc-scan runs in the payload block, ahead of the consumer block's own check, so # its absence is guarded here. It is NOT a `require`: fontconfig-utils is a hard # Depends of this package, so an absent fc-scan is a defect in the port rather # than a legitimately absent tool, and a skip would report it green. if [ -x /usr/bin/fc-scan ]; then bst_ok $S/fc-scan-present else bst_notok $S/fc-scan-present "fontconfig-utils is a Depends of ttf-bitstream-vera but /usr/bin/fc-scan is absent"; fi if [ ! -f "$REF" ] || [ ! -f "$FACTS" ] || [ ! -f "$FACES" ]; then bst_notok $S/ref-present "the reference data did not ship: $REF / $FACTS / $FACES" exit 0 fi bst_ok $S/ref-present # ------------------------------------------------------------- payload shape # One find over BOTH payload directories AND over the Debian font path the # recipe must not have produced, so every absence is a measurement from the same # pass. %y catches a symlink where a regular file is meant and %m the mode -- # neither of which `test -f` can see. find "$TTFDIR" "$DOCDIR" /usr/share/fonts/truetype \ -maxdepth 1 -printf '%p %y %s %m %l\n' 2>/dev/null | LC_ALL=C sort > "$W.inv" # One sha256sum over all thirteen, PLUS 57-freetype's fixture -- so the # fixture-identity case below costs no extra exec. The globs are the shell's, so # this is one exec whatever the file count, and fourteen paths is ~650 bytes of # argv -- nowhere near the guest's 8192-byte __wasm32_args_buf ceiling # (brintos/hardwarejs#15). sha256sum "$TTFDIR"/Vera*.ttf "$DOCDIR"/*.TXT "$FIX" > "$W.sums" 2>&1 # One fc-scan over all ten. fontconfig opens each file through FreeType, so this # single exec is a second, independent parse of every face -- and it reads # fields freetype's own API does not name (the PostScript name, the derived # slant/weight/spacing, the charset). It reads the FILES, not the cache, so it # is independent of the fc-cache below. LC_ALL=C fc-scan --format '%{file|basename}|%{family}|%{style}|%{slant}|%{weight}|%{spacing}|%{postscriptname}|%{charset}\n' \ "$TTFDIR"/Vera*.ttf 2>/dev/null | LC_ALL=C sort > "$W.scan" LC_ALL=C gawk -v REF="$REF" -v SUMS="$W.sums" -v INV="$W.inv" \ -v FACES="$FACES" -v SCAN="$W.scan" -v FIX="$FIX" \ -f "$D/ttf-bitstream-vera-verify.awk" "$REF" "$W.sums" "$W.inv" "$FACES" "$W.scan" # ---------------------------------------------------- the packaging on target # THE PACKAGE DATABASE IS THE ONLY PLACE THE SPLIT IS VISIBLE ON TARGET. The # file-* cases above would look identical if do_install had simply written all # thirteen into one package, so they cannot say that ttf-bitstream-vera-doc is a # real package the image installs rather than a line in IMAGE_INSTALL that # resolved to nothing. One sed pulls both stanzas; the loop is shell builtins. # The stanzas are read from a tmpfs copy, never redirected from the rootfs # (brintos/linux#73). sed -n '/^Package: ttf-bitstream-vera$/,/^$/p; /^Package: ttf-bitstream-vera-doc$/,/^$/p' \ /var/lib/dpkg/status > "$W.st" 2>/dev/null _pkg=""; _main=0; _doc=0; _dep=0; _arch=0 while IFS= read -r l; do case "$l" in "Package: ttf-bitstream-vera") _pkg=main; _main=1 ;; "Package: ttf-bitstream-vera-doc") _pkg=doc; _doc=1 ;; "Architecture: all") [ "$_pkg" = main ] && _arch=1 ;; "Depends: "*) if [ "$_pkg" = main ]; then case "$l" in *fontconfig-utils*) _dep=1 ;; esac fi ;; esac done < "$W.st" if [ "$_main" -eq 1 ]; then bst_ok $S/pkg-known else bst_notok $S/pkg-known "dpkg does not know the package ttf-bitstream-vera"; fi # THE -doc PACKAGE IS ON THE IMAGE BY DECISION, NOT BY ACCIDENT. A dropped # IMAGE_INSTALL line would take the three .TXT file-* cases with it; this case # says the cause is packaging rather than three missing files. if [ "$_doc" -eq 1 ]; then bst_ok $S/pkg-doc-known else bst_notok $S/pkg-doc-known "dpkg does not know the package ttf-bitstream-vera-doc"; fi # `allarch` reached the package metadata, which is what makes the byte-identity # reference above a statement about this build rather than about a coincidence. if [ "$_arch" -eq 1 ]; then bst_ok $S/pkg-arch-all else bst_notok $S/pkg-arch-all "ttf-bitstream-vera is not Architecture: all"; fi # THE fontcache CLASS'S RDEPENDS, READ BACK OFF THE TARGET. The recipe declares # no runtime dependency at all -- fontcache.bbclass appends fontconfig-utils -- # so this is the one case that says the class did its half and that the # dependency resolved against a package this image already carried. if [ "$_dep" -eq 1 ]; then bst_ok $S/pkg-depends-fontconfig else bst_notok $S/pkg-depends-fontconfig "ttf-bitstream-vera does not Depend on fontconfig-utils"; fi # ------------------------------------------------- the deferred fontcache hook if [ -f "$POSTINST" ]; then bst_ok $S/postinst-present else bst_notok $S/postinst-present "$POSTINST is absent -- the fontcache postinst did not reach the image"; fi # It calls fc-cache on the branch run-postinsts takes ($D unset at first boot). check_out $S/postinst-runs-fc-cache "fc-cache" cat "$POSTINST" # ...and the tool that branch needs is present, so the deferred run cannot fail # for want of it. This is the ticket's stated risk, and it is a shell builtin. if [ -x /usr/bin/fc-cache ]; then bst_ok $S/fc-cache-present else bst_notok $S/fc-cache-present "fontconfig-utils is a Depends of ttf-bitstream-vera but /usr/bin/fc-cache is absent"; fi # ------------------------------------------------------ freetype parses them # The rootfs is read-only at browser runtime and data/ arrives 0644, so the # probe is copied to /tmp (mode=1777 tmpfs, no noexec) and the bit is set there. # Never `test -x` the shipped payload: that passes against a hand-placed copy # and fails against every image that ever built the package. if [ ! -f "$PROBE_SRC" ]; then bst_notok $S/probe-present "$PROBE_SRC did not ship" rm -f "$W.inv" "$W.sums" "$W.scan" "$W.st" 2>/dev/null exit 0 fi bst_ok $S/probe-present probe=$tmp/bst-ttfvera-probe.$$ if cp "$PROBE_SRC" "$probe" 2>/dev/null && chmod +x "$probe" 2>/dev/null; then bst_ok $S/probe-stage else bst_notok $S/probe-stage "cannot stage $PROBE_SRC" rm -f "$probe" "$W.inv" "$W.sums" "$W.scan" "$W.st" 2>/dev/null exit 0 fi # One run per face, all appended to one fenced file. The font list comes from # the FACTS file rather than from a glob, so a face the manifest knows about but # the image lost is reported as "the probe was never run over X" instead of # silently dropping out of the loop -- and so the argv stays one path long. : > "$W.ft" _faces=$(LC_ALL=C gawk '!/^#/ && NF >= 5 { print $1 }' "$FACTS") for _f in $_faces; do # NO TRAILING == ON THE FENCE. `==FONT Vera.ttf==` puts "Vera.ttf==" in awk's # $2, which matches no key in the facts table, and every per-face case then # reds with "the probe was never run over ..." while ft-ran stays green. Caught # by the host pre-flight before it shipped. printf '==FONT %s\n' "$_f" >> "$W.ft" "$probe" "$TTFDIR/$_f" >> "$W.ft" 2>&1 _rc=$? printf '==RC %s %d\n' "$_f" "$_rc" >> "$W.ft" done rm -f "$probe" 2>/dev/null LC_ALL=C gawk -v FACTS="$FACTS" -v FT="$W.ft" \ -f "$D/ttf-bitstream-vera-ftverify.awk" "$FACTS" "$W.ft" # --------------------------------------------------------- the consumer block # fontconfig-utils is a Depends of this package and is already on this image. # Its ABSENCE is a real defect in this port, not a legitimate skip -- a `require` # here would report the port green on an image where nothing can read the fonts # it just installed. if [ ! -x /usr/bin/fc-list ]; then bst_notok $S/fc-utils-present "fontconfig-utils is a Depends of ttf-bitstream-vera but /usr/bin/fc-list is absent" rm -f "$W.inv" "$W.sums" "$W.scan" "$W.st" "$W.ft" 2>/dev/null exit 0 fi bst_ok $S/fc-utils-present # THE CACHE IS REBUILT HERE RATHER THAN ASSUMED, for the boot-mode reason in the # header: every case below is then independent of whether the deferred postinst # had already run. # `fc-cache`, NOT `fc-cache -f`, SINCE BRINTOS-1725. The -f rebuilt every # directory whether or not its cache was valid, which means mapping every face on # the image; a file-backed mapping above roughly 578 kB is REFUSED over the cloud # FUSE bridge and the caller PARKS, beyond SIGKILL (linux-in-the-browser#165, # bracketed by BRINTOS-1450). Before ttf-pt-sans the image's largest face was # Ubuntu-L.ttf at 415,552 bytes, under the bracket, and this call was harmless; # that port adds four faces above it, the largest 584,812. # # THIS SUITE WAS NOT ITSELF MEASURED PARKING, and that is worth stating rather # than implying. Four of its peers were, on the ported image, each reaching its # consumer block and dying at the 600 s bound -- 99-cantarell-fonts at 43 of 50, # 99-ttf-gentium at 47 of 55, 99-source-code-pro-fonts at 86 of 96 and # 99-liberation-fonts at 24 of 25 -- against 50/50 and 55/55 for the first two on # the same base pin without the port. This suite makes the same call over the # same font set, so it is amended by the same rule. Without -f, fc-cache rebuilds # only what is stale, and nothing is: /var/cache/fontconfig ships populated, and # `fc-cache` returns 0 in 3 s on that image where `timeout -k 5 150 fc-cache -f` # never returned. The case still asserts what it always did. # # BRINTOS-WORKAROUND: linux-in-the-browser#165 revisit -- drop -f so only a stale # cache is rebuilt; it goes back in when the mmap population path tolerates a # parking read. fc-cache > "$W.cache" 2>&1 _rc=$? if [ "$_rc" -eq 0 ]; then bst_ok $S/fc-cache-rc else bst_notok $S/fc-cache-rc "fc-cache exited $_rc: $(_bst_1line "$(cat "$W.cache")")"; fi # One fc-list for the whole consumer read; the parsing below is shell builtins. fc-list : file family > "$W.list" 2>&1 # EACH FACE IS MATCHED TO ITS FAMILY BY FILENAME, NOT BY SUBSTRING. "Bitstream # Vera Sans" is a prefix of "Bitstream Vera Sans Mono", so a case counting lines # containing the first would count the mono faces too and a sans/mono swap would # pass. The basename decides which family is expected and the comparison is # exact. _seen=0; _sans=0; _mono=0; _serif=0; _wrong="" while IFS= read -r l; do case "$l" in "$TTFDIR"/Vera*) ;; *) continue ;; esac _p=${l%%:*} _fam=${l#*: } _b=${_p##*/} _seen=$((_seen+1)) case "$_b" in VeraMo*) _want="Bitstream Vera Sans Mono" ;; VeraSe*) _want="Bitstream Vera Serif" ;; Vera*) _want="Bitstream Vera Sans" ;; *) _want="" ;; esac if [ "$_fam" = "$_want" ]; then case "$_want" in "Bitstream Vera Sans Mono") _mono=$((_mono+1)) ;; "Bitstream Vera Serif") _serif=$((_serif+1)) ;; "Bitstream Vera Sans") _sans=$((_sans+1)) ;; esac else _wrong="$_wrong $_b=>'$_fam'" fi done < "$W.list" # All ten faces are DISCOVERABLE, not merely present on disk. A cache that # serialised wrongly, or a FreeType that refused the outlines, shows up here as # a count below ten while every file-* case above stays green -- which is the # split that says where to look. if [ "$_seen" -eq 10 ]; then bst_ok $S/fc-list-faces else bst_notok $S/fc-list-faces "fc-list reports $_seen Vera faces under $TTFDIR, want 10"; fi if [ -z "$_wrong" ]; then bst_ok $S/fc-family-exact else bst_notok $S/fc-family-exact "fc-list family disagrees with the filename for:$_wrong"; fi for pair in "sans:$_sans:4" "mono:$_mono:4" "serif:$_serif:2"; do nm=${pair%%:*}; rest=${pair#*:}; n=${rest%%:*}; w=${rest#*:} if [ "$n" -eq "$w" ]; then bst_ok "$S/fc-family-$nm" else bst_notok "$S/fc-family-$nm" "fc-list reports $n faces for the $nm family, want $w"; fi done # ...AND EACH FAMILY RESOLVES TO A VERA FILE. fc-match never fails -- it always # returns some face -- so on an image carrying one font family this would assert # nothing. It is real here because liberation-fonts is installed alongside in the # same directory: a family name that did not reach the cache falls through to # Liberation, which is exactly what 99-terminus-font's first on-target run # reported for its own family. for fam in "Bitstream Vera Sans" "Bitstream Vera Sans Mono" "Bitstream Vera Serif"; do id=$(printf '%s' "$fam" | tr ' ' '-') _out=$(fc-match --format '%{file} %{family}\n' "$fam" 2>&1) case "$_out" in "$TTFDIR"/Vera*" $fam") bst_ok "$S/fc-match-$id" ;; *) bst_notok "$S/fc-match-$id" "fc-match '$fam' => '$(_bst_1line "$_out")', want a $TTFDIR/Vera*.ttf of family '$fam'" ;; esac done rm -f "$W.inv" "$W.sums" "$W.scan" "$W.st" "$W.ft" "$W.cache" "$W.list" 2>/dev/null