. "$BST_LIB" # BST-TIMEOUT: 420 # two probe runs plus ~110 shell extractions. The static # run is syscall-free apart from two failing display # opens; the loop run is bounded by its own gate and by # three timers totalling 180 ms -- UNLESS this arm's poll # timeout is ignored, in which case Xt reaches each # deadline by spinning and the same 180 ms costs real # wall clock. The bound is headroom for that, not a cap on # anything this suite is known to approach. # libXt 1.3.0 -- the X Toolkit Intrinsics (poky recipes-graphics/xorg-lib). # See libxt_%.bbappend for what the port adds and why. # # --------------------------------------------------------------------------- # THERE IS NO DISPLAY ON THIS IMAGE AND THERE WILL NOT BE ONE # # libXt is the framework Xaw, Motif and the long tail of classic X applications # are built on, and nearly all of its surface needs a widget -- which needs a # display. So what this suite has to answer is not "does the toolkit work" but # "is this library CORRECT ON THIS ARM for the day a toolkit binds to it". # # That question has real content and none of it needs a display: # # THE ABI XtResource, Arg, XtCallbackRec, XtActionsRec and # XtConvertArgRec are filled in BY THE CALLER. A widget set # writes a static XtResource[] in its own translation unit and # hands the address across, so the layout is a contract between # two separately compiled objects. On wasm32 a pointer and a # long are 4 bytes; on the build host, 8. # THE PARSER XtParseTranslationTable and XtParseAcceleratorTable are pure # text-to-state-tree compilers. Their contract is exact: a NULL # source returns NULL and does not warn, any other source # returns a table, and a malformed production warns. # THE CACHE XtDirectConvert takes no Display, so the real conversion cache # can be driven and asserted to HIT. # THE CONVERTERS the standard string-to-type converters never touch their # Display argument except to warn, so their success path and # their too-small-buffer path are both reachable with a NULL # one. Their FAILURE path is not, and is not attempted. # # --------------------------------------------------------------------------- # THE EVENT LOOP IS THE ONE PLATFORM QUESTION, AND IT IS ASKED LAST # # Xt's wait is one line: NextEvent.c's IoWait() is `poll(fdlist, fdlistlen, # poll_wait)' under USE_POLL and a select(2) without it. libXt contains no epoll # call anywhere, so epoll_create's ENOSYS on this arm (brintos/linux#53) cannot # reach it -- unlike nng, nanomsg and mosquitto, where denying epoll WAS the # port. Section 5 asserts which primitive this build actually got, read out of # the config.h the build produced rather than out of the source. # # What IS exposed here is the relative timer, not the multiplexer # (brintos/linux-in-the-browser#120 -- the same defect that makes sleep(1) a # no-op; timeout(1) never fired before the brintos/linux#77 fix). Two outcomes # follow, and section 11 tells # them apart instead of assuming either: # # TIMEOUT IGNORED _XtWaitForSomething reports `timed out' at once, # XtAppProcessEvent re-reads the wall clock and waits again. # A spin rather than a sleep -- slow, but CORRECT, and it # terminates as long as the clock advances. The timers still # fire, in order, and section 11 goes green. # poll(2) FAILING with anything but EINTR/EAGAIN, NextEvent.c warns and # `continue's -- a loop with no exit. # # THE PROBE IS RUN TWICE FOR THAT REASON. The second, gated run is the only # thing on this image that can hang, and it is started only after every case in # sections 1 to 10 has already been emitted -- so if the guest wedges there, the # runner's own kill reports `libxt/suite-timeout' after ~100 recorded cases # rather than after none. The probe gates itself as well: it measures poll(2) # and the clock in its own code and refuses to enter Xt if either is unsound, # which turns a wedge into a reported non-result. # # --------------------------------------------------------------------------- # READ A RED IN SECTION ORDER; THE FIRST SECTION TO GO RED LOCALISES THE DEFECT # # 1 the files arrived packaging only # 2 xt.pc resolves consumers find the library at all # 3 the probe runs to its end the linked code executes here # 4 the data model and the ABI struct layout on this arm # 5 the wait primitive which defect the loop is exposed to # 6 the translation parser the text compiler, valid and malformed # 7 the standard converters resource conversion arithmetic # 8 the conversion cache the registry, not its shape # 9 argument and varargs lists what a caller packs # 10 the app context and the refusal XtOpenDisplay with nothing to open # 11 the event loop THE PLATFORM CASE -- runs last # # A build where 3 passes and 4 does not has a struct-layout fault and nothing # below it can be trusted. 7 failing while 4 passes is arithmetic, not layout. # 11 failing on its own is the platform's, not libXt's -- and section 5 says # which platform defect to read it against. # --------------------------------------------------------------------------- # A root prefix, empty on the target and set only when this suite is validated # NATIVELY under dash before it ships. The runner never sets it, so on the image # every path below is exactly the absolute one. R="${BST_LIBXT_ROOT:-}" INC=$R/usr/include/X11 PCD=$R/usr/lib/pkgconfig LIB=$R/usr/lib XTT=$R/usr/libexec/libxt-tests work=${TMPDIR:-/tmp}/bst-libxt.$$ rm -rf "$work"; mkdir -p "$work" hasfile() { if [ -s "$2" ]; then bst_ok "$1"; else bst_notok "$1" "missing or empty: $2"; fi } # xt_line # # The prefix is matched with grep -F rather than the whole line, so a red # reports WHAT THE PROBE ACTUALLY SAID for that case instead of merely # "absent". Every prefix is unique by construction: each label is followed by a # separator that ends it. xt_line() { _l=$(grep -F -- "$2" "$work/out" 2>/dev/null | head -1) check_eq "$1" "$2$3" "$_l" } # xt_warned # # For the malformed inputs only, where the assertion is `at least one warning' # rather than an exact count. That is deliberate and is not vagueness: one # malformed production raises Syntax(), then ShowLine(), then # XtParseTranslationTable's own summary warning, so the count is THREE today and # is upstream's to change on any release. What must never change is that a # rejected production warns at all, and that a valid one does not -- which is # why the valid cases below are pinned to warn=0 exactly. xt_warned() { _l=$(grep -F -- "$2" "$work/out" 2>/dev/null | head -1) _n=${_l##*warn=} case "$_n" in ''|*[!0-9]*) bst_notok "$1" "no warn= count in '$(_bst_1line "$_l")'" ;; 0) bst_notok "$1" "accepted a malformed table: '$(_bst_1line "$_l")'" ;; *) bst_ok "$1" ;; esac } # --------------------------------------------------------------------------- # 0. THE CANARY, which is not an assertion about libXt at all. # # The guest console drops a suite's FIRST BST result line before the host parser # sees it -- brintos/linux#96. The harness computes its pass count as # total-minus-failures, so THE DROPPED LINE IS COUNTED AS A PASS, and a suite # whose first case is a genuine `not ok' loses the one line that mattered. So # the first line out of this file asserts something already true. # Remove this case when #96 is fixed. bst_ok libxt/suite-canary # --------------------------------------------------------------------------- # 1. THE PAYLOAD. # # Four headers by name out of the thirty-three installed. Intrinsic.h and # IntrinsicP.h are what every widget includes; StringDefs.h and Shell.h are # GENERATED by util/makestrs into the build tree rather than shipped in the # tarball, which makes losing one of them invisible in the source and visible # only here. check_rc libxt/dir-X11 0 test -d "$INC" hasfile libxt/hdr-Intrinsic-h "$INC/Intrinsic.h" hasfile libxt/hdr-IntrinsicP-h "$INC/IntrinsicP.h" hasfile libxt/hdr-StringDefs-h "$INC/StringDefs.h" hasfile libxt/hdr-Shell-h "$INC/Shell.h" hasfile libxt/hdr-Composite-h "$INC/Composite.h" hasfile libxt/hdr-Core-h "$INC/Core.h" hasfile libxt/hdr-Vendor-h "$INC/Vendor.h" # The census, bounded FROM BELOW and pinned in the recipe as well. 33 is the # complete default set: 31 shipped headers plus the two generated ones. nh=$(find "$INC" -maxdepth 1 -type f -name '*.h' 2>/dev/null | wc -l) if [ "${nh:-0}" -ge 33 ]; then bst_ok libxt/hdr-count else bst_notok libxt/hdr-count "found ${nh:-0} headers under X11, expected not less than 33"; fi # THE ARCHIVE, AND THE ABSENCE OF A SHARED OBJECT. This distro appends # --disable-shared to every class-target configure, so libtool produces libXt.a # and no libXt.so -- which is why ${PN} is empty, why the image names # libxt-staticdev rather than a bare libxt, and why the probe is statically # linked. A libXt.so appearing here would mean the distro-wide flag stopped # reaching this recipe, and every consumer's link line would change silently. hasfile libxt/archive "$LIB/libXt.a" # Branched per arm, as 57-libxcb is: on the converted arm the same sentence reads # "whatever shared object shipped must be usable". if bst_ps_converted "$LIB/libXt.so" "$LIB/libXt.so.*"; then bst_lib_shape libxt/shared-usable "$LIB/libXt.so*" else check_rc libxt/no-shared-object 1 test -e "$LIB/libXt.so" fi hasfile libxt/probe-shipped "$XTT/libxt-probe" hasfile libxt/ctx-probe-shipped "$XTT/libxt-ctx-probe" # THE STACK UNDER IT, asserted here because it is a cross-package fact no libXt # file records: Intrinsic.h includes X11/Xlib.h on its first page, and libXt's # own objects call into libSM and libICE unconditionally. A consumer that has # libXt's headers without those cannot compile a line, and one that links -lXt # without them gets undefined symbols nobody would attribute to this package. check_rc libxt/xlib-reachable 0 test -f "$R/usr/include/X11/Xlib.h" check_rc libxt/smlib-reachable 0 test -f "$R/usr/include/X11/SM/SMlib.h" check_rc libxt/icelib-reachable 0 test -f "$R/usr/include/X11/ICE/ICElib.h" # --------------------------------------------------------------------------- # 2. xt.pc -- how a consumer finds any of the above. hasfile libxt/pc-in-libdir "$PCD/xt.pc" # NOT `require pkg-config'. require ENDS THE FILE when the tool is absent, and # everything this suite exists for -- sections 3 to 11 -- comes after this one. if command -v pkg-config >/dev/null 2>&1; then bst_ok libxt/pkg-config-present check libxt/pc-exists pkg-config --exists xt check libxt/pc-validate pkg-config --validate xt check_eq libxt/pc-modversion 1.3.0 "$(pkg-config --modversion xt 2>/dev/null)" # x11 is on Requires and ice/sm are on Requires.PRIVATE -- upstream puts the # latter there because a consumer of the SHARED library need not name them, # and this distro has no shared library at all. So on THIS image the private # line is the load-bearing one, and it is asserted with --static, which is # what every consumer here is. check libxt/pc-static-libs-name-SM sh -c \ "PKG_CONFIG_ALLOW_SYSTEM_LIBS=1 pkg-config --static --libs xt | grep -q -- -lSM" check libxt/pc-static-libs-name-ICE sh -c \ "PKG_CONFIG_ALLOW_SYSTEM_LIBS=1 pkg-config --static --libs xt | grep -q -- -lICE" check libxt/pc-static-libs-name-X11 sh -c \ "PKG_CONFIG_ALLOW_SYSTEM_LIBS=1 pkg-config --static --libs xt | grep -q -- -lX11" # PKG_CONFIG_ALLOW_SYSTEM_LIBS is required for the same reason # 57-xtrans.test needs its CFLAGS counterpart: pkg-config SUPPRESSES a -L # naming a directory already on the linker's system path, so the bare query # answers correctly and uselessly. The variable asks for the flag pkg-config # COMPUTED rather than the one it decided to print. libs=$(PKG_CONFIG_ALLOW_SYSTEM_LIBS=1 pkg-config --libs xt 2>/dev/null) case "$libs" in *-lXt*) bst_ok libxt/pc-libs-names-Xt ;; *) bst_notok libxt/pc-libs-names-Xt "no -lXt in '$(_bst_1line "$libs")'" ;; esac # xt.pc carries variables no other .pc in this stack does -- the app-defaults # directory and the error database -- and a toolkit reads them at build time # to compile in its own search paths. An empty one is not a packaging detail; # it is a widget set that cannot find its resources. adir=$(pkg-config --variable=appdefaultdir xt 2>/dev/null) case "$adir" in /*) bst_ok libxt/pc-appdefaultdir ;; *) bst_notok libxt/pc-appdefaultdir "not an absolute path: '$(_bst_1line "$adir")'" ;; esac else bst_skip libxt/pkg-config-present "pkg-config not installed on this image" fi # --------------------------------------------------------------------------- # 3. THE PROBE RUNS, AND RUNS TO ITS END. # # do_install ships it 0755, but the rootfs is read-only at browser runtime and # the exec bit is not reliably readable there either, so it is copied to /tmp -- # a mode=1777 tmpfs with no noexec -- and given the bit where the copy lands. probe=$work/libxt-probe if cp "$XTT/libxt-probe" "$probe" 2>/dev/null && chmod +x "$probe" 2>/dev/null; then "$probe" >"$work/out" 2>"$work/err" rc=$? # The probe exits 0 whatever it measures -- every expectation lives in this # file, not in the binary -- so a non-zero status here means it did not RUN: # a trap, a missing import, an exec refusal. On this arm that is a real and # common failure and is what this case is for. if [ "$rc" -eq 0 ]; then bst_ok libxt/probe-runs else bst_notok libxt/probe-runs "rc=$rc $(_bst_1line "$(cat "$work/err")")"; fi total=$(sed -n 's/^XT-TOTAL //p' "$work/out") if [ -n "$total" ]; then bst_ok libxt/probe-complete else bst_notok libxt/probe-complete \ "no XT-TOTAL line: $(_bst_1line "$(cat "$work/out")")"; fi # The measurement count, bounded FROM BELOW. Adding a fixture raises it; a # probe that got THINNER while still terminating cleanly is what this catches, # and nothing else here would. # 45, not the 50 this floor carried before the probe was split in two: the # application context's five measurements moved to libxt-ctx-probe and are # bounded there. 45 + 5 is the same total, counted in the two places it is # now produced. if [ -n "$total" ] && [ "$total" -ge 45 ]; then bst_ok libxt/probe-coverage else bst_notok libxt/probe-coverage \ "reported ${total:-0} measurements, expected not less than 45"; fi # Xt's default error handler EXITS, so an error raised anywhere in the run # would truncate it. The probe installs its own handler and prints instead, # which turns a silent truncation into a named line -- asserted absent here so # that a red says WHICH error rather than only that the total is short. if grep -q '^XT ERROR ' "$work/out" 2>/dev/null; then bst_notok libxt/no-xt-error "$(_bst_1line "$(grep '^XT ERROR ' "$work/out" | head -1)")" else bst_ok libxt/no-xt-error fi # ------------------------------------------------------------------------- # 4. THE DATA MODEL AND THE ABI. # The one line where agreeing with the build host would be the defect: a long # and a pointer are 4 bytes on wasm32 and 8 on x86-64. xt_line libxt/data-model "XT MODEL " "long=4 ptr=4 int=4 short=2" # The Xt scalar typedefs, stated absolutely. Boolean is a CHAR here and an int # on some other toolkits' arms, which is exactly the kind of thing a widget # set gets wrong once and then everywhere. xt_line libxt/scalar-types "XT TYPE " \ "Boolean=1 Cardinal=4 Dimension=2 Position=2 XtArgVal=4 XtPointer=4 XtIntervalId=4" # The caller-filled structures. XtResource is three String, two Cardinal, a # String and an XtPointer -- 28 bytes here with no padding anywhere. xt_line libxt/abi-sizes "XT SIZE " \ "XtResource=28 Arg=8 XtCallbackRec=8 XtActionsRec=8 XtConvertArgRec=12" # The offsets, separately from the sizes. A struct can be the right total size # with its members in the wrong places -- and a widget set writing # default_type into what Xt reads as resource_offset is a fault inside # XtGetApplicationResources, not a wrong answer. xt_line libxt/abi-offsets-resource "XT OFF XtResource " \ "name=0 class=4 type=8 size=12 offset=16 deftype=20 defaddr=24" xt_line libxt/abi-offsets-arg "XT OFF Arg " "name=0 value=4" # The specification release the installed headers claim. A header set from one # release beside a library from another compiles, links, and then disagrees # about a struct. xt_line libxt/spec-release "XT SPECREL " "release=7" # ------------------------------------------------------------------------- # 5. THE WAIT PRIMITIVE -- which platform defect this port is exposed to. # # Read out of the config.h THIS build produced, not out of the source: # configure's only multiplexer test is AC_CHECK_FUNC(poll), which is a LINK # test, so the answer is a property of the cross toolchain and can change # without a line of this port changing. `poll' is the answer that keeps the # loop clear of epoll_create's ENOSYS (brintos/linux#53) entirely; `select' # would be a different exposure and is worth a red rather than a note. xt_line libxt/wait-primitive "XT WAIT " "poll" # ------------------------------------------------------------------------- # 6. THE TRANSLATION AND ACCELERATOR PARSERS. # # A NULL source is the ONE input that returns NULL, and it returns before the # parse loop, so it must not warn. Every other input returns a table -- # ParseTranslationTable always ends in _XtCreateXlations -- which is why the # WARNING COUNT, not the pointer, carries the verdict for the malformed ones. xt_line libxt/parse-null "XT PARSE null " "ptr=no warn=0" xt_line libxt/parse-empty "XT PARSE empty " "ptr=yes warn=0" xt_line libxt/parse-one "XT PARSE one " "ptr=yes warn=0" xt_line libxt/parse-multi "XT PARSE multi " "ptr=yes warn=0" xt_line libxt/parse-directive "XT PARSE directive " "ptr=yes warn=0" # Four malformed tables, one defect each and each a DIFFERENT one, so a red # names the production that stopped being rejected rather than "parsing # broke". xt_warned libxt/parse-bad-noevent "XT PARSE bad-noevent " xt_warned libxt/parse-bad-unclosed "XT PARSE bad-unclosed " xt_warned libxt/parse-bad-nocolon "XT PARSE bad-nocolon " xt_warned libxt/parse-bad-modifier "XT PARSE bad-modifier " xt_line libxt/accel-null "XT ACCEL null " "ptr=no warn=0" xt_line libxt/accel-one "XT ACCEL one " "ptr=yes warn=0" xt_line libxt/accel-multi "XT ACCEL multi " "ptr=yes warn=0" xt_warned libxt/accel-bad-noevent "XT ACCEL bad-noevent " xt_warned libxt/accel-bad-unclosed "XT ACCEL bad-unclosed " # ------------------------------------------------------------------------- # 7. THE STANDARD STRING-TO-TYPE CONVERTERS. # # Every case asserts the RETURN CODE, the size libXt declared it wrote, and # the value read back out of the caller's own slot. The size is not # decoration: it is how a caller sizes the buffer for the next conversion, and # a converter that returned True having written four bytes while declaring two # is a silent overrun in somebody else's resource table. xt_line libxt/cvt-int-positive "XT CVT int-positive " "rc=1 size=4 val=1234" xt_line libxt/cvt-int-negative "XT CVT int-negative " "rc=1 size=4 val=-7" xt_line libxt/cvt-short "XT CVT short " "rc=1 size=2 val=300" xt_line libxt/cvt-dimension "XT CVT dimension " "rc=1 size=2 val=42" xt_line libxt/cvt-uchar "XT CVT uchar " "rc=1 size=1 val=200" # The truthy and falsy spellings both ways. Boolean stores a CHAR and Bool an # int, which is the distinction a widget set writing the wrong one gets away # with on a little-endian arm until it does not. xt_line libxt/cvt-boolean-true "XT CVT boolean-true " "rc=1 size=1 val=1" xt_line libxt/cvt-boolean-false "XT CVT boolean-false " "rc=1 size=1 val=0" xt_line libxt/cvt-boolean-yes "XT CVT boolean-yes " "rc=1 size=1 val=1" xt_line libxt/cvt-boolean-off "XT CVT boolean-off " "rc=1 size=1 val=0" xt_line libxt/cvt-bool-on "XT CVT bool-on " "rc=1 size=4 val=1" # Gravity's numbers are the X protocol's own (X.h: NorthWestGravity 1, # CenterGravity 5, StaticGravity 10) and RestartStyle's are XSMP's # (SMlib.h: SmRestartIfRunning 0 ... SmRestartNever 3). A wrong answer in # either is a wrong byte on somebody else's wire, not a local mistake -- and # the numeric gravity spelling is in because the converter's own table maps # "10" and "static" to the same value, so a table that lost a row shows up # here and not in the named cases. xt_line libxt/cvt-gravity-northwest "XT CVT gravity-northwest " "rc=1 size=4 val=1" xt_line libxt/cvt-gravity-center "XT CVT gravity-center " "rc=1 size=4 val=5" xt_line libxt/cvt-gravity-numeric "XT CVT gravity-numeric " "rc=1 size=4 val=10" xt_line libxt/cvt-restartstyle-never "XT CVT restartstyle-never " "rc=1 size=1 val=3" xt_line libxt/cvt-restartstyle-ifrunning "XT CVT restartstyle-ifrunning " "rc=1 size=1 val=0" # Float, reported SCALED to an integer. A decimal rendering would make this a # test of printf's float path on this arm, which is a different package's # business -- but the conversion itself is libXt's sscanf("%g") and is this # package's. xt_line libxt/cvt-float "XT CVT float " "rc=1 size=4 val_x100=250" # CommandArgArray is the one converter that ALLOCATES and returns a vector, so # both ends of it are named: a splitter that dropped the last token would # otherwise pass on the count alone. xt_line libxt/cvt-cmdargarray "XT CVT cmdargarray " \ "rc=1 n=3 first=one last=three" # THE TOO-SMALL BUFFER, which is the only failure path in this family that # does not need a display. done_typed() must set toVal->size to what it NEEDED # and return False without writing. A converter that wrote anyway is a silent # overrun, and it is invisible to every case above. # NO SHORT-BUFFER CASES. The probe no longer drives done_typed_string()'s # toVal-too-small arm: that arm is one call to # XtDisplayStringConversionWarning, which asks for an application context the # NULL display was never registered in, and _XtGetPerDisplay answers with a # FATAL XtErrorMsg. Measured on this image: it ended the probe after 57 lines # and XT-TOTAL was never printed, so every case below this point was lost. # The contract needs a registered display and is unreachable here. # ------------------------------------------------------------------------- # 8. THE CONVERSION CACHE, through XtDirectConvert -- the one entry point in # the whole resource machinery that takes no Display. # # The value is strlen(from), so it is checkable; but the CALL COUNT is the # assertion. A cache that missed every time would be correct and quadratic, # and every widget creation in a real toolkit re-converts the same resource # strings for every instance. xt_line libxt/cache-first "XT CACHE first " "calls=1 value=5" xt_line libxt/cache-hit "XT CACHE second " "calls=1 value=5" # A DIFFERENT input must MISS. Without this, a cache that returned its first # answer to everything would pass the case above. xt_line libxt/cache-miss "XT CACHE other " "calls=2 value=2" # ------------------------------------------------------------------------- # 9. ARGUMENT AND VARARGS LISTS -- what a caller packs before any widget # exists. xt_line libxt/arg-setarg "XT ARG setarg " "name=two value=2" # ORDER, not count: a merge that reversed either half would hand a caller the # wrong value for every resource it set, while still returning four entries. xt_line libxt/arg-merge "XT ARG merge " "ptr=yes order=1234 last=four" # The varargs list. Its packed form is private and every display-free consumer # of one needs a widget, so what is asserted is that a list, and a list # NESTING it, are both built. On an arm with a 4-byte pointer and a promoted # argument ABI that is not a given: XtVaCreateArgsList walks its own va_list, # and each XtVaTypedArg entry pushes four arguments of three different widths. xt_line libxt/varargs-list "XT VARARGS list " "ptr=yes" xt_line libxt/varargs-nested "XT VARARGS nested " "ptr=yes" else bst_notok libxt/probe-runs "could not stage $XTT/libxt-probe into $work" fi # --------------------------------------------------------------------------- # 10. THE APPLICATION CONTEXT AND THE DOCUMENTED REFUSAL -- A SECOND BINARY. # # libxt-ctx-probe, not libxt-probe. The context is the heaviest thing libXt # links -- 3 636 419 of the combined probe's 3 826 059 bytes, measured in this # recipe. The split is not a size ceiling; the combined binary execs. It is that # the two halves shared a process, so a failure in either took the other's cases # with it -- which is exactly what happened when the combined probe met a fatal # Xt error in the converters. A failure here now costs these cases and the # loop's, not the forty-five section 3 has already recorded. # BRINTOS-WORKAROUND: brintos/linux#44 revisit -- read this from libxt-probe # again when the two binaries can be one. ctxprobe=$work/libxt-ctx-probe if cp "$XTT/libxt-ctx-probe" "$ctxprobe" 2>/dev/null && chmod +x "$ctxprobe" 2>/dev/null; then "$ctxprobe" >"$work/ctxout" 2>"$work/ctxerr" crc=$? if [ "$crc" -eq 0 ]; then bst_ok libxt/ctx-probe-runs else bst_notok libxt/ctx-probe-runs "rc=$crc $(_bst_1line "$(cat "$work/ctxerr")")"; fi ctotal=$(sed -n 's/^XT-CTX-TOTAL //p' "$work/ctxout") if [ -n "$ctotal" ]; then bst_ok libxt/ctx-probe-complete else bst_notok libxt/ctx-probe-complete \ "no XT-CTX-TOTAL line: $(_bst_1line "$(cat "$work/ctxout")")"; fi # The other half of the 50 the single probe used to report. Bounded from # below for the same reason as section 3's: a context half that got THINNER # while still terminating cleanly is what this catches. if [ -n "$ctotal" ] && [ "$ctotal" -ge 5 ]; then bst_ok libxt/ctx-probe-coverage else bst_notok libxt/ctx-probe-coverage \ "reported ${ctotal:-0} measurements, expected not less than 5"; fi # Xt's default error handler exits, so an error anywhere in this run would # truncate it. The probe installs its own and prints instead. if grep -q '^XT ERROR ' "$work/ctxout" 2>/dev/null; then bst_notok libxt/ctx-no-xt-error "$(_bst_1line "$(grep '^XT ERROR ' "$work/ctxout" | head -1)")" else bst_ok libxt/ctx-no-xt-error fi cline() { _l=$(grep -F -- "$2" "$work/ctxout" 2>/dev/null | head -1) check_eq "$1" "$2$3" "$_l" } cline libxt/app-context "XT APP context " "ptr=yes" # Registering a type converter needs no display; INVOKING one through the # registry does, which is why section 8 reaches the cache by the old-style # path instead. A warning here would mean Xt rejected the registration. cline libxt/app-set-type-converter "XT APP set-type-converter " "warn=0" # XtOpenDisplay with nothing to connect to. Display.c RETURNS NULL and records # the name it tried -- it does not raise an error, and that distinction is the # whole of this case: an error would have ended the run, and section 3's # no-xt-error case would have named it. Both spellings are asserted, the # explicit unconnectable one and the environment default, because they take # different paths through _XtPreparseCommandLine. cline libxt/opendisplay-explicit "XT OPENDISPLAY explicit " "ret=null warn=0" cline libxt/opendisplay-default "XT OPENDISPLAY default " "ret=null warn=0" cline libxt/app-destroyed "XT APP destroyed " "ok" else bst_notok libxt/ctx-probe-runs "could not stage $XTT/libxt-ctx-probe into $work" fi # --------------------------------------------------------------------------- # 11. THE EVENT LOOP. LAST IN THE FILE, DELIBERATELY. # # This is the only thing on this image that can hang, and everything above is # already on the console by the time it starts -- so a wedge here is reported by # the runner as `libxt/suite-timeout' after ~100 recorded cases rather than # after none. The probe gates itself too: it measures poll(2) and the wall clock # in its own code and refuses to enter Xt if either is unsound, which turns the # wedge into a reported non-result. See loop_gate() in libxt_probe.c. if [ -x "$ctxprobe" ]; then "$ctxprobe" --loop >"$work/loop" 2>"$work/looperr" lrc=$? if [ "$lrc" -eq 0 ]; then bst_ok libxt/loop-runs else bst_notok libxt/loop-runs "rc=$lrc $(_bst_1line "$(cat "$work/looperr")")"; fi ltotal=$(sed -n 's/^XT-LOOP-TOTAL //p' "$work/loop") if [ -n "$ltotal" ]; then bst_ok libxt/loop-complete else bst_notok libxt/loop-complete \ "no XT-LOOP-TOTAL line: $(_bst_1line "$(cat "$work/loop")")"; fi lline() { _l=$(grep -F -- "$2" "$work/loop" 2>/dev/null | head -1) check_eq "$1" "$2$3" "$_l" } lline libxt/loop-wait "XT LOOP wait " "poll" # THE GATE ITSELF, scored as a case rather than read only as a precondition. # rc=0 is poll returning `timed out' with no descriptors, which is exactly # what Xt's IoWait() degenerates to when nothing is connected. An errno or a # non-zero rc here is the platform's answer to the ticket's question and is # the single most informative line this suite produces. gate=$(grep -F -- "XT LOOP pollgate " "$work/loop" 2>/dev/null | head -1) case "$gate" in *"rc=0 errno=0"*) bst_ok libxt/loop-pollgate ;; "") bst_notok libxt/loop-pollgate "the probe printed no pollgate line" ;; *) bst_notok libxt/loop-pollgate "poll(2) with no fds did not time out cleanly: $(_bst_1line "$gate")" ;; esac # WHETHER THE TIMEOUT WAS HONOURED, reported SEPARATELY and NOT as a failure # of this package. A poll that returns 0 immediately rather than after 150 ms # is brintos/linux-in-the-browser#120, the known relative-timer defect; Xt # still works under it, by spinning to each deadline instead of sleeping, so # the timers below must fire either way. This case exists so that a reader can # tell a spinning loop from a sleeping one, which no other line here would # show. elapsed=${gate##*elapsed_ms=} case "$elapsed" in ''|*[!0-9]*) bst_notok libxt/loop-timeout-honoured "no elapsed_ms in '$(_bst_1line "$gate")'" ;; *) if [ "$elapsed" -ge 100 ]; then bst_ok libxt/loop-timeout-honoured else bst_notok libxt/loop-timeout-honoured \ "poll(2) asked for 150 ms and returned after ${elapsed} ms -- the relative timer is not honoured on this arm (brintos/linux-in-the-browser#120). Xt still terminates by spinning to each deadline, so the cases below are the ones that say whether the port works." ; fi ;; esac # The clock gate. If this is red the probe stopped before entering Xt, on # purpose, and every case below it is absent rather than failing. clock=$(grep -F -- "XT LOOP clockgate " "$work/loop" 2>/dev/null | head -1) case "$clock" in *"guard_exhausted=0"*) bst_ok libxt/loop-clock-advances ;; "") bst_notok libxt/loop-clock-advances "the probe printed no clockgate line" ;; *) bst_notok libxt/loop-clock-advances "the wall clock did not advance: $(_bst_1line "$clock")" ;; esac if grep -q '^XT LOOP skipped ' "$work/loop" 2>/dev/null; then # The probe refused to enter Xt. That is the gate doing its job, and it is # a red for THIS suite because the ticket's question then has no answer -- # but it is a red that names the platform rather than the port. skipped=$(grep -F -- "XT LOOP skipped " "$work/loop" | head -1) bst_notok libxt/loop-entered "$(_bst_1line "$skipped")" else bst_ok libxt/loop-entered lline libxt/loop-context "XT LOOP context " "ptr=yes" # Nothing is queued and nothing is connected, so every mask must be idle. A # non-zero answer would mean Xt believes it has work on an empty context, # and every case below would be measuring that instead. lline libxt/loop-pending-empty "XT LOOP pending-empty " "0" # THE TICKET'S CASE. Three timers registered LEAST-soon first, so a queue # that merely preserved registration order gives `cba' and is caught; a # fourth, registered between two of them and then XtRemoveTimeOut'd, must # not appear at all -- which is the half of the contract an "it fired" # assertion misses. elapsed_ms is not asserted: whether the loop slept or # spun to those deadlines is section 11's timeout case above, not this one. lloop=$(grep -F -- "XT LOOP timers " "$work/loop" 2>/dev/null | head -1) case "$lloop" in *"n=3 order=abc "*) bst_ok libxt/loop-timers ;; "") bst_notok libxt/loop-timers "the probe printed no timers line" ;; *) bst_notok libxt/loop-timers "want 'n=3 order=abc', got '$(_bst_1line "$lloop")'" ;; esac # The work procedure runs on the iteration that would otherwise block, so a # non-zero count proves the loop reached its idle path at all rather than # returning straight out of the timer check. lline libxt/loop-workproc "XT LOOP workproc " "ran=1" lline libxt/loop-pending-drained "XT LOOP pending-drained " "0" lline libxt/loop-destroyed "XT LOOP destroyed " "ok" fi else bst_notok libxt/loop-runs "the probe was never staged, so the event loop was not measured" fi rm -rf "$work"