. "$BST_LIB" # BST-TIMEOUT: 300 # 64 cases; measured 67s on target, over the 60s default # qrencode 4.1.1 (meta-oe recipes-graphics/qrencode) -- the QR Code generator: # libqrencode.a plus the /usr/bin/qrencode CLI over it. # # WHAT MAKES THIS PORT UNUSUALLY GATEABLE, AND WHY THAT IS WORTH SAYING. Every # other codec this Epic has landed had to argue about its oracle: 57-mpg123.test # cannot cmp at all (-ffast-math makes gcc and clang disagree), 57-tiff.test had # to stop pinning its zip bytes because the stream is zlib's and not libtiff's, # and 57-libjpeg-turbo.test could pin only its integer path. QR encoding has # none of those escape hatches: the version, the error-correction codewords, the # interleave and the mask are all fixed by ISO/IEC 18004 rule and by an INTEGER # penalty score, with no floating point and no third-party stream anywhere in # the text output paths. Measured, not assumed: gcc 16.1.1 and clang 22.1.8 # builds of this tarball were compared over 4 payloads x 10 output types x 4 EC # levels -- 160 comparisons -- and every one is byte-identical. So the reference # files in $BST_DATA are ground truth and the gate is `cmp`, with no tolerance. # # THE ONE OUTPUT THAT IS NOT PINNED BY BYTES IS PNG, DELIBERATELY, and it is the # libtiff zip lesson applied before it could cost a red. A PNG from qrencode is # libpng's and zlib's bytes, not qrencode's: the host control links libpng # 1.6.58 and Fedora's zlib-ng-compat while the image carries poky's libpng # 1.6.42 and stock zlib 1.3.1, and those make different match-finding choices. # Pinning the file would gate this port on library versions it does not own. So # the PNG leg is gated on STRUCTURE and on a ROUND TRIP instead -- signature, # IHDR geometry, and re-reading the file with libpng's own pngfix, which is on # this image and is a stricter reader than a byte compare is a test. # # THE PROBE IS THE LIBRARY HALF. EMCRAFT_TOOLS ships qrencode-staticdev, so # libqrencode.a is a deliverable in its own right and "the CLI runs" says # nothing about it. data/qrencode-probe is cross-linked against the very # /usr/lib/libqrencode.a on this rootfs, and its first case re-renders the # library's module matrix in qrenc.c's own ASCII form -- so it is compared # against THE SAME reference file the CLI is gated on. That single cmp is what # ties library, tool and x86-64 control together; two independent green legs # would not have said it. tmp=${TMPDIR:-/tmp} out=$tmp/bst-qr.$$ png=$tmp/bst-qr.$$.png ascii=$tmp/bst-qr-probe.$$.txt require qrencode qrencode/present # ------------------------------------------------------------- image shape --- # check_rc with `test` is an ash builtin and forks nothing, unlike check_out # whose command substitution does -- worth having on an arm where every exec is # a fresh wasm module instantiation. check_rc qrencode/tool 0 test -x /usr/bin/qrencode check_rc qrencode/lib-static 0 test -f /usr/lib/libqrencode.a check_rc qrencode/header 0 test -f /usr/include/qrencode.h check_rc qrencode/pkgconfig 0 test -f /usr/lib/pkgconfig/libqrencode.pc # No shared object on a distro that configures --disable-shared image-wide. One # appearing here would mean the static-only rule silently stopped holding. if [ -e /usr/lib/libqrencode.so ] || [ -e /usr/lib/libqrencode.so.4 ]; then bst_notok qrencode/static-only "a shared libqrencode shipped on a --disable-shared distro" else bst_ok qrencode/static-only fi # The tool is the ONLY thing upstream installs into bindir under BUILD_TOOLS # (Makefile.am: bin_PROGRAMS = qrencode). Asserted so a second binary appearing # is noticed rather than absorbed. n=$(ls /usr/bin/qrencode* 2>/dev/null | wc -l) check_eq qrencode/one-binary 1 "$n" check_out qrencode/version "qrencode version 4.1.1" qrencode --version # --------------------------------------------------- byte-exact references --- # Each of these is the whole output of one invocation compared against a stored # x86-64 reference with cmp. No digest, no substring: a single flipped module # anywhere in the symbol fails the case. qrencode -t ASCII -o "$out" 'BRINTOS-596' check qrencode/ascii cmp -s "$out" "$BST_DATA/qrencode-ref-ascii.txt" qrencode -t ASCII -o - 'BRINTOS-596' > "$out" 2>/dev/null check qrencode/ascii-stdout cmp -s "$out" "$BST_DATA/qrencode-ref-ascii.txt" qrencode -t ASCIIi -o "$out" 'BRINTOS-596' check qrencode/asciii cmp -s "$out" "$BST_DATA/qrencode-ref-asciii.txt" qrencode -t UTF8 -o "$out" 'BRINTOS-596' check qrencode/utf8 cmp -s "$out" "$BST_DATA/qrencode-ref-utf8.txt" qrencode -t EPS -o "$out" 'BRINTOS-596' check qrencode/eps cmp -s "$out" "$BST_DATA/qrencode-ref-eps.eps" qrencode -t SVG -o "$out" 'BRINTOS-596' check qrencode/svg cmp -s "$out" "$BST_DATA/qrencode-ref-svg.svg" qrencode -t XPM -o "$out" 'BRINTOS-596' check qrencode/xpm cmp -s "$out" "$BST_DATA/qrencode-ref-xpm.xpm" # A different payload AND a different error-correction level, so the reference # set is not four renderings of one encode. qrencode -t ASCII -l H -o "$out" 'https://brintos.io/' check qrencode/ascii-url-h cmp -s "$out" "$BST_DATA/qrencode-ref-url-h.txt" # Forced version, forced EC level, non-default margin -- three flags that each # change the geometry, pinned together. qrencode -t ASCII -v 5 -l Q -m 2 -o "$out" '1234567890' check qrencode/ascii-v5-q-m2 cmp -s "$out" "$BST_DATA/qrencode-ref-num-v5.txt" # -r reads the payload from a file, and -8 forces 8-bit mode. The reference was # produced from the same shipped input file, whose trailing newline is part of # the payload -- which is exactly why this differs from the string cases above. qrencode -t ASCII -8 -r "$BST_DATA/qrencode-in.txt" -o "$out" check qrencode/ascii-file-8bit cmp -s "$out" "$BST_DATA/qrencode-ref-file8.txt" # ...and it must NOT equal the plain string encode, or -r/-8 would be inert and # every case above would still pass. qrencode -t ASCII -o "$out" 'BRINTOS-596' if cmp -s "$out" "$BST_DATA/qrencode-ref-file8.txt"; then bst_notok qrencode/file8-differs "8-bit file encode equals the alphanumeric string encode" else bst_ok qrencode/file8-differs fi # ------------------------------------------------------------- geometry ----- # The ASCII form is (width + 2*margin) rows of (width + 2*margin)*2 columns. # Version 1 at the default margin 4 is 21 -> 29 rows of 58 columns. Derived from # the format rather than copied off a run, so a change in either is a red. qrencode -t ASCII -o "$out" 'BRINTOS-596' check_eq qrencode/ascii-rows 29 "$(wc -l < "$out" | tr -d ' ')" check_eq qrencode/ascii-cols 58 "$(awk 'NR==5{print length($0)}' "$out")" # margin 0 removes exactly the quiet zone: 21 rows of 42 columns. qrencode -t ASCII -m 0 -o "$out" 'BRINTOS-596' check_eq qrencode/margin0-rows 21 "$(wc -l < "$out" | tr -d ' ')" check_eq qrencode/margin0-cols 42 "$(awk 'NR==1{print length($0)}' "$out")" # width = 4*version + 17, checked at three forced versions. for v in 1 5 40; do w=$((4 * v + 17)) qrencode -t ASCII -m 0 -v "$v" -l L -o "$out" 'BRINTOS-596' check_eq "qrencode/width-v$v" "$w" "$(wc -l < "$out" | tr -d ' ')" done # -S/--structured splits an oversized payload across several symbols, each # carrying its own structured-append header. 220 'A's do not fit one version-1 # symbol, so the split is the point. Note the output naming: -S does NOT write # $out, it writes $out-01 .. $out-NN with the type suffix preserved, which is # why this case counts files rather than lines. A version of this case that # looked at $out alone found an empty file and would have read as a broken -S. sdir=$tmp/bst-qr-s.$$ mkdir -p "$sdir" awk 'BEGIN{ while (i++ < 220) printf "A" }' > "$sdir/in.txt" if qrencode -t ASCII -v 1 -l L -S -r "$sdir/in.txt" -o "$sdir/out.txt" 2>/dev/null; then nsym=$(ls "$sdir"/out-*.txt 2>/dev/null | wc -l | tr -d ' ') check_eq qrencode/structured-count 11 "$nsym" # every piece is a whole version-1 symbol, not a fragment bad=0 for f in "$sdir"/out-*.txt; do [ "$(wc -l < "$f" | tr -d ' ')" = "29" ] || bad=$((bad + 1)) done check_eq qrencode/structured-each-v1 0 "$bad" # the pieces must differ from each other -- identical symbols would mean the # structured-append header and the payload split were not applied at all if cmp -s "$sdir/out-01.txt" "$sdir/out-02.txt"; then bst_notok qrencode/structured-differ "structured symbols 01 and 02 are identical" else bst_ok qrencode/structured-differ fi else bst_notok qrencode/structured-count "qrencode -S failed on a payload too large for one symbol" bst_notok qrencode/structured-each-v1 "qrencode -S failed" bst_notok qrencode/structured-differ "qrencode -S failed" fi rm -rf "$sdir" # --------------------------------------------------------------- refusals --- # A tool that silently truncated an over-long payload would still pass every # byte-exact case above, because those payloads all fit. check_rc qrencode/reject-too-long 1 sh -c "awk 'BEGIN{while(i++<4096)printf \"A\"}' | qrencode -v 1 -l H -t ASCII -o /dev/null" check_rc qrencode/reject-bad-type 1 qrencode -t NOSUCHTYPE -o /dev/null 'x' check_rc qrencode/reject-bad-version 1 qrencode -v 41 -t ASCII -o /dev/null 'x' check_rc qrencode/reject-bad-level 1 qrencode -l Z -t ASCII -o /dev/null 'x' check_out qrencode/reject-msg "Input data too large" \ sh -c "awk 'BEGIN{while(i++<4096)printf \"A\"}' | qrencode -v 1 -l H -t ASCII -o /dev/null 2>&1; true" # ------------------------------------------------------------------- PNG ---- # Structure and round-trip, never bytes -- see the header. This is the leg that # exercises qrenc.c's setjmp(png_jmpbuf()), the only setjmp in the tarball. rm -f "$png" if qrencode -o "$png" 'BRINTOS-596' 2>"$out"; then bst_ok qrencode/png-write else bst_notok qrencode/png-write "$(_bst_1line "$(cat "$out")")" fi check_rc qrencode/png-nonempty 0 test -s "$png" # The 8-byte PNG signature, read without assuming any tool but od. sig=$(od -An -tx1 -N8 "$png" 2>/dev/null | tr -d ' \n') check_eq qrencode/png-signature 89504e470d0a1a0a "$sig" # IHDR width/height at offsets 16 and 20. Compared as the raw big-endian HEX # rather than decoded with `od --endian`, which is a GNU coreutils extension # busybox's od does not have and which of the two answers `od` here is a PATH # accident. Version 1 at margin 4 is 29 modules at the default 3 px per module, # so both are 87 = 0x00000057. w=$(od -An -tx1 -j16 -N4 "$png" 2>/dev/null | tr -d ' \n') h=$(od -An -tx1 -j20 -N4 "$png" 2>/dev/null | tr -d ' \n') check_eq qrencode/png-width 00000057 "$w" check_eq qrencode/png-height 00000057 "$h" # The round trip: libpng's own pngfix reads the file back. A PNG that passed the # structural cases above but carried a corrupt IDAT stream -- exactly what a # broken zlib or a broken setjmp path would produce -- fails here and nowhere # else. pngfix is in libpng-tools, already on this image. if [ -x /usr/bin/pngfix ]; then check qrencode/png-reads-back /usr/bin/pngfix -q "$png" else bst_notok qrencode/png-reads-back "no /usr/bin/pngfix on the image -- libpng-tools is expected here" fi # -s scales the module size, so the pixel geometry must scale with it. qrencode -s 1 -o "$png" 'BRINTOS-596' w=$(od -An -tx1 -j16 -N4 "$png" 2>/dev/null | tr -d ' \n') check_eq qrencode/png-size1-width 0000001d "$w" # PNG32 is a distinct colour type: 8-bit RGBA (6) rather than 1-bit palette (3). # Byte 25 of a PNG is IHDR's colour-type field. qrencode -t PNG32 -o "$png" 'BRINTOS-596' ct=$(od -An -tx1 -j25 -N1 "$png" 2>/dev/null | tr -d ' \n') check_eq qrencode/png32-colourtype 06 "$ct" if [ -x /usr/bin/pngfix ]; then check qrencode/png32-reads-back /usr/bin/pngfix -q "$png" fi # --------------------------------------------------------------- library ---- # The probe is installed 0644 and the rootfs is read-only at browser runtime, so # it is staged to a writable path before it can run. probe=$tmp/bst-qrencode-probe.$$ check_rc qrencode/probe-shipped 0 test -f "$BST_DATA/qrencode-probe" if cp "$BST_DATA/qrencode-probe" "$probe" 2>/dev/null && chmod +x "$probe" 2>/dev/null; then pout=$("$probe" "$ascii" 2>&1); prc=$? # Whole-probe gate first, then the individual values, so a probe that died # early is one red rather than twenty ambiguous ones. case "$pout" in *"QR-END fails=0"*) bst_ok qrencode/probe-all ;; *) bst_notok qrencode/probe-all "rc=$prc $(_bst_1line "$pout")" ;; esac qrv() { printf '%s\n' "$pout" | awk -v k="$1" '$1=="QR" && $2==k {print $3; exit}'; } check_eq qrencode/probe-api 4.1.1 "$(qrv api)" check_eq qrencode/probe-ver 1 "$(qrv enc-default-ver)" check_eq qrencode/probe-width 21 "$(qrv enc-default-width)" check_eq qrencode/probe-dark 228 "$(qrv enc-default-dark)" check_eq qrencode/probe-fnv 3554849303 "$(qrv enc-default-fnv)" check_eq qrencode/probe-m-fnv 3980009097 "$(qrv enc-m-fnv)" check_eq qrencode/probe-q-fnv 1790967349 "$(qrv enc-q-fnv)" check_eq qrencode/probe-h-width 25 "$(qrv enc-h-width)" check_eq qrencode/probe-h-fnv 776997359 "$(qrv enc-h-fnv)" check_eq qrencode/probe-v5-width 37 "$(qrv enc-v5-width)" check_eq qrencode/probe-v5-fnv 2260100155 "$(qrv enc-v5-fnv)" check_eq qrencode/probe-data-fnv 3083427871 "$(qrv enc-data-fnv)" check_eq qrencode/probe-mqr-width 11 "$(qrv enc-mqr-width)" check_eq qrencode/probe-mqr-fnv 2467060793 "$(qrv enc-mqr-fnv)" check_eq qrencode/probe-input-fnv 3554849303 "$(qrv enc-input-fnv)" check_eq qrencode/probe-overflow 1 "$(qrv overflow-refused)" check_eq qrencode/probe-erange 1 "$(qrv overflow-errno-erange)" check_eq qrencode/probe-check-an 0 "$(qrv check-an-ok)" check_eq qrencode/probe-check-num 1 "$(qrv check-num-rejects-alpha)" # THE TIE. The library's own module matrix, rendered by the probe in qrenc.c's # ASCII form, against the same file the CLI is gated on above. check qrencode/probe-matches-cli cmp -s "$ascii" "$BST_DATA/qrencode-ref-ascii.txt" else bst_notok qrencode/probe-stage "cannot stage $BST_DATA/qrencode-probe" fi rm -f "$out" "$png" "$ascii" "$probe"