# BST-TIMEOUT: 300 # measured 89 s for 79 cases (hz1, 2026-08-18); ~3x headroom # # BRINTOS-980 -- Little CMS 2.16 on wasm32-hwjs: liblcms2.a and the five # utilities the package installs. # # THE COLOUR GATE IS A REFERENCE TABLE, NOT A ROUND TRIP, and that is the whole # point of this suite. A round trip sRGB -> Lab -> sRGB passes just as happily # when BOTH directions are wrong by the same amount -- a broken matrix, a # mis-built tone curve, a float that lost precision in the wasm32 lowering all # cancel out of it. So the six sRGB->Lab rows below carry ABSOLUTE Lab values, # measured on x86-64 from this same 2.16 tarball (native build, --without-jpeg # --without-tiff --disable-shared, 2026-08-18), and this arm has to reproduce # them to 0.01. The round trip is kept as its own separate case, where a failure # means something different: the inverse transform, not the forward one. # # THE TOLERANCE IS 0.01 rather than exact-match because the numbers are printed # at %.4f from a double pipeline; 0.01 is two orders of magnitude wider than any # formatting difference and two orders of magnitude tighter than any of the # errors that would matter (a wrong white point moves a* by whole units, a # missing gamma curve moves L* by tens). The values themselves are not arbitrary # either: pure red, green and blue put the three matrix columns under test one # at a time, mid-grey exercises the tone curve away from its endpoints, and # black and white pin the two ends the curve is most likely to get right by # accident. # # THE DEVICE LINK IS COMPARED AGAINST THE LIVE TRANSFORM, not against a stored # digest. linkicc collapses sRGB->Lab into a CLUT, so its output is quantised # and CANNOT be byte- or value-identical to the direct path -- pinning its bytes # would gate this port on the exact grid size lcms happens to choose. What # matters is that the collapsed pipeline still answers the same question, so the # gate is "the link agrees with the direct transform to 0.01" plus the two ICC # header fields that say it really is a device link. # # tificc IS DRIVEN OVER data/tiff-fixture.tif, WHICH BELONGS TO 57-tiff.test. # That is deliberate re-use, not a shortcut: it is a 64x64 RGB8 baseline TIFF # built by hand from the TIFF spec rather than written by libtiff, it is already # in this package's data/ directory, and 57-tiff.test documents exactly what is # in it. Generating a second fixture here would add a second thing to keep # correct for no extra coverage. If that file ever goes, this suite says so in # its own case rather than failing obscurely three cases later. # # THREE OUTPUTS ARE PINNED AS CROSS-TOOLCHAIN IDENTITIES, and what is digested # in each case is the whole point. All three references were produced on x86-64 # from this same tarball (hz1, 2026-08-18): # # * psicc's CSA is digested WHOLE. It carries no timestamp, and its bytes # reproduce across builds, across hours and on wasm32. # * psicc's CRD is digested with its "% Created:" line REMOVED, and that line # is asserted to exist so the removal cannot silently become a no-op. What # the digest then covers is the entire 220 kB sampled CLUT -- the strongest # claim in this suite, because it is 3,700 floating-point values computed # through the interpolator and printed, agreeing to the last digit between # x86-64 and wasm32. # * The device link is pinned by SIZE only: bytes 24-35 of an ICC header are # the creation date, in binary and in the middle of the file, so there is no # line to strip. # # THE TIMESTAMPS WERE FOUND THE HARD WAY AND THE METHOD IS WORTH RECORDING. The # CRD was first pinned by whole-file MD5, on the strength of two consecutive # native runs agreeing -- which proved nothing, because both fell inside the # same second. The target then reported a third digest and the case went red. # Two native builds fifteen minutes apart, one of them forced through the # non-SSE2 path to test the obvious hypothesis, differ in exactly one line: the # timestamp. The SSE2 theory was wrong and the whole CLUT was identical all # along. A reproducibility check that cannot see a clock is not one. # # UPSTREAM SPELLS THE TIFF TOOL tificc, WITH ONE f. The ticket says tifficc. # Both spellings are pinned below -- the one that must exist and the one that # must not -- so a future rename is a visible failure rather than a silent one. . "$BST_LIB" require transicc lcms/present tmp=${TMPDIR:-/tmp}/bst-lcms.$$ rm -rf "$tmp"; mkdir -p "$tmp" || { bst_notok lcms/tmpdir "cannot mkdir $tmp"; exit 0; } OBS="" # near -- absolute-difference assert for the # %.4f floats every utility here prints. A non-numeric actual (an empty line # from a tool that died) fails rather than comparing as zero. near() { r=$(awk -v a="$2" -v b="$3" -v t="$4" 'BEGIN{ if (b == "") { print "no"; exit } d = a - b; if (d < 0) d = -d; print (d <= t) ? "ok" : "no" }' 2>/dev/null) if [ "$r" = ok ]; then bst_ok "$1"; else bst_notok "$1" "want $2 +/-$4, got '$3'"; fi } field() { echo "$2" | awk -v n="$1" '{print $n}'; } # ---- 1. the shipped shape ------------------------------------------------ for t in transicc linkicc tificc jpgicc psicc; do check lcms/tool-$t command -v $t done if command -v tifficc >/dev/null 2>&1; then bst_notok lcms/absent-tifficc "tifficc is present; upstream builds tificc, with one f" else bst_ok lcms/absent-tifficc fi check lcms/archive test -f /usr/lib/liblcms2.a check lcms/header test -f /usr/include/lcms2.h check lcms/header-plugin test -f /usr/include/lcms2_plugin.h check lcms/pkgconfig test -f /usr/lib/pkgconfig/lcms2.pc check_out lcms/pkgconfig-version "2.16" cat /usr/lib/pkgconfig/lcms2.pc # Shape, not absence: this arm converts to shared libraries per recipe # (BRINTOS-1740), so a .so here is a conversion, not a policy breach. bst_lib_shape lcms/static-only "/usr/lib/liblcms2.so*" # ---- 2. versions ---------------------------------------------------------- # Every utility prints its banner to stderr and exits 0 with no arguments. tban=$(transicc &1 | head -1) case "$tban" in *"LittleCMS 2.16"*) bst_ok lcms/version ;; *) bst_notok lcms/version "$(_bst_1line "$tban")" ;; esac case "$tban" in *"ColorSpace conversion calculator - 5.1"*) bst_ok lcms/banner-transicc ;; *) bst_notok lcms/banner-transicc "$(_bst_1line "$tban")" ;; esac check_out lcms/banner-linkicc "device link generator - v3.2" sh -c 'linkicc 2>&1 | head -1' check_out lcms/banner-psicc "PostScript generator - v2.1" sh -c 'psicc 2>&1 | head -1' check_out lcms/banner-tificc "profile applier for TIFF - v8.0" sh -c 'tificc 2>&1 | head -1' check_out lcms/banner-jpgicc "LittleCMS 2.16" sh -c 'jpgicc 2>&1 | head -1' check_rc lcms/transicc-usage-rc 0 sh -c 'transicc /dev/null 2>&1' # ---- 3. the colour gate: sRGB -> Lab against x86-64 reference values ------- for spec in "red:255:0:0:54.2896:80.8144:69.8897" \ "green:0:255:0:87.8194:-79.2749:80.9927" \ "blue:0:0:255:29.5659:68.2862:-112.0329" \ "grey:128:128:128:53.5850:0.0000:0.0000" \ "black:0:0:0:0.0000:0.0000:0.0000" \ "white:255:255:255:100.0000:0.0000:0.0000" ; do nm=$(echo "$spec" | cut -d: -f1) r=$(echo "$spec" | cut -d: -f2); g=$(echo "$spec" | cut -d: -f3); b=$(echo "$spec" | cut -d: -f4) wL=$(echo "$spec" | cut -d: -f5); wa=$(echo "$spec" | cut -d: -f6); wb=$(echo "$spec" | cut -d: -f7) out=$(printf '%s\n%s\n%s\n' "$r" "$g" "$b" | transicc -n -i '*sRGB' -o '*Lab' 2>/dev/null | head -1) OBS="$OBS $nm=[$(echo $out)]" near lcms/srgb2lab-$nm-L "$wL" "$(field 1 "$out")" 0.01 near lcms/srgb2lab-$nm-a "$wa" "$(field 2 "$out")" 0.01 near lcms/srgb2lab-$nm-b "$wb" "$(field 3 "$out")" 0.01 done # The white point the sRGB profile actually carries, in XYZ*100. A wrong or # missing chromatic adaptation moves these before it moves anything above. xyz=$(printf '255\n255\n255\n' | transicc -n -i '*sRGB' -o '*XYZ' 2>/dev/null | head -1) OBS="$OBS white-xyz=[$(echo $xyz)]" near lcms/srgb2xyz-white-X 96.4200 "$(field 1 "$xyz")" 0.01 near lcms/srgb2xyz-white-Y 100.0000 "$(field 2 "$xyz")" 0.01 near lcms/srgb2xyz-white-Z 82.4900 "$(field 3 "$xyz")" 0.01 # ---- 4. the inverse transform, as its own claim --------------------------- rt=$(printf '54.2896\n80.8144\n69.8897\n' | transicc -n -i '*Lab' -o '*sRGB' 2>/dev/null | head -1) OBS="$OBS lab2srgb-red=[$(echo $rt)]" near lcms/lab2srgb-red-R 255.0000 "$(field 1 "$rt")" 0.05 near lcms/lab2srgb-red-G 0.0000 "$(field 2 "$rt")" 0.05 near lcms/lab2srgb-red-B 0.0000 "$(field 3 "$rt")" 0.05 wt=$(printf '100\n0\n0\n' | transicc -n -i '*Lab' -o '*sRGB' 2>/dev/null | head -1) near lcms/lab2srgb-white-R 255.0000 "$(field 1 "$wt")" 0.05 near lcms/lab2srgb-white-G 255.0000 "$(field 2 "$wt")" 0.05 near lcms/lab2srgb-white-B 255.0000 "$(field 3 "$wt")" 0.05 # ---- 5. linkicc: a real device link, and it answers the same question ------ link="$tmp/srgb-to-lab.icc" if linkicc -o "$link" '*sRGB' '*Lab' >"$tmp/link.out" 2>&1; then bst_ok lcms/linkicc-rc else bst_notok lcms/linkicc-rc "$(_bst_1line "$(cat "$tmp/link.out")")"; fi check lcms/linkicc-file test -s "$link" if [ -s "$link" ]; then lsz=$(wc -c < "$link" | tr -d ' ') OBS="$OBS link-bytes=$lsz" # Size, not digest -- the ICC header's creation date makes the bytes differ # between any two runs. See the header note. check_eq lcms/link-bytes-matches-x86 216436 "$lsz" # ICC header: device class at byte 12, the 'acsp' signature at byte 36. check_eq lcms/link-class $(printf 'link') "$(dd if="$link" bs=1 skip=12 count=4 2>/dev/null)" check_eq lcms/link-acsp $(printf 'acsp') "$(dd if="$link" bs=1 skip=36 count=4 2>/dev/null)" lk=$(printf '255\n0\n0\n' | transicc -n -l "$link" 2>/dev/null | head -1) OBS="$OBS link-red=[$(echo $lk)]" near lcms/link-agrees-L 54.2896 "$(field 1 "$lk")" 0.01 near lcms/link-agrees-a 80.8144 "$(field 2 "$lk")" 0.01 near lcms/link-agrees-b 69.8897 "$(field 3 "$lk")" 0.01 fi # ---- 6. tificc over the 57-tiff fixture ----------------------------------- FIX="$BST_DATA/tiff-fixture.tif" if [ -f "$FIX" ]; then bst_ok lcms/tiff-fixture # 6a. a null transform: sRGB in, sRGB out. Not a byte identity -- tificc # rewrites the file through libtiff and embeds nothing -- but the geometry # must survive and the pixels must not move far. if tificc -i '*sRGB' -o '*sRGB' "$FIX" "$tmp/null.tif" 2>"$tmp/err"; then bst_ok lcms/tificc-null-rc else bst_notok lcms/tificc-null-rc "$(_bst_1line "$(cat "$tmp/err")")"; fi if [ -f "$tmp/null.tif" ]; then nsz=$(wc -c < "$tmp/null.tif" | tr -d ' ') OBS="$OBS tificc-null=$nsz/$(md5sum "$tmp/null.tif" 2>/dev/null | cut -d' ' -f1)" # 64*64*3 payload plus libtiff's header and directory. check_eq lcms/tificc-null-bytes 12504 "$nsz" ni=$(tiffinfo "$tmp/null.tif" 2>&1) case "$ni" in *"Image Width: 64"*) bst_ok lcms/tificc-null-width ;; *) bst_notok lcms/tificc-null-width "$(_bst_1line "$ni")" ;; esac case "$ni" in *"Image Length: 64"*) bst_ok lcms/tificc-null-length ;; *) bst_notok lcms/tificc-null-length "geometry not preserved" ;; esac case "$ni" in *"Samples/Pixel: 3"*) bst_ok lcms/tificc-null-spp ;; *) bst_notok lcms/tificc-null-spp "not 3 samples/pixel" ;; esac case "$ni" in *"RGB color"*) bst_ok lcms/tificc-null-photometric ;; *) bst_notok lcms/tificc-null-photometric "not RGB" ;; esac fi # 6b. a real transform: sRGB -> Lab. The output must be a Lab TIFF, and it # must NOT be the same file as the null transform above -- an engine that # quietly ignored -o would produce an identical file and pass 6a. if tificc -i '*sRGB' -o '*Lab' "$FIX" "$tmp/lab.tif" 2>"$tmp/err"; then bst_ok lcms/tificc-lab-rc else bst_notok lcms/tificc-lab-rc "$(_bst_1line "$(cat "$tmp/err")")"; fi if [ -f "$tmp/lab.tif" ]; then lsz2=$(wc -c < "$tmp/lab.tif" | tr -d ' ') OBS="$OBS tificc-lab=$lsz2/$(md5sum "$tmp/lab.tif" 2>/dev/null | cut -d' ' -f1)" check_eq lcms/tificc-lab-bytes 12504 "$lsz2" li=$(tiffinfo "$tmp/lab.tif" 2>&1) case "$li" in *"CIE L*a*b*"*) bst_ok lcms/tificc-lab-photometric ;; *) bst_notok lcms/tificc-lab-photometric "$(_bst_1line "$li")" ;; esac if cmp -s "$tmp/lab.tif" "$tmp/null.tif"; then bst_notok lcms/tificc-lab-differs "the Lab output equals the sRGB one -- -o was ignored" else bst_ok lcms/tificc-lab-differs; fi fi # 6c. through the device link built above. if [ -s "$link" ] && tificc -l "$link" "$FIX" "$tmp/vialink.tif" 2>"$tmp/err"; then bst_ok lcms/tificc-link-rc OBS="$OBS tificc-link=$(wc -c < "$tmp/vialink.tif" | tr -d ' ')" else bst_notok lcms/tificc-link-rc "$(_bst_1line "$(cat "$tmp/err")")"; fi else bst_notok lcms/tiff-fixture "$BST_DATA/tiff-fixture.tif is absent (it belongs to 57-tiff.test)" fi # ---- 7. psicc: CSA from an input profile, CRD from an output one ----------- if psicc -i '*sRGB' "$tmp/csa.ps" 2>"$tmp/err"; then bst_ok lcms/psicc-csa-rc else bst_notok lcms/psicc-csa-rc "$(_bst_1line "$(cat "$tmp/err")")"; fi check lcms/psicc-csa-file test -s "$tmp/csa.ps" check_out lcms/psicc-csa-body "/CIEBasedABC" cat "$tmp/csa.ps" if psicc -o '*sRGB' "$tmp/crd.ps" 2>"$tmp/err"; then bst_ok lcms/psicc-crd-rc else bst_notok lcms/psicc-crd-rc "$(_bst_1line "$(cat "$tmp/err")")"; fi check_out lcms/psicc-crd-header "%!PS-Adobe-3.0" head -1 "$tmp/crd.ps" check_out lcms/psicc-crd-body "Color Rendering Dictionary" cat "$tmp/crd.ps" csa_md5=$(md5sum "$tmp/csa.ps" 2>/dev/null | cut -d' ' -f1) crd_md5=$(md5sum "$tmp/crd.ps" 2>/dev/null | cut -d' ' -f1) crd_body=$(grep -v '^% Created: ' "$tmp/crd.ps" 2>/dev/null | md5sum | cut -d' ' -f1) OBS="$OBS csa=$(wc -c < "$tmp/csa.ps" 2>/dev/null | tr -d ' ')/$csa_md5" OBS="$OBS crd=$(wc -c < "$tmp/crd.ps" 2>/dev/null | tr -d ' ')/$crd_md5 crd-body=$crd_body" # The CSA whole: no timestamp anywhere in it. check_eq lcms/psicc-csa-matches-x86 e06af1442519245165a2c7381358dd04 "$csa_md5" # The CRD without its clock line. The line is asserted separately: if upstream # ever drops it, the grep above becomes a no-op and this digest would then be # comparing a different thing under the same name. check_out lcms/psicc-crd-has-created "% Created: " cat "$tmp/crd.ps" check_eq lcms/psicc-crd-body-matches-x86 4f5a78926d1f2ec7ece65bfb43f4f362 "$crd_body" # ---- 8. jpgicc over a JPEG this suite generates ---------------------------- # An 8x8 P6 with a horizontal red ramp, a vertical green ramp and a constant # blue -- three channels that move independently, so a transform that dropped # one is visible. cjpeg/djpeg are libjpeg-turbo's, already on this image. awk 'BEGIN{ printf "P6\n8 8\n255\n"; for (i = 0; i < 64; i++) printf "%c%c%c", (i % 8) * 32, int(i / 8) * 32, 128 }' \ > "$tmp/in.ppm" 2>/dev/null check lcms/jpg-ppm test -s "$tmp/in.ppm" if cjpeg -quality 90 -outfile "$tmp/in.jpg" "$tmp/in.ppm" 2>"$tmp/err"; then bst_ok lcms/jpg-fixture else bst_notok lcms/jpg-fixture "$(_bst_1line "$(cat "$tmp/err")")"; fi if [ -s "$tmp/in.jpg" ]; then if jpgicc -i '*sRGB' -o '*sRGB' "$tmp/in.jpg" "$tmp/out.jpg" 2>"$tmp/err"; then bst_ok lcms/jpgicc-rc else bst_notok lcms/jpgicc-rc "$(_bst_1line "$(cat "$tmp/err")")"; fi if [ -s "$tmp/out.jpg" ]; then OBS="$OBS jpgicc=$(wc -c < "$tmp/out.jpg" | tr -d ' ')" # SOI marker: a JPEG and not a truncated write. check_eq lcms/jpgicc-soi "ffd8" "$(dd if="$tmp/out.jpg" bs=1 count=2 2>/dev/null | od -An -tx1 | tr -d ' \n')" if djpeg -outfile "$tmp/back.ppm" "$tmp/out.jpg" 2>"$tmp/err"; then bst_ok lcms/jpgicc-decodes else bst_notok lcms/jpgicc-decodes "$(_bst_1line "$(cat "$tmp/err")")"; fi check_out lcms/jpgicc-geometry "8 8" head -2 "$tmp/back.ppm" fi fi # ---- 9. negative cases ---------------------------------------------------- # A profile that is not there must be refused, not silently replaced by sRGB. out=$(printf '0\n0\n0\n' | transicc -n -i "$tmp/does-not-exist.icc" 2>&1); rc=$? if [ $rc -ne 0 ]; then bst_ok lcms/missing-profile-rc else bst_notok lcms/missing-profile-rc "transicc accepted a missing input profile"; fi # A file that is not a profile at all. printf 'not an icc profile\n' > "$tmp/garbage.icc" out=$(printf '0\n0\n0\n' | transicc -n -i "$tmp/garbage.icc" 2>&1); rc=$? if [ $rc -ne 0 ]; then bst_ok lcms/garbage-profile-rc else bst_notok lcms/garbage-profile-rc "transicc accepted a non-profile"; fi # The shipped truncated profile: 158 bytes, smaller than an ICC header. if [ -f /usr/libexec/lcms-tests/toosmall.icc ]; then out=$(printf '0\n0\n0\n' | transicc -n -i /usr/libexec/lcms-tests/toosmall.icc 2>&1); rc=$? if [ $rc -ne 0 ]; then bst_ok lcms/toosmall-refused else bst_notok lcms/toosmall-refused "a 158-byte file was accepted as a profile"; fi fi printf 'not a tiff\n' > "$tmp/garbage.tif" check_rc lcms/tificc-garbage-rc 1 tificc "$tmp/garbage.tif" "$tmp/nope.tif" printf '==LCMS-OBSERVED%s==\n' "$OBS" rm -rf "$tmp"