. "$BST_LIB" # BST-TIMEOUT: 180 # measured 21s; 14 execs of a 3-7 MB binary, and each exec # # on this arm is a fresh wasm module instantiation, so the # # 60s default leaves under 3x on a loaded host. # mpg123 1.32.10 (poky recipes-multimedia/mpg123) -- the MPEG-1 Layer 1/2/3 # decoder. `mpg123 -w out.wav in.mp3` writes a RIFF/WAVE file instead of driving # a sound card, so the whole of this suite runs offline with no audio device, # which is the ticket's own framing. # # THE ORACLE IS A REFERENCE DECODE, BUT NOT THE ONE 57-lame.test USES, AND THE # DIFFERENCE WAS MEASURED RATHER THAN ASSUMED. lame's gate is a plain byte-for- # byte cmp against a host-built reference, and the equivalent here does not hold: # mpg123's configure puts -ffast-math in CFLAGS, and under it a gcc build and a # clang build OF THE IDENTICAL SOURCE disagree -- 8 samples of 16384, by exactly # 1 LSB. Recompiling both with -fno-fast-math appended makes them bit-identical, # so the divergence is reassociation licensed by fast-math, not libm and not a # port defect. A cmp against any one compiler's fast-math output would therefore # assert which compiler built the reference, not whether this arm decodes MP3 # correctly. # # So data/mpg123-ref-strict.wav is the -fno-fast-math decode of the fixture -- # the value gcc and clang both produce, which makes it a compiler-independent # ground truth anyone can regenerate rather than one host's artifact. The # correctness gate is agreement with it inside a bound (mpg123/pcm-*), and the # byte-exact gate is a pinned digest of this arm's own output (mpg123/digest). # The two answer different questions and both are wanted: the bound says the # decode is RIGHT, the digest says it has not DRIFTED. # # WHAT THE BOUNDS ARE AND WHERE THEY COME FROM. Measured 2026-08-08 against # data/mpg123-ref-strict.wav: this wasm32 build differs on 7 of 16384 samples, # max 1 LSB -- closer to the strict reference than either host fast-math build # (gcc 12 samples, clang 14, both max 1 LSB). max|delta| is bounded at 1 LSB # because that is what fast-math reassociation can cost and anything larger is a # real arithmetic fault; the differing-sample count is bounded at 64 (0.39%), # ~9x the observed 7 and ~4.5x the worst host build, so ordinary toolchain churn # does not trip it while a broken synth filter would. # # READING A RED. mpg123/digest alone is toolchain drift -- a new wasmcc/clang # reassociating differently -- and is NOT a defect if the pcm-* cases stay green; # re-pin it. mpg123/pcm-max-deviation or a decode-size mismatch is a genuine # decoder fault. The fixture and the reference are both digest-pinned first, so # neither red can ever be blamed on a payload that changed underneath. # # THE FIXTURE IS 57-lame.test's OWN OUTPUT, DELIBERATELY. data/lame-ref-128cbr.mp3 # is already on the image, its provenance is documented (lame 3.100, -b 128 # --cbr, from 57-flac.test's sawtooth), and it is digest-pinned here before use. # Shipping a second, byte-identical MP3 under another name would buy nothing; # decoding the encoder suite's reference instead makes the image's two MP3 tools # meet on one stream. # # WHAT RUNS ON TARGET IS THE PORTABLE C. configure resolves wasm32-hwjs-linux # through its generic *-*-linux* arm to cpu_type=generic_fpu, so no assembly and # no intrinsic is compiled in; mpg123/decoder-generic asserts that at run time, # because every number above is a claim about that decoder and not another. require mpg123 mpg123/present tmp=${TMPDIR:-/tmp}/bst-mpg123.$$ mkdir -p "$tmp" 2>/dev/null src=$BST_DATA/lame-ref-128cbr.mp3 ref=$BST_DATA/mpg123-ref-strict.wav out=$tmp/out.wav if [ ! -f "$src" ] || [ ! -f "$ref" ]; then bst_notok mpg123/payloads "missing $src or $ref -- remaining cases not run" rm -rf "$tmp" exit 0 fi md5of() { md5sum "$1" 2>/dev/null | cut -d' ' -f1; } size() { wc -c < "$1" 2>/dev/null | tr -d ' '; } # ----------------------------------------------------------------- payloads --- # Pinned BEFORE anything is decoded. Without this a later mismatch has two # indistinguishable causes -- the decoder changed, or the payload did -- and the # wrong one gets reported. if command -v md5sum >/dev/null 2>&1; then check_eq mpg123/fixture-digest c15eb97bcb066cab224876c497f1e676 "$(md5of "$src")" check_eq mpg123/reference-digest f168370591fb0312953f39f1a2e57123 "$(md5of "$ref")" else bst_skip mpg123/fixture-digest "no md5sum on this image" bst_skip mpg123/reference-digest "no md5sum on this image" fi # --------------------------------------------------------------- the decode --- check_out mpg123/version "mpg123 1.32.10" mpg123 --version check mpg123/decode mpg123 -q -w "$out" "$src" check_eq mpg123/decode-size 32812 "$(size "$out")" # 44 canonical RIFF/WAVE bytes: the format fields (mono, 44100 Hz, 16-bit signed # LE, 32768-byte data chunk) are asserted as a unit by matching the reference's # header exactly, which is stricter than probing them one at a time. check mpg123/wav-header cmp -s -n 44 "$out" "$ref" # The byte-exact gate. Scoped to this arm and this toolchain by construction -- # see READING A RED above. if command -v md5sum >/dev/null 2>&1; then check_eq mpg123/digest 6cd46a771e59a22eea8e000812d364fc "$(md5of "$out")" else bst_skip mpg123/digest "no md5sum on this image" fi # The correctness gate: agreement with the compiler-independent reference. if command -v od >/dev/null 2>&1 && command -v awk >/dev/null 2>&1; then od -An -v -td2 -w2 -j44 "$out" > "$tmp/a.txt" 2>/dev/null od -An -v -td2 -w2 -j44 "$ref" > "$tmp/b.txt" 2>/dev/null set -- $(awk 'NR==FNR{a[FNR]=$1;next} {d=a[FNR]-$1; if(d<0)d=-d; if(d>m)m=d; if(d)n++} END{print m+0, n+0, FNR}' "$tmp/a.txt" "$tmp/b.txt") maxd=${1:-}; ndiff=${2:-}; nsamp=${3:-0} # Evidence line, outside the BST protocol like ==BST-SUITE== -- it is how the # bounds below get re-derived from a measurement instead of re-guessed. echo "==MPG123-AGREE max=${maxd:-?} ndiff=${ndiff:-?} of ${nsamp}==" check_eq mpg123/pcm-sample-count 16384 "$nsamp" if [ -n "$maxd" ] && [ "$maxd" -le 1 ]; then bst_ok mpg123/pcm-max-deviation else bst_notok mpg123/pcm-max-deviation "max |delta| ${maxd:-unknown} LSB > 1 (differing ${ndiff:-?} of ${nsamp})" fi if [ -n "$ndiff" ] && [ "$ndiff" -le 64 ]; then bst_ok mpg123/pcm-exact-fraction else bst_notok mpg123/pcm-exact-fraction "${ndiff:-unknown} of ${nsamp} samples differ, bound 64" fi else bst_skip mpg123/pcm-sample-count "no od/awk on this image" bst_skip mpg123/pcm-max-deviation "no od/awk on this image" bst_skip mpg123/pcm-exact-fraction "no od/awk on this image" fi # A decoder that is deterministic is what makes a stored reference an oracle at # all, so it is asserted rather than assumed. mpg123 -q -w "$tmp/out2.wav" "$src" 2>/dev/null check mpg123/determinism cmp -s "$out" "$tmp/out2.wav" # ------------------------------------------------- what the decoder reports --- tv=$(mpg123 -t -v "$src" 2>&1) case "$tv" in *"Decoder: generic"*) bst_ok mpg123/decoder-generic ;; *) bst_notok mpg123/decoder-generic "$(_bst_1line "$tv")" ;; esac case "$tv" in *"MPEG 1.0 L III cbr128 44100 mono"*) bst_ok mpg123/stream-format ;; *) bst_notok mpg123/stream-format "$(_bst_1line "$tv")" ;; esac # ------------------------------------------------- the other builtin writers --- # raw, au and cdr are compiled into libout123 rather than dlopen'd, which is what # makes them usable on this arm at all; each is checked by the size its own # container arithmetic forces. mpg123 -q -s "$src" > "$tmp/raw.pcm" 2>/dev/null check_eq mpg123/raw-size 32768 "$(size "$tmp/raw.pcm")" tail -c +45 "$out" > "$tmp/payload.pcm" 2>/dev/null check mpg123/raw-matches-wav cmp -s "$tmp/raw.pcm" "$tmp/payload.pcm" mpg123 -q --au "$tmp/o.au" "$src" 2>/dev/null check_eq mpg123/au-size 32800 "$(size "$tmp/o.au")" check_out mpg123/au-magic ".snd" head -c 4 "$tmp/o.au" # --cdr forces CD-DA: 44100 Hz, stereo, 16-bit. 16384 mono frames become 65536 # bytes, so the size also proves the mono->stereo duplication happened. mpg123 -q --cdr "$tmp/o.cdr" "$src" 2>/dev/null check_eq mpg123/cdr-size 65536 "$(size "$tmp/o.cdr")" check_out mpg123/wav-builtin "RIFF WAVE file (builtin)" mpg123 --list-modules # ------------------------------------------------------ libsyn123 conversion --- mpg123 -q --8bit -w "$tmp/o8.wav" "$src" 2>/dev/null check_eq mpg123/8bit-size 16428 "$(size "$tmp/o8.wav")" mpg123 -q --stereo -w "$tmp/os.wav" "$src" 2>/dev/null check_eq mpg123/stereo-size 65580 "$(size "$tmp/os.wav")" mpg123 -q --rate 22050 -w "$tmp/or.wav" "$src" 2>/dev/null check_eq mpg123/resample-size 16428 "$(size "$tmp/or.wav")" # ------------------------------------------------------------ ID3, and why ---- # The tag is built here from ASCII bytes rather than written by a tool, so this # exercises mpg123's own ID3 READER and nothing else. That matters: 57-lame.test # found lame writing empty tag text on this image because its writer routes # through iconv_open, which is refused here (no gconv data). mpg123 links no # iconv at all -- `grep -rl iconv src/` over the built sources matches only a # shell script -- so the reader has no such dependency and these cases are # expected green, not witness-gated. pad() { _s=$1; _n=$2; printf '%s' "$_s"; _i=${#_s} while [ "$_i" -lt "$_n" ]; do printf '\000'; _i=$((_i+1)); done } { printf 'TAG'; pad 'brintOS mpg123 port' 30; pad 'Emcraft' 30; pad 'BRINTOS' 30 printf '2026'; pad '' 30; printf '\014'; } > "$tmp/id3v1" check_eq mpg123/id3v1-trailer-size 128 "$(size "$tmp/id3v1")" cat "$src" "$tmp/id3v1" > "$tmp/tagged.mp3" check_eq mpg123/id3-tagged-size 7232 "$(size "$tmp/tagged.mp3")" d=$(mpg123-id3dump "$tmp/tagged.mp3" 2>&1) case "$d" in *"Title: brintOS mpg123 port"*) bst_ok mpg123/id3dump-title ;; *) bst_notok mpg123/id3dump-title "$(_bst_1line "$d")" ;; esac case "$d" in *"Artist: Emcraft"*) bst_ok mpg123/id3dump-artist ;; *) bst_notok mpg123/id3dump-artist "$(_bst_1line "$d")" ;; esac case "$d" in *"Year: 2026"*) bst_ok mpg123/id3dump-year ;; *) bst_notok mpg123/id3dump-year "$(_bst_1line "$d")" ;; esac # mpg123-strip drops everything that is not an MPEG frame, so a tagged stream # must come back byte-identical to the untagged original -- an exact assertion, # not a size heuristic. mpg123-strip < "$tmp/tagged.mp3" > "$tmp/stripped.mp3" 2>/dev/null check mpg123/strip-restores-original cmp -s "$tmp/stripped.mp3" "$src" # ------------------------------------------------------------------ out123 ---- # The sibling binary, driven from the decoder's own raw output: agreeing with # mpg123 -w byte for byte shows both reach the same builtin WAV writer. out123 --wav "$tmp/o123.wav" --rate 44100 --channels 1 --encoding s16 \ < "$tmp/raw.pcm" 2>/dev/null check mpg123/out123-wav cmp -s "$tmp/o123.wav" "$out" # ------------------------------------------------------- shape on this image --- # DISTRO_FEATURES is empty and the distro is --disable-shared, so configure # disables the dlopen module mechanism outright and no output module is # installed. Asserting the absence keeps a future image that grows a modules # directory from doing so unnoticed. if [ -d /usr/lib/mpg123 ]; then bst_notok mpg123/no-module-dir "/usr/lib/mpg123 exists -- dlopen modules were installed" else bst_ok mpg123/no-module-dir fi so=$(ls /usr/lib/libmpg123.so* /usr/lib/libout123.so* /usr/lib/libsyn123.so* 2>/dev/null) if [ -z "$so" ]; then bst_ok mpg123/static-only else bst_notok mpg123/static-only "$(_bst_1line "$so")" fi rm -rf "$tmp"