. "$BST_LIB" # BST-TIMEOUT: 600 # ~110 cases, each one exec of a multi-MB wasm image; that # exec is the whole cost here -- every case is bounded and # none of them waits on anything. This is the widest option # surface in the round, hence the larger bound than the # sibling suites'. Replace with a measured elapsed once this # suite has run on target a few times. # 57-xset.test -- xset 1.2.5 (poky meta/recipes-graphics/xorg-app): the # user-preference utility. It sets and queries the bell, keyclick, pointer # acceleration, auto-repeat, screen saver, DPMS and the font path. See # recipes-graphics/xorg-app/xset_%.bbappend for what the port changes -- a # static-link closure assembled from two pkg-config queries, and deliberately no # patch. # # EVERY SETTING XSET TOUCHES LIVES IN THE SERVER, so with none running the # documented refusal IS the assertion. What makes that worth a suite is that the # whole connection path runs to produce it: XOpenDisplay(), the socket address, # the setup handshake. A wasm32 socket-address length or struct-packing mistake # lands exactly there, and it lands as a crash, a hang, or a diagnostic that is # not this one. Every connection case below therefore asserts four things at # once -- the stream, the exact bytes, the status, and a bound -- because a red # in any one of them names a different fault. # # AND IT IS WORTH PROVING ON THE WIDEST OPTION SURFACE IN THIS ROUND. xset has # more sub-commands than any other member (b, bc, c, dpms, fp, led, m, p, q, r, # s, k, and their `-' and `+' forms), and section 7 drives every one of them to # the same refusal. That is what separates "the port works and there is no # server" from "the port's argument handling is broken": a sub-command that # died in the parser would produce a DIFFERENT string, and this suite would say # which one. # # THE EXACT SPELLING, and it is not the sibling ports' spelling. xset.c:183 is # # fprintf(stderr, "%s: unable to open display \"%s\"\n", # argv[0], XDisplayName(disp)); # # so: TWO spaces after the colon, and DOUBLE quotes around the name. xprop # (BRINTOS-2461) writes SINGLE quotes from dsimple.c; a case copied from that # suite would go red on the quoting alone. xlsatoms (BRINTOS-2465) happens to # match this spelling from its own fprintf, which is a coincidence of two # programs and not a shared code path. # # WHAT THIS PORT NEEDED NO PATCH FOR, AND WHY THAT IS NARROWER THAN IT LOOKS. # main() runs TWO argument loops with XOpenDisplay() BETWEEN them # (xset.c:165-186), and the FIRST loop recognises exactly three things: # # for (int i = 1; i < argc; i++) { # if (-display / -d) { store the name; missing arg -> usage(...) } # else if (-version) { puts(PACKAGE_STRING); exit(EXIT_SUCCESS); } # else { hasargs = True; } /* and NOTHING else */ # } # if (!hasargs) usage(NULL); # dpy = XOpenDisplay(disp); # # So -version and the usage block are already answered with no server, as # shipped -- which is what xprop, xwud and xkbcomp each needed a patch for. # Sections 5 and 6 are those. # # BUT THE REST OF THE OPTION SURFACE IS NOT A PARSER SURFACE HERE. Every other # diagnostic xset has -- `unknown option', `missing argument to p', `hi-mark # must be greater than 0', `balance must be 10 to 90', `missing fp= argument', # `missing argument to dpms force' -- lives in the SECOND loop, whose every # branch takes `dpy' and calls into Xlib. None of them can be reached on an # image with no server, and no patch of the shape xprop needed could change # that: hoisting them would mean splitting each sub-command's validation from # its action, which is a rewrite of main() rather than a reordering. # # Section 8 therefore asserts those as an ABSENCE. They are NOT evidence that # the sub-command validators work; they are evidence that the validators never # run, which is a smaller claim and the honest one. Asserting a diagnostic that # cannot arrive would be a case that fails for the wrong reason for the life of # the port. # # THE BUILD SWITCHES ARE OBSERVABLE, and section 9 reads them off the usage # block. Measured in this build's own config.h: HAVE_X11_EXTENSIONS_DPMS_H and # HAVE_X11_EXTENSIONS_MITMISC_H are defined (libXext supplies both headers, and # SET_XEXT_CFLAGS is left to configure by the .bbappend); HAVE_X11_XKBLIB_H, # HAVE_X11_EXTENSIONS_FONTCACHE_H and HAVE_X11_EXTENSIONS_XF86MISC_H are not -- # the first two from the recipe's `--disable-xkb --without-fontcache', the third # from --with-xf86misc defaulting to no. That is four #ifdef'd blocks of the # usage text present or absent, and it is the only place on this image where the # recipe's EXTRA_OECONF is visible end to end. S=xset require xset $S/present W=${TMPDIR:-/tmp}/bst-xset.$$ mkdir -p "$W" 2>/dev/null check $S/tmp-writable sh -c "printf x > $W/.w && rm -f $W/.w" # The exact refusal, built from the display name the run should report. # # The `%s' before the colon is argv[0] with no basename() -- so this is `xset' # only because every invocation here goes through PATH. A case that ran # /usr/bin/xset by path would have to expect the path. refusal() { printf 'xset: unable to open display "%s"' "$1"; } # The first line of the usage block. Also two spaces after the colon, from a # different fprintf (xset.c:1578) than the refusal's -- asserted separately for # that reason. USAGE1='usage: xset [-display host:dpy] option ...' # nodisp : run xset with DISPLAY REMOVED from the environment, # bounded, folding stderr into stdout; sets $out and $rc. # # `env -u DISPLAY' rather than `DISPLAY= ': XDisplayName(NULL) returns # getenv("DISPLAY") or "" when that is absent, so an EMPTY DISPLAY and an ABSENT # one both end up reporting "" -- but only the absent one is the state this image # actually boots in, and asserting the state we ship is the point. nodisp() { _t=$1; shift; bst_run "$_t" env -u DISPLAY xset "$@"; } # split : the same run with the two streams kept APART; sets # $sout, $serr and $rc. # # It exists because half of what this suite asserts is WHICH STREAM the text came # out on: the refusal, the usage block and every diagnostic are stderr, and the # version is stdout. That split is the whole difference between "the program # answered" and "the program complained", and a folded capture cannot tell a # caller which happened. Both files are under $TMPDIR, never the rootfs. split() { _t=$1; shift # BST-REDIRECT-OK: $W is under $TMPDIR, never the rootfs. bst_bound "$_t" env -u DISPLAY xset "$@" >"$W/o" 2>"$W/e" rc=$? sout=$(cat "$W/o" 2>/dev/null); serr=$(cat "$W/e" 2>/dev/null) } # withdisp : as split, but with DISPLAY SET. withdisp() { _t=$1; _dpy=$2; shift 2 # BST-REDIRECT-OK: $W is under $TMPDIR, never the rootfs. bst_bound "$_t" env DISPLAY="$_dpy" xset "$@" >"$W/o" 2>"$W/e" rc=$? sout=$(cat "$W/o" 2>/dev/null); serr=$(cat "$W/e" 2>/dev/null) } # --- 1. the packaging -------------------------------------------------------- # Asserted against dpkg's own file list as well as by stat: a binary that reached # the image by some other route satisfies the stat and does not satisfy the claim. check $S/install-path test -x /usr/bin/xset check_eq $S/which "/usr/bin/xset" "$(command -v xset)" check $S/man-installed test -s /usr/share/man/man1/xset.1 pkglist=$(cat /var/lib/dpkg/info/xset.list 2>/dev/null) if [ -n "$pkglist" ]; then bst_ok $S/pkg-list nl=' ' hasline() { case "$nl$3$nl" in *"$nl$2$nl"*) bst_ok "$1" ;; *) bst_notok "$1" "not listed: $2" ;; esac; } hasline $S/pkg-binary /usr/bin/xset "$pkglist" # xorg-app-common.inc's FILES:${PN} would also take ${libdir}/X11/xset and # the app-defaults directory if the package had grown either; xset ships # neither -- bin_PROGRAMS is the one binary and man/ is the one page, and the # page is in xset-doc -- so the one binary IS the package's runtime content. check_eq $S/pkg-one-binary 1 \ "$(printf '%s\n' "$pkglist" | LC_ALL=C grep -c '^/usr/bin/')" else bst_notok $S/pkg-list "no /var/lib/dpkg/info/xset.list -- the packaging cases cannot run" fi # --- 2. the binary ran at all ------------------------------------------------ # THE FIRST THING TO ESTABLISH, and it is the static-link closure's case. # # A main whose recorded needed entries cannot be bound is refused by the loader # before its first instruction, and that refusal arrives as exit 127 (or 126 on # the needed-entry leg) with no output of the program's own -- see # lib/bst-census.sh for why those two statuses and not others. xset failing to # open a display is a status the PROGRAM chose, so a status of 1 with xset's own # diagnostic on stderr is proof that the image bound libXmuu, libXext, libX11, # libxcb, libXau and libXdmcp and entered main(). If the .bbappend's XSET_LIBS # closure were missing, wasmld would have refused the image long before this -- # but if such a binary ever did ship, this is the case that would name it. nodisp 30 q check_eq $S/linked-and-ran "1" "$rc" # --- 3. the documented refusal, with no DISPLAY ------------------------------ # `q' is xset's query mode -- the one sub-command that only READS -- so this is # the program's most ordinary use reaching its documented refusal. split 30 q check_eq $S/nodisplay-rc "1" "$rc" check_eq $S/nodisplay-stderr "$(refusal '')" "$serr" check_eq $S/nodisplay-stdout "" "$sout" # TWICE, IDENTICALLY. A connection setup that scribbles -- an uninitialised # sockaddr, a struct whose wasm32 packing differs from the host's -- can produce # the right answer once and a different one next time. Two runs agreeing is cheap # and is the only case here that could catch that. split 30 q _first=$serr split 30 q check_eq $S/nodisplay-repeatable "$_first" "$serr" # NOTHING IS WRITTEN ON THE WAY OUT. query() is never reached, so a refusing run # must produce no stdout at all -- asserted above -- and it must also not have # got far enough to emit a partial report. A line count of the whole folded # capture pins that: the refusal is one line and nothing else. nodisp 30 q check_eq $S/refusal-is-one-line "1" "$(printf '%s\n' "$out" | wc -l | tr -d ' ')" # --- 4. the refusal with a DISPLAY nothing is listening on ------------------- # TWO TRANSPORTS, and they are different code in libxcb below XOpenDisplay: a TCP # display goes through a connect(2) to a port, a `:N' display through a unix # socket in /tmp/.X11-unix. Either could hang rather than refuse on a target # whose socket layer is new, and a hang is the failure this bound exists to name. # # 15 seconds is far more than a refusal needs and far less than a stalled connect # would take; a 124 here IS the finding, and it is reported as one rather than as # a wrong string. # # The name in the message comes from XDisplayName(NULL) -> getenv("DISPLAY") # here, so these also assert that the reported name is the one that was tried. withdisp 15 127.0.0.1:9 q check_eq $S/tcp-rc "1" "$rc" check_eq $S/tcp-stderr "$(refusal '127.0.0.1:9')" "$serr" check_eq $S/tcp-stdout "" "$sout" withdisp 15 :99 q check_eq $S/unix-rc "1" "$rc" check_eq $S/unix-stderr "$(refusal ':99')" "$serr" check_eq $S/unix-stdout "" "$sout" # --- 5. -version: answered BEFORE the connection, as shipped ----------------- # xset.c:171-174 is # # } else if (strcmp(arg, "-version") == 0) { # puts(PACKAGE_STRING); # exit(EXIT_SUCCESS); # # inside the first loop, which runs before XOpenDisplay(). puts() is stdout and # exit(0) is the status, and both are asserted: a build that somehow reordered # main() would show up here as the refusal, with nothing else in the suite # changing. split 30 -version check_eq $S/version-rc "0" "$rc" check_eq $S/version-stdout "xset 1.2.5" "$sout" check_eq $S/version-stderr "" "$serr" # The version is PACKAGE_STRING, which is AC_INIT's name and version joined -- so # this case is also the recipe's PV asserted end to end, from configure.ac # through the .bb to the shipped binary. # IT STILL ANSWERS WITH A DISPLAY SET TO SOMETHING DEAD, because the connection # is downstream of it. Without this, `version-stdout' above would be equally # consistent with a program that connects first and happens to find no server. withdisp 15 127.0.0.1:9 -version check_eq $S/version-beats-connect-rc "0" "$rc" check_eq $S/version-beats-connect-stdout "xset 1.2.5" "$sout" check_eq $S/version-beats-connect-stderr "" "$serr" # AND IT ANSWERS AFTER A SUB-COMMAND THAT WOULD HAVE NEEDED A SERVER. The first # loop walks the WHOLE list, so a `q' ahead of it only sets hasargs and -version # still wins. This is the case that shows the first loop is a full pass and not # a peek at argv[1]. split 30 q -version check_eq $S/version-after-q-rc "0" "$rc" check_eq $S/version-after-q-stdout "xset 1.2.5" "$sout" # `-v' IS NOT `-version'. The comparison is a full strcmp, so `-v' falls through # to `hasargs = True' and reaches the connection like any other operand -- # upstream's choice, and the kind of thing a later "cleanup" would quietly # change. Note this differs from xlsatoms, where `-v' is a usage error. split 30 -v check_eq $S/v-is-not-version-rc "1" "$rc" check_eq $S/v-is-not-version-stdout "" "$sout" check_eq $S/v-is-not-version-stderr "$(refusal '')" "$serr" # --- 6. the usage block, and the exit status that goes with it --------------- # `if (!hasargs) usage(NULL);' is the OTHER thing answered before the connection, # and it is reached by two different routes: no arguments at all, and arguments # that are ALL consumed by the first loop. # # usage() ENDS `exit(EXIT_SUCCESS)' ON EVERY PATH, including its error path # (xset.c:1647). So a bare `xset' exits 0, and so does `xset -display' with its # argument missing. That is upstream's choice; it is asserted as what the port # ships rather than bent into a `usage is an error' case, and it is the one thing # in this suite most likely to be "corrected" by somebody later. split 30 check_eq $S/bare-rc "0" "$rc" check_eq $S/bare-stdout "" "$sout" case "$serr" in "$USAGE1"*) bst_ok $S/bare-usage-first-line ;; *) bst_notok $S/bare-usage-first-line "unexpected: $serr" ;; esac # `-display' IS consumed by the first loop and does NOT set hasargs -- so a # well-formed `-display' with no other operand ALSO prints usage and exits 0, # without ever attempting the connection it names. This is the case that proves # the first loop's consumption is real rather than inferred. split 15 -display 127.0.0.1:9 check_eq $S/display-only-rc "0" "$rc" check_eq $S/display-only-stdout "" "$sout" case "$serr" in "$USAGE1"*) bst_ok $S/display-only-usage ;; *) bst_notok $S/display-only-usage "unexpected: $serr" ;; esac # ...and specifically NOT the refusal, which is what it would be if -display set # hasargs. Stated as its own case because the two outcomes differ by one line of # main() and by nothing a reader would notice in a diff. case "$serr" in *"unable to open display"*) bst_notok $S/display-only-not-refusal "connected: $serr" ;; *) bst_ok $S/display-only-not-refusal ;; esac # THE MISSING-ARGUMENT DIAGNOSTIC. This is the ONLY diagnostic of xset's own that # an image with no server can observe, because it is the only usage() call in the # first loop (xset.c:169). usage(fmt) prints `xset: ' -- two spaces, from # xset.c:1569 -- then the message, then a blank line, then the whole block. noarg() { _id=$1; _opt=$2 split 30 "$_opt" if [ "$rc" != "0" ]; then bst_notok "$S/noarg-$_id" "rc=$rc, expected 0" elif [ -n "$sout" ]; then bst_notok "$S/noarg-$_id" "wrote to stdout: $sout" else case "$serr" in "xset: missing argument to -display"*) bst_ok "$S/noarg-$_id" ;; *) bst_notok "$S/noarg-$_id" "unexpected: $serr" ;; esac fi } noarg display -display noarg d -d # The diagnostic is followed by the block, on the same stream. Asserted once. split 30 -display case "$serr" in *"$USAGE1"*) bst_ok $S/noarg-usage-follows ;; *) bst_notok $S/noarg-usage-follows "usage() did not print the block" ;; esac # --- 7. every sub-command is ACCEPTED and reaches the same refusal ----------- # THE SWEEP, AND THE WIDEST ONE IN THIS ROUND. Each of these is left alone by the # first loop (it only sets hasargs) and is therefore consumed by the SECOND loop, # which is downstream of the connection -- so each must produce exactly the # connection's answer. # # Passing here does not mean the sub-command was ACTED on. It means nothing # rejected it before the display was reached, which is the strongest statement an # image with no server can make about it -- and it is what keeps an # option-parsing failure distinguishable from a connection failure: a # sub-command that died in the parser would print something else, and the case # would name which. sub_refuses() { _id=$1; shift split 30 "$@" if [ "$rc" = "1" ] && [ "$serr" = "$(refusal '')" ] && [ -z "$sout" ]; then bst_ok "$S/sub-$_id" elif [ "$rc" = "124" ]; then bst_notok "$S/sub-$_id" "did not return inside the bound" else bst_notok "$S/sub-$_id" "rc=$rc out:'$sout' err:'$serr'" fi } # the bell: off, on, and the three-argument form sub_refuses b-off -b sub_refuses b-off-word b off sub_refuses b-zero b 0 sub_refuses b-on b on sub_refuses b-vol b 50 sub_refuses b-vpd b 50 400 100 # keyclick sub_refuses c-off -c sub_refuses c-off-word c off sub_refuses c-on c on sub_refuses c-vol c 50 # auto-repeat sub_refuses r-off -r sub_refuses r-off-word r off sub_refuses r-on r on sub_refuses r-keycode r 24 sub_refuses r-off-key -r 24 # screen saver sub_refuses s-timeout s 600 sub_refuses s-tc s 600 60 sub_refuses s-default s default sub_refuses s-on s on sub_refuses s-off s off sub_refuses s-blank s blank sub_refuses s-noblank s noblank sub_refuses s-expose s expose sub_refuses s-noexpose s noexpose sub_refuses s-activate s activate sub_refuses s-reset s reset # pointer acceleration and threshold sub_refuses m-default m default sub_refuses m-acc m 2 sub_refuses m-acc-thr m 2 4 sub_refuses m-ratio m 3/2 4 # DPMS -- present only because HAVE_X11_EXTENSIONS_DPMS_H is defined; if the # xext header check ever stops finding dpms.h these become `unknown option' with # a server and are indistinguishable here, which is what section 9 is for. sub_refuses dpms-off -dpms sub_refuses dpms-on +dpms sub_refuses dpms-times dpms 600 900 1200 sub_refuses dpms-force dpms force off sub_refuses dpms-forceon dpms force on # the font path, in all five forms sub_refuses fp-set fp= /usr/share/fonts sub_refuses fp-default fp default sub_refuses fp-rehash fp rehash sub_refuses fp-prepend +fp /usr/share/fonts sub_refuses fp-append fp+ /usr/share/fonts sub_refuses fp-remove -fp /usr/share/fonts sub_refuses fp-remove2 fp- /usr/share/fonts # LEDs -- the numeric forms only. `led named ' is XKB-only and is not # compiled in here; section 9 asserts its absence from the usage block. sub_refuses led-on led sub_refuses led-off -led sub_refuses led-n led 3 sub_refuses led-off-n -led 3 # bug-compatibility mode -- MITMisc, and present for the same reason DPMS is. sub_refuses bc-on bc sub_refuses bc-off -bc # lock, pixels, and the query sub_refuses k-on k sub_refuses k-off -k sub_refuses p-pixel p 1 red sub_refuses q q sub_refuses q-dash -q # several at once -- the first loop walks them all and none of them short- # circuits, so the refusal is still the one answer. sub_refuses many b off c off r on s 600 q # --- 8. the diagnostics that CANNOT arrive, asserted as an ABSENCE ----------- # THESE ARE THE CASES MOST LIKELY TO BE MISREAD LATER, so they are stated as what # they are. Every one of them would produce a diagnostic of xset's own on a # machine with a server, and none of them can here, because every validator lives # in the second loop -- downstream of XOpenDisplay(). # # They are NOT evidence that the validators work. They are evidence that they # never run. order_refuses() { _id=$1; shift split 30 "$@" if [ "$rc" = "1" ] && [ "$serr" = "$(refusal '')" ]; then bst_ok "$S/order-$_id" else bst_notok "$S/order-$_id" "rc=$rc err:'$serr'" fi } # `unknown option %s' (xset.c:756) -- unreachable. order_refuses unknown -brintos-no-such-option order_refuses unknown2 brintos-no-such-subcommand order_refuses bare-dash - # `-help' IS NOT AN OPTION. xset has none: it falls through the first loop to # `hasargs = True' and reaches the connection, so on this image it produces the # refusal and NOT the usage block. Asserted explicitly, because `-help prints # usage' is the natural assumption and it is wrong here -- the usage block is # reached by giving NO arguments (section 6), not by asking for it. split 30 -help check_eq $S/help-rc "1" "$rc" check_eq $S/help-stdout "" "$sout" check_eq $S/help-stderr "$(refusal '')" "$serr" # `missing argument to p' (xset.c:737) -- unreachable. order_refuses p-noarg p order_refuses p-onearg p 1 # `invalid pixel number %s' (xset.c:744) -- unreachable. order_refuses p-badpixel p notanumber red # `missing argument to dpms force' (xset.c:538) -- unreachable. order_refuses dpms-force-noarg dpms force # The fp diagnostics (xset.c:349-375) -- `missing fp= argument', `missing +fp # argument', `missing fp+ argument', `missing -fp argument', `missing fp- # argument'. All five unreachable. order_refuses fp-set-noarg fp= order_refuses fp-prepend-noarg +fp order_refuses fp-append-noarg fp+ order_refuses fp-remove-noarg -fp order_refuses fp-remove2-noarg fp- # A non-numeric bell volume. is_number() rejects it and the branch calls # usage(...) -- in the second loop. Unreachable. order_refuses b-nonnumeric b loud # A negative screen-saver timeout, likewise. order_refuses s-negative s -600 # A malformed mouse ratio, likewise. order_refuses m-badratio m 3/0 # --- 9. the build switches, read off the usage block ------------------------- # THE ONLY PLACE ON THIS IMAGE WHERE EXTRA_OECONF IS VISIBLE END TO END. Four # blocks of the usage text are #ifdef'd on header checks configure ran; the # usage block a bare `xset' prints is the compiled-in set, so its presences and # absences ARE the recipe's `--disable-xkb --without-fontcache' and the # xext header probe, asserted from the shipped binary rather than from config.h. # # Measured in this build's config.h: DPMS and MITMisc defined, XKB, FONTCACHE and # XF86MISC not. split 30 usage_has() { case "$serr" in *"$2"*) bst_ok "$S/usage-has-$1" ;; *) bst_notok "$S/usage-has-$1" "usage block does not carry: $2" ;; esac } usage_lacks() { case "$serr" in *"$2"*) bst_notok "$S/usage-lacks-$1" "usage block carries: $2" ;; *) bst_ok "$S/usage-lacks-$1" ;; esac } # unconditional text -- a truncated or garbled usage block is a red here rather # than a pass on the header line alone. usage_has bell 'To turn bell off:' usage_has keyclick 'To turn keyclick off:' usage_has fontpath 'To set the font path:' usage_has led 'To set LED states off or on:' usage_has mouse 'To set mouse acceleration and threshold:' usage_has repeat 'To turn auto-repeat off or on:' usage_has saver 'For screen-saver control:' usage_has status 'For status information: q' usage_has version 'To print version: -version' # MITMisc -- HAVE_X11_EXTENSIONS_MITMISC_H usage_has mitmisc 'To disable bug compatibility mode:' # DPMS -- HAVE_X11_EXTENSIONS_DPMS_H usage_has dpms 'To control Display Power Management Signaling (DPMS) features:' # --without-fontcache: the fc block must be absent. usage_lacks fontcache 'To control font cache:' # --disable-xkb: the named-LED lines must be absent. usage_lacks xkbled "-led named 'name'" # Neither XF86MISC nor XKB: the `r rate' line is guarded on their disjunction, so # its absence is the two switches asserted together. usage_lacks rrate 'r rate [delay [rate]]' # --- 10. -display is consumed pre-connection, and it WINS over $DISPLAY ------ # `-display' is the one option whose first-loop effect is observable on this # image: xset.c:170 stores it and xset.c:182 passes it to XOpenDisplay(), so the # name in the refusal is how a caller can tell the option was parsed. It needs a # second operand to get as far as the connection at all -- see section 6 for why. split 15 -display 127.0.0.1:9 q check_eq $S/display-consumed-rc "1" "$rc" check_eq $S/display-consumed-stderr "$(refusal '127.0.0.1:9')" "$serr" # With BOTH set, the flag wins -- asserted because the precedence is a real # branch (XDisplayName returns its argument when non-NULL) and not a coincidence # of the two producing the same text. withdisp 15 :99 -display 127.0.0.1:9 q check_eq $S/display-beats-env "$(refusal '127.0.0.1:9')" "$serr" # `-d' is the documented short form and takes the argument the same way. split 15 -d 127.0.0.1:9 q check_eq $S/d-alias-stderr "$(refusal '127.0.0.1:9')" "$serr" # THE LAST -display WINS: the first loop overwrites `disp' each time it sees one. split 15 -display :98 -display 127.0.0.1:9 q check_eq $S/display-last-wins "$(refusal '127.0.0.1:9')" "$serr" # ...and a -display AFTER the sub-command is still consumed by the first loop, # which is a full pass over argv rather than a prefix scan. split 15 q -display 127.0.0.1:9 check_eq $S/display-after-sub "$(refusal '127.0.0.1:9')" "$serr" rm -rf "$W" 2>/dev/null