# BST-TIMEOUT: 700 # 44 cases. MEASURED at elapsed=58s and 86s on two runs of # # the same image on the same host (2026-08-26, local) -- # # both read off the ==BST-SUITE line, neither guessed. # # Set from the SLOWER reading: 700 is ~8x of 86s, the # # headroom 99-hicolor-icon-theme carries, and a bound taken # # from the 58s run alone would have been derived from # # whichever of the two happened to come first. # # # The spread is the suite's shape, not noise to average # # away. Its cost is two finds over 716 entries on a # # virtiofs root, ~10 BATCHED sha256sum execs over 423 # # files, two probe runs (one parsing 508 configuration # # files), and one gawk that reads all 419 *.conf itself. # # The exec count is bounded at ~18, so there is no tail the # # way a suite dominated by repeated execs has one -- but # # ten execs is ten more than a single directory walk, and # # that is where a 1.5x swing between two runs comes from. # # alsa-ucm-conf 1.2.11 -- the ALSA Use Case Manager configuration tree: the # per-card use-case descriptions that tell an ALSA client which device to open # for playback, capture, or a given profile. poky # meta/recipes-multimedia/alsa/alsa-ucm-conf_1.2.11.bb, unmodified: no bbappend, # no patch, and nothing for one to fix, because the recipe has no configure and # no compile at all -- `inherit allarch` and a do_install that is `cp -r ${S}/ucm` # and `cp -r ${S}/ucm2` into ${datadir}/alsa. # # THERE IS NOTHING HERE TO EXECUTE, AND NOTHING HERE OPENS A SOUND DEVICE. This # machine has no audio hardware and will not get any. The deliverable is the # configuration payload a ported alsa-lib reads once a card exists, so the whole # of what this port can be asked to prove is that the payload is installed, is # upstream's bytes, and is machine-readable by ALSA's own parser. # # WHAT THE IMAGE ACTUALLY INSTALLS, MEASURED, NOT ASSUMED: 716 entries in one # package -- 423 regular files, 205 directories and 88 SYMLINKS -- under # /usr/share/alsa/{ucm,ucm2}. 419 of the files are *.conf; the other four are # ucm/README.md, ucm2/README.md, ucm2/DEBUG.md and an empty # ucm2/conf.virt.d/.gitignore. The tarball's own LICENSE and top-level README.md # are NOT installed: the recipe copies the two trees and nothing else. # # THE 88 SYMLINKS ARE THE PART A PACKAGING PATH LOSES. They are how ucm reaches # a card's configuration from a kernel driver name -- ucm2/module/snd_soc_*.conf # and most of ucm2/conf.d/* are links into the vendor directories -- and a deb or # tar round trip that dereferenced them would leave every size and every hash in # this suite green while the tree gained 88 copies. `inv-types` and `inv-link` # are the cases that say it did not, and `symlink-resolve` says none of them # dangles. # # THE REFERENCE IS UPSTREAM'S OWN BYTES. `allarch` plus a do_install that is two # `cp -r`s means nothing architecture-dependent touches a byte of this package, # so a native x86_64 staging of the same tarball produces the same tree by # construction. Verified rather than argued: `diff -r --no-dereference` between # the wasm32 .deb's payload and a native staging of # alsa-ucm-conf-1.2.11.tar.bz2 (sha256 387c01cf...) reports nothing, over all # 716 entries (2026-08-26). src/alsa_ucm_conf_mknative.sh regenerates the # control, and `--check` proves it reproducible over twelve runs. # # THE PARSER IS alsa-lib's, NOT ONE WRITTEN HERE. The ticket asks that every # configuration file parse as ALSA config syntax, and a parser written for a # suite answers that question with its own opinion. So the parse arm drives # data/alsa-lib-probe -- the probe BRINTOS-1466 cross-links against the very # libasound.a this image ships -- through `loadall`, which is snd_config_load() # over every *.conf under a directory, one process for the whole sweep. Both # arms run the same probe over the same tree, and each file's line carries its # node count and an FNV-1a of its whole sorted canonical namespace, so a file # that parses to a DIFFERENT namespace is caught and not merely one that fails. # # ONE FILE IN 508 NEEDS MORE THAN snd_config_load(), AND IT IS COVERED RATHER # THAN EXCUSED. ucm2/sof-soundwire/HiFi.conf is the only file in the tree using # the bracket-include form (``). alsa-lib resolves such # a path against a list of include directories, and the PUBLIC snd_config_load() # passes NULL there, while alsa-lib's own ucm loader passes {, # NULL} (src/ucm/utils.c, uc_mgr_config_load_into). So under the public entry # point that one file returns -ENOENT -- on x86_64 exactly as on wasm32 -- and # `parse-files-load-status` pins that agreement verbatim, so a SECOND load # failure would be a red. # # What establishes the file itself is well-formed is `parse-include-match`: a # two-line wrapper, `` then ``, fed to # the same probe. `` is alsa-lib's own way of adding an include # directory (src/conf.c), and input_stdio_open walks the whole enclosing chain, # so the nested include resolves too -- which is exactly the include semantics # ucm gives the file at runtime, obtained with no parser of ours and no second # probe binary. It parses to 65 nodes on both arms. # # AND THE 296 LITERAL Include SITES ARE RESOLVED, WHICH IS THE TICKET'S SECOND # CLAUSE. `include-targets` walks every *.conf in the tree, extracts each # `Include..File ""` whose path is not built from a ${...} # substitution, and resolves it by alsa-lib's own rule (src/ucm/parser.c, # ucm_filename: a leading `/` is stripped and the rest taken from the ucm2 root; # anything else is relative to the configuration directory the including file # came from). All 296 resolve. This is the case that says WHY a dropped file # matters, and it reddens for a reason `inv-set` cannot state. # # THE CENSUS IS SCOPED BY DPKG OWNERSHIP AS WELL AS BY PATH, and the two are # separate cases. /usr/share/alsa is shared -- alsa-conf owns alsa.conf and the # cards/, ctl/ and pcm/ trees in the same directory, and BRINTOS-1458's # alsa-topology-conf will add ucm2's sibling -- so a census scoped only by path # prefix reports a neighbour's files as this package's. `dpkg-owns-tree` and # `dpkg-list-complete` are the two directions of that question. # # WRITTEN TO EXEC AS LITTLE AS POSSIBLE. Two finds, one batched sha256sum, two # probe runs, one sed and one gawk cover every case; the gawk reads all 419 # configuration files itself with getline rather than forking a grep per file. . "$BST_LIB" S=alsa-ucm-conf # THE CANARY, which asserts nothing about alsa-ucm-conf. # # 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. 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 DD=/usr/share/alsa U2=$DD/ucm2 MAN=$D/alsa-ucm-conf-manifest.txt SUMS=$D/alsa-ucm-conf-sums.txt PARSE=$D/alsa-ucm-conf-parse.txt PROBE_SRC=$D/alsa-lib-probe STATUS=/var/lib/dpkg/status LIST=/var/lib/dpkg/info/alsa-ucm-conf.list POSTINST=/var/lib/dpkg/info/alsa-ucm-conf.postinst tmp=${TMPDIR:-/tmp} W=$tmp/bst-alsa-ucm-conf.$$ mkdir -p "$W" 2>/dev/null require gawk $S/gawk-present require sha256sum $S/sha256sum-present require find $S/find-present require xargs $S/xargs-present if [ ! -f "$MAN" ] || [ ! -f "$SUMS" ] || [ ! -f "$PARSE" ]; then bst_notok $S/ref-present "the reference data did not ship: $MAN / $SUMS / $PARSE" exit 0 fi bst_ok $S/ref-present # Asserted on the SHIPPED payload with test -f, never test -x: data/ installs at # 0644 and the rootfs is read-only at browser runtime, so a probe that passed an # -x assertion would be one placed by hand. The cp/chmod below carries the # executable question. check $S/probe-shipped test -f "$PROBE_SRC" if [ ! -d "$U2" ]; then bst_notok $S/root-present "$U2 did not reach the image -- every case below would be vacuous" exit 0 fi bst_ok $S/root-present # ---------------------------------------------------------- the entry points # # The paths alsa-lib goes to FIRST when a client asks for a use case, named one # by one rather than left to the census: the census says the tree is upstream's, # and these say the tree is reachable. ucm.conf is the toplevel file alsa-lib # includes; lib/generic.conf is the one file ucm.conf itself pulls in # (`Include.libgen.File "/lib/generic.conf"`); conf.d and conf.virt.d are the two # directories its UseCasePath blocks probe, for a card with and without hardware. check $S/entry-ucm-dir test -d "$DD/ucm" check $S/entry-ucm2-dir test -d "$U2" check $S/entry-ucm-conf test -f "$U2/ucm.conf" check $S/entry-lib-generic test -f "$U2/lib/generic.conf" check $S/entry-lib-cardinit test -f "$U2/lib/card-init.conf" check $S/entry-lib-ctlremap test -f "$U2/lib/ctl-remap.conf" check $S/entry-confd test -d "$U2/conf.d" check $S/entry-confvirtd test -d "$U2/conf.virt.d" # The v1 tree is a README and nothing else, on every build of this version # including upstream's own -- ucm.conf gates the v1 lookup paths behind # `Define.V1 ""`, which is empty, so nothing reads it. Asserted so that a build # which grew v1 content, or lost the directory, is a red rather than a silence. check $S/v1-readme-only sh -c "[ \"\$(find $DD/ucm -mindepth 1 | wc -l)\" = 1 ] && test -f $DD/ucm/README.md" # --------------------------------------------------------------- the census # # ONE find over the whole of /usr/share/alsa/{ucm,ucm2}. %y catches a symlink # where a file is meant and %m the mode -- neither of which `test -f` can see -- # and %l the link target, which is the only fact about a symlink worth # recording. The output is ONE argv entry (a redirect), so the 8192-byte # __wasm32_args_buf ceiling (brintos/hardwarejs#15) is not in play however many # entries the tree grows. # # NO -mindepth: the two roots ucm and ucm2 are themselves entries the package # installs, and the control counts them. Naming the two subtrees rather than # walking $DD is what keeps alsa-conf's own files -- alsa.conf and the cards/, # ctl/ and pcm/ trees, in this same directory -- out of this package's census. find "$DD/ucm" "$U2" -printf '%y|%m|%s|%l|%p\n' 2>/dev/null > "$W.inv" # 423 paths at ~45 bytes each is 19 KB of argv, so this MUST be batched: a # single sha256sum over the lot would exceed __wasm32_args_buf and never return. # `xargs -s 3000` bounds each exec's argv block explicitly rather than trusting # the guest's reported ARG_MAX, which is not the limit that bites. The cd is in # a SUBSHELL, which forks before it runs, so a chdir(2) that returns 513 on the # virtiofs root (brintos/linux#73) costs this one case and not the suite. ( cd "$DD" && find ucm ucm2 -type f -print0 \ | xargs -0 -s 3000 sha256sum ) > "$W.sums" 2>/dev/null check $S/sums-ran test -s "$W.sums" # ----------------------------------------------------------------- the parse PROBE=$W/alsa-lib-probe : > "$W.parse" if cp "$PROBE_SRC" "$PROBE" 2>/dev/null && chmod +x "$PROBE" 2>/dev/null; then bst_ok $S/probe-stage # ALSA_CONFIG_DIR is what resolves against and what the # probe rewrites to @DATADIR@ on the way out; HOME and XDG_CONFIG_HOME are # pointed at an empty scratch dir for the same reason the control's generator # does it -- ~/.asoundrc and $XDG_CONFIG_HOME/alsa/asoundrc are @hook sources # and neither is a fact about this package. H=$W/home; mkdir -p "$H/.config" 2>/dev/null mkdir -p "$W/wrap" 2>/dev/null printf '\n\n' > "$W/wrap/include-resolve.conf" { echo "== files" HOME=$H XDG_CONFIG_HOME=$H/.config ALSA_CONFIG_DIR=$DD "$PROBE" loadall "$U2" echo "== include" HOME=$H XDG_CONFIG_HOME=$H/.config ALSA_CONFIG_DIR=$DD "$PROBE" loadall "$W/wrap" } > "$W.parse" 2>/dev/null check $S/probe-ran test -s "$W.parse" else bst_notok $S/probe-stage "cannot stage $PROBE_SRC" bst_notok $S/probe-ran "the probe was never staged" fi # ------------------------------------------------------------- the scoring # # The dpkg list is copied rather than named directly so that an absent one is an # empty file the verifier reports on, not a gawk that dies on its first ARGV. if [ -f "$LIST" ]; then cp "$LIST" "$W.list" 2>/dev/null || : > "$W.list" else : > "$W.list"; fi LC_ALL=C gawk -v S="$S" -v DD="$DD" \ -f "$D/alsa-ucm-conf-verify.awk" \ "$MAN" "$SUMS" "$PARSE" "$W.list" "$W.inv" "$W.sums" "$W.parse" # --------------------------------------------------- the packaging on target # # The payload cases above would look identical whether or not the package had # dragged something in as a Depends -- a dependency's own files are not under # /usr/share/alsa. One sed pulls the stanza; the loop is shell builtins, reading # from the tmpfs copy rather than redirecting from the rootfs (brintos/linux#73). sed -n '/^Package: alsa-ucm-conf$/,/^$/p' "$STATUS" > "$W.st" 2>/dev/null _known=0; _arch=0; _dep=""; _rec="" while IFS= read -r l; do case "$l" in "Package: alsa-ucm-conf") _known=1 ;; "Architecture: all") _arch=1 ;; "Depends: "*) _dep=${l#Depends: } ;; "Recommends: "*) _rec=${l#Recommends: } ;; esac done < "$W.st" if [ "$_known" -eq 1 ]; then bst_ok $S/pkg-known else bst_notok $S/pkg-known "dpkg does not know the package alsa-ucm-conf"; fi # `allarch` reached the package metadata, which is what makes the byte-identity # reference above 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 "alsa-ucm-conf is not Architecture: all"; fi # Configuration data pulls nothing. Upstream's own recipe declares no RDEPENDS # and the built .deb declares no Depends -- notably NOT alsa-lib, because the # dependency runs the other way: alsa-lib reads this tree, this tree reads # nothing. if [ -z "$_dep" ]; then bst_ok $S/pkg-no-depends else bst_notok $S/pkg-no-depends "alsa-ucm-conf declares Depends: $_dep -- configuration data should pull nothing"; fi if [ -z "$_rec" ]; then bst_ok $S/pkg-no-recommends else bst_notok $S/pkg-no-recommends "alsa-ucm-conf declares Recommends: $_rec"; fi # ...and no maintainer script: there is no cache to build and no first-boot step, # so a scriptlet here would mean the package had grown one. if [ ! -e "$POSTINST" ]; then bst_ok $S/pkg-no-postinst else bst_notok $S/pkg-no-postinst "$POSTINST exists -- this package should register no scriptlet"; fi rm -rf "$W" 2>/dev/null rm -f "$W.inv" "$W.sums" "$W.parse" "$W.st" "$W.list" 2>/dev/null