# BST-TIMEOUT: 300 # MEASURED, not predicted: elapsed=29s and elapsed=35s over # # 61 cases on two independent clean boots (2026-08-30, hz1, # # local-verify on the runtime the image was built from), # # read off this suite's own ==BST-SUITE elapsed= line. So # # the bound is ~8.6x the slower run. That is the convention # # at this size -- 57-fontconfig sits at 300 on a measured # # 76s -- and reading the line rather than reasoning about it # # is the correction BRINTOS-1449 had to ship when it guessed # # at 48x the truth. # # # # THE "NO PROBE" PREDICTION HELD ON TOTAL AS WELL AS PER # # CASE, WHICH IS WHERE BRINTOS-1721's DID NOT. Starting no # # probe binary makes each case cheaper -- 0.48-0.57s here # # against 0.58-0.60s for 99-cantarell-fonts, which ran in # # the same two boots and does drive one. 99-ttf-liberation # # was cheapest per case and STILL the most expensive in wall # # clock, because at 80 cases it carried more than twice its # # comparators; this suite's 61 is close enough to # # cantarell's 50 that the per-case saving is not swamped, # # and it came in below that suite on both boots. # # ttf-hunkyfonts 0.3.1 -- the Hunky family, a Latin/Cyrillic/Greek sans and serif # pair drawn in FontForge and released on SourceForge in 2004. meta-openembedded # meta-oe/recipes-graphics/ttf-fonts/ttf-hunkyfonts_0.3.1.bb. # # ============================================================================ # THE PACKAGE NAMES DO NOT MATCH THE RECIPE NAME, AND THAT IS THE FIRST THING # THIS PORT CAN GET WRONG. # ============================================================================ # # The recipe is ttf-hunkyfonts. The packages are ttf-hunky-sans and # ttf-hunky-serif -- `PACKAGES` is an ASSIGNMENT naming exactly those two, so # there is NO ttf-hunkyfonts package and NO ttf-hunkyfonts-doc, and an # `IMAGE_INSTALL += "ttf-hunkyfonts"` would simply not resolve. pkg-no-bare- # package and pkg-no-doc-package assert those absences, because "the line was # forgotten" and "there is no such package by design" look identical from the # file layer alone. # # ============================================================================ # THE SPLIT IS ASYMMETRIC -- FOUR SANS FACES AND TWO SERIF -- AND IT IS # UPSTREAM, NOT A LOST PAYLOAD. # ============================================================================ # # FILES_INFO:ttf-hunky-sans HunkySans-Bold.ttf 47,464 # HunkySans-BoldOblique.ttf 51,656 # HunkySans-Oblique.ttf 54,408 # HunkySans-Roman.ttf 48,408 # FILES_INFO:ttf-hunky-serif HunkySerif-Bold.ttf 47,620 # HunkySerif-Roman.ttf 49,456 # # READ OFF THE BUILT PACKAGES, not predicted from the FILES globs. The tarball # ships six TTFs and six matching FontForge sources in SFD/ -- HunkySans in # Roman, Bold, Oblique and BoldOblique, HunkySerif in Roman and Bold ONLY. # There is no serif oblique to install and there never was. A suite that # assumed 4+4 by symmetry would report two absent files that were never drawn, # so fcq-family-Hunky-Serif wants 2 and pkg-serif-owns-two wants 2, and the # consumer block records what fontconfig does with the gap (see below). # # ============================================================================ # fc-match RESOLVES TO THIS PACKAGE, WHICH IS NOT WHAT THE LAST FONT PORT FOUND. # ============================================================================ # # MEASURED (2026-08-30, fontconfig 2.15.0 in a container, over a reconstruction # of this image's whole font tree with this port's six faces added): # # fc-match "Hunky Sans" -> /usr/share/fonts/truetype/HunkySans-Roman.ttf # fc-match "Hunky Serif" -> /usr/share/fonts/truetype/HunkySerif-Roman.ttf # # So unlike ttf-liberation (BRINTOS-1721), whose faces are outranked by poky's # newer Liberation release and which had to assert the property its ticket was # reaching for instead of the wording, this port meets the ticket's fc-match # requirement LITERALLY. Nothing on this image supplies either family name. # # AND IT TAKES NO GENERIC. Measured in the same control: `sans-serif` still # resolves to Liberation Sans, `serif` to Liberation Serif and `monospace` to # Source Code Pro, unchanged by this port. The mechanism was read rather than # inferred -- `grep -ri hunky` over the image's whole /etc/fonts returns # nothing, so neither family appears on any preference list in 60-latin.conf or # any other fragment, and neither is monospaced (fontconfig leaves FC_SPACING # unset on all six faces). This is the failure ttf-inconsolata actually caused # one ticket ago, when it took `monospace` from Liberation Mono, so it is # asserted from both sides: fcq-no-monospace from the parse and the three # fc-match-generic-* cases from the consumer. # # THE SERIF FAMILY HAS NO OBLIQUE, AND THE CONSUMER SIDE SHOWS IT. Measured: # # fc-match "Hunky Sans:italic" -> HunkySans-Oblique.ttf (slant 110) # fc-match "Hunky Serif:italic" -> HunkySerif-Roman.ttf (slant 0) # # fontconfig has no sloped serif face to offer and returns the upright one # rather than synthesising or falling through to another family. That is the # asymmetric split arriving at the layer a user would actually see it, and # fc-match-serif-italic-falls-to-roman pins it -- so if a later release adds a # serif oblique, or a FILES glob starts capturing one, it is a named red rather # than a silent change in what the image renders. # # SLANT IS 110 AND NOT 100. Every other font suite on this image pins 100 # (FC_SLANT_ITALIC) for its sloped faces; fontconfig reads 110 # (FC_SLANT_OBLIQUE) here because this family names its sloped faces Oblique. # A reader arriving from a sibling suite would take that for a defect, so # fcq-oblique-slant states it explicitly. It is NOT subsumed by the # field-by-field fcq-* comparison: those compare the image against the # committed control, and would BOTH go green if the control were "corrected" to # 100. Mutation-tested in exactly that form -- see the design doc. # # ============================================================================ # # THE DIRECTORY IS NOW SHARED EIGHT WAYS. /usr/share/fonts/truetype was created # by ttf-inconsolata, then joined by ttf-gentium, ttf-droid, source-code-pro- # fonts, ttf-liberation, ttf-roboto and the Ubuntu Font Family, and joined again # here -- 72 files before this port, 78 after. (At the pin this port was BUILT # and VERIFIED at, the first four were the only tenants: 26 before, 32 after, # live-counted in the staged rootfs. The other three landed on main in between, # and the counts were recomposed at the merge from the union of the eight # truetype manifests -- see ttf-hunkyfonts-verify.awk's WANT_OTHERS note.) Every # neighbour's own "no-extras" case is scoped by ITS basename prefix # (^Inconsolata, ^Gen.*\.ttf$, ^Droid.*\.ttf$, ^SourceCodePro.*\.(otf|ttf)$) and # the two "no-debian-fontpath" guards by ^Liberation and ^Vera, so NONE of them # had to be amended: Hunky* matches nothing they look for. The whole co-tenancy # sweep is in the design doc; exactly one case anywhere on this image moves -- # cantarell-fonts-verify.awk's cotenant-truetype, a whole-directory count, from # 26 to 32, which is that case working exactly as it moved 12 -> 26 for # BRINTOS-1455. # # NO FREETYPE PROBE, DELIBERATELY. Four font suites beside this one run # data/freetype-probe because their tickets ask for freetype's own # num_faces/num_glyphs/units_per_EM. THIS ticket asks for fc-list, fc-query and # fc-match and nothing else -- so the second, independent parse here is # fontconfig's own (fc-query, against a committed 2.15.0 control) rather than # FreeType's API. That is the ticket's scope, not a gap: a 7.8 MB probe run per # face would cost this suite ~40 s to assert something the ticket did not ask # about. # # THE fontcache POSTINST IS CHECKED RATHER THAN ASSUMED, IN THE ONE FORM THAT IS # BOOT-MODE INDEPENDENT. fontcache.bbclass appends fontconfig-utils to RDEPENDS # for every FONT_PACKAGES entry -- read back off the target here, not off the # recipe -- 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 would report the # runner's choice as a package red. What is asserted instead is that the # scriptlet is on the image for BOTH packages, it calls fc-cache, and fc-cache # is present, so the deferred run cannot fail for want of the tool. # # THIS PORT ADDS NO NEW MAXIMUM MAPPING, WHICH IS WHY THE CONSUMER BLOCK IS # SAFE. File-backed mmap on this arm is REFUSED above a mapping-length threshold # and the consumer PARKS rather than falling back (linux-in-the-browser#165, # bracketed on this image between ~66 kB and 577,708 bytes). This package's # largest face is 54,408 bytes -- BELOW the bottom of that bracket, and smaller # than ttf-bitstream-vera's largest face, which every fc-cache on this image has # walked since BRINTOS-1449 without ever being seen to park. So this package # cannot introduce a new failure mode or a new maximum. The consumer block is # still ordered LAST and the fc-cache call still bounded, for the reason # 99-ttf-droid gives: that ordering is what protects the evidence, not the bound. . "$BST_LIB" S=ttf-hunkyfonts # THE CANARY, which asserts nothing about ttf-hunkyfonts. # # 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 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-hunkyfonts-manifest.txt FACES=$D/ttf-hunkyfonts-fcfaces.txt TTFDIR=/usr/share/fonts/truetype COTDIR=/usr/share/fonts/ttf PI_SANS=/var/lib/dpkg/info/ttf-hunky-sans.postinst PI_SERIF=/var/lib/dpkg/info/ttf-hunky-serif.postinst L_SANS=/var/lib/dpkg/info/ttf-hunky-sans.list L_SERIF=/var/lib/dpkg/info/ttf-hunky-serif.list tmp=${TMPDIR:-/tmp} W=$tmp/bst-ttf-hunkyfonts.$$ require gawk $S/gawk-present require sha256sum $S/sha256sum-present require find $S/find-present # fc-query 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 both packages, so an absent fc-query is a defect in the port # rather than a legitimately absent tool, and a skip would report it green. if [ -x /usr/bin/fc-query ]; then bst_ok $S/fc-query-present else bst_notok $S/fc-query-present "fontconfig-utils is a Depends of ttf-hunky-sans but /usr/bin/fc-query is absent"; fi if [ ! -f "$REF" ] || [ ! -f "$FACES" ]; then bst_notok $S/ref-present "the reference data did not ship: $REF / $FACES" exit 0 fi bst_ok $S/ref-present # The six basenames, named once and reused. Ordered as the manifest is. FACELIST="HunkySans-Bold.ttf HunkySans-BoldOblique.ttf HunkySans-Oblique.ttf HunkySans-Roman.ttf HunkySerif-Bold.ttf HunkySerif-Roman.ttf" # ------------------------------------------------------------- payload shape # One find over this package's directory and over the other font directory, so # every count below 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" "$COTDIR" \ -maxdepth 1 -printf '%p %y %s %m %l\n' 2>/dev/null | LC_ALL=C sort > "$W.inv" # One sha256sum over the six payload files. Named explicitly rather than # globbed: a glob of "$TTFDIR"/Hunky* would be right today but would also # silently absorb a seventh Hunky file, which is precisely what ttf-count # exists to catch. _paths="" for _b in $FACELIST; do _paths="$_paths $TTFDIR/$_b"; done # shellcheck disable=SC2086 sha256sum $_paths > "$W.sums" 2>&1 # One fc-query sweep over the six installed faces -- the ticket's named check. # It reads the FILES, not the cache, so it is independent of the fc-cache in # the consumer block below. Field order matches the committed control. # shellcheck disable=SC2086 LC_ALL=C fc-query --format '%{file|basename}|%{family}|%{style}|%{slant}|%{weight}|%{spacing}|%{fontformat}\n' \ $_paths 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-hunkyfonts-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, and the # split is the part of this port the ticket says can go wrong. Both packages # install into ONE directory, so the file-* cases would look identical if # do_install had written all six files into a single package -- they cannot say # that ttf-hunky-serif is a real package the image installs rather than an # IMAGE_INSTALL line that resolved to nothing. One sed pulls every stanza; the # loop is shell builtins. The stanzas are read from a tmpfs copy, never # redirected from the rootfs (brintos/linux#73). # # THE ANCHORS ARE EXACT ON PURPOSE. `ttf-hunky` is a prefix of both real package # names and `ttf-hunkyfonts` is not a prefix of either, so an unanchored match # would misfile stanzas and make pkg-no-bare-package unfalsifiable. sed -n '/^Package: ttf-hunkyfonts$/,/^$/p; /^Package: ttf-hunkyfonts-doc$/,/^$/p; /^Package: ttf-hunky-sans$/,/^$/p; /^Package: ttf-hunky-serif$/,/^$/p' \ /var/lib/dpkg/status > "$W.st" 2>/dev/null _pkg=""; _sans=0; _serif=0; _bare=0; _doc=0 _asans=0; _aserif=0; _dsans=0; _dserif=0 while IFS= read -r l; do case "$l" in "Package: ttf-hunkyfonts") _pkg=bare; _bare=1 ;; "Package: ttf-hunkyfonts-doc") _pkg=doc; _doc=1 ;; "Package: ttf-hunky-sans") _pkg=sans; _sans=1 ;; "Package: ttf-hunky-serif") _pkg=serif; _serif=1 ;; "Architecture: all") [ "$_pkg" = sans ] && _asans=1 [ "$_pkg" = serif ] && _aserif=1 ;; "Depends: "*) case "$l" in *fontconfig-utils*) [ "$_pkg" = sans ] && _dsans=1 [ "$_pkg" = serif ] && _dserif=1 ;; esac ;; esac done < "$W.st" # THE TWO PACKAGES THIS IMAGE INSTALLS, BY THEIR REAL NAMES. The ticket asks for # exactly this ("the suite asserts both packages by their real names"). A # dropped IMAGE_INSTALL line would take that family's file-* and fcq-* cases # with it, and these cases say the cause is a missing PACKAGE rather than # missing files. if [ "$_sans" -eq 1 ]; then bst_ok $S/pkg-sans-known else bst_notok $S/pkg-sans-known "dpkg does not know the package ttf-hunky-sans"; fi if [ "$_serif" -eq 1 ]; then bst_ok $S/pkg-serif-known else bst_notok $S/pkg-serif-known "dpkg does not know the package ttf-hunky-serif"; fi # THERE IS NO ttf-hunkyfonts PACKAGE, AND ITS ABSENCE IS THE RECIPE. `PACKAGES` # is an assignment that omits ${PN} entirely, so `IMAGE_INSTALL += "ttf-hunkyfonts"` # would not resolve. This case names that, because a future recipe revision # restoring the default PACKAGES would change what the image must ask for. if [ "$_bare" -eq 0 ]; then bst_ok $S/pkg-no-bare-package else bst_notok $S/pkg-no-bare-package "dpkg knows a ttf-hunkyfonts package -- PACKAGES no longer omits \${PN}"; fi if [ "$_doc" -eq 0 ]; then bst_ok $S/pkg-no-doc-package else bst_notok $S/pkg-no-doc-package "dpkg knows a ttf-hunkyfonts-doc package -- PACKAGES no longer omits \${PN}-doc"; fi # `allarch` reached the package metadata, which is what makes the byte-identity # claim in the manifest a statement about this build rather than a coincidence. # Asserted for both: they are built by one recipe but packaged separately, and a # class that reached one need not have reached the other. if [ "$_asans" -eq 1 ]; then bst_ok $S/pkg-sans-arch-all else bst_notok $S/pkg-sans-arch-all "ttf-hunky-sans is not Architecture: all"; fi if [ "$_aserif" -eq 1 ]; then bst_ok $S/pkg-serif-arch-all else bst_notok $S/pkg-serif-arch-all "ttf-hunky-serif is not Architecture: all"; fi # THE fontcache CLASS'S RDEPENDS, READ BACK OFF THE TARGET. The recipe declares # no runtime dependency at all and ttf.inc sets INHIBIT_DEFAULT_DEPS -- # fontcache.bbclass appends fontconfig-utils to each FONT_PACKAGES entry -- so # these are the cases that say the class did its half for BOTH packages and that # the dependency resolved against a package this image already carried # (fontconfig-utils has been here since BRINTOS-1032). if [ "$_dsans" -eq 1 ]; then bst_ok $S/pkg-sans-depends-fontconfig else bst_notok $S/pkg-sans-depends-fontconfig "ttf-hunky-sans does not Depend on fontconfig-utils"; fi if [ "$_dserif" -eq 1 ]; then bst_ok $S/pkg-serif-depends-fontconfig else bst_notok $S/pkg-serif-depends-fontconfig "ttf-hunky-serif does not Depend on fontconfig-utils"; fi # WHICH PACKAGE OWNS WHICH FACE, FROM THE PACKAGE DATABASE. This is the ticket's # stated risk answered directly, and it is the one thing neither the file layer # nor fc-query can see: both packages install into ONE directory, so a face # packaged into the wrong one leaves every file-* case green, every fcq-* case # green, and the family counts green too -- because the family comes from the # font's name table, not from the package. dpkg's own file lists are the only # place the mapping exists on target. # # BOTH LISTS ARE COPIED TO TMPFS FIRST AND READ FROM THERE. A `while read ... < # /var/lib/dpkg/info/x.list` in the TOP-LEVEL shell is a stdin redirect FROM the # rootfs, which on this arm ends the suite where it stands and emits nothing # further -- brintos/linux#73, and the repo's own check-suite-redirects.py # refuses it at do_install. It caught this block; the `cat` below is a # subprocess and is not the same thing. # # BRINTOS-WORKAROUND: brintos/linux#73 remove -- stage both dpkg file lists on # tmpfs rather than redirecting the top-level shell's stdin from the rootfs. { printf 'PKG sans\n'; cat "$L_SANS" 2>/dev/null printf 'PKG serif\n'; cat "$L_SERIF" 2>/dev/null; } > "$W.ls" _ns=0; _nse=0; _wrong=""; _which="" _cur="" while IFS= read -r l; do case "$l" in "PKG sans") _cur=s; continue ;; "PKG serif") _cur=se; continue ;; esac case "$l" in "$TTFDIR"/Hunky*) ;; *) continue ;; esac _which="$_which ${l##*/}:$_cur" done < "$W.ls" for _b in $FACELIST; do _in_s=0; _in_se=0 for _e in $_which; do case "$_e" in "$_b:s") _in_s=1 ;; "$_b:se") _in_se=1 ;; esac done case "$_b" in HunkySans-*) _want=s ;; HunkySerif-*) _want=se ;; esac if [ "$_in_s" -eq 1 ] && [ "$_in_se" -eq 1 ]; then _wrong="$_wrong $_b(both)" elif [ "$_in_s" -eq 1 ]; then _ns=$((_ns+1)); [ "$_want" = s ] || _wrong="$_wrong $_b(in-sans)" elif [ "$_in_se" -eq 1 ]; then _nse=$((_nse+1)); [ "$_want" = se ] || _wrong="$_wrong $_b(in-serif)" else _wrong="$_wrong $_b(neither)"; fi done if [ "$_ns" -eq 4 ]; then bst_ok $S/pkg-sans-owns-four else bst_notok $S/pkg-sans-owns-four "ttf-hunky-sans's file list claims $_ns of the six faces, want 4"; fi if [ "$_nse" -eq 2 ]; then bst_ok $S/pkg-serif-owns-two else bst_notok $S/pkg-serif-owns-two "ttf-hunky-serif's file list claims $_nse of the six faces, want 2 -- upstream draws no serif oblique"; fi if [ -z "$_wrong" ]; then bst_ok $S/pkg-face-ownership else bst_notok $S/pkg-face-ownership "face(s) owned by the wrong package or by none:$_wrong"; fi # ------------------------------------------------- the deferred fontcache hook # BOTH packages are in FONT_PACKAGES, so both get a scriptlet. if [ -f "$PI_SANS" ]; then bst_ok $S/postinst-sans-present else bst_notok $S/postinst-sans-present "$PI_SANS is absent -- the fontcache postinst did not reach the image"; fi if [ -f "$PI_SERIF" ]; then bst_ok $S/postinst-serif-present else bst_notok $S/postinst-serif-present "$PI_SERIF is absent -- the fontcache postinst did not reach the image"; fi # They call fc-cache on the branch run-postinsts takes ($D unset at first boot). check_out $S/postinst-sans-runs-fc-cache "fc-cache" cat "$PI_SANS" check_out $S/postinst-serif-runs-fc-cache "fc-cache" cat "$PI_SERIF" # ...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-hunky-sans but /usr/bin/fc-cache is absent"; fi # --------------------------------------------------------- the consumer block # # EVERYTHING BELOW THIS LINE CAN COST THE GUEST, AND THAT IS WHY IT IS LAST. # fc-cache mmaps each face it walks, and file-backed mmap on this arm is REFUSED # above a mapping-length threshold (linux-in-the-browser#165) -- and the # consumer does not get a failed mmap and fall back, it PARKS, beyond the reach # of both SIGTERM and SIGKILL, taking the guest and every suite behind it in the # same boot. Ordering this block last is the only thing that actually protects # the evidence: every case above has already been emitted by the time the first # mapping happens. # # THIS PORT'S SIX FACES SIT BELOW THE BOTTOM OF THE BRACKET. #165's bracket on # this image is ~66 kB to 577,708 bytes; these are 47,464 to 54,408 -- every one # of them smaller than ttf-bitstream-vera's largest face, which every fc-cache # on this image has walked since BRINTOS-1449. So this package cannot introduce # a new failure mode or a new maximum. # # `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; that ticket MEASURED four font suites parking here on an image # whose largest face had gone over the line, each dying at its bound # (99-cantarell-fonts at 43 of 50, 99-ttf-gentium at 47 of 55, # 99-source-code-pro-fonts at 86 of 96, 99-liberation-fonts at 24 of 25). # Without -f, fc-cache rebuilds only what is stale. This suite makes the same # call over the same font set as those four and follows the same convention; # the case still asserts what it always did -- fc-cache runs and returns 0. # # BRINTOS-WORKAROUND: linux-in-the-browser#165 revisit -- drop -f so only a # stale cache is rebuilt, bound the call, and order the consumer block last; all # three go when the mmap population path tolerates a parking read. # BST-TIMEOUT-OK: fc-cache walks the font dirs and exits; -k 5 90 is belt-and-braces. timeout -k 5 90 fc-cache > "$W.cache" 2>&1 _rc=$? if [ "$_rc" -eq 0 ]; then bst_ok $S/fc-cache-rc elif [ "$_rc" -eq 124 ] || [ "$_rc" -eq 137 ]; then bst_notok $S/fc-cache-rc "fc-cache did not return within 90s -- linux-in-the-browser#165 (mmap population parked)" 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 FACES ARE DISCOVERABLE THROUGH THE CACHE, not merely present on disk and # parseable by fc-query. A cache that serialised wrongly, or a FreeType that # refused the outlines, shows up here while every file-* and fcq-* case above # stays green -- which is the split that says where to look. Matched on the FULL # PATH, so nothing outside this package's directory can satisfy it. for _b in $FACELIST; do case "$_b" in HunkySans-*) _wantfam="Hunky Sans" ;; HunkySerif-*) _wantfam="Hunky Serif" ;; esac _seen=0; _fam="" while IFS= read -r l; do case "$l" in "$TTFDIR/$_b":*) ;; *) continue ;; esac _seen=$((_seen+1)) _fam=${l#*: } done < "$W.list" if [ "$_seen" -eq 1 ] && [ "$_fam" = "$_wantfam" ]; then bst_ok "$S/fc-list-$_b" else bst_notok "$S/fc-list-$_b" "fc-list reports $_seen entries for $TTFDIR/$_b family '$_fam', want 1 of '$_wantfam'" fi done # EACH FAMILY RESOLVES TO THIS PACKAGE'S REGULAR FACE. This is the ticket's # "proves the font is actually usable and not merely present on disk" check, and # unlike the last font port this image took, it holds in the ticket's own # wording: nothing else here supplies either family name. fc-match never fails # -- it always returns some face -- so this is real only because ~70 other faces # are installed alongside: a family that did not reach the cache falls through # to one of them, which is exactly what 99-terminus-font's first on-target run # reported for its own family. The FILE is asserted, not just the family, so a # fallthrough that happened to be renamed cannot pass. for _pair in "Hunky Sans:HunkySans-Roman.ttf" "Hunky Serif:HunkySerif-Roman.ttf"; do _fam=${_pair%%:*}; _wantf=${_pair#*:} _id=$(printf '%s' "$_fam" | tr ' ' '-') _out=$(fc-match --format '%{file} %{family}' "$_fam" 2>&1) if [ "$_out" = "$TTFDIR/$_wantf $_fam" ]; then bst_ok "$S/fc-match-$_id" else bst_notok "$S/fc-match-$_id" "fc-match '$_fam' => '$(_bst_1line "$_out")', want '$TTFDIR/$_wantf $_fam'"; fi done # THE SERIF FAMILY HAS NO SLOPED FACE, AND THIS IS WHERE THAT ARRIVES. # `Hunky Sans:italic` gets the Oblique; `Hunky Serif:italic` gets the ROMAN, # because there is none to give -- fontconfig returns the upright face rather # than synthesising one or falling through to another family. Both halves are # asserted: the sans half would go green on its own if the serif package had # quietly gained a face, and the serif half would go green on its own if the # whole family had fallen through. MEASURED on a 2.15.0 control over this # image's font tree (2026-08-30), not reasoned from the file list. _out=$(fc-match --format '%{file}' "Hunky Sans:italic" 2>&1) if [ "$_out" = "$TTFDIR/HunkySans-Oblique.ttf" ]; then bst_ok $S/fc-match-sans-italic-is-oblique else bst_notok $S/fc-match-sans-italic-is-oblique "fc-match 'Hunky Sans:italic' => '$(_bst_1line "$_out")', want $TTFDIR/HunkySans-Oblique.ttf"; fi _out=$(fc-match --format '%{file}' "Hunky Serif:italic" 2>&1) if [ "$_out" = "$TTFDIR/HunkySerif-Roman.ttf" ]; then bst_ok $S/fc-match-serif-italic-falls-to-roman else bst_notok $S/fc-match-serif-italic-falls-to-roman "fc-match 'Hunky Serif:italic' => '$(_bst_1line "$_out")', want $TTFDIR/HunkySerif-Roman.ttf -- upstream draws no serif oblique"; fi # A FAMILY THIS PACKAGE DOES NOT PROVIDE MUST NOT RESOLVE INTO IT. The ticket # asks for this so "a mis-scoped fontconfig entry fails rather than passing by # accident". The probes are Hunky Mono and Hunky Fantasy: both are plausible # members of this family that do not exist, and both are what a FILES glob # widened to Hunky* would capture. An UNSPACED spelling would be no probe at all # -- fc-match resolves `HunkySans` to this package's own face, measured -- which # is why neither probe is one. The assertion is on the DIRECTORY, not on the # answer: fc-match must return something, and what it must not return is ours. for _fam in "Hunky Mono" "Hunky Fantasy"; do _out=$(fc-match --format '%{file}' "$_fam" 2>&1) _id=$(printf '%s' "$_fam" | tr ' ' '-') case "$_out" in "$TTFDIR"/Hunky*) bst_notok "$S/fc-match-unprovided-$_id" "fc-match '$_fam' resolved into this package: '$(_bst_1line "$_out")' -- a family this package does not provide must not match here" ;; *) bst_ok "$S/fc-match-unprovided-$_id" ;; esac done # THE THREE GENERICS THIS PORT DOES **NOT** TAKE OVER, ASSERTED FROM THIS SIDE. # Neither family appears on any preference list in this image's /etc/fonts # (measured: `grep -ri hunky` over the whole tree returns nothing), and neither # is monospaced, so all three generics keep their incumbents. This is NOT a # restatement of the fc-match-* cases above: those stay green if installing this # family ALSO displaced a generic, which is precisely what installing # ttf-inconsolata did to `monospace` one ticket after it landed. The incumbents # are named so a red says which one moved. # AMENDED FOR BRINTOS-1729 (ttf-tlwg), WHICH TAKES sans-serif AND serif # IMAGE-WIDE. ttf-tlwg ships ten fragments into /etc/fonts/conf.d # at 64; PREPENDS ahead of the generic while 30-liberation-aliases.conf's # only APPENDS behind it, so Kinnari takes `serif` and Waree takes # `sans-serif` for every suite on this image at once. It is CONFIGURATION-driven, # so it does not depend on how large the font set is. monospace is unaffected -- # source-code-pro-fonts' own loads at 44, ahead of these. THE CASE IS # AMENDED, NOT DELETED: what it asserts is that THIS port does not take a generic, # which is still true, and only the value it is compared against moved. for _pair in "sans-serif:DejaVu Sans" "serif:DejaVu Serif" "monospace:Source Code Pro"; do _g=${_pair%%:*}; _want=${_pair#*:} _out=$(fc-match --format '%{family}' "$_g" 2>&1) if [ "$_out" = "$_want" ]; then bst_ok "$S/fc-match-generic-$_g" else bst_notok "$S/fc-match-generic-$_g" "fc-match $_g => '$(_bst_1line "$_out")', want '$_want' -- this port must not take a generic"; fi done rm -f "$W.inv" "$W.sums" "$W.scan" "$W.st" "$W.ls" "$W.cache" "$W.list" 2>/dev/null