. "$BST_LIB" # libestr is Adiscon's "essentials for string handling" -- the string library # rsyslog is built on, and the sibling of libfastjson (81-libfastjson.test), which # is rsyslog's JSON library. It is tiny: two translation units, 763 lines, and one # installed header. There is no CLI and no consumer on this image (rsyslog is not # ported), so the whole payload is /usr/include/libestr.h (-dev), # /usr/lib/pkgconfig/libestr.pc (-dev) and /usr/lib/libestr.a (-staticdev). # # --------------------------------------------------------------------------- # WHY THIS SUITE RUNS A PROBE INSTEAD OF STOPPING AT PAYLOAD SHAPE # # BRINTOS-217 is why: oniguruma COMPILED and PACKAGED perfectly on this arm and # then returned silently wrong offsets to every caller, so a payload-only suite # would have gone green on the exact defect the port existed to find. # # libestr's exposure is narrower than libfastjson's but sharper. Its whole design # is ONE POINTER-ARITHMETIC TRICK: es_str_t stores only two es_size_t counters and # keeps the character data immediately AFTER the struct, reached by # `(unsigned char*)s + sizeof(es_str_t)`. There is no data pointer to follow. So # every accessor depends on the struct's exact size, and every append depends on # es_extendBuf's realloc MOVING THE WHOLE OBJECT -- which is why es_addChar and # es_addBuf take es_str_t ** rather than es_str_t *. That is the shape to measure # on a coroutinized arm with a relocating allocator, and data/estr-probe measures # it: cross-linked with wasmcc-glibc against THE VERY ARCHIVE THAT SHIPS # (/usr/lib/libestr.a), staged to $TMPDIR and run here. Its source is # src/estr_probe.c. # # The groups written for platform reasons rather than for libestr's, and the ones # to read first on a red run: # # sizeof-* bufaddr-* the flexible-array-by-pointer-arithmetic layout. es_size_t # is unsigned int, so es_str_t is 8 bytes and the data # offset is 8 on x86_64 and wasm32 alike. If these two # diverge, every other case is meaningless. # growth-* addbuf-* the realloc path, asserted as the EXACT lenBuf sequence # extendbuf-* the policy produces (1,2,4,4,8,8,8,8,16,16 from a zero # hint). A relocating realloc that failed to carry the # counters, or an es_str_t ** the coroutine rewrite got # wrong, changes these numbers rather than crashing. # scale-* the same path 1000 appends deep (lenBuf 1024), plus a scan # across all 1000 bytes and a 1000-byte in-place tolower. # num-* s2n-* 64-bit long long formatting and parsing -- i64 on wasm32 -- # roundtrip-* through the digit loops, and libc isdigit/isxdigit/tolower. # unesc-* in-place rewriting with two cursors (iSrc/iDst) over one # buffer, the loop-carried-cursor shape hwjs-cc#24 broke for # oniguruma. # # EVERY EXPECTED VALUE IS THE VALUE A NATIVE x86_64 BUILD OF THE IDENTICAL RELEASE # PRODUCES. The probe compares each value internally and prints it on both the OK # and the FAIL path, so a target run can be diffed against the native control line # by line rather than compared by tally. # # FOUR OF THE ROWS PIN UPSTREAM DEFECTS AS MEASURED VALUES, and are marked # -defect. All four reproduce identically on native x86_64, so none is this arm's # and none is worked around -- pinning what the code really does means a # platform-side change shows up as a CHANGED VALUE instead of hiding behind a # relaxed check. They are: the two es_str2num hex bugs (s2n-hex-*), the dead # length branches in es_strncasecmp (strncasecmp-*-defect), the \x fall-through in # doUnescape (unesc-hex-*-defect), and the es_size_t underflow in # es_newStrFromSubStr for an empty source (substr-empty-source-defect). # # The suite uses builtin `case` matching over variables read ONCE, never # `check_out ... cat` per case: on this runtime the forking is the cost rather # than the work, and the cjson suite next door was killed by the runner's # per-suite bound at 156 cases written the other way. This suite is 179 cases and # forks twice (two greps over the archive), once for the probe, and once more for # the on-target byte-diff against the native control. # --------------------------------------------------------------------------- hdr=/usr/include/libestr.h lib=/usr/lib/libestr.a pc=/usr/lib/pkgconfig/libestr.pc nl=' ' # hasfile -- ok iff is a non-empty regular file. # has -- ok iff contains the literal . # hasline -- ok iff carries as a WHOLE line. # Substring matching would let es_strncasecmp # satisfy a lookup for es_strncmp. # noline -- ok iff does NOT carry as a line. hasfile() { if [ -s "$2" ]; then bst_ok "$1"; else bst_notok "$1" "absent or empty: $2"; fi; } has() { case "$3" in *"$2"*) bst_ok "$1" ;; *) bst_notok "$1" "not found: $2" ;; esac; } hasline() { case "$nl$3$nl" in *"$nl$2$nl"*) bst_ok "$1" ;; *) bst_notok "$1" "not in archive: $2" ;; esac; } noline() { case "$nl$3$nl" in *"$nl$2$nl"*) bst_notok "$1" "unexpectedly in archive: $2" ;; *) bst_ok "$1" ;; esac; } # An image built without libestr is a coverage gap, not a failure: skip once and # stop rather than reporting 179 reds for an absent package. if [ ! -s "$lib" ]; then bst_skip libestr/present "libestr.a not installed on this image" exit 0 fi bst_ok libestr/present # --- the shipped payload ----------------------------------------------------- # Upstream installs exactly one header (include/Makefile.am's estrinc_HEADERS), # straight into ${includedir} rather than a per-package subdirectory -- unlike # libfastjson. So there is no include dir to assert, only the one file. hasfile libestr/header "$hdr" hasfile libestr/archive "$lib" hasfile libestr/pkgconfig "$pc" # There is deliberately no libestr/no-shared-object case: shared objects are # refused distro-wide on this arm, so an absent libestr.so is the distro's # property and not this port's, and every sibling library would assert it too. pctext=$(cat "$pc" 2>/dev/null) has libestr/pc-name "Name: libestr" "$pctext" has libestr/pc-version "Version: 0.1.11" "$pctext" has libestr/pc-libs "-lestr" "$pctext" has libestr/pc-libdir "libdir=/usr/lib" "$pctext" has libestr/pc-includedir "includedir=/usr/include" "$pctext" # --- what the installed header must carry ------------------------------------ # es_str_t's two counters and the pointer-arithmetic accessor are the library's # whole ABI: the character data has no pointer of its own, it simply follows the # struct. A header that stopped declaring es_size_t as unsigned int, or moved # lenStr off the front, would change the layout every case below depends on -- # and lenStr is required by upstream's own comment to be the FIRST member, # because the inline es_strlen reads it through the struct. hdrtext=$(cat "$hdr" 2>/dev/null) has libestr/hdr-size-type "typedef unsigned int es_size_t" "$hdrtext" has libestr/hdr-lenstr-first "es_size_t lenStr" "$hdrtext" has libestr/hdr-lenbuf "es_size_t lenBuf" "$hdrtext" has libestr/hdr-bufaddr "sizeof(es_str_t)" "$hdrtext" has libestr/hdr-version-decl "char *es_version(void)" "$hdrtext" has libestr/hdr-constcmp "define es_strconstcmp" "$hdrtext" has libestr/hdr-addconstcstr "define es_addBufConstcstr" "$hdrtext" # --- symbol completeness, derived from the INSTALLED header ------------------- # Every es_* function the installed header declares must be a real symbol of the # installed archive, so a partial compile, a visibility mistake or a # header/library skew all fail this. The list is DERIVED from the header rather # than written here, so it survives a version bump. # # Nine of the header's es_* names have no archive symbol BY CONSTRUCTION, and the # derivation has to drop them: seven are `static inline` (so they are compiled # into each caller, not exported) and two are function-like macros. Dropping the # macros is automatic -- the preprocessor-line filter below removes their # #define lines -- so only the seven inlines are named. That exclusion list is # then TESTED rather than trusted: inlines-absent asserts each of the seven is # genuinely NOT in the archive, so a future release that turns one into a real # function fails a case instead of silently shrinking the checked set. arcsyms=$(grep -ao 'es_[A-Za-z0-9_]*' "$lib" 2>/dev/null | sort -u) hdrsyms=$(grep -v '^[[:space:]]*#' "$hdr" 2>/dev/null \ | grep -oE '\bes_[A-Za-z0-9_]*\(' | sed 's/($//;s/(//' | sort -u) inlines="es_getBufAddr es_strlen es_emptyStr es_strdup es_strcmp es_strcasecmp es_addStr" nsym=0 nmiss=0 missing="" for f in $hdrsyms; do skip=0 for i in $inlines; do [ "$f" = "$i" ] && skip=1 done [ "$skip" -eq 1 ] && continue nsym=$((nsym + 1)) case "$nl$arcsyms$nl" in *"$nl$f$nl"*) ;; *) nmiss=$((nmiss + 1)); missing="$missing $f" ;; esac done if [ "$nmiss" -eq 0 ]; then bst_ok libestr/symbols-complete else bst_notok libestr/symbols-complete "$nmiss missing:$missing" fi check_eq libestr/symbols-count 20 "$nsym" # the exclusion list, tested rather than assumed for i in $inlines; do noline "libestr/inline-absent-$i" "$i" "$arcsyms" done # A spot-check that the derivation above is really reading the archive and not an # empty string that would make the loop vacuously green. hasline libestr/sym-newstr-from-number es_newStrFromNumber "$arcsyms" hasline libestr/sym-newstr-from-substr es_newStrFromSubStr "$arcsyms" hasline libestr/sym-extendbuf es_extendBuf "$arcsyms" hasline libestr/sym-strcasebufcmp es_strcasebufcmp "$arcsyms" hasline libestr/sym-case-contains es_strCaseContains "$arcsyms" hasline libestr/sym-unescape es_unescapeStr "$arcsyms" # --- the library RUNNING, via the cross-linked probe ------------------------- probe=${TMPDIR:-/tmp}/bst-estr-probe.$$ if cp "$BST_DATA/estr-probe" "$probe" 2>/dev/null && chmod +x "$probe" 2>/dev/null; then # The probe is run ONCE and its exit status kept, rather than re-run for the rc # case: on this runtime a multi-MB wasm module's start-up dominates the whole # suite's cost. out=$("$probe" 2>&1); prc=$? # identity and layout. es_size_t is unsigned int on both arms, so es_str_t is # 8 bytes on x86_64 and wasm32 alike and es_getBufAddr's pointer arithmetic # (the string data lives immediately AFTER the struct) lands on the same offset. has libestr/run-sizeof-es_size_t "ES-sizeof-es_size_t OK" "$out" has libestr/run-sizeof-es_str_t "ES-sizeof-es_str_t OK" "$out" has libestr/run-bufaddr-offset "ES-bufaddr-offset OK" "$out" has libestr/run-version "ES-version OK" "$out" # es_newStr rounds its length hint UP to a multiple of 8 -- an allocation # policy, so the resulting lenBuf is an exact, platform-independent value. has libestr/run-newstr-0-lenbuf "ES-newstr-0-lenbuf OK" "$out" has libestr/run-newstr-0-lenstr "ES-newstr-0-lenstr OK" "$out" has libestr/run-newstr-1-lenbuf "ES-newstr-1-lenbuf OK" "$out" has libestr/run-newstr-7-lenbuf "ES-newstr-7-lenbuf OK" "$out" has libestr/run-newstr-8-lenbuf "ES-newstr-8-lenbuf OK" "$out" has libestr/run-newstr-9-lenbuf "ES-newstr-9-lenbuf OK" "$out" has libestr/run-newstr-16-lenbuf "ES-newstr-16-lenbuf OK" "$out" has libestr/run-newstr-17-lenbuf "ES-newstr-17-lenbuf OK" "$out" has libestr/run-newstr-64-lenbuf "ES-newstr-64-lenbuf OK" "$out" # constructors, and es_emptyStr's contract: rewind lenStr, keep the buffer. has libestr/run-fromcstr-len "ES-fromcstr-len OK" "$out" has libestr/run-fromcstr-content "ES-fromcstr-content OK" "$out" has libestr/run-fromcstr-lenbuf "ES-fromcstr-lenbuf OK" "$out" has libestr/run-emptystr-lenstr "ES-emptystr-lenstr OK" "$out" has libestr/run-emptystr-lenbuf-kept "ES-emptystr-lenbuf-kept OK" "$out" has libestr/run-fromcstr-empty-len "ES-fromcstr-empty-len OK" "$out" has libestr/run-frombuf-len "ES-frombuf-len OK" "$out" has libestr/run-frombuf-content "ES-frombuf-content OK" "$out" # es_newStrFromNumber -- long long formatting, i64 on wasm32. LLONG_MIN is # special-cased upstream because it cannot be negated. has libestr/run-num-0 "ES-num-0 OK" "$out" has libestr/run-num-1 "ES-num-1 OK" "$out" has libestr/run-num--1 "ES-num--1 OK" "$out" has libestr/run-num-42 "ES-num-42 OK" "$out" has libestr/run-num--42 "ES-num--42 OK" "$out" has libestr/run-num-1000000 "ES-num-1000000 OK" "$out" has libestr/run-num-9223372036854775807 "ES-num-9223372036854775807 OK" "$out" has libestr/run-num--9223372036854775807 "ES-num--9223372036854775807 OK" "$out" has libestr/run-num-llong-min "ES-num-llong-min OK" "$out" # es_str2num -- decimal, octal, hex and the bSuccess flag. THE FOUR HEX ROWS # CARRY TWO UPSTREAM DEFECTS and are pinned as measured values, not as what # the documentation promises: es_str2num_hex does `num*16 + tolower(c)-'a'` # with no `+ 10`, so every hex LETTER is 10 too small (0x1f -> 21, 0xff -> # 85), and it bounds its loop by lenStr instead of lenStr-2 after skipping # "0x", so it also reads two bytes past the content and reports bSuccess=0 # for input that parsed fine. Both reproduce identically on native x86_64. has libestr/run-s2n-dec "ES-s2n-dec OK" "$out" has libestr/run-s2n-dec-neg "ES-s2n-dec-neg OK" "$out" has libestr/run-s2n-zero "ES-s2n-zero OK" "$out" has libestr/run-s2n-oct "ES-s2n-oct OK" "$out" has libestr/run-s2n-oct-777 "ES-s2n-oct-777 OK" "$out" has libestr/run-s2n-oct-baddigit "ES-s2n-oct-baddigit OK" "$out" has libestr/run-s2n-empty "ES-s2n-empty OK" "$out" has libestr/run-s2n-partial "ES-s2n-partial OK" "$out" has libestr/run-s2n-nondigit "ES-s2n-nondigit OK" "$out" has libestr/run-s2n-large "ES-s2n-large OK" "$out" has libestr/run-s2n-hex-digits "ES-s2n-hex-digits OK" "$out" has libestr/run-s2n-hex-letter "ES-s2n-hex-letter OK" "$out" has libestr/run-s2n-hex-ff "ES-s2n-hex-ff OK" "$out" has libestr/run-s2n-hex-bare "ES-s2n-hex-bare OK" "$out" has libestr/run-s2n-null-bsuccess "ES-s2n-null-bsuccess OK" "$out" # comparison. es_strbufcmp returns the byte difference, or +/-1 for a pure # length difference. has libestr/run-bufcmp-equal "ES-bufcmp-equal OK" "$out" has libestr/run-bufcmp-lt "ES-bufcmp-lt OK" "$out" has libestr/run-bufcmp-gt "ES-bufcmp-gt OK" "$out" has libestr/run-bufcmp-buf-shorter "ES-bufcmp-buf-shorter OK" "$out" has libestr/run-bufcmp-buf-longer "ES-bufcmp-buf-longer OK" "$out" has libestr/run-strconstcmp-macro "ES-strconstcmp-macro OK" "$out" has libestr/run-bufcmp-empty-empty "ES-bufcmp-empty-empty OK" "$out" has libestr/run-bufcmp-empty-vs-one "ES-bufcmp-empty-vs-one OK" "$out" has libestr/run-casebufcmp-equal "ES-casebufcmp-equal OK" "$out" has libestr/run-casebufcmp-lt "ES-casebufcmp-lt OK" "$out" has libestr/run-strcmp-equal "ES-strcmp-equal OK" "$out" has libestr/run-strcmp-lt "ES-strcmp-lt OK" "$out" has libestr/run-strcasecmp-equal "ES-strcasecmp-equal OK" "$out" # es_strncmp (correct) beside es_strncasecmp (DEFECTIVE). es_strncasecmp is a # copy of es_strncmp with s2 left unrenamed: all four length branches test # s1->lenStr, so both length-difference arms are dead code and a longer s1 # compares against whatever follows s2's content. The -defect rows pin what # it actually returns, and -diverge pins the disagreement with es_strncmp # for the same inputs -- which is the durable evidence of the bug. has libestr/run-strncmp-prefix "ES-strncmp-prefix OK" "$out" has libestr/run-strncmp-diff "ES-strncmp-diff OK" "$out" has libestr/run-strncmp-s1-shorter "ES-strncmp-s1-shorter OK" "$out" has libestr/run-strncmp-s1-longer "ES-strncmp-s1-longer OK" "$out" has libestr/run-strncmp-both-short "ES-strncmp-both-short OK" "$out" has libestr/run-strncmp-zero-len "ES-strncmp-zero-len OK" "$out" has libestr/run-strncasecmp-equal "ES-strncasecmp-equal OK" "$out" has libestr/run-strncasecmp-diff "ES-strncasecmp-diff OK" "$out" has libestr/run-strncasecmp-s1-shorter-defect "ES-strncasecmp-s1-shorter-defect OK" "$out" has libestr/run-strncasecmp-s1-longer-defect "ES-strncasecmp-s1-longer-defect OK" "$out" has libestr/run-strncmp-strncasecmp-diverge "ES-strncmp-strncasecmp-diverge OK" "$out" # substring search, both cases. has libestr/run-contains-mid "ES-contains-mid OK" "$out" has libestr/run-contains-whole "ES-contains-whole OK" "$out" has libestr/run-contains-absent "ES-contains-absent OK" "$out" has libestr/run-contains-needle-longer "ES-contains-needle-longer OK" "$out" has libestr/run-contains-overlap "ES-contains-overlap OK" "$out" has libestr/run-contains-last "ES-contains-last OK" "$out" has libestr/run-contains-empty-needle "ES-contains-empty-needle OK" "$out" has libestr/run-casecontains-mid "ES-casecontains-mid OK" "$out" has libestr/run-casecontains-absent "ES-casecontains-absent OK" "$out" # es_tolower, in place, via libc tolower(). has libestr/run-tolower-mixed "ES-tolower-mixed OK" "$out" has libestr/run-tolower-len-kept "ES-tolower-len-kept OK" "$out" has libestr/run-tolower-idempotent "ES-tolower-idempotent OK" "$out" # THE REALLOC GROWTH PATH, which is the platform-facing heart of this library: # es_addChar/es_addBuf take es_str_t ** because es_extendBuf reallocs and the # object pointer MOVES. The lenBuf sequence is fully determined by the growth # policy (first append sizes to exactly what is needed, then doubling), so it # is asserted as one exact string rather than a range. has libestr/run-addchar-rc-all-zero "ES-addchar-rc-all-zero OK" "$out" has libestr/run-growth-lenbuf-seq "ES-growth-lenbuf-seq OK" "$out" has libestr/run-growth-lenstr "ES-growth-lenstr OK" "$out" has libestr/run-growth-content "ES-growth-content OK" "$out" # append surfaces, including the two convenience macros. has libestr/run-addbuf-rc "ES-addbuf-rc OK" "$out" has libestr/run-addbuf-append "ES-addbuf-append OK" "$out" has libestr/run-addbuf-zero-len "ES-addbuf-zero-len OK" "$out" has libestr/run-addbuf-grow-from-zero "ES-addbuf-grow-from-zero OK" "$out" has libestr/run-addbuf-grow-doubling "ES-addbuf-grow-doubling OK" "$out" has libestr/run-addbufconstcstr-macro "ES-addbufconstcstr-macro OK" "$out" has libestr/run-addstr-inline "ES-addstr-inline OK" "$out" # es_extendBuf called directly: <= lenBuf doubles, > lenBuf adds. has libestr/run-extendbuf-doubles "ES-extendbuf-doubles OK" "$out" has libestr/run-extendbuf-keeps-content "ES-extendbuf-keeps-content OK" "$out" has libestr/run-extendbuf-big-adds "ES-extendbuf-big-adds OK" "$out" # es_str2cstr and its NUL-escape paths. Its malloc size expression relies on # 32-bit unsigned wraparound when nulEsc is NULL (nbrNUL * (0u - 1)), which # happens to compute the right size -- and computes it the same way on # wasm32, where es_size_t is still 32 bits. has libestr/run-str2cstr-basic "ES-str2cstr-basic OK" "$out" has libestr/run-str2cstr-empty "ES-str2cstr-empty OK" "$out" has libestr/run-str2cstr-nul-dropped "ES-str2cstr-nul-dropped OK" "$out" has libestr/run-str2cstr-nul-1char "ES-str2cstr-nul-1char OK" "$out" has libestr/run-str2cstr-nul-multichar "ES-str2cstr-nul-multichar OK" "$out" has libestr/run-str2cstr-two-nuls "ES-str2cstr-two-nuls OK" "$out" # substrings and duplication. THE -defect ROW: for an EMPTY source, # es_strlen(str)-1 underflows es_size_t to UINT_MAX, so the length clamp # never fires and len bytes are copied out of a zero-length string. A correct # implementation returns an empty string; this returns lenStr=4. has libestr/run-substr-mid "ES-substr-mid OK" "$out" has libestr/run-substr-full "ES-substr-full OK" "$out" has libestr/run-substr-clamped "ES-substr-clamped OK" "$out" has libestr/run-substr-start-past-end "ES-substr-start-past-end OK" "$out" has libestr/run-substr-start-at-end "ES-substr-start-at-end OK" "$out" has libestr/run-substr-overflow-null "ES-substr-overflow-null OK" "$out" has libestr/run-strdup-content "ES-strdup-content OK" "$out" has libestr/run-strdup-independent "ES-strdup-independent OK" "$out" has libestr/run-substr-empty-source-defect "ES-substr-empty-source-defect OK" "$out" # es_unescapeStr, in place. THE TWO -defect ROWS: doUnescape's \x error arm # writes the fallback byte and then FALLS THROUGH (no break) into the very # hex computation it was meant to replace, so \xZZ yields 0x33 0x5a and the # truncated \x4 yields 0x14 -- neither is the documented "use as is". # unesc-trailing-backslash additionally makes doUnescape read c[lenStr]; the # probe builds that input in a pre-zeroed buffer so the outcome is defined. has libestr/run-unesc-newline "ES-unesc-newline OK" "$out" has libestr/run-unesc-tab "ES-unesc-tab OK" "$out" has libestr/run-unesc-bel "ES-unesc-bel OK" "$out" has libestr/run-unesc-backspace "ES-unesc-backspace OK" "$out" has libestr/run-unesc-formfeed "ES-unesc-formfeed OK" "$out" has libestr/run-unesc-cr "ES-unesc-cr OK" "$out" has libestr/run-unesc-squote "ES-unesc-squote OK" "$out" has libestr/run-unesc-dquote "ES-unesc-dquote OK" "$out" has libestr/run-unesc-question "ES-unesc-question OK" "$out" has libestr/run-unesc-backslash "ES-unesc-backslash OK" "$out" has libestr/run-unesc-hex-upper "ES-unesc-hex-upper OK" "$out" has libestr/run-unesc-hex-lower "ES-unesc-hex-lower OK" "$out" has libestr/run-unesc-hex-ff "ES-unesc-hex-ff OK" "$out" has libestr/run-unesc-no-escape "ES-unesc-no-escape OK" "$out" has libestr/run-unesc-multiple "ES-unesc-multiple OK" "$out" has libestr/run-unesc-undefined "ES-unesc-undefined OK" "$out" has libestr/run-unesc-empty "ES-unesc-empty OK" "$out" has libestr/run-unesc-nul "ES-unesc-nul OK" "$out" has libestr/run-unesc-hex-invalid-defect "ES-unesc-hex-invalid-defect OK" "$out" has libestr/run-unesc-hex-truncated-defect "ES-unesc-hex-truncated-defect OK" "$out" has libestr/run-unesc-hex-bare "ES-unesc-hex-bare OK" "$out" has libestr/run-unesc-trailing-backslash "ES-unesc-trailing-backslash OK" "$out" # scale: 1000 appends through the doubling path, a full-length scan, 100 # es_addStr appends and a 1000-byte es_tolower. has libestr/run-scale-addchar-rc "ES-scale-addchar-rc OK" "$out" has libestr/run-scale-lenstr "ES-scale-lenstr OK" "$out" has libestr/run-scale-lenbuf "ES-scale-lenbuf OK" "$out" has libestr/run-scale-content "ES-scale-content OK" "$out" has libestr/run-scale-str2cstr-len "ES-scale-str2cstr-len OK" "$out" has libestr/run-scale-contains-tail "ES-scale-contains-tail OK" "$out" has libestr/run-scale-addstr-rc "ES-scale-addstr-rc OK" "$out" has libestr/run-scale-addstr-lenstr "ES-scale-addstr-lenstr OK" "$out" has libestr/run-scale-tolower-digits "ES-scale-tolower-digits OK" "$out" # round trips, number and C string. has libestr/run-roundtrip-num "ES-roundtrip-num OK" "$out" has libestr/run-roundtrip-num-neg "ES-roundtrip-num-neg OK" "$out" has libestr/run-roundtrip-cstr "ES-roundtrip-cstr OK" "$out" # The pointer width, which is the ONE line that must legitimately differ # between the control and this arm -- and so is excluded from the byte-diff # below rather than allowed to fail it. has libestr/run-pointer-width "ES-PROBE libestr probe, 32-bit pointers" "$out" # BYTE-IDENTITY AGAINST THE NATIVE CONTROL, asserted here ON TARGET rather than # by eyeballing two logs off-target. data/estr-native.txt is the verbatim output # of this same probe source built natively against a host autotools build of the # identical release (146 lines, the pointer-width line removed). Comparing it # inside the guest keeps any terminal capture out of the comparison -- the # console dump truncates long lines, which is exactly how an off-target diff of # this output misleads -- and turns "matches native" from a one-off manual check # into a permanent regression guard: a platform change in libc number # conversion, ctype, or the allocator's growth policy shows up as a CHANGED # VALUE here even if every verdict above still passes. ctl=$BST_DATA/estr-native.txt gotf=${TMPDIR:-/tmp}/bst-estr-got.$$ printf '%s\n' "$out" | grep -v '^ES-PROBE ' > "$gotf" 2>/dev/null if [ ! -s "$ctl" ]; then bst_notok libestr/native-byte-identical "control absent or empty: $ctl" elif cmp -s "$ctl" "$gotf"; then bst_ok libestr/native-byte-identical else bst_notok libestr/native-byte-identical \ "first diff: $(diff "$ctl" "$gotf" 2>/dev/null | sed -n '2,3p' | tr '\n' ' ' | cut -c1-90)" fi rm -f "$gotf" # The probe's own tally and its exit status. 144/144 is what the same source # scores natively against a host autotools build of the identical release. has libestr/run-summary "ES-SUMMARY 144/144" "$out" check_eq libestr/run-exit0 0 "$prc" rm -f "$probe" else bst_notok libestr/run-stage "cannot stage $BST_DATA/estr-probe" fi