. "$BST_LIB" # BST-TIMEOUT: 300 # NOT MEASURED ON TARGET YET. One probe run plus ~150 shell # extractions -- the shape of 57-libxext.test, which carries # the same 300 for ~130 of them. Replace this note with a # measured elapsed the first time the suite runs on target. # libXi 1.8.1 -- the client half of the X Input extension (poky # recipes-graphics/xorg-lib). See libxi_%.bbappend for what the port adds and # why. # # --------------------------------------------------------------------------- # NOTHING HERE HAS RUN ON A TARGET YET # # Every expectation below is DERIVED FROM THE SOURCE of libXi 1.8.1 and from the # staged xorgproto 2023.2 headers -- read, not measured. The suite was written in # a phase that builds at recipe scope only and never boots an image, so no value # in it is a finding: they are predictions, and the first on-target run is what # turns them into results. Where a red appears, read it as this file having been # wrong until the case is checked against the source again. # # --------------------------------------------------------------------------- # WHAT THIS PORT IS AND IS NOT # # libXi is the CLIENT LIBRARY of XI2 -- the device, valuator, button and touch # model every toolkit binds for keyboard and pointer input. It is not an input # path. This image has no /dev/input/event* and no X server, and nothing in this # port changes that; what it buys is the packages above it. # # --------------------------------------------------------------------------- # MOST OF THIS LIBRARY CANNOT REACH A SERVER HERE, AND THAT IS WHAT IS MEASURED # # Seventy-six of libXi's eighty-two public entry points resolve a display and # then put a request on a wire. XOpenDisplay returns NULL on this image, so # those seventy-six are not untested today -- they are unreachable by # construction, and the probe does not call them: each would dereference that # NULL and end the run rather than report anything. # # What IS reachable is the part that never touches a Display, and in libXi that # part is not a corner -- it is the XI2 DEVICE-CLASS PARSER: # # size_classes(from, nclasses) how many bytes the copy will need # copy_classes(to, from, &n) perform it, report the wire consumed # # Between them they are the whole of XIQueryDevice's and XI_DeviceChanged's # answer: button state masks and label atoms, keycode arrays, valuator # min/max/value in FP3232, scroll increments, touch and gesture counts. They are # also the code here most likely to differ between the LP64 host these headers # are usually read on and this ILP32 target, because they pack that layout BY # HAND -- pad_to_double, next_block, and a leading pointer array sized with # sizeof(XIAnyClassInfo*). Group S is that parser; S06 is the case that matters # most, since an overrun of the budget size_classes asked for is a heap smash # inside a consumer rather than anything visible here. # # _XiCheckVersion is the other reachable one -- it reads the versions[] table # compiled into the archive and answers against a caller-supplied # XExtDisplayInfo, touching no Display. Group V walks all eleven rungs. # # --------------------------------------------------------------------------- # GROUP M IS NOT A STATEMENT ABOUT LIBXI, AND MUST NOT BE READ AS ONE # # XISetMask / XIClearMask / XIMaskIsSet / XIMaskLen are MACROS in xorgproto's # XI2.h -- there is no function version of any of them anywhere in libXi. They # compile into the probe with the probe's own compiler and execute not one byte # of libXi.a. They are asserted because they are the bit layout every consumer # of XISelectEvents depends on and their arithmetic is where an ILP32 shift # would show, but a green group M says the staged header is right, not that the # archive is. # # --------------------------------------------------------------------------- # ONE METHOD THIS PORT'S SIBLINGS HAVE AND IT DOES NOT # # 57-libxext and 57-libxfixes drive their extension-info caches by seeding the # list with an identity token, because XextFindDisplay and XFixesFindDisplay # compare their Display* and never load through it. That is unavailable here: # libXi's list lives in a file-static (xinput_info in XExtInt.c) with no # external name, and on a miss XInput_find_display calls # _XiFindEventsSupported(dpy), which LockDisplay()s and dereferences. So there # is no equivalent of libxfixes' per-family no-extension arm in this suite, and # group D reports the display state rather than discriminating over it. PROBE=${BST_LIBXI_PROBE:-/usr/libexec/libxi-tests/libxi-probe} ARCHIVE=/usr/lib/libXi.a PCFILE=/usr/lib/pkgconfig/xi.pc HDR=/usr/include/X11/extensions/XInput.h HDR2=/usr/include/X11/extensions/XInput2.h # The version the recipe pins. Asserted rather than read back, so a recipe # bumped without revisiting this suite is a red here instead of a silent change # of subject -- every expectation below is derived from THIS version's source. WANT_VERSION=1.8.1 # The first BST result line of a suite is dropped by the guest console # (brintos/linux#96), consistently and every run. This case asserts something # already true so that what is lost is a line that costs nothing; remove it when # #96 lands. if [ -f "$ARCHIVE" ]; then bst_ok libxi/suite-canary else bst_notok libxi/suite-canary "$ARCHIVE is not a file"; fi if [ ! -f "$ARCHIVE" ]; then bst_skip libxi/present "libxi not installed on this image" exit 0 fi bst_ok libxi/present # --- the package is whole ----------------------------------------------------- check libxi/staticlib test -s "$ARCHIVE" check libxi/pkgconfig test -s "$PCFILE" check libxi/hdr-xinput test -s "$HDR" check libxi/hdr-xinput2 test -s "$HDR2" # The archive is the whole library deliverable until this recipe converts; from # then on what matters is that any .so it ships is a real, non-empty file. bst_lib_shape libxi/no-shared "/usr/lib/libXi.so" "/usr/lib/libXi.so.6" pcver=$(sed -n 's/^Version: *//p' "$PCFILE" 2>/dev/null | head -1) check_eq libxi/pkgconfig-version "$WANT_VERSION" "$pcver" # xi.pc must name what a consumer resolves through it. On a static-only distro a # missing entry is not a warning -- it is a link that fails in the consumer, # because there is no shared object to carry the dependency. libXi's own # xi.pc.in splits them: inputproto is public (a consumer compiles against its # headers through this file) and x11/xext are private (needed to link, not to # compile), and on this distro both matter since every link is static. pcpub=$(sed -n 's/^Requires: *//p' "$PCFILE" 2>/dev/null | head -1) case "$pcpub" in *inputproto*) bst_ok libxi/pkgconfig-requires-inputproto ;; *) bst_notok libxi/pkgconfig-requires-inputproto "xi.pc Requires does not name inputproto: '$pcpub'" ;; esac pcpriv=$(sed -n 's/^Requires\.private: *//p' "$PCFILE" 2>/dev/null | head -1) case "$pcpriv" in *x11*) bst_ok libxi/pkgconfig-requires-x11 ;; *) bst_notok libxi/pkgconfig-requires-x11 "xi.pc Requires.private does not name x11: '$pcpriv'" ;; esac case "$pcpriv" in *xext*) bst_ok libxi/pkgconfig-requires-xext ;; *) bst_notok libxi/pkgconfig-requires-xext "xi.pc Requires.private does not name xext: '$pcpriv'" ;; esac # The archive carries the API. grep rather than nm: the image ships no binutils # nm, and the names are present as plain bytes. -a where the grep supports it -- # on this arm the archive members are LLVM IR bitcode (ThinLTO), so a grep that # suppresses matches in a file containing NULs reports nothing; GNU grep needs # -a, busybox grep as shipped does not. Probing keeps this measuring libXi # rather than measuring the image's grep. if echo x | grep -aq x 2>/dev/null; then _grepq="grep -aq"; else _grepq="grep -q"; fi # One symbol per family plus the two internals the probe drives, so a dropped # object is named rather than merely counted. The probe's link is the exhaustive # check; this is the part that survives the probe being absent. for sym in XIQueryVersion XIQueryDevice XIFreeDeviceInfo \ XISelectEvents XIGetSelectedEvents \ XIGrabDevice XIUngrabDevice XIAllowEvents XIAllowTouchEvents \ XIGrabTouchBegin XIGrabPinchGestureBegin XIGrabSwipeGestureBegin \ XIQueryPointer XIWarpPointer XIDefineCursor \ XIChangeHierarchy XISetClientPointer XIGetClientPointer \ XIListProperties XIChangeProperty XIGetProperty \ XIBarrierReleasePointer XIBarrierReleasePointers \ XListInputDevices XOpenDevice XCloseDevice XQueryDeviceState \ XGetExtensionVersion XSendExtensionEvent XSelectExtensionEvent \ XGetDeviceMotionEvents XGetFeedbackControl XGetDeviceControl \ XGetDeviceProperty XChangeDeviceProperty \ size_classes copy_classes; do if $_grepq "$sym" "$ARCHIVE" 2>/dev/null; then bst_ok "libxi/sym-$sym" else bst_notok "libxi/sym-$sym" "$sym absent from $ARCHIVE" fi done # The installed headers must declare what a consumer compiles against. XInput2.h # carries the XI2 API and the class structs the parser fills; XInput.h carries # the XI1 API and the XDevice types still used by XTest and friends. for decl in XIQueryDevice XIFreeDeviceInfo XISelectEvents XIQueryVersion \ XIDeviceInfo XIAnyClassInfo XIButtonClassInfo XIKeyClassInfo \ XIValuatorClassInfo XIScrollClassInfo XITouchClassInfo \ XIGestureClassInfo XIEventMask XIDeviceEvent XIRawEvent \ XIBarrierEvent XIGrabModifiers; do if grep -q "$decl" "$HDR2" 2>/dev/null; then bst_ok "libxi/hdr2-decl-$decl" else bst_notok "libxi/hdr2-decl-$decl" "$decl is not declared in $HDR2" fi done for decl in XDevice XDeviceInfo XOpenDevice XListInputDevices \ XDeviceState XDeviceControl XFeedbackState XDeviceTimeCoord; do if grep -q "$decl" "$HDR" 2>/dev/null; then bst_ok "libxi/hdr-decl-$decl" else bst_notok "libxi/hdr-decl-$decl" "$decl is not declared in $HDR" fi done # XI 2.4 is what makes the gesture class in group S meaningful, and it comes # from xorgproto rather than from libXi. A staged xorgproto older than 2021 # would compile this library with no XIGestureClass at all. XI2H=/usr/include/X11/extensions/XI2.h if grep -q 'XIGestureClass' "$XI2H" 2>/dev/null; then bst_ok libxi/proto-has-gesture-class else bst_notok libxi/proto-has-gesture-class "$XI2H does not define XIGestureClass -- staged xorgproto predates XI 2.4" fi # --- the probe ---------------------------------------------------------------- if [ ! -x "$PROBE" ]; then bst_skip libxi/probe-runs "$PROBE absent from the image" exit 0 fi pout=${TMPDIR:-/tmp}/bst-libxi-out.$$ "$PROBE" > "$pout" 2>&1 prc=$? if [ "$prc" -eq 0 ]; then bst_ok libxi/probe-runs elif bst_execfail "$(head -c 200 "$pout" 2>/dev/null)"; then bst_unrun libxi/probe-runs "$(head -c 200 "$pout" 2>/dev/null)" rm -f "$pout" exit 0 else bst_notok libxi/probe-runs "probe exited $prc: $(_bst_1line "$(head -c 300 "$pout")")" fi if grep -q '^PROBE-END$' "$pout" 2>/dev/null; then bst_ok libxi/probe-complete else bst_notok libxi/probe-complete "probe output has no PROBE-END terminator (last line: $(tail -1 "$pout" 2>/dev/null))" fi # val -- the probe's value for one case, empty when the probe stopped # first. Matches on the ID in field 1 rather than taking the line by position, # because the probe's output is captured with 2>&1 and a platform diagnostic # could land in the middle of it. val() { awk -v k="$1" '$1==k { $1=""; sub(/^ /,""); print; exit }' "$pout"; } # want -- score one probe value against a derived answer. # A missing value is reported as the probe having stopped early rather than as a # mismatch against '', which is a different fault and localises to a different # line. want() { _got=$(val "$1") if [ -z "$_got" ]; then bst_notok "libxi/$2" "no value on target (probe stopped before $1); want='$3'" elif [ "$_got" = "$3" ]; then bst_ok "libxi/$2" else bst_notok "libxi/$2" "want:'$3' got:'$_got'" fi } # --- W: the wire structs are fixed by the protocol ----------------------------- # Identical on every architecture by definition -- XI2proto.h states its own # datatypes in stdint widths and #defines Atom to uint32_t for the length of the # file. A different answer here means the staged xorgproto is not describing X # Input 2, which would make every byte offset in group S meaningless. want W01 wire-any-info 8 want W02 wire-button-info 8 want W03 wire-key-info 8 want W04 wire-valuator-info 44 want W05 wire-scroll-info 24 want W06 wire-touch-info 8 want W07 wire-gesture-info 8 want W08 wire-fp3232 8 want W09 wire-device-info 12 # --- A: the in-memory ABI this arm hands the packages above it ----------------- # ILP32 against the LP64 host these declarations are usually read on, which is # the whole reason to print them. Derived by hand from XInput2.h with 4-byte # pointers, 4-byte Atom (XID is unsigned long) and 8-byte double alignment: # XIAnyClassInfo 2 ints = 8 # XIButtonClassInfo 3 ints + Atom* + XIButtonState{int,ptr} = 24 # XIKeyClassInfo 3 ints + int* = 16 # XIValuatorClassInfo 3 ints + Atom (16) + 3 doubles (16..40) # + 2 ints (40..48) = 48 # XIScrollClassInfo 4 ints (16) + double (16..24) + int (24..28), # padded to the double's alignment = 32 # XITouchClassInfo 4 ints = 16 # XIGestureClassInfo 3 ints = 12 # XIDeviceInfo 5 ints + int + XIAnyClassInfo** = 28 # XIEvent int, unsigned long, Bool, Display*, 2 ints, # Time = 28 want A01 sizeof-pointer 4 want A02 sizeof-atom 4 want A03 sizeof-any-class 8 want A04 sizeof-button-class 24 want A05 sizeof-key-class 16 want A06 sizeof-valuator-class 48 want A07 sizeof-scroll-class 32 want A08 sizeof-touch-class 16 want A09 sizeof-gesture-class 12 want A10 sizeof-device-info 28 want A11 sizeof-xi-event 28 want A12 sizeof-valuator-state 12 want A13 sizeof-button-state 8 want A14 sizeof-modifier-state 16 want A15 alignof-double 8 # --- S: size_classes and copy_classes, the real archive code ------------------- # The fixture is one hand-built XI2 class list carrying every class the parser # knows plus one it does not, with the unknown one THIRD so the documented skip # is tested where it could corrupt the pointer array rather than merely truncate # it: # # [0] button 9 buttons [1] key 8 keycodes [2] type 999 (unknown) # [3] valuator [4] scroll [5] touch [6] gesture # # Wire length, from the struct sizes asserted in group W above: # button 8 + 4 (mask, padded to four bytes) + 9*4 (label atoms) = 48 # key 8 + 8*4 = 40 # unknown 8 # valuator 44 scroll 24 touch 8 gesture 8 # total = 180 want S00 fixture-wire-len 180 # size_classes' answer, and the probe's independent recomputation of it from the # PUBLIC headers. The two are compiled from different sources -- the library's # from ${S} against the sysroot at ITS do_compile, the probe's against the # sysroot now -- so S03 is what catches a stale staged header, the same # assertion 57-libxfixes makes with its two version constants. # # pointer array pad8(7 * 4) = 32 # button pad8(24) + pad8(1*4) + pad8(9*4 + 1*4) = 24 + 8 + 40 = 72 # key pad8(16) + pad8(8*4) = 16 + 32 = 48 # unknown nothing -- size_classes' switch has no default arm # valuator pad8(48) = 48 scroll pad8(32) = 32 # touch pad8(16) = 16 gesture pad8(12) = 16 # total = 264 want S01 size-classes-lib 264 want S02 size-classes-own 264 want S03 size-classes-agree 1 # copy_classes returns the WIRE bytes it consumed -- all 180, the unknown class # included, because it advances past what it skips -- and resets *nclasses to # the number it actually recognised. want S04 copy-returns-wire-len 180 want S05 nclasses-reset 6 # THE CASE THAT MATTERS MOST. The probe stamps its destination with a fill, # hands copy_classes exactly the budget size_classes asked for, and requires the # 64 bytes past that budget to still hold the fill. A parser that walked off its # own budget on this 4-byte-pointer arm would be a heap overrun inside a # consumer, months later and nowhere near here; this is the only place in this # suite that could see it. want S06 budget-canary-intact 1 # Button. state.mask_len is the PADDED size (pad8(4) = 8), larger than the four # bytes the wire carried, so bytes 4..7 are zero because of the library's tail # memset -- S16 is that memset. The wire mask is 0x55 0x01, i.e. buttons # 1,3,5,7,9 of nine. want S10 button-type 1 want S11 button-sourceid 3 want S12 button-count 9 want S13 button-mask-len 8 want S14 button-mask-byte0 85 want S15 button-mask-byte1 1 want S16 button-mask-tail-zeroed 1 want S17 button-label-first 1001 want S18 button-label-last 1009 # Key. KEYCODE TRUNCATION -- AN UPSTREAM DEFECT, SCORED AS WHAT IT IS. # The wire carries num_keycodes CARD32s; XExtInt.c copies # # memcpy(cls_lib->keycodes, &cls_wire[1], cls_lib->num_keycodes); # # -- num_keycodes BYTES, not num_keycodes * sizeof(int). So the first int of the # destination takes the low bytes of the first four wire keycodes and the rest # of the array is never written at all. This is libXi 1.8.1 as upstream ships # it, identical on x86-64; it is NOT a property of this port, nothing here # repairs it, and per the project's no-public-upstream-reports rule nothing is # filed for it either. # # S23 therefore asserts the TRUNCATION -- keycodes[2] still holding the probe's # fill -- rather than the value a correct copy would leave. The day upstream # widens that memcpy, S23 reds and names itself, which is the whole point of # writing it down: a suite that quietly accepted either answer would be # measuring nothing. S22 and S24 are the two ints that DO survive: wire keycodes # 1,2,3,4 little-endian are 01 00 00 00 02 00 00 00, of which eight bytes are # copied. want S20 key-type 0 want S21 key-count 8 want S22 key-code0 1 want S23 key-codes-truncated 1 want S24 key-code1 2 # Valuator: the FP3232 -> double conversion, the only arithmetic in this library # whose answer could differ between the LP64 build host and this ILP32 target. # Printed as exact scaled integers -- every fixture value is a dyadic fraction, # so the scaling is lossless. min = -5, max = 1023 + 0x80000000/2^32 = 1023.5, # value = 7 + 0x40000000/2^32 = 7.25. want S30 valuator-type 2 want S31 valuator-number 4 want S32 valuator-label 2002 want S33 valuator-min-milli -5000 want S34 valuator-max-milli 1023500 want S35 valuator-value-milli 7250 want S36 valuator-resolution 1234 want S37 valuator-mode 1 want S40 scroll-type 3 want S41 scroll-number 5 want S42 scroll-scroll-type 1 want S43 scroll-flags 2 want S44 scroll-increment-milli 3500 want S50 touch-type 8 want S51 touch-mode 1 want S52 touch-count 10 want S60 gesture-type 9 want S61 gesture-count 5 # The pointer array in order. This is where a mishandled skip shows: the unknown # class was third on the wire and must be ABSENT from this list with everything # after it moved down, not left as a hole. The types are XIKeyClass 0, # XIButtonClass 1, XIValuatorClass 2, XIScrollClass 3, XITouchClass 8, # XIGestureClass 9. want S70 class-order "1 0 2 3 8 9" # size_classes sized the pointer array for the INPUT count while copy_classes # filled six, and it zeroes the whole array before filling. The seventh slot is # therefore NULL rather than the fill -- the only evidence here that the memset # covered the array rather than just the part that was written. want S71 seventh-slot-null 1 # --- V: the versions[] table compiled into the archive ------------------------- # _XiCheckVersion reads that table and compares it against the version in the # XExtDisplayInfo it is handed. It touches no Display, so the whole ladder is # reachable. Every rung is walked with the XI.h/XI2.h constant pair that names # it, so a table disagreeing with the staged protocol headers reds here rather # than in some consumer's version negotiation. want V01 ladder-all-rungs-agree 11 lad=$(grep -c '^LADDER ' "$pout" 2>/dev/null) check_eq libxi/ladder-lines 11 "$lad" bad=$(awk '$1=="LADDER" && $3!="0" { printf "%s(%s) ", $2, $3 }' "$pout") if [ -z "$bad" ]; then bst_ok libxi/ladder-no-disagreement else bst_notok libxi/ladder-no-disagreement "rungs disagreeing with the staged headers: $bad"; fi # Dont_Check is index 0, whose table entry is {XI_Absent, 0, 0}. The 0 # major_version IS the sentinel and it is answered before info is looked at at # all -- so V02 passes even on the info V07 later fails on. want V02 dont-check-is-zero 0 want V03 server-below-is-minus-one -1 want V04 server-above-is-one 1 want V05 major-below-is-minus-one -1 # -2 is a different answer from "older": it says the caller never got a version # out of the server at all. Two distinct causes, both reachable here. want V06 no-version-is-minus-two -2 want V07 no-data-is-minus-two -2 # --- M: xorgproto's mask MACROS. NOT libXi code. See the head of this file. ---- # XIMaskLen(e) is (e >> 3) + 1, so it steps at each byte boundary; the set/clear # family is byte [e>>3], bit (1 << (e & 7)). The mask is four bytes with bits # 0, 7, 8, 15, 23 and 31 set: byte0 = 0x81 = 129, byte1 = 0x81 = 129, # byte2 = 0x80 = 128, byte3 = 0x80 = 128. XI_LASTEVENT is XI_GestureSwipeEnd, # 32, so its mask needs 5 bytes -- which is the number a consumer sizing an # XIEventMask for "everything" has to get right. want M01 masklen-0 1 want M02 masklen-7 1 want M03 masklen-8 2 want M04 masklen-15 2 want M05 masklen-16 3 want M06 masklen-63 8 want M07 masklen-lastevent 5 want M08 mask-byte0 129 want M09 mask-byte1 129 want M10 mask-byte2 128 want M11 mask-byte3 128 want M12 isset-counts-six 6 want M13 isset-clear-bit-is-zero 0 want M14 after-clear-byte0 1 want M15 clear-left-bit0-set 1 # --- F: the six public entry points that need no display ----------------------- # Every other one of the eighty-two goes to the wire. These are frees, and what # each case asserts is that CONTROL RETURNED -- a free that mis-walks its # argument on this arm ends the probe here rather than printing 1, so an absent # value in this group is the finding. XIFreeDeviceInfo is the one with real work # to do: it walks its array to a name==NULL sentinel, freeing each element's # classes and name. # # XFreeDeviceList is deliberately not exercised: it frees # list->name - sizeof(XDeviceList), so a fixture would have to reproduce # XListInputDevices' single-allocation layout and a wrong guess would be a bad # free rather than a finding. want F01 free-device-info 1 want F02 free-feedback-list 1 want F03 free-device-state 1 want F04 free-device-control 1 want F05 free-device-motion 1 # --- D: the honest-failure arm ------------------------------------------------- # There is no X server on this image and no /dev/input/event*. D03 is the count # of entry points that cannot be executed here: 82 public, less the six frees of # group F. It is a floor on the honesty of this suite as much as a measurement # -- if it ever falls, something in this image opened a display. want D01 no-display 1 want D02 display-env-unset "(unset)" want D03 wire-bound-count 76 # --- C: the census ------------------------------------------------------------- # The probe names every public entry point by address, so the recipe's link is # what proves the archive still contains each one. This case is the floor on the # table itself: a probe cut down in some future change would keep passing while # quietly proving less. want C01 census-entry-points 82 cens=$(grep -c '^CENSUS ' "$pout" 2>/dev/null) check_eq libxi/census-lines 82 "$cens" unres=$(awk '$1=="CENSUS" && $3!="resolved" { print $2 }' "$pout" | tr '\n' ' ') if [ -z "$unres" ]; then bst_ok libxi/census-all-resolved else bst_notok libxi/census-all-resolved "unresolved: $unres"; fi rm -f "$pout"