# BST-TIMEOUT: 600 # ONE run of the 7.8 MB data/freetype-probe, where # # 99-ttf-droid does seven and 99-ttf-bitstream-vera ten. # # MEASURED at elapsed=41s on target (read off the # # ==BST-SUITE elapsed= line, not estimated) -- the same 41s # # 99-ttf-droid measures with seven probe runs, which is what # # says the cost is the binary and not the face count. The # # bound is deliberately the SAME 600 # # those four suites carry, for the reason vera's header # # gives: all five drive the same binary and a suite whose # # cost is dominated by one binary should not claim a # # different bound for it. Erring on the family's bound also # # avoids the mistake BRINTOS-1449 shipped and had to correct # # -- a bound taken from a guess, at 48x the truth. # # cantarell-fonts 0.303.1 -- Cantarell, the humanist sans-serif GNOME uses as its # interface font. poky meta/recipes-graphics/cantarell-fonts/cantarell-fonts_0.303.1.bb. # # ============================================================================ # ONE FILE, ONE PACKAGE, AND A DIRECTORY OF ITS OWN. # ============================================================================ # # The recipe builds with -Duseprebuilt=true, so meson takes prebuilt/meson.build # instead of src/. That file installs Cantarell-VF.otf under `buildvf` and five # static faces under `buildstatics`; meson_options.txt defaults buildvf=TRUE and # buildstatics=FALSE and the recipe overrides neither, so exactly one file is # installed. The five statics are not merely un-installed, they are NOT IN THE # TARBALL -- prebuilt/ holds one .otf -- so a recipe revision that turned them on # would fail the build rather than quietly ship five more faces. # # cantarell-fonts /usr/share/fonts/cantarell/Cantarell-VF.otf 170588 0644 # cantarell-fonts-dev, -dbg empty placeholders, not installed # cantarell-fonts-doc DOES NOT EXIST -- see below # # fontsdir defaults to ${datadir}/fonts/cantarell and the recipe does not # override it, so this is the FIRST font port on this image to get a directory of # its own. /usr/share/fonts/ttf holds the 22 Liberation+Vera faces and # /usr/share/fonts/truetype the 12 Inconsolata+Gentium+Droid ones; this port # writes to neither. That removes the whole class of co-tenancy repair the last # four font ports each had to do to their neighbours' suites -- no sibling suite # is amended by this ticket -- and it is why dir-count can census the WHOLE # directory rather than scoping by basename the way those four must. # # NO -doc PACKAGE, AND NO LICENCE FILE ON THE IMAGE. FILES:${PN} is an ASSIGNMENT # that drops the default, which is the same shape as ttf-bitstream-vera's -- but # where that recipe's own do_install writes three .TXT files for ${PN}-doc to # pick up, this one installs NOTHING but the font. COPYING (the SIL OFL 1.1 text) # stays in ${S}; no package could carry it, and dpkg builds no -doc package at # all. That is the recipe's behaviour, not this port's choice, and requirement 3 # is every file the RECIPE DECLARES, all of which ship. pkg-no-doc-package # asserts the absence, because "the -doc line was forgotten" and "there is no # -doc package to install" look identical from the file layer alone. The licence # is not lost: name IDs 13/14 inside the font carry the OFL notice and its URL. # # FILES ALSO NAMES ${datadir}/fontconfig, AND NOTHING INSTALLS THERE. There is no # fontconfig fragment anywhere in cantarell-fonts-0.303.1, on either meson path. # The directory nevertheless exists on the image, owned by the `fontconfig` # package, so "absent" and "empty" are both the wrong assertion -- # pkgfiles-no-fontconfig asserts the one thing that is this port's business: # cantarell-fonts owns no path under it. This matters more than an unused FILES # entry usually would, because a fragment landing in /etc/fonts/conf.d would # outrank every co-tenant's aliases silently. # # ============================================================================ # THIS PORT TAKES NO GENERIC -- INCLUDING system-ui, WHICH THE CONFIG SAYS IT # SHOULD. THAT IS A MEASUREMENT, AND IT OVERTURNED THE READING OF THE CONFIG. # ============================================================================ # # Cantarell occurs in exactly two files in the whole of fontconfig 2.15.0's # conf.avail -- checked exhaustively, not sampled -- and both are about one # generic: # # 45-latin.conf:285 Cantarell # system-ui # 60-latin.conf:80 system-ui's list, FIRST entry # # So serif, sans-serif, monospace, fantasy and cursive cannot be affected by # construction, and the config plainly says system-ui SHOULD move. An earlier # draft of this suite asserted exactly that, positively, on the strength of those # two lines. It is wrong. # # MEASURED ON TARGET, WITH EVERYTHING ELSE HELD FIXED: the image built one ticket # earlier -- same base, same fontconfig, WITHOUT this package -- already answers # `fc-match system-ui` => Liberation Mono, and so does this one. The delta is # ZERO on all four generics. The image does enable both conf files (the # fontconfig package ships them as symlinks in /etc/fonts/conf.d, and 60-latin is # also why `fc-match monospace` answers Inconsolata at all), so this is not a # missing-config story. # # NOR IS IT A wasm32 DIVERGENCE. A native fontconfig 2.15.0, built from this # image's own recipe on x86-64 with Cantarell installed, answers Liberation Mono # for system-ui too. It is upstream fontconfig behaviour, reproducible # off-target, and NOT a brintOS defect -- so nothing is filed for it. # `system-ui` is not one of the generics 49-sansserif.conf seeds, and whatever # the prefer list contributes does not survive the sort. # # fc-match-system-ui-unchanged therefore sits alongside the other three rather # than inverting them. It is worth carrying rather than dropping precisely # because the config predicts otherwise: if a fontconfig bump ever makes the # prefer list bite, this case is what says so, and it will need re-pointing # together with the design-doc note. # # NO SIBLING SUITE ASSERTS system-ui -- checked across the whole tests/ tree -- # so nothing goes red either way, and no neighbour is amended. 99-ttf-droid, # 99-ttf-inconsolata and 99-liberation-fonts assert the other three generics from # their side, so a disagreement between the suites stays visible. # # ============================================================================ # 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 -- 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 and never reaches inittab -- so asserting # the configured state would report the runner's choice as a package red. What is # asserted instead is boot-mode independent and is the actual risk the ticket # names: 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. Nothing had to # be overridden. Image-wide postinst success stays 79-run-postinsts' no-unpacked # case and is not re-litigated here; the fc-* block forces `fc-cache -f` itself # so no other case depends on any of this. # # ============================================================================ # FOUR LAYERS, AND A RED IN AN EARLIER ONE LOCALISES A RED IN A LATER ONE: # # file-*, dir-*, the payload arrived intact -- size, bytes, mode, type -- # pkgfiles-*, the package's real footprint is the one file it claims, # cotenant-* and the two directories this port does not write to are # unchanged. No font machinery involved at all. # pkg-*, postinst-* the one package the image really installs, the fontcache # class's RDEPENDS, and the deferred 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 -- the layer the ticket # asks for by name -- and additionally recognises it as a # VARIABLE font, which is what fontconfig's six patterns # downstream rest on. # 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 -- and where the # six-patterns-from-one-file shape has to be asked for # correctly, see the fc-list call for what that cost. . "$BST_LIB" S=cantarell-fonts # THE CANARY, which asserts nothing about cantarell-fonts. # # 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/cantarell-fonts-manifest.txt FACTS=$D/cantarell-fonts-fontfacts.txt FACES=$D/cantarell-fonts-fcfaces.txt PROBE_SRC=$D/freetype-probe FDIR=/usr/share/fonts/cantarell COT1=/usr/share/fonts/ttf COT2=/usr/share/fonts/truetype OTF=$FDIR/Cantarell-VF.otf PKGLIST=/var/lib/dpkg/info/cantarell-fonts.list PI=/var/lib/dpkg/info/cantarell-fonts.postinst tmp=${TMPDIR:-/tmp} W=$tmp/bst-cantarell.$$ 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 cantarell-fonts, 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 cantarell-fonts 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 this port's own directory AND both co-tenant directories, 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. # There is no doc directory to walk: this recipe installs none (see the header). find "$FDIR" "$COT1" "$COT2" \ -maxdepth 1 -printf '%p %y %s %m %l\n' 2>/dev/null | LC_ALL=C sort > "$W.inv" # One sha256sum over the one payload file. Named explicitly rather than globbed: # the manifest is the list, and dir-no-extras is what says the directory holds # nothing the manifest does not name. sha256sum "$OTF" > "$W.sums" 2>&1 # The package's own file list, copied to tmpfs rather than read from the rootfs # in a redirect (brintos/linux#73). An absent list is not fatal here -- the awk's # pkgfiles-loaded case reports it -- so this is not guarded with an exit. cat "$PKGLIST" > "$W.pkgf" 2>/dev/null || : > "$W.pkgf" # One fc-scan over the one installed file. 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 variable flag and the instance index). It reads the FILE, not the # cache, so it is independent of the fc-cache in the consumer block below. # # SIX PATTERNS COME BACK FROM ONE FILE. See the fcfaces control's header. LC_ALL=C fc-scan --format '%{file|basename}|%{family}|%{style}|%{slant}|%{weight}|%{spacing}|%{postscriptname}|%{fontformat}|%{scalable}|%{outline}|%{variable}|%{index}\n' \ "$OTF" 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 PKGF="$W.pkgf" \ -v FACES="$FACES" -v SCAN="$W.scan" \ -f "$D/cantarell-fonts-verify.awk" "$REF" "$W.sums" "$W.inv" "$W.pkgf" "$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 some other package had installed the same # bytes, so they cannot say that cantarell-fonts 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. `cantarell-fonts` is a PREFIX of # `cantarell-fonts-dev`, `-dbg` and `-doc`, so an unanchored match would report # one stanza as another and make pkg-no-doc-package unfalsifiable. sed -n '/^Package: cantarell-fonts$/,/^$/p; /^Package: cantarell-fonts-doc$/,/^$/p; /^Package: cantarell-fonts-dev$/,/^$/p; /^Package: cantarell-fonts-dbg$/,/^$/p' \ /var/lib/dpkg/status > "$W.st" 2>/dev/null _pkg=""; _main=0; _doc=0; _dev=0; _dbg=0; _arch=0; _dep=0 while IFS= read -r l; do case "$l" in "Package: cantarell-fonts") _pkg=main; _main=1 ;; "Package: cantarell-fonts-doc") _pkg=doc; _doc=1 ;; "Package: cantarell-fonts-dev") _pkg=dev; _dev=1 ;; "Package: cantarell-fonts-dbg") _pkg=dbg; _dbg=1 ;; "Architecture: all") [ "$_pkg" = main ] && _arch=1 ;; "Depends: "*) case "$l" in *fontconfig-utils*) [ "$_pkg" = main ] && _dep=1 ;; esac ;; esac done < "$W.st" # THE ONE PACKAGE THIS IMAGE INSTALLS. A dropped IMAGE_INSTALL line would take # every file-* and ft-* case with it; this case says the cause is a missing # PACKAGE rather than missing files. if [ "$_main" -eq 1 ]; then bst_ok $S/pkg-known else bst_notok $S/pkg-known "dpkg does not know the package cantarell-fonts"; fi # THERE IS NO -doc PACKAGE, AND ITS ABSENCE IS THE RECIPE. Nothing but the font # is installed, so ${PN}-doc is empty and dpkg builds none. This case names that, # because a future recipe revision that installed COPYING would change what the # image must ask for -- and because ttf-bitstream-vera, the port this one most # resembles, DOES ship a -doc and this one deliberately does not. if [ "$_doc" -eq 0 ]; then bst_ok $S/pkg-no-doc-package else bst_notok $S/pkg-no-doc-package "dpkg knows a cantarell-fonts-doc package -- the recipe now installs documentation"; fi # ...AND -dev / -dbg ARE NOT INSTALLED. Both are built and both are empty. They # are asserted absent rather than ignored: an IMAGE_INSTALL that pulled them in # would add two packages to the manifest for no payload at all, and nothing else # in this suite would notice. if [ "$_dev" -eq 0 ]; then bst_ok $S/pkg-no-dev else bst_notok $S/pkg-no-dev "cantarell-fonts-dev is installed -- it is an empty placeholder"; fi if [ "$_dbg" -eq 0 ]; then bst_ok $S/pkg-no-dbg else bst_notok $S/pkg-no-dbg "cantarell-fonts-dbg is installed -- it is an empty placeholder"; 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 "cantarell-fonts 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 to # each FONT_PACKAGES entry -- so this is the 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 "cantarell-fonts does not Depend on fontconfig-utils"; fi # ------------------------------------------------- the deferred fontcache hook if [ -f "$PI" ]; then bst_ok $S/postinst-present else bst_notok $S/postinst-present "$PI 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 "$PI" # ...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 cantarell-fonts 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" "$W.pkgf" 2>/dev/null exit 0 fi bst_ok $S/probe-present probe=$tmp/bst-cantarell-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" "$W.pkgf" 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 Cantarell-VF.otf==` puts # "Cantarell-VF.otf==" 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 -- the defect BRINTOS-1449's host pre-flight caught. : > "$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" "$FDIR/$_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/cantarell-fonts-ftverify.awk" "$FACTS" "$W.ft" # --------------------------------------------------------- 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 ONE FACE SITS WELL INSIDE THE IMAGE'S EXISTING WORKING SET. #165's # bracket on this image is ~66 kB to 577,708 bytes; this face is 170,588, the 52 # font files already walked by every fc-cache run are 23,895 to 415,816, and # TWENTY-TWO of those 52 are larger than this one. So this package cannot # introduce a new failure mode or a new maximum. Nothing is held out and there # are no held-out-* cases, which is what distinguishes this suite from # 99-ttf-droid's. # # THE BOUND IS BEST-EFFORT AND IS NOT WHAT MAKES THIS SAFE. #165 measured that a # parked fc-cache survives SIGKILL, so `timeout` cannot reclaim it and will # itself hang when the park is the unkillable kind. It is here for the case where # the call merely runs long, and the ordering above is here for the case where it # does not come back at all. Recorded rather than dressed up, because a bound # that looks protective and is not is worse than none. # # BRINTOS-WORKAROUND: linux-in-the-browser#165 remove -- bound the fc-cache call # and order the consumer block last; both go when the mmap population path # tolerates a parking read. # `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, and MEASURED on the # resulting image this suite parked here -- reaching 43 of its 50 cases against # 50/50 in 30 s on the same base pin without the port. 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 -- fc-cache runs and # returns 0. # # 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. # 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. # # THE OBJECT SET MUST CARRY `style`, AND THAT IS NOT COSMETIC. fc-list DEDUPES # its output on the properties it was ASKED for, not on the underlying patterns. # A variable font's six patterns share one file and one family and differ only in # style and index -- so `fc-list : file family`, which is what every other font # suite on this image correctly uses, collapses all six into ONE line. Measured # on target: that spelling reports 1 where `fc-cache -f -v` says # "/usr/share/fonts/cantarell: caching, new cache contents: 6 fonts" and an # unrestricted fc-list returns all six. The cache is right; the query was wrong. fc-list : file family style > "$W.list" 2>&1 # THE FACE IS DISCOVERABLE, not merely present on disk. A cache that serialised # wrongly, or a FreeType that refused the outlines, shows up here while every # file-* case above stays green -- which is the split that says where to look. # # SIX ENTRIES, NOT ONE. fc-list reports the cached patterns, and a variable font # contributes one per named instance plus one for itself -- the same six fc-scan # produced from the file directly. Asserting 1 here, which is what every other # font suite on this image correctly asserts for its static faces, would be a red # on a working port. The line format is now ": :style=