. "$BST_LIB" require perl libfile-fnmatch-perl/present # File::FnMatch 0.02 is an XS binding to fnmatch(3) -- shell-style pattern # matching taken straight out of libc rather than reimplemented in Perl, and the # ninth Perl module on this image under BRINTOS-999. # # It is the SECOND that is not pure Perl, after libxml-parser-perl (entry 305), # and it meets the same wall one step earlier. XML::Parser reaches DynaLoader # only as XSLoader's fallback; FnMatch.pm reaches it DIRECTLY -- `require # DynaLoader`, `@ISA = qw(Exporter DynaLoader)`, then `bootstrap File::FnMatch` # at file scope. This arm's perl is built -Uusedl -Uuseshrplib (--disable-shared # is distro policy), so the package installs as FnMatch.a with no shared object, # and DynaLoader.pm is in no perl package this arm produces. `require DynaLoader` # therefore fails while COMPILING FnMatch.pm, before any of this module's own # code runs. # # And unlike every module before it there is no pure-Perl half to fall back to: # File::FnMatch is 22 executable lines of Perl over one XS call, so the XS layer # IS the module. Surfacing that is part of this port's deliverable rather than a # reason to withhold it. # # So the suite is built in three layers, and each is honest about what it can # see: # 1. filesystem facts -- what the port SHIPS. Green on target today. # 2. the XS gap, as NAMED REDS attributed to BRINTOS-881. They flip green by # themselves when 881 lands, which is the regression signal this suite is # bought for. They are not skips: nothing here is legitimately absent. # 3. the value battery, driven by the payload. Unreachable today behind (2) -- # every expectation in it was measured against a native x86-64 perl built # from the SAME pristine CPAN tarball this image builds (meta-oe carries # File-FnMatch-0.02 with no patches at all), so it is written and # demonstrated rather than guessed. # ------------------------- 1. what the port SHIPS: filesystem facts ---------- # Asserted before anything starts perl, so the deliverable is measured even on a # boot where the interpreter cannot load a module at all. XS modules land under # the ARCH subdirectory of vendor_perl, not beside the pure-Perl ports. V=$(ls -d /usr/lib/perl5/vendor_perl/*/*-linux 2>/dev/null | head -1) check libfile-fnmatch-perl/vendor-arch-dir test -n "$V" check libfile-fnmatch-perl/module-installed test -f "$V/File/FnMatch.pm" # THE XS ARTIFACT ITSELF. This is what the compile produced -- the port did # reach the compiler and did build, which is worth asserting separately from # whether it can be loaded. For this module the .a IS the whole implementation. check libfile-fnmatch-perl/xs-archive test -f "$V/auto/File/FnMatch/FnMatch.a" check libfile-fnmatch-perl/payload-installed \ test -f "$BST_DATA/libfile-fnmatch-perl-selftest.pl" # ------------------- 2. the XS gap, named rather than skipped ---------------- # Tracked by BRINTOS-881 (migrate to shared libraries and dynamic linking, # glibc arm) -- named in prose rather than as a BRINTOS-WORKAROUND: marker, and # deliberately: that marker's grammar is #, a brintos.io issue for # brintos_reconcile.py to chase, and a JIRA key put in its place parses as # MALFORMED -- which leaves the artifact reading as tracked while nothing # tracks it. This gap is OURS (--disable-shared is distro policy; brintOS has a # working dynamic arm and this image deliberately is not it), so there is no # forge issue to chase. Same treatment as 36-libxml-parser-perl.test and # 90-intltool.test, which carry the same gap. # # Both cases below are the ticket's "the call really reaches libc" clause, # asserted as filesystem facts so they are measured on target even while the # interpreter cannot run; delete this section when 881 lands and section 4's # R10-R14 assert the same thing at runtime, which is stronger. # # They are written in the direction that FLIPS: each asserts what must become # true, so the pair goes green the day shared libraries arrive, rather than # going red and reading as a regression. Asserting the absence instead -- "there # is no FnMatch.so" -- would pass today and fail on success, which is the wrong # way round for a pin whose whole purpose is to notice the fix. if [ -f "$V/auto/File/FnMatch/FnMatch.so" ]; then bst_ok libfile-fnmatch-perl/xs-shared-object else bst_notok libfile-fnmatch-perl/xs-shared-object \ "no FnMatch.so beside FnMatch.a -- this arm is -Uusedl -Uuseshrplib, so the XS layer cannot be loaded (BRINTOS-881)" fi DLP=$(ls /usr/lib/perl5/*/DynaLoader.pm /usr/lib/perl5/*/*-linux/DynaLoader.pm 2>/dev/null | head -1) if [ -n "$DLP" ]; then bst_ok libfile-fnmatch-perl/dynaloader-present else bst_notok libfile-fnmatch-perl/dynaloader-present \ "DynaLoader.pm is in no perl package in the feed, and FnMatch.pm requires it DIRECTLY at file scope (BRINTOS-881)" fi # ------------------ 3. can perl LOAD this module on this boot at all? -------- # BRINTOS-WORKAROUND: linux-in-the-browser#154 remove -- perl aborts in glibc's # allocator while COMPILING a module, so this guard is needed on top of the 881 # gap; delete it and the battery in section 4 runs unconditionally again once # BOTH close. Two independent blocks sit between this port and its own value # battery and the guard names whichever one it actually met, because attributing # this port's reds to the wrong one is exactly the error a later reader cannot # detect. pout=$(perl -MFile::FnMatch -e 'print "FNM-OK\n"' 2>&1); prc=$? case "$pout" in *FNM-OK*) bst_ok libfile-fnmatch-perl/import-load ;; *) # The interpreter's locale warning is a four-line preamble on this image and # is NOT the failure: it precedes the real diagnostic and would otherwise # fill the 100-char detail field, leaving the red reading "Setting locale # failed" -- the wrong diagnosis in the one field a later reader has to # attribute by. pdiag=$(printf '%s\n' "$pout" | awk '/^perl: warning/ { skip=1; next } /^[ \t]/ && skip { next } { skip=0; print }') [ -n "$pdiag" ] || pdiag=$pout case "$pdiag" in *DynaLoader*|*"Can't load"*|*"object file"*|*"Can't locate loadable object"*) why="BRINTOS-881 -- no DynaLoader on this -Uusedl arm, so the XS layer cannot load" ;; *mremap_chunk*|*"corrupted size"*|*"free(): "*|*"malloc(): "*) why="linux-in-the-browser#154 -- perl aborts in glibc's allocator while compiling a module" ;; *) why="unclassified -- neither BRINTOS-881's DynaLoader signature nor #154's allocator abort" ;; esac bst_notok libfile-fnmatch-perl/import-load "rc=$prc $(_bst_1line "$pdiag") -- $why" # Named rather than silent. This ticket's headline criteria are the pattern # syntax and the three flags each CHANGING behaviour, and those are exactly # what could not be measured. A suite that merely stopped here would report # six greens and read as a pass. bst_notok libfile-fnmatch-perl/match-unverified \ "not run: perl cannot load File::FnMatch ($why) -- the *, ? and bracket pattern syntax and the FNM_PATHNAME / FNM_PERIOD / FNM_CASEFOLD behaviour changes are unverified on wasm32-hwjs" exit 0 ;; esac # ------------------------- 4. the module's own answers, at values ------------ out=$(perl "$BST_DATA/libfile-fnmatch-perl-selftest.pl" 2>&1); rc=$? case "$out" in *ALL-FNMATCH-OK*) all=1 ;; *) all=0 ;; esac if [ "$rc" -eq 0 ] && [ "$all" -eq 1 ]; then bst_ok libfile-fnmatch-perl/all-ok else bst_notok libfile-fnmatch-perl/all-ok "rc=$rc $(_bst_1line "$out")"; fi val() { printf '%s\n' "$out" | awk -v t="$1" '$1==t{ $1=""; sub(/^ /,""); print; exit }'; } # --- the module loads, from the image rather than a stray @INC dir ---------- check_eq libfile-fnmatch-perl/version 0.02 "$(val R1-version)" case "$(val R2-inc-path)" in /usr/*) bst_ok libfile-fnmatch-perl/installed-path ;; *) bst_notok libfile-fnmatch-perl/installed-path "resolved to $(val R2-inc-path)" ;; esac # --- the XS layer is REALLY loaded ------------------------------------------ # For THIS module the distinction is the whole deliverable: File::FnMatch has no # pure-Perl half, so if these are wrong nothing below is measuring libc. The # XSUB flag is set by newXS at bootstrap and by nothing else; the DynaLoader # record names the object actually mapped; and the NAME LIST catches a partial # bootstrap that a bare count would miss. check_eq libfile-fnmatch-perl/xs-fnmatch 1 "$(val R10-xsub-fnmatch)" check_eq libfile-fnmatch-perl/xs-dynaloaded 1 "$(val R11-dynaloader-count)" check_eq libfile-fnmatch-perl/xs-object-name FnMatch.so "$(val R12-dynaloader-basename)" check_eq libfile-fnmatch-perl/xs-census 9 "$(val R13-xsub-census)" # `bootstrap` is in this list and is not an accident: DynaLoader's # dl_install_xsub installs the module's own boot symbol into its stash, so its # presence is an independent witness that DynaLoader -- not XSLoader, not a Perl # stub -- is what brought this module up. check_eq libfile-fnmatch-perl/xs-names \ 'FNM_CASEFOLD,FNM_EXTMATCH,FNM_FILE_NAME,FNM_LEADING_DIR,FNM_NOESCAPE,FNM_PATHNAME,FNM_PERIOD,bootstrap,fnmatch' \ "$(val R14-xsub-names)" # --- the constants, at libc's own values ------------------------------------ # ABI facts read out of at compile time, not Perl values, so they # are the cheapest proof the compile saw a real glibc header. The three GNU # extensions being present is itself the evidence that _GNU_SOURCE was in effect. check_eq libfile-fnmatch-perl/const-pathname 1 "$(val R20-FNM_PATHNAME)" check_eq libfile-fnmatch-perl/const-noescape 2 "$(val R21-FNM_NOESCAPE)" check_eq libfile-fnmatch-perl/const-period 4 "$(val R22-FNM_PERIOD)" check_eq libfile-fnmatch-perl/const-leading-dir 8 "$(val R23-FNM_LEADING_DIR)" check_eq libfile-fnmatch-perl/const-casefold 16 "$(val R24-FNM_CASEFOLD)" check_eq libfile-fnmatch-perl/const-extmatch 32 "$(val R25-FNM_EXTMATCH)" check_eq libfile-fnmatch-perl/const-file-name-alias 1 "$(val R26-FILE_NAME-is-PATHNAME)" check_eq libfile-fnmatch-perl/const-count 7 "$(val R27-const-count)" check_eq libfile-fnmatch-perl/export-ok-count 8 "$(val R28-export-ok-count)" # --- WHAT THE TICKET ASKS (1/2): the pattern syntax ------------------------- # `*`, `?` and bracket patterns, each in BOTH directions. A matcher that # returned true for everything would pass every match case and fail every reject # case, so the rejects are what make the matches mean anything. check_eq libfile-fnmatch-perl/star-match 1 "$(val R30-star-match)" check_eq libfile-fnmatch-perl/star-reject 0 "$(val R31-star-reject)" check_eq libfile-fnmatch-perl/star-empty 1 "$(val R32-star-empty)" check_eq libfile-fnmatch-perl/star-mid 1 "$(val R33-star-mid)" check_eq libfile-fnmatch-perl/star-mid-reject 0 "$(val R34-star-mid-reject)" check_eq libfile-fnmatch-perl/qmark-match 1 "$(val R35-qmark-match)" check_eq libfile-fnmatch-perl/qmark-one-only 0 "$(val R36-qmark-one-only)" check_eq libfile-fnmatch-perl/qmark-not-empty 0 "$(val R37-qmark-not-empty)" check_eq libfile-fnmatch-perl/qmark-multi 1 "$(val R38-qmark-multi)" check_eq libfile-fnmatch-perl/bracket-set 1 "$(val R39-bracket-set)" check_eq libfile-fnmatch-perl/bracket-reject 0 "$(val R40-bracket-reject)" check_eq libfile-fnmatch-perl/bracket-range 1 "$(val R41-bracket-range)" check_eq libfile-fnmatch-perl/bracket-range-rej 0 "$(val R42-bracket-range-rej)" check_eq libfile-fnmatch-perl/bracket-negate 1 "$(val R43-bracket-negate)" check_eq libfile-fnmatch-perl/bracket-negate-rej 0 "$(val R44-bracket-negate-rej)" check_eq libfile-fnmatch-perl/bracket-class 1 "$(val R45-bracket-class)" check_eq libfile-fnmatch-perl/bracket-class-rej 0 "$(val R46-bracket-class-rej)" check_eq libfile-fnmatch-perl/literal 1 "$(val R47-literal)" check_eq libfile-fnmatch-perl/literal-reject 0 "$(val R48-literal-reject)" # --- WHAT THE TICKET ASKS (2/2): each flag CHANGES behaviour ---------------- # Every flag is a PAIR on the same pattern and string: the answer without it and # the answer with it. Asserting only the flagged answer would pass against a # binding that silently discarded its third argument, because most of these # strings match under flags=0 anyway. The PAIR is the measurement, and a pair # that differs is proof the flag reached libc and changed libc's behaviour. # FNM_PATHNAME -- `*` stops at a slash. check_eq libfile-fnmatch-perl/pathname-off 1 "$(val R50-pathname-off)" check_eq libfile-fnmatch-perl/pathname-on 0 "$(val R51-pathname-on)" check_eq libfile-fnmatch-perl/pathname-one-level 1 "$(val R52-pathname-one-level)" check_eq libfile-fnmatch-perl/pathname-qmark-off 1 "$(val R53-pathname-qmark-off)" check_eq libfile-fnmatch-perl/pathname-qmark-on 0 "$(val R54-pathname-qmark-on)" # FNM_PERIOD -- a leading '.' is protected from the wildcards. check_eq libfile-fnmatch-perl/period-off 1 "$(val R55-period-off)" check_eq libfile-fnmatch-perl/period-on 0 "$(val R56-period-on)" check_eq libfile-fnmatch-perl/period-explicit 1 "$(val R57-period-explicit)" check_eq libfile-fnmatch-perl/period-qmark-on 0 "$(val R58-period-qmark-on)" check_eq libfile-fnmatch-perl/period-nonleading 1 "$(val R59-period-nonleading)" check_eq libfile-fnmatch-perl/period-after-slash-off 1 "$(val R60-period-after-slash-off)" check_eq libfile-fnmatch-perl/period-after-slash-on 0 "$(val R61-period-after-slash-on)" # FNM_CASEFOLD -- case-insensitive comparison (a GNU extension). check_eq libfile-fnmatch-perl/casefold-off 0 "$(val R62-casefold-off)" check_eq libfile-fnmatch-perl/casefold-on 1 "$(val R63-casefold-on)" check_eq libfile-fnmatch-perl/casefold-bracket-off 0 "$(val R64-casefold-bracket-off)" check_eq libfile-fnmatch-perl/casefold-bracket-on 1 "$(val R65-casefold-bracket-on)" # FNM_NOESCAPE -- backslash stops being an escape character. check_eq libfile-fnmatch-perl/noescape-off 1 "$(val R66-noescape-off)" check_eq libfile-fnmatch-perl/noescape-off-rej 0 "$(val R67-noescape-off-rej)" check_eq libfile-fnmatch-perl/noescape-on 1 "$(val R68-noescape-on)" # FNM_LEADING_DIR -- the pattern may match just the leading directory part. check_eq libfile-fnmatch-perl/leading-dir-off 0 "$(val R70-leading-dir-off)" check_eq libfile-fnmatch-perl/leading-dir-on 1 "$(val R71-leading-dir-on)" # FNM_EXTMATCH -- ksh-style extended patterns. GNU-only, and the single hardest # thing for a Perl-side reimplementation to fake, so it is the strongest one-case # proof that these answers come from glibc rather than from Perl. check_eq libfile-fnmatch-perl/extmatch-off 0 "$(val R72-extmatch-off)" check_eq libfile-fnmatch-perl/extmatch-on 1 "$(val R73-extmatch-on)" check_eq libfile-fnmatch-perl/extmatch-not 1 "$(val R74-extmatch-not)" check_eq libfile-fnmatch-perl/extmatch-not-rej 0 "$(val R75-extmatch-not-rej)" # --- the call's own contract ------------------------------------------------- # The POD says fnmatch "Returns true if PATTERN matches STRING, undef otherwise." # The XS says `int RETVAL = !fnmatch(...)`, so a non-match is a DEFINED 0. Pinned # because the documentation is wrong and code written against it (`defined $r`) # is true for every input -- a caller that tested definedness would treat every # non-match as a match. check_eq libfile-fnmatch-perl/return-hit 1 "$(val R90-return-hit)" check_eq libfile-fnmatch-perl/return-miss-defined 1 "$(val R91-return-miss-defined)" check_eq libfile-fnmatch-perl/return-miss 0 "$(val R92-return-miss)" check_eq libfile-fnmatch-perl/two-arg-form 1 "$(val R93-two-arg-form)" check_eq libfile-fnmatch-perl/empty-both 1 "$(val R94-empty-both)" check_eq libfile-fnmatch-perl/empty-pattern-reject 0 "$(val R95-empty-pattern-reject)" # The payload installs a __WARN__ handler and counts what it caught. A module # that started warning on ordinary input would otherwise be invisible here, # because the runner reads stdout and the warnings go to stderr. check_eq libfile-fnmatch-perl/no-warnings 0 "$(val R99-warnings)"