. "$BST_LIB" require perl libio-pty-perl/present # IO::Tty 1.20 (meta-oe recipes-devtools/perl/libio-pty-perl_1.20.bb) -- IO::Pty # and IO::Tty, the pseudo-terminal handles for Perl, and the fourteenth Perl # module on this image under BRINTOS-999. # # THIS SUITE'S PASS CRITERION IS THE DISTRIBUTION'S OWN t/, NOT A HAND-WRITTEN # BATTERY -- the ticket's own wording. The port installs the suite # (libio-pty-perl-ptest, from ptest-perl, enabled for this recipe alone) and # section 5 runs `prove -r t/` over it. # # Measured on an x86-64 control running the IDENTICAL 1.20 tree, installed to a # prefix and driven exactly as section 5 drives it -- from @INC with no blib, # over a staged copy of t/: 2 files, 6 tests, Result PASS, rc 0. Repeated twice. # Those are the figures section 5 asserts against, and they are demonstrated # rather than guessed. Two mutations show the criterion is load-bearing: with # the module off @INC it is Result FAIL / Tests=0, and with IO/Tty.pm present # but its loadable object removed -- the arm that predicts THIS target -- it is # Result FAIL / Tests=0 with "Can't locate loadable object for module IO::Tty". # # WHAT THE PORT CANNOT DO ON THIS IMAGE, AND WHY IT SHIPS ANYWAY. IO::Tty is XS, # and a third-party XS module cannot be loaded on this arm at all: perl here is # -Uusedl -Uuseshrplib, so the compile produces auto/IO/Tty/Tty.a and there is # no DynaLoader to load an .so that was never built (BRINTOS-881). Section 4 # classifies WHICH defect this boot met, because attributing a red to the wrong # one is the error a later reader cannot detect; section 5 then names the # criterion as unverified rather than letting the suite stop quietly on greens. # # THE ONE PLATFORM QUESTION THE TICKET CARRIES IS ANSWERED IN SECTION 3, NOT # INFERRED. The ticket asks whether UNIX98 pseudo-terminals work on this target. # Because the module cannot load, its t/ cases cannot answer that -- so section 3 # drives the SAME kernel interface IO::Tty's XS drives (/dev/ptmx, TIOCSPTLCK, # TIOCGPTN, /dev/pts/) directly from the interpreter, with no XS at all. That # also removes a genuine cross-compile hazard from the answer: -DHAVE_DEV_PTMX # is set by Makefile.PL probing the BUILD HOST's /dev, so the build's own belief # about this target's ptys is an inference. Section 3 measures it. # # Five layers: # 1. filesystem facts -- what the port SHIPS, asserted before anything starts # perl, so they hold on a boot where the interpreter cannot load a module. # 2. the two XS-load facts, RED today and written so they flip green by # themselves when BRINTOS-881 lands. # 3. the platform control: UNIX98 ptys, driven without the module. Placed # BEFORE the load attempt because its result must survive section 4 ending # the file. # 4. can perl load IO::Pty on this boot, classified on the diagnostic. # 5. the criterion: prove -r t/ over the installed suite. V=$(ls -d /usr/lib/perl5/vendor_perl/*/*-linux 2>/dev/null | head -1) CORE=$(ls -d /usr/lib/perl5/5.* 2>/dev/null | head -1) PT=/usr/lib/libio-pty-perl/ptest # ------------------------- 1. what the port SHIPS: filesystem facts ---------- # IO::Tty is XS, so its .pm files install into the ARCH directory # (vendor_perl//-linux), not the pure-Perl vendor_perl//. check libio-pty-perl/vendor-arch-dir test -n "$V" check libio-pty-perl/pty-installed test -f "$V/IO/Pty.pm" check libio-pty-perl/tty-installed test -f "$V/IO/Tty.pm" # Constant.pm is GENERATED by Makefile.PL, not shipped in the tarball -- it is # not in MANIFEST. Asserted because a build that stopped generating it would # leave IO::Tty.pm with an unsatisfiable `use IO::Tty::Constant` and no other # case here would say so. check libio-pty-perl/constant-generated test -f "$V/IO/Tty/Constant.pm" # The XS artifact the compile produced. On a -Uusedl perl this is the whole of # the compiled half: an archive nothing can load. Asserted so that a build which # silently stopped compiling the XS is a red rather than an invisible change. check libio-pty-perl/xs-archive test -f "$V/auto/IO/Tty/Tty.a" # THE INSTALLED SUITE -- the deliverable the ticket's criterion runs on. check libio-pty-perl/ptest-dir test -d "$PT/t" check libio-pty-perl/ptest-test test -f "$PT/t/test.t" check libio-pty-perl/ptest-winsize test -f "$PT/t/pty_get_winsize.t" check libio-pty-perl/ptest-runptest test -f "$PT/run-ptest" # Pinned EXACTLY at 2: the distribution ships exactly these two .t files and the # recipe removes none, so a count that moves means the packaging changed. check_eq libio-pty-perl/ptest-count 2 "$(ls "$PT"/t/*.t 2>/dev/null | wc -l | tr -d ' ')" # THE HARNESS THE CRITERION NAMES. prove reaches this image from libdbi-perl # (perl-misc + perl-module-app-prove) and RUNS because libdev-checklib-perl # added the 43 perl-module-tap-* splits. Asserted as ordinary reds, not as a # `require`: their absence is a regression to report, not a reason to skip. check libio-pty-perl/prove-installed test -x /usr/bin/prove check libio-pty-perl/prove-lib test -f "$CORE/App/Prove.pm" # ------------------ 2. the XS-load facts: red today, self-flipping ----------- # 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), so there is no forge # issue to chase. Same treatment as 36-libdbi-perl.test and 36-libxml-parser-perl.test. # # Both are written in the direction that is TRUE ON A NORMAL PERL, so they are # red here and go green by themselves the day 881 lands. Nothing has to be # edited for that to happen, which is the point: a suite that asserted "no .so, # as expected" would go on passing after the platform fixed the thing it exists # to report. check libio-pty-perl/xs-shared-object test -f "$V/auto/IO/Tty/Tty.so" check libio-pty-perl/dynaloader-present test -f "$CORE/DynaLoader.pm" # ------------- 3. the platform control: UNIX98 ptys, without the module ------ # The ticket's one platform question. This drives the exact kernel interface # IO::Tty's XS uses under -DHAVE_DEV_PTMX -- open /dev/ptmx, TIOCSPTLCK to # unlock the slave (what unlockpt(3) does), TIOCGPTN to learn its number (what # ptsname(3) does), open /dev/pts/, then round-trip a line master -> slave -- # using nothing but perl's own sysopen/ioctl/sysread/syswrite. No XS module is # loaded, so this answers whether the PLATFORM works even on a boot where the # port cannot. # # grantpt(3) is not exercised: on Linux with devpts mounted (gid=5,mode=620) it # is a no-op, and there is no way to call it without XS. The three ioctls above # are the substantive half and are what the x86-64 run of t/test.t is observed # to take (posix_openpt -> grantpt -> unlockpt -> ptsname_r -> open /dev/pts/N). # # Validated on an x86-64 control before being shipped, in both directions: it # passes there (PTY-RT-OK), and three mutations each redden it for the right # reason -- ptmx path wrong -> "open-ptmx No such file or directory"; TIOCGPTN # number wrong -> read times out; payload changed so it cannot match -> read # times out. A control that only ever passed would be evidence of nothing. check libio-pty-perl/ptmx-chardev test -c /dev/ptmx check libio-pty-perl/devpts-dir test -d /dev/pts pty=$(perl -e ' my $m; sysopen($m, "/dev/ptmx", 2 | 0400) or do { print "PTY-FAIL open-ptmx $!\n"; exit 1 }; my $z = pack("i", 0); ioctl($m, 0x40045431, $z) or do { print "PTY-FAIL unlockpt $!\n"; exit 1 }; my $n = pack("i", 0); ioctl($m, 0x80045430, $n) or do { print "PTY-FAIL ptsname $!\n"; exit 1 }; my $idx = unpack("i", $n); my $s; sysopen($s, "/dev/pts/$idx", 2 | 0400) or do { print "PTY-FAIL open-slave /dev/pts/$idx: $!\n"; exit 1 }; select((select($m), $| = 1)[0]); syswrite($m, "PTY-RT\n") or do { print "PTY-FAIL write-master $!\n"; exit 1 }; my $buf = ""; eval { local $SIG{ALRM} = sub { die "timeout\n" }; alarm 10; while ($buf !~ /PTY-RT/) { my $r = sysread($s, my $c, 64); last unless $r; $buf .= $c } alarm 0; }; if ($@) { print "PTY-FAIL read-slave $@"; exit 1 } $buf =~ s/\s+\z//; print(($buf =~ /PTY-RT/) ? "PTY-RT-OK pts=$idx\n" : "PTY-FAIL mismatch $buf\n"); ' 2>&1) # perl writes a four-line locale warning to stderr on every start here; strip it # so the detail field carries the pty diagnosis and not "Setting locale failed". ptyd=$(printf '%s\n' "$pty" | awk '/^perl: warning/ { skip=1; next } /^[ \t]/ && skip { next } { skip=0; print }') [ -n "$ptyd" ] || ptyd=$pty case "$ptyd" in *PTY-RT-OK*) bst_ok libio-pty-perl/unix98-pty-roundtrip ;; *) bst_notok libio-pty-perl/unix98-pty-roundtrip "$(_bst_1line "$ptyd")" ;; esac # ------------------ 4. can perl LOAD this module on this boot at all? -------- # BRINTOS-WORKAROUND: brintos/linux-in-the-browser#154 remove -- perl aborts in # glibc's allocator while COMPILING a module, so section 5 cannot run on an # image that meets it; delete this guard and section 5 runs unconditionally # again once #154 closes AND BRINTOS-881 has given the arm a loader. # # The guard classifies on the DIAGNOSTIC because there are several distinct ways # this load fails here and they are not the same finding: #154's allocator # abort, BRINTOS-881's missing loader -- which has TWO spellings, "Can't locate # DynaLoader.pm" when the loader module itself is absent (this arm) and "Can't # locate loadable object" when it is present but the .so is not -- and a missing # dependency split, which would be OURS. pout=$(perl -MIO::Pty -e 'print "IOPTY-OK\n"' 2>&1); prc=$? case "$pout" in *IOPTY-OK*) bst_ok libio-pty-perl/import-load ;; *) pdiag=$(printf '%s\n' "$pout" | awk '/^perl: warning/ { skip=1; next } /^[ \t]/ && skip { next } { skip=0; print }') [ -n "$pdiag" ] || pdiag=$pout case "$pdiag" in *"Can't locate loadable object for module IO::Tty"*|*"Can't locate DynaLoader"*) why="BRINTOS-881 -- this arm's perl is -Uusedl -Uuseshrplib, so IO::Tty's XS half is auto/IO/Tty/Tty.a and there is no loader for it" ;; *mremap_chunk*|*munmap_chunk*|*"corrupted size"*|*"corrupted double-linked list"*|\ *"double free or corruption"*|*"free(): "*|*"malloc(): "*|*"realloc(): "*) # The pattern set is WIDE on purpose and was widened by measurement under # BRINTOS-1011, not by guesswork: glibc has a dozen spellings for one # abort, and missing one turns an attributed red into a false alarm in # the single field a later reader attributes by. why="linux-in-the-browser#154 -- perl aborts in glibc's allocator while compiling a module" ;; *"Can't locate IO/Pty.pm"*) why="OURS -- libio-pty-perl is not on this image, or did not install into \@INC" ;; *"Can't locate IO/Tty/Constant.pm"*) why="OURS -- Makefile.PL did not generate Tty/Constant.pm, or it was not packaged" ;; *"Can't locate"*) why="OURS -- a module in IO::Pty's closure is missing from this image; read the name in the diagnostic" ;; *) if [ "$prc" = 134 ]; then why="unclassified SIGABRT -- rc=134 with no allocator diagnostic; probably #154 with a spelling this guard has not seen, but not asserted as one" else why="unclassified -- none of #154's allocator abort, BRINTOS-881's missing loader, or a missing-module diagnostic" fi ;; esac bst_notok libio-pty-perl/import-load "rc=$prc $(_bst_1line "$pdiag") -- $why" # NAMED, not silent. The ticket's criterion IS section 5, and a suite that # merely stopped here would report a dozen greens and read as a pass. bst_notok libio-pty-perl/prove-unverified \ "not run: perl cannot load IO::Pty ($why) -- \`prove -r t/\` over the 2 installed test files (6 assertions on the x86-64 control) is unverified on wasm32-hwjs" exit 0 ;; esac # ------------------------- 5. THE CRITERION: prove -r t/ --------------------- # Run from a WRITABLE COPY, not in place: the deployed rootfs is read-only at # browser runtime, and prove writes nothing into t/ but its harness may. Copying # costs nothing here (two files) and removes a whole class of false red. WD=${TMPDIR:-/tmp}/bst-libio-pty-perl.$$ mkdir -p "$WD" && cp -r "$PT/t" "$WD/t" 2>/dev/null check libio-pty-perl/suite-staged test -f "$WD/t/test.t" out=$(cd "$WD" && prove -r t/ 2>&1); rc=$? # `Result: PASS` is prove's own verdict over the whole run and is the ticket's # "reporting zero failures" verbatim; the exit status is asserted beside it # because a harness that died before printing a verdict leaves neither. case "$out" in *"Result: PASS"*) bst_ok libio-pty-perl/prove-result ;; *) bst_notok libio-pty-perl/prove-result "rc=$rc $(_bst_1line "$out")" ;; esac check_eq libio-pty-perl/prove-rc 0 "$rc" case "$out" in *"All tests successful."*) bst_ok libio-pty-perl/prove-all-successful ;; *) bst_notok libio-pty-perl/prove-all-successful "$(_bst_1line "$out")" ;; esac # The file count is pinned EXACTLY -- it is a property of the package, already # asserted on the rootfs in section 1, and a harness that silently ran fewer # files is the failure a green verdict cannot show. files=$(printf '%s\n' "$out" | sed -n 's/^Files=\([0-9]*\),.*/\1/p' | tail -1) check_eq libio-pty-perl/prove-files 2 "$files" # The assertion count is bounded from BELOW, not pinned. 6 is what the x86-64 # control ran; these tests have never run on this target, and pty_get_winsize.t # carries a plan that skips on some platforms, so a legitimate skip here would # move the total without meaning anything is wrong. A total that collapses means # files stopped running, which is what the bound catches. tests=$(printf '%s\n' "$out" | sed -n 's/^Files=[0-9]*, Tests=\([0-9]*\),.*/\1/p' | tail -1) case "$tests" in ''|*[!0-9]*) bst_notok libio-pty-perl/prove-tests "no Tests= total in prove's summary -- $(_bst_1line "$out")" ;; *) if [ "$tests" -ge 5 ]; then bst_ok libio-pty-perl/prove-tests else bst_notok libio-pty-perl/prove-tests "Tests=$tests, want >= 5 (x86-64 control ran 6)"; fi ;; esac # One pty exercised THROUGH THE MODULE, outside the harness, so that a `prove` # which somehow reported PASS over zero real work still has to answer a direct # question -- and so that the module's own answer can be compared against # section 3's raw-syscall answer on the same boot. ex=$(perl -MIO::Pty -e ' my $pty = IO::Pty->new; my $tty = $pty->slave; print "IOPTY-SLAVE ", (defined $tty && $tty->ttyname =~ m{^/dev/} ? "yes" : "no"), "\n";' 2>&1) check_eq libio-pty-perl/pty-slave-name "IOPTY-SLAVE yes" "$(printf '%s\n' "$ex" | tail -1)" rm -rf "$WD"