. "$BST_LIB" # BST-TIMEOUT: 420 # ~100 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. Replace with a measured # elapsed once this suite has run on target a few times. # 57-xsetroot.test -- xsetroot 1.1.3 (meta-oe recipes-graphics/xorg-app): the # root-window parameter setting utility. It sets the root window's background, # its cursor and its name. See recipes-graphics/xorg-app/xsetroot_%.bbappend # for what the port changes -- a static-link closure, and nothing else. # # THERE IS NO ROOT WINDOW HERE. xsetroot is an X client through and through, not # a file editor filed under xorg-app the way xauth and iceauth are: every # attribute it sets lives on a server, and there is no server on this image. So # what is reachable is its OPTION PARSER and its REFUSAL, and the whole of this # suite is about those two things and the boundary between them. # # THE BOUNDARY IS THE POINT, AND IT IS THE OPPOSITE WAY ROUND FROM xprop'S. # xprop (BRINTOS-2461) calls Setup_Display_And_Screen() BEFORE its own option # loop, so on this image even `xprop -version' dies at the connection and its # port needed a patch to get anything else out of the binary. xsetroot is built # the other way: main()'s whole argv loop (xsetroot.c:126-220) runs to # completion, THEN XOpenDisplay is called at :230. That single ordering fact # decides every case below, so the suite asserts it in BOTH directions: # # PRE-CONNECT, answering as themselves -- `-version' (:135, stdout, exit 0), # `-help' (:96, stderr, exit 1), an unrecognized argument (:217), a missing # option argument (usage() with its own message), and the # {solid,gray,bitmap,mod} conflict check (:224). None of these needs a server # and none of them is patched in; they work as shipped. # # POST-CONNECT, replaced by the refusal -- everything that reads a file or # parses a colour. ReadBitmapFile for `-bitmap' (:284), # CreateCursorFromFiles for `-cursor' (:243), XcursorFilenameLoadImages for # `-xcf' (:258), NameToPixel for `-solid' (:277). All of them run only after # XOpenDisplay has SUCCEEDED. # # SO THE READING LOSES THE RACE WITH THE CONNECTING, and that is the question # BRINTOS-2467 was opened to settle. A malformed or absent bitmap is NOT # reportable on this image: # # xsetroot -bitmap /nonexistent -> exit 2, "unable to open display ''" # -- NOT exit 1, ":549 can't open file" # # The order-* cases below assert exactly that, against a committed VALID bitmap # and a committed MALFORMED one, so the two inputs that would be told apart by a # reachable reader are measured to be indistinguishable here. They are the cases # that would go red the day this image grows a server -- which is the right # time to be told. # # THE REFUSAL ITSELF IS THE PORT'S OWN CASE. A wasm32 socket-address or # struct-packing mistake lands in connection setup, and it lands there as a # crash, a hang, or a diagnostic that is not this one. Every refusal case is # therefore the same four-part shape: the documented text on STDERR, nothing on # STDOUT, status 2, and inside a bound. A red names which of the four the target # got wrong. # # EXIT 2, NOT 1. xsetroot.c:233 is `exit (2)' -- unlike xprop's dsimple.c, which # exits 1 from the same-looking diagnostic. Writing 1 here would make every # refusal case red against a correct binary. # # EVERY EXPECTATION BELOW IS AN x86-64 MEASUREMENT, NOT A READING OF THE SOURCE. # The host used to author this port ships xsetroot 1.1.3 -- the SAME version the # recipe pins -- so each case was run against that binary with DISPLAY removed # before it was written here: the refusal text and its two spaces, exit 2, the # stream each diagnostic uses, all sixteen argument-error messages in upstream's # own non-uniform wording, the {solid,gray,bitmap,mod} text, the fifteen usage # lines, and the -bitmap/-solid orderings. That is what a phase-A suite can # honestly assert without a target: it removes typo-shaped reds from the first # on-target run, so a red that DOES arrive is about wasm32 rather than about the # expectation. # # WHAT THE CONTROL CANNOT SAY, and the target run is what settles it: whether # wasm32's connect path reaches this refusal at all rather than crashing or # hanging (sections 5 and 6 bound it for exactly that), and the packaging cases # in section 1, which are about this image's .deb and have no x86-64 analogue. # # WHY THE OPTION SWEEP IS WORTH ITS CASES. Each of -def, -solid, -gray, -grey, # -bg, -fg, -rv, -bitmap, -mod, -cursor, -cursor_name, -xcf and -name reaching # the SAME refusal is what keeps an option-parsing failure distinguishable from # a connection failure. If any one of them ever answers something else, the # difference is the finding. S=xsetroot require xsetroot $S/present W=${TMPDIR:-/tmp}/bst-xsetroot.$$ mkdir -p "$W" 2>/dev/null check $S/tmp-writable sh -c "printf x > $W/.w && rm -f $W/.w" nl=' ' # The exact refusal, built from the display name the run should report. # # TWO SPACES after the colon: xsetroot.c:231 is # `fprintf(stderr, "%s: unable to open display '%s'\n", ...)'. Writing one # would make every case below red for a typo in the expectation. # # The `%s' is program_name, and :124 is `program_name = argv[0]' with no # basename() -- so this is `xsetroot' only because every invocation here goes # through PATH. A case that ran /usr/bin/xsetroot by path would expect the path. refusal() { printf "xsetroot: unable to open display '%s'" "$1"; } # first : its first line, with shell builtins only (no fork). first() { case "$1" in *"$nl"*) printf '%s' "${1%%$nl*}" ;; *) printf '%s' "$1" ;; esac; } # split : run xsetroot with DISPLAY REMOVED, bounded, keeping # the two streams APART; sets $sout, $serr and $rc. # # `env -u DISPLAY' rather than `DISPLAY= ': XDisplayName() treats an EMPTY # DISPLAY and an ABSENT one alike, so both reach the same diagnostic -- but only # the absent one is the state this image actually boots in, and asserting the # state we ship is the point. # # Keeping the streams apart is half of what this suite asserts: the version is # stdout and every diagnostic is stderr, and a folded capture cannot tell a # caller that a diagnostic started arriving on stdout. Both files are under # $TMPDIR, never the rootfs. split() { _t=$1; shift bst_bound "$_t" env -u DISPLAY xsetroot "$@" >"$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_bound "$_t" env DISPLAY="$_dpy" xsetroot "$@" >"$W/o" 2>"$W/e" rc=$? sout=$(cat "$W/o" 2>/dev/null); serr=$(cat "$W/e" 2>/dev/null) } # refuses : the whole four-part refusal shape, as three # cases. Used by the option sweep, where the assertion is identical every time # and only the argv changes. refuses() { _rid=$1; _rt=$2; shift 2 split "$_rt" "$@" check_eq "$S/$_rid-rc" "2" "$rc" check_eq "$S/$_rid-stderr" "$(refusal '')" "$serr" check_eq "$S/$_rid-stdout" "" "$sout" } # rejects : the pre-connect # diagnostic shape -- status 1, the tool's own message as the FIRST stderr line, # the usage block behind it, and nothing on stdout. # # The first line only, not the whole blob: usage() prints a 15-line option list # and pinning it per case would assert the help text fifteen times over. That # text is asserted ONCE, in the -help cases below. rejects() { _jid=$1; _jt=$2; _jwant=$3; shift 3 split "$_jt" "$@" check_eq "$S/$_jid-rc" "1" "$rc" check_eq "$S/$_jid-stderr" "$_jwant" "$(first "$serr")" check_eq "$S/$_jid-stdout" "" "$sout" case "$serr" in *"Usage: xsetroot [options]"*) bst_ok "$S/$_jid-usage" ;; *) bst_notok "$S/$_jid-usage" "no usage block behind the diagnostic: $(_bst_1line "$serr")" ;; esac } # --- 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/xsetroot check_eq $S/which "/usr/bin/xsetroot" "$(command -v xsetroot)" check $S/man-installed test -s /usr/share/man/man1/xsetroot.1 pkglist=$(cat /var/lib/dpkg/info/xsetroot.list 2>/dev/null) if [ -n "$pkglist" ]; then bst_ok $S/pkg-list 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/xsetroot "$pkglist" # xorg-app-common.inc's FILES:${PN} would also take ${libdir}/X11/xsetroot # and the app-defaults directory if the package had grown either; xsetroot # ships neither, 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/xsetroot.list -- the packaging cases cannot run" fi # The man page is a SEPARATE package, and naming it in the roster is a decision # a split could silently undo. doclist=$(cat /var/lib/dpkg/info/xsetroot-doc.list 2>/dev/null) if [ -n "$doclist" ]; then bst_ok $S/pkg-doc-list hasline $S/pkg-doc-man /usr/share/man/man1/xsetroot.1 "$doclist" else bst_notok $S/pkg-doc-list "no /var/lib/dpkg/info/xsetroot-doc.list" fi # dpkg's own view of the install, which is what "the .deb installs on target" # means as an assertion rather than as a claim. sed -n '/^Package: xsetroot$/,/^$/p' /var/lib/dpkg/status > "$W/st" 2>/dev/null check $S/pkg-known test -s "$W/st" check_out $S/pkg-installed "Status: install ok installed" cat "$W/st" check_out $S/pkg-arch "Architecture: wasm32" cat "$W/st" # --- 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. xsetroot failing # to open a display is a status the PROGRAM chose, so a status of 2 carrying # xsetroot's own diagnostic is proof that the image bound libXmuu, libX11, # libxcb, libXau, libXcursor, libXrender, libXfixes and libXdmcp and entered # main(). If the .bbappend's XSETROOT_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. split 30 -def check_eq $S/linked-and-ran "2" "$rc" # --- 3. -version: the one thing that answers on stdout ----------------------- # xsetroot.c:135-136 is inside the argv loop, so it answers BEFORE any connect # and needs no patch. PACKAGE_STRING is autoconf's ` ' from # AC_INIT, which is what pins the shipped version to the recipe's PV. split 30 -version check_eq $S/version-rc "0" "$rc" check_eq $S/version-stdout "xsetroot 1.1.3" "$sout" check_eq $S/version-stderr "" "$serr" # -version WINS OVER A LATER OPTION, because the loop exits at it. Without this # the suite would not have measured that the answer is an exit rather than a # flag consulted afterwards. split 30 -version -solid red check_eq $S/version-first-rc "0" "$rc" check_eq $S/version-first-stdout "xsetroot 1.1.3" "$sout" # ...and it does NOT win over an earlier hard error, for the same reason: the # loop reaches the unrecognized argument first and never gets to -version. split 30 -nosuchopt -version check_eq $S/version-after-error-rc "1" "$rc" # --- 4. -help: the usage text, asserted once --------------------------------- # usage(NULL) prints to STDERR and exits 1 -- not stdout and not 0. That is # upstream's choice for `-help' and the suite asserts what ships rather than # what would be conventional. split 30 -help check_eq $S/help-rc "1" "$rc" check_eq $S/help-stdout "" "$sout" check_eq $S/help-first "Usage: xsetroot [options]" "$(first "$serr")" # The option list itself, in one pass. Every name here is an option the sweep # below drives, so a help text that stopped documenting one of them would be # caught beside the option still working. for _o in -help -version -def -cursor -cursor_name -xcf -bitmap -mod -gray \ -bg -fg -rv -solid -name -display; do case "$serr" in *" $_o"*) bst_ok "$S/help-documents$_o" ;; *) bst_notok "$S/help-documents$_o" "not in the usage text" ;; esac done # usage() emits no errmsg line when it is called with NULL, so the FIRST line is # the Usage: line and there is no blank line before it. `-help' and an argument # error differ in exactly that, which is what makes `rejects' able to tell them # apart. check_eq $S/help-no-errmsg-line "Usage: xsetroot [options]" "$(first "$serr")" # --- 5. the documented refusal, with no DISPLAY ------------------------------ split 30 -def check_eq $S/nodisplay-rc "2" "$rc" check_eq $S/nodisplay-stderr "$(refusal '')" "$serr" check_eq $S/nodisplay-stdout "" "$sout" # A bare `xsetroot' -- no arguments at all -- takes the same path. It is worth # its own case because :239 makes it a DIFFERENT program: with no excl and no # nonexcl option it sets restore_defaults itself. That branch is after the # connect, so the observable answer is identical, and asserting so is what says # the argument-less path was not overlooked. split 30 check_eq $S/bare-rc "2" "$rc" check_eq $S/bare-stderr "$(refusal '')" "$serr" check_eq $S/bare-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 -def _firstrun=$serr split 30 -def check_eq $S/nodisplay-repeatable "$_firstrun" "$serr" # --- 6. the refusal with a DISPLAY nothing is listening on ------------------- # TWO TRANSPORTS, and they are different code in libX11's xtrans: 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. withdisp 15 127.0.0.1:9 -def check_eq $S/tcp-rc "2" "$rc" check_eq $S/tcp-stderr "$(refusal '127.0.0.1:9')" "$serr" check_eq $S/tcp-stdout "" "$sout" withdisp 15 :99 -def check_eq $S/unix-rc "2" "$rc" check_eq $S/unix-stderr "$(refusal ':99')" "$serr" check_eq $S/unix-stdout "" "$sout" # `-display ' on the COMMAND LINE, which is a different variable from the # environment one (display_name at :129 rather than XDisplayName's getenv), and # it must win. The refusal naming the argument is what proves the option was # consumed rather than ignored. split 15 -display 127.0.0.1:9 -def check_eq $S/opt-display-rc "2" "$rc" check_eq $S/opt-display-stderr "$(refusal '127.0.0.1:9')" "$serr" # -d is the documented short form of the same option. split 15 -d 127.0.0.1:9 -def check_eq $S/opt-d-rc "2" "$rc" check_eq $S/opt-d-stderr "$(refusal '127.0.0.1:9')" "$serr" # The command line beats the environment, in that order. withdisp 15 :99 -display 127.0.0.1:9 -def check_eq $S/opt-display-wins "$(refusal '127.0.0.1:9')" "$serr" # --- 7. the option sweep ----------------------------------------------------- # Every option the parser accepts, each asserted to be CONSUMED (no usage, no # `unrecognized argument') and to reach the same refusal. This is what keeps an # option-parsing failure distinguishable from a connection failure. refuses opt-def 30 -def refuses opt-default 30 -default refuses opt-solid 30 -solid red refuses opt-gray 30 -gray refuses opt-grey 30 -grey refuses opt-bg 30 -bg white refuses opt-background 30 -background white refuses opt-fg 30 -fg black refuses opt-foreground 30 -foreground black refuses opt-rv 30 -rv refuses opt-reverse 30 -reverse refuses opt-name 30 -name bst-root refuses opt-mod 30 -mod 4 4 refuses opt-cursor_name 30 -cursor_name left_ptr # -bitmap, -cursor and -xcf name FILES, and the files exist. That is what makes # these three cases about the ORDERING rather than about a missing file -- see # section 8, which poses the same options against files that are absent or # malformed and measures the identical answer. XBM=$BST_DATA/xsetroot-gray.xbm BAD=$BST_DATA/xsetroot-malformed.xbm check $S/fixture-valid-present test -s "$XBM" check $S/fixture-malformed-present test -s "$BAD" refuses opt-bitmap 30 -bitmap "$XBM" refuses opt-cursor 30 -cursor "$XBM" "$XBM" refuses opt-xcf 30 -xcf "$XBM" 32 # Two non-exclusive options together are accepted -- nonexcl is a COUNT, not a # flag, so it has no conflict check of its own. refuses opt-two-nonexcl 30 -name bst-root -cursor_name left_ptr # ...and a non-exclusive option beside an exclusive one likewise. refuses opt-mixed 30 -gray -name bst-root # --- 8. THE ORDERING, which is what this ticket asked ------------------------ # Each of these poses an input a REACHABLE reader would reject, and asserts that # the connect refusal arrives instead. They are the cases that answer "which of # the reading and the connecting happens first". # A bitmap file that does not exist. ReadBitmapFile (:549) would say # `can't open file' and exit 1; it never runs. split 30 -bitmap /nonexistent/bst-xsetroot.xbm check_eq $S/order-bitmap-missing-rc "2" "$rc" check_eq $S/order-bitmap-missing-stderr "$(refusal '')" "$serr" case "$serr" in *"can't open file"*) bst_notok $S/order-bitmap-missing-not-read \ "ReadBitmapFile ran BEFORE XOpenDisplay -- the ordering this port documented has changed" ;; *) bst_ok $S/order-bitmap-missing-not-read ;; esac # A bitmap file that exists and is MALFORMED. ReadBitmapFile (:551) would say # `bad bitmap format file' and exit 1; it never runs either -- so a malformed # bitmap and an absent one are INDISTINGUISHABLE on this image, which is the # ticket's answer stated as a measurement. split 30 -bitmap "$BAD" check_eq $S/order-bitmap-malformed-rc "2" "$rc" check_eq $S/order-bitmap-malformed-stderr "$(refusal '')" "$serr" case "$serr" in *"bad bitmap format"*) bst_notok $S/order-bitmap-malformed-not-read \ "ReadBitmapFile parsed the file BEFORE XOpenDisplay -- ordering changed" ;; *) bst_ok $S/order-bitmap-malformed-not-read ;; esac # THE TWO ARE THE SAME ANSWER, asserted directly rather than left to a reader # comparing two cases above. This is the single case that says "not reportable". split 30 -bitmap /nonexistent/bst-xsetroot.xbm _missing=$serr split 30 -bitmap "$BAD" check_eq $S/order-bitmap-indistinguishable "$_missing" "$serr" # A valid bitmap reaches the same place, so the file's CONTENT is not consulted # at all. Three inputs, one answer. split 30 -bitmap "$XBM" check_eq $S/order-bitmap-valid-same "$_missing" "$serr" # An unparseable colour. NameToXColor (:520) would say `unknown color' and # exit 1. split 30 -solid nosuchcolourname check_eq $S/order-solid-rc "2" "$rc" check_eq $S/order-solid-stderr "$(refusal '')" "$serr" case "$serr" in *"unknown color"*) bst_notok $S/order-solid-not-parsed \ "NameToPixel ran BEFORE XOpenDisplay -- ordering changed" ;; *) bst_ok $S/order-solid-not-parsed ;; esac # A cursor file that does not exist -- CreateCursorFromFiles (:415) is likewise # out of reach. split 30 -cursor /nonexistent/c.xbm /nonexistent/m.xbm check_eq $S/order-cursor-missing-rc "2" "$rc" check_eq $S/order-cursor-missing-stderr "$(refusal '')" "$serr" # An Xcursor file that is not one. :260's `Invalid cursor file' is the one # post-connect diagnostic that does NOT exit -- it warns and carries on -- so it # is worth naming separately: out of reach here for the same reason, but it # would not have changed the status even if reached. split 30 -xcf "$BAD" 32 check_eq $S/order-xcf-rc "2" "$rc" check_eq $S/order-xcf-stderr "$(refusal '')" "$serr" case "$serr" in *"Invalid cursor file"*) bst_notok $S/order-xcf-not-read \ "XcursorFilenameLoadImages ran BEFORE XOpenDisplay -- ordering changed" ;; *) bst_ok $S/order-xcf-not-read ;; esac # A cursor-font name that is not one. CreateCursorFromName -> :504's `unknown # color or bad color format' / XmuCursorNameToIndex are both post-connect. split 30 -cursor_name nosuchcursorname check_eq $S/order-cursor-name-rc "2" "$rc" check_eq $S/order-cursor-name-stderr "$(refusal '')" "$serr" # --- 9. -mod's atoi, which is NOT an argument error -------------------------- # THE ONE OPTION WHOSE BAD INPUT IS SILENTLY ACCEPTED. :204-209 is # `mod_x = atoi(argv[i]); if (mod_x <= 0) mod_x = 1;' -- atoi returns 0 for a # non-numeric string and there is no errno check, so `-mod foo bar' becomes # `-mod 1 1' and proceeds to the connect. It is NOT diagnosed, and a reader # expecting the ticket's "non-numeric" case to be an argument error would have # it backwards; only a MISSING argument is one (section 10). refuses mod-nonnumeric 30 -mod foo bar refuses mod-zero 30 -mod 0 0 refuses mod-negative 30 -mod -3 -3 # `-mod -3 -3' is the case that shows the clamp is on the VALUE and not on the # leading `-': a parser treating -3 as an option would have said # `unrecognized argument', and the refusal above says it did not. # --- 10. the pre-connect argument errors ------------------------------------- # Status 1, the tool's own first line, the usage block behind it, nothing on # stdout. Every one of these is diagnosed BEFORE any connect is attempted, so # they are the half of the boundary that answers as itself. rejects unknown-opt 30 "xsetroot: unrecognized argument '-nosuchopt'" -nosuchopt # A bare non-option word is the same path -- there are no positional arguments. rejects stray-word 30 "xsetroot: unrecognized argument 'stray'" stray # ...including one that looks like a long option. rejects gnu-style 30 "xsetroot: unrecognized argument '--help'" --help # A missing argument for each option that takes one. usage(errmsg) prints # `: ' as its first line, so these assert upstream's own # wording -- which is not uniform, and that is upstream's business rather than # something for this port to normalise. rejects miss-display 30 "xsetroot: -display requires an argument" -display rejects miss-d 30 "xsetroot: -display requires an argument" -d rejects miss-name 30 "xsetroot: -name requires an argument" -name rejects miss-fg 30 "xsetroot: -foreground requires an argument" -fg rejects miss-foreground 30 "xsetroot: -foreground requires an argument" -foreground rejects miss-bg 30 "xsetroot: -background requires an argument" -bg rejects miss-background 30 "xsetroot: -background requires an argument" -background rejects miss-solid 30 "xsetroot: -solid requires an argument" -solid rejects miss-bitmap 30 "xsetroot: -bitmap requires an argument" -bitmap rejects miss-cursor-name 30 "xsetroot: -cursor_name requires an argument" -cursor_name # -cursor and -xcf take TWO arguments and have a distinct message per position. rejects miss-cursor-both 30 \ "xsetroot: missing cursorfile & maskfile arguments for -cursor" -cursor rejects miss-cursor-mask 30 \ "xsetroot: missing maskfile argument for -cursor" -cursor /tmp/c.xbm rejects miss-xcf-both 30 \ "xsetroot: missing cursorfile & cursorsize arguments for -xcf" -xcf rejects miss-xcf-size 30 \ "xsetroot: missing cursorsize argument for -xcf" -xcf /tmp/c.xbm rejects miss-mod-both 30 \ "xsetroot: missing x & y arguments for -mod" -mod rejects miss-mod-y 30 \ "xsetroot: missing y argument for -mod" -mod 4 # THE EXCLUSIVE-OPTION CONFLICT, which is the one diagnostic emitted AFTER the # loop and still before the connect (:222-227). It counts rather than # remembering which, so any two of the four collide. rejects excl-solid-gray 30 \ "xsetroot: choose only one of {solid, gray, bitmap, mod}" -solid red -gray rejects excl-bitmap-mod 30 \ "xsetroot: choose only one of {solid, gray, bitmap, mod}" -bitmap "$XBM" -mod 4 4 rejects excl-gray-bitmap 30 \ "xsetroot: choose only one of {solid, gray, bitmap, mod}" -grey -bitmap "$XBM" rejects excl-three 30 \ "xsetroot: choose only one of {solid, gray, bitmap, mod}" -solid red -gray -mod 4 4 # THE COUNT IS OVER OCCURRENCES, NOT OVER DISTINCT OPTIONS, which is why the # same option twice collides with itself: -gray sets a flag AND increments excl, # so `-gray -gray' reaches excl == 2 exactly as `-solid red -gray' does. That is # upstream's shape and it is worth an assertion because the message names four # option names and thereby reads as a check for two DIFFERENT ones. rejects excl-gray-twice 30 \ "xsetroot: choose only one of {solid, gray, bitmap, mod}" -gray -gray # The conflict beats the connect, which is the ordering statement for this # diagnostic specifically: it is emitted at :224 and the connect is at :230. split 30 -solid red -gray case "$serr" in *"unable to open display"*) bst_notok $S/excl-before-connect \ "the connect was attempted before the {solid,gray,bitmap,mod} conflict was diagnosed" ;; *) bst_ok $S/excl-before-connect ;; esac rm -rf "$W" 2>/dev/null