# BST-TIMEOUT: 600 # 33 cases, ONE of them a run of the 7.8 MB # # data/freetype-probe. MEASURED at elapsed=30s on a fresh # # guest (2026-08-25, local runner, unloaded) -- the same # # order as its neighbours on this image, 99-liberation-fonts # # 25s and 99-ttf-bitstream-vera 37s, which is expected: this # # suite runs the probe ONCE where vera runs it ten times, # # and the payload it digests is two files rather than # # thirteen. The bound is 57-freetype's and vera's: all three # # drive the SAME probe binary, and a suite whose cost is # # dominated by one binary should not claim a different bound # # for it. 20x the measurement, so a hung suite costs ten # # minutes of a sweep rather than thirty. Set from the # # ==BST-SUITE elapsed= line, not guessed. # # ttf-inconsolata 20100526 -- Inconsolata, Raph Levien's monospace face for # terminals and code listings. meta-openembedded # meta-oe/recipes-graphics/ttf-fonts/ttf-inconsolata_20100526.bb. # # WHAT THIS IMAGE ACTUALLY INSTALLS, MEASURED, NOT ASSUMED: TWO files in TWO # packages. One 58,464-byte face at /usr/share/fonts/truetype/Inconsolata.ttf # from ttf-inconsolata, and the 4,599-byte OFL licence text at # /usr/share/doc/ttf-inconsolata/OFL.txt from ttf-inconsolata-doc. # # THE SPLIT IS NOT THE ONE THE RECIPE ASKS FOR, AND THAT IS THIS PORT'S ONE # PACKAGING DECISION. The recipe sets # # FILES:${PN} = "${datadir}/fonts/truetype/Inconsolata.ttf \ # ${datadir}/doc/ttf-inconsolata/*" # # -- it NAMES the licence text as part of the main package. It does not get it. # PACKAGES orders ${PN}-doc BEFORE ${PN} and packaging is first-match-wins, so # the default FILES:${PN}-doc, which begins with ${docdir}, claims # /usr/share/doc/ttf-inconsolata/OFL.txt before FILES:${PN} is ever consulted. # Read off the built packages, not off the recipe: # # FILES_INFO:ttf-inconsolata {"/usr/share/fonts/truetype/Inconsolata.ttf": 58464} # FILES_INFO:ttf-inconsolata-doc {"/usr/share/doc/ttf-inconsolata/OFL.txt": 4599} # # So the image installs BOTH packages. It has to: this face's name table carries # neither name ID 13 (licence) nor 14 (licence URL), so OFL.txt is the only copy # of the OFL on the image, and OFL-1.1 permits redistribution only with that # notice included. `pkg-doc-known` is what says the -doc line in IMAGE_INSTALL # resolved to a real package rather than to nothing. # # THE FILE IS AN OpenType/CFF FONT WEARING A .ttf NAME. SRC_URI fetches # Inconsolata.otf; do_configure is one `cp` to Inconsolata.ttf and ttf.inc's # do_install installs that. Its sfnt tag is OTTO and its outlines are in a CFF # table -- there is no glyf/loca pair. Every other font on this image is a real # TrueType, so the ft-* and fcq-format cases here are the ONLY coverage on the # image of freetype's CFF driver: a build that lost it would leave # 99-ttf-bitstream-vera's ten faces green and fail exactly here. # # THIS IS THE FIRST PACKAGE ON THE IMAGE TO USE THE DEBIAN FONT PATH, AND THAT IS # ITS ONE CO-TENANT EFFECT. /usr/share/fonts/truetype did not exist before this # port; liberation-fonts and ttf-bitstream-vera both install into # /usr/share/fonts/ttf. Two consequences, both repaired by this ticket in the # suites that own them rather than papered over here: # # 99-liberation-fonts and 99-ttf-bitstream-vera each carried a # `no-debian-fontpath` case asserting /usr/share/fonts/truetype does not # exist. That was a true statement about THEIR OWN payload's install path # dressed as a statement about the directory, and it goes red for a co-tenant # neither package owns. Both are now scoped to their own basenames. # # 99-liberation-fonts asserted `fc-match monospace` resolves to Liberation # Mono. It no longer does, and the reason is in fontconfig's own shipped # configuration rather than in anything this port chose: 60-latin.conf ranks # the monospace generic as Noto Sans Mono, DejaVu Sans Mono, Inconsolata, # Andale Mono, Courier New, ... Liberation Mono is NOT on that list -- it won # before only through 30-metric-aliases.conf binding it to Courier New at rank # 5. Installing Inconsolata puts a rank-3 family on the image, so it wins on # merit. That case became Inconsolata's, and this suite's fc-match-monospace # asserted the same fact from this side. # # IT IS NO LONGER OURS EITHER, SINCE BRINTOS-1455. source-code-pro-fonts ships # its own /etc/fonts/conf.d/44-source-code-pro-fonts-fontconfig.conf, whose # for the monospace alias sorts ahead of 60-latin.conf and so # prepends Source Code Pro to the whole ranked list -- an explicit preference # a merit claim cannot outrank. Both cases were amended again, in the same # direction and for the same reason; see fc-match-monospace below. Asking for # Inconsolata BY NAME still answers our file, which is fc-match-family and is # untouched. # # 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, # doc-*, cotenant-* count -- and the co-tenant directory is untouched. No font # machinery involved at all. # pkg-*, postinst-* the two packages the image really installs, the fontcache # class's RDEPENDS, and the deferred first-boot scriptlet. # ft-* the image's OWN freetype PARSES the face through its CFF # driver and agrees about num_faces, num_glyphs, # units_per_EM, family and style. This is the layer the # ticket asks for by name. # fcq-*, fc-* fontconfig's independent parse (fc-scan, against a native # x86-64 control), then discovery and matching THROUGH THE # CACHE, which is a different path again. # # ft-* AND fcq-* ARE NOT THE SAME MEASUREMENT TWICE. They read one file through # two consumers of one library and disagree about different things: a defect in # the CFF parse moves both, a defect in fontconfig's derivation of spacing moves # only fcq-*, and a truncated charstring index moves only ft-*. # # THE fontcache POSTINST IS THE THING THE TICKET SAYS TO CHECK RATHER THAN # ASSUME, AND IT IS CHECKED 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 -- the local runner goes straight to a shell -- 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 forces `fc-cache -f` itself so it is independent of all of this. # # WRITTEN TO EXEC AS LITTLE AS POSSIBLE. One find, one sha256sum, one fc-scan, # one sed, one probe run and two gawks cover every case. . "$BST_LIB" S=ttf-inconsolata # THE CANARY, which asserts nothing about ttf-inconsolata. # # 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-inconsolata-manifest.txt FACTS=$D/ttf-inconsolata-fontfacts.txt FACES=$D/ttf-inconsolata-fcfaces.txt PROBE_SRC=$D/freetype-probe TTFDIR=/usr/share/fonts/truetype DOCDIR=/usr/share/doc/ttf-inconsolata COTDIR=/usr/share/fonts/ttf FACE=$TTFDIR/Inconsolata.ttf POSTINST=/var/lib/dpkg/info/ttf-inconsolata.postinst tmp=${TMPDIR:-/tmp} W=$tmp/bst-ttf-inconsolata.$$ 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-inconsolata 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 co-tenant font directory, # so every count 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" "$COTDIR" \ -maxdepth 1 -printf '%p %y %s %m %l\n' 2>/dev/null | LC_ALL=C sort > "$W.inv" # One sha256sum over both payload files. Named explicitly rather than globbed: # the manifest is the list, and a glob would silently digest a co-tenant if one # ever lands in the same directory. sha256sum "$FACE" "$DOCDIR"/OFL.txt > "$W.sums" 2>&1 # One fc-scan over the installed face. fontconfig opens it through FreeType, so # this is a second, independent parse -- and it reads fields freetype's own API # does not name (the PostScript name, the derived slant/weight/spacing, the # format, the charset). It reads the FILE, 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}|%{fontformat}|%{scalable}|%{outline}|%{charset}\n' \ "$FACE" 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" \ -f "$D/ttf-inconsolata-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 would look identical if do_install had written both files into one # package, so they cannot say that ttf-inconsolata-doc is a real package the # image installs rather than an IMAGE_INSTALL line 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-inconsolata$/,/^$/p; /^Package: ttf-inconsolata-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-inconsolata") _pkg=main; _main=1 ;; "Package: ttf-inconsolata-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-inconsolata"; fi # THE -doc PACKAGE IS ON THE IMAGE BY DECISION, NOT BY ACCIDENT -- see the header # on why FILES:${PN} naming the licence text does not get it. A dropped # IMAGE_INSTALL line would take file-OFL.txt and doc-count with it; this case # says the cause is packaging rather than a missing file. 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-inconsolata-doc"; fi # `allarch` reached the package metadata, which is what makes the byte-identity # reference in the manifest a statement about this build rather than a # coincidence. if [ "$_arch" -eq 1 ]; then bst_ok $S/pkg-arch-all else bst_notok $S/pkg-arch-all "ttf-inconsolata is not Architecture: all"; fi # THE fontcache CLASS'S RDEPENDS, READ BACK OFF THE TARGET. The recipe declares # no runtime dependency at all and INHIBIT_DEFAULT_DEPS is set -- 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 (fontconfig-utils has been here since BRINTOS-1032). if [ "$_dep" -eq 1 ]; then bst_ok $S/pkg-depends-fontconfig else bst_notok $S/pkg-depends-fontconfig "ttf-inconsolata 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-inconsolata but /usr/bin/fc-cache is absent"; fi # ------------------------------------------------------ freetype parses it # 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-ttfincon-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 # The face 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. # # NO TRAILING == ON THE FENCE. `==FONT Inconsolata.ttf==` puts "Inconsolata.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. : > "$W.ft" _faces=$(LC_ALL=C gawk '!/^#/ && NF >= 5 { print $1 }' "$FACTS") for _f in $_faces; do 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-inconsolata-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 font it # just installed. if [ ! -x /usr/bin/fc-list ]; then bst_notok $S/fc-utils-present "fontconfig-utils is a Depends of ttf-inconsolata 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. # # THIS CALL IS THE ONE THING ON THIS IMAGE THAT WOULD NOT SURVIVE A LARGER FONT. # fc-cache mmaps each face it walks, and file-backed mmap on this arm is REFUSED # above a length threshold bracketed between ~66 kB and ~578 kB # (linux-in-the-browser#165) -- the consumer does not get a failed mmap and fall # back, it wedges beyond the reach of SIGKILL and takes the guest with it. That # is what holds BRINTOS-1450 (ttf-dejavu, whose first face is 577,708 bytes). # Inconsolata.ttf is 58,464 bytes, below the bracket's lower bound, and every # other font already on this image is smaller still -- which is why this suite # calls fc-cache plainly, as its three neighbours do, rather than bounding it. # `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 # THE FACE IS DISCOVERABLE, not merely present on disk. A cache that serialised # wrongly, or a FreeType that refused the CFF outlines, shows up here while every # file-* case above stays green -- which is the split that says where to look. _seen=0; _fam="" while IFS= read -r l; do case "$l" in "$FACE":*) ;; *) continue ;; esac _seen=$((_seen+1)) _fam=${l#*: } done < "$W.list" if [ "$_seen" -eq 1 ]; then bst_ok $S/fc-list-face else bst_notok $S/fc-list-face "fc-list reports $_seen entries for $FACE, want 1"; fi if [ "$_fam" = "Inconsolata" ]; then bst_ok $S/fc-family-exact else bst_notok $S/fc-family-exact "fc-list family for $FACE is '$_fam', want 'Inconsolata'"; fi # ...AND THE FAMILY RESOLVES BACK TO OUR FILE. fc-match never fails -- it always # returns some face -- so this is real only because 22 other faces are installed # alongside in a different 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. _out=$(fc-match --format '%{file} %{family}\n' Inconsolata 2>&1) case "$_out" in "$FACE Inconsolata") bst_ok $S/fc-match-family ;; *) bst_notok $S/fc-match-family "fc-match Inconsolata => '$(_bst_1line "$_out")', want '$FACE Inconsolata'" ;; esac # monospace IS NO LONGER OURS, AND THAT IS A CONFIGURATION THIS PORT CANNOT # OUTRANK RATHER THAN A REGRESSION. This case asserted Inconsolata until # source-code-pro-fonts (BRINTOS-1455) arrived. Inconsolata won the generic on # MERIT -- fontconfig's own 60-latin.conf ranks monospace as Noto Sans Mono, # DejaVu Sans Mono, Inconsolata, Andale Mono, Courier New, ..., the first two are # absent from this image and Liberation Mono is not on the list at all, so # Inconsolata was the highest-ranked family present. That is still true and is # no longer what decides: source-code-pro-fonts SHIPS ITS OWN FRAGMENT, # # /etc/fonts/conf.d/44-source-code-pro-fonts-fontconfig.conf # monospace # Source Code Pro # # and 44 sorts before 60, so prepends Source Code Pro ahead of the # whole ranked list. A merit claim cannot outrank an explicit preference. # # THE CASE IS AMENDED RATHER THAN DELETED, for the reason 99-liberation-fonts # gives for the amendment THIS port made to it one round earlier: what the case # is worth is that the generic resolves to the family the configuration actually # selects, and it is still that, from the other side. 99-source-code-pro-fonts' # own fc-match-monospace asserts the same fact positively and its # fc-conf-loaded says the fragment is what did it, so a disagreement between the # suites is visible rather than silent. fc-match-family above is untouched: # asking for Inconsolata BY NAME still answers our file, which is what says this # port is intact. _out=$(fc-match --format '%{family}\n' monospace 2>&1) case "$_out" in "Source Code Pro") bst_ok $S/fc-match-monospace ;; *) bst_notok $S/fc-match-monospace "fc-match monospace => '$(_bst_1line "$_out")', want 'Source Code Pro' (source-code-pro-fonts' 44-*.conf prefers it; this port no longer owns the generic)" ;; esac rm -f "$W.inv" "$W.sums" "$W.scan" "$W.st" "$W.ft" "$W.cache" "$W.list" 2>/dev/null