. "$BST_LIB" # BST-TIMEOUT: 300 # one probe run, then 155 extractions that fork nothing at # all (see xp_line). Measured at 45 s end to end; the bound # is headroom for a loaded host, not a budget being spent. # libXpresent 1.0.1 -- the Present extension client library (meta-oe # recipes-graphics/xorg-lib). See libxpresent_%.bbappend for what the port adds # and why. # # --------------------------------------------------------------------------- # ALMOST NONE OF THIS LIBRARY CAN REACH A SERVER HERE, AND THAT IS THE POINT # # libXpresent is the client half of Present -- the modern X path for handing a # finished pixmap to the server for display at a chosen frame, replacing the # copy-and-hope of the older extensions. Every operation it exposes is a round # trip: XPresentFindDisplay(dpy), then a request on a wire. THIS IMAGE HAS NO X # SERVER, so no display opens (section 12) and not one of those requests can # complete -- not untested today, unreachable by construction. # # A shell suite is therefore left saying that an archive and a header arrived, # which is a statement about tar rather than about a cross compiler. So the port # builds the consumer libXpresent does not ship, and this suite drives what # genuinely stands alone. # # PRESENT IS THE EXTENSION WHERE 64-BIT COUNTERS MEET A 32-BIT TARGET HEAD ON, # and that is the half this suite can assert absolutely. Serials, target MSCs # and UST timestamps are 64-bit quantities fixed by the protocol -- # XPresentPixmap takes three of them, PresentCompleteNotify carries two back -- # and on wasm32 those are `long long' rather than `long', aligned to 8 inside an # otherwise ILP32 struct. A layout that came out wrong here would be complete, # installed and silently truncating, and no amount of "the package built" would # see it. Section 9 is that assertion, member by member. # # WHY THE HONEST-FAILURE ARM IS SAFE, AND WHY IT DISCRIMINATES. # # 57-libxext.test's probe refuses to pass an invented Display into an entry # point, and is right to: libXext's find_display misses, falls into # XextAddDisplay and dereferences. libXpresent is the libXfixes shape instead. # It keeps its own list -- `XPresentExtInfo XPresentExtensionInfo;', external # linkage, src/Xpresent.c -- and XPresentExtFindDisplay's fast path COMPARES the # pointer without loading through it. Seeded with entries whose codes are NULL, # XPresentFindDisplay returns on the cache hit or on the list walk and never # reaches XInitExtension. # # Every entry point then runs its real prologue out of libXpresent.a and takes # the arm XPresentCheckExtension defines for a server without Present -- which # is the state this image is permanently in. The arm differs per family: # # 0 XPresentQueryVersion (Status), XPresentSelectInput (XID), # XPresentQueryCapabilities (uint32_t) # void XPresentPixmap, XPresentNotifyMSC, XPresentFreeInput -- measured by # RETURNING AT ALL, which is not weak: a build that stopped # short-circuiting would run on into LockDisplay(token) and fault # False XPresentQueryExtension, which asks XPresentHasExtension itself and # leaves ALL THREE out-parameters untouched # # The second seeded entry is keyed on NULL, so the ticket's NULL-Display error # case is measured as the documented refusal rather than as the undefined # behaviour a raw NULL into an unseeded library would be. Section 8 says so in # its own comment; it is a real limit of the fixture and is stated, not hidden. # # TWO THINGS THE TICKET ASKED FOR CANNOT BE ASSERTED AS WRITTEN, and this suite # asserts the true thing instead rather than inventing an answer: # # - THERE IS NO SONAME. This distro is static-only: libtool produces # libXpresent.a and no libXpresent.so.1, and ${PN} -- whose FILES is # ${libdir}/libXpresent.so.* -- is never written at all. Section 1 asserts # the archive AND the absence of the shared object, which is the fact. # - THE HEADER'S VERSION MACROS ARE NOT 1.0.1. PRESENT_MAJOR and PRESENT_MINOR # come from xorgproto's presenttokens.h and are the PROTOCOL version, 1.3; # PRESENT_REVISION is Xpresent.h's own 0; PRESENT_VERSION is therefore # 10300. 1.0.1 is the LIBRARY version and is asserted where it actually # lives -- pkg-config --modversion, and the probe's own -DLIBXPRESENT_PV. # Keeping the two apart is what makes section 6 evidence: the archive's # answer against the sysroot's headers says whether libXpresent.a and the # xorgproto on this image came from the same place. # # AND ONE EVENT IS NOT HERE AT ALL. XPresentRedirectNotifyEvent, the # PresentRedirectNotify event code and PresentRedirectNotifyMask all sit behind # `#if PRESENT_FUTURE_VERSION', and nothing in xorgproto, in libXpresent or in # this distro ever defines that macro. Asserting its offsets is impossible; # section 9 asserts that it is compiled out and that the compatibility # PRESENT_REDIRECT_NOTIFY_MASK is therefore 0, which is what a consumer needs to # know. # # THE SECTIONS # 1 the payload headers, archive, no .so, probe # 2 the .pc how a consumer finds any of it # 3 the packaging dpkg's own file lists # 4 the probe runs, to its end # 5 the trap did not fire # 6 the version protocol out of the archive, library out of PV # 7 the extension list the seeded cache and its walk # 8 the honest-failure arm including NULL display and a zero notify list # 9 the layouts sizeof, offsetof, every 64-bit field # 10 the protocol constants # 11 the entry-point census # 12 the display that is not there # # A build where 10 passes and 8 does not has a fault in libXpresent.a rather # than in the headers -- 10 is compiled on this image, 8 came out of the # archive. One where 9 goes red on a single offset has a struct that changed # shape; one where it goes red on a width has a 64-bit counter that stopped # being 64 bits, which is the specific regression this port exists to notice. # --------------------------------------------------------------------------- # 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_LIBXPRESENT_ROOT:-}" INC=$R/usr/include/X11/extensions LIB=$R/usr/lib PCD=$R/usr/lib/pkgconfig DPKG=$R/var/lib/dpkg/info XT=$R/usr/libexec/libxpresent-tests work=${TMPDIR:-/tmp}/bst-libxpresent.$$ rm -rf "$work"; mkdir -p "$work" hasfile() { if [ -s "$2" ]; then bst_ok "$1"; else bst_notok "$1" "missing or empty: $2"; fi } # xp_line # # A red reports WHAT THE PROBE ACTUALLY SAID for that case instead of merely # "absent". Every prefix is unique by construction: each is a section tag plus a # case name, and the case name is followed by a space that ends it. # # THE EXTRACTION FORKS NOTHING, AND THAT IS WHY THIS SUITE RUNS AT ALL. # This file calls xp_line 155 times. Written the obvious way -- `$(grep -F ... | # head -1)' -- each call costs three spawns, and guest memory is not returned on # process exit (brintos/linux#21, brintos/hardwarejs#20), so the ~465 spawns grew # the HOST guest process at ~24 GiB/min to a peak of 18.5 GiB. That is past # brintos_local_verify.py's runaway bound, so the guard ended the guest and # quarantined the suite with ZERO cases -- which reads exactly like a suite that # hung at case 0, and is how this port was dropped from batch-2471 twice while # passing every one of its 203 cases. Measured, not inferred: with the greps the # same boot peaks at 18500 MiB, and the probe alone peaks at 347 MiB. # # So the probe's output is read ONCE into a shell variable by the builtin `read' # below, and every lookup here is parameter expansion. The match is anchored at # a line start -- XP_OUT carries a leading newline so that the first line is # reachable by the same pattern as the rest -- which is also stricter than the # grep -F it replaces, since that would have matched a prefix mid-line. NL=' ' xp_line() { case "$XP_OUT" in *"$NL$2"*) _r=${XP_OUT#*"$NL$2"}; _l="$2${_r%%"$NL"*}" ;; *) _l= ;; esac check_eq "$1" "$2$3" "$_l" } # --------------------------------------------------------------------------- # 0. THE CANARY, which is not an assertion about libXpresent 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 libxpresent/suite-canary # --------------------------------------------------------------------------- # 1. THE PAYLOAD. # # libXpresent installs exactly one public header, and it is unusable alone: # Xpresent.h includes presenttokens.h for the protocol constants and the # library's own PRESENT_VERSION formula is built out of them. presentproto.h is # what the wire structures live in. Both are xorgproto's rather than this # recipe's, and an image carrying one and not the other fails a consumer's # do_compile rather than anything on this image -- which is exactly why they are # asserted here. check_rc libxpresent/dir-extensions 0 test -d "$INC" hasfile libxpresent/hdr-Xpresent "$INC/Xpresent.h" hasfile libxpresent/hdr-presenttokens "$INC/presenttokens.h" hasfile libxpresent/hdr-presentproto "$INC/presentproto.h" # Xpresent.h also takes XserverRegion from Xfixes.h, RRCrtc from Xrandr.h and # XSyncFence from sync.h -- the three DEPENDS this port has. A consumer that # includes Xpresent.h needs all three present, so their absence is this port's # problem to report even though none is this recipe's file. hasfile libxpresent/hdr-dep-Xfixes "$INC/Xfixes.h" hasfile libxpresent/hdr-dep-Xrandr "$INC/Xrandr.h" hasfile libxpresent/hdr-dep-sync "$INC/sync.h" # THE ARCHIVE IS THE WHOLE DELIVERY, AND THE ABSENCE OF THE .so IS THE OTHER # HALF OF THAT STATEMENT. This distro appends --disable-shared to every # class-target configure, so a libXpresent.so.1 appearing here would mean the # recipe had started producing something nothing on this image can load -- and # that ${PN}, which is empty and never written, had started having content. # This is also the case that stands in for the ticket's "assert the shipped # soname": there is no soname on this arm, and saying so is the true assertion. hasfile libxpresent/archive "$LIB/libXpresent.a" check_rc libxpresent/no-shared 1 test -e "$LIB/libXpresent.so.1" check_rc libxpresent/no-shared-link 1 test -e "$LIB/libXpresent.so" hasfile libxpresent/probe-shipped "$XT/libxpresent-probe" # Asserted on contents rather than size: a truncated install would still be # non-empty. The formula is what section 6 measures the archive against, and # XPresentQueryExtension is the entry point every consumer starts at. check libxpresent/hdr-has-version-formula \ grep -q 'PRESENT_VERSION' "$INC/Xpresent.h" check libxpresent/hdr-has-queryextension \ grep -q 'XPresentQueryExtension' "$INC/Xpresent.h" # The header's own statement that the redirect event is not compiled in. Read # here as text, and measured by the probe as a preprocessor fact in section 9; # the two together say the header ships the guard AND that the guard is closed. check libxpresent/hdr-redirect-is-guarded \ grep -q 'PRESENT_FUTURE_VERSION' "$INC/Xpresent.h" # --------------------------------------------------------------------------- # 2. THE .pc -- how a consumer finds any of the above. hasfile libxpresent/pc-present "$PCD/xpresent.pc" # THE .pc IS ASSERTED ON ITS CONTENTS FIRST, AND THROUGH pkg-config SECOND. # Read directly, these lines are true whatever else is installed; asked through # pkg-config they are true only if the packages xpresent.pc NAMES are also on # the image, which is a fact about the image rather than about this port. Both # are worth having, and separating them is what makes a red say which. # # `Requires: xproto presentproto' -- both from xorgproto-dev, so this is the # dependency edge Yocto turns into an RDEPENDS on libxpresent-dev (poky's # oe.package.process_pkgconfig reads `Requires' and NOT `Requires.private'). check libxpresent/pc-requires-presentproto \ grep -q '^Requires:.*presentproto' "$PCD/xpresent.pc" check libxpresent/pc-requires-xproto \ grep -q '^Requires:.*xproto' "$PCD/xpresent.pc" # x11, xext, xfixes and xrandr are PRIVATE requires, and correctly so: a # consumer of the shared library would not need them, but this distro is # static-only, so every consumer here does. x11 is the one libXpresent.a # actually calls into -- XInitExtension, _XReply, LockDisplay; the other three # supply the TYPES Xpresent.h takes (XserverRegion, RRCrtc, XSyncFence), which # is why the probe's own link line names libX11 and not them. check libxpresent/pc-requires-private-x11 \ grep -q '^Requires\.private:.*x11' "$PCD/xpresent.pc" check libxpresent/pc-requires-private-xfixes \ grep -q '^Requires\.private:.*xfixes' "$PCD/xpresent.pc" check libxpresent/pc-requires-private-xrandr \ grep -q '^Requires\.private:.*xrandr' "$PCD/xpresent.pc" check libxpresent/pc-libs-name grep -q '\-lXpresent' "$PCD/xpresent.pc" # NOT `require pkg-config'. require ENDS THE FILE when the tool is absent, and # everything this suite exists for comes after this section. Gating the probe on # a query tool would mean an image without pkg-config reported a clean skip for # the library it never exercised. So these cases skip individually and the file # runs on. if command -v pkg-config >/dev/null 2>&1; then bst_ok libxpresent/pkg-config-present # The graph queries below resolve xpresent.pc's requires, so they answer about # xorgproto-dev and libx11-dev as much as about this port. Gated on both being # present, and gated by NAME, so that an image missing one reports which -- # rather than reporting `xpresent.pc is broken', which it would not be. if [ -f "$PCD/presentproto.pc" ] && [ -f "$PCD/x11.pc" ]; then bst_ok libxpresent/pc-deps-present check libxpresent/pc-exists pkg-config --exists xpresent check libxpresent/pc-validate pkg-config --validate xpresent # THE LIBRARY VERSION, and the only place on this image where 1.0.1 is a # correct expectation. The header's macros are the PROTOCOL version; see # this file's opening comment and section 6. check_eq libxpresent/pc-modversion 1.0.1 \ "$(pkg-config --modversion xpresent 2>/dev/null)" # PKG_CONFIG_ALLOW_SYSTEM_LIBS is required for the same reason # 57-xtrans.test records for its --cflags case: pkg-config SUPPRESSES a -L # naming a directory already on the linker's default path, so the bare query # answers usefully only for the -l part. Asked for the flags it COMPUTED, # the library name has to be there. check_out libxpresent/pc-libs -lXpresent \ env PKG_CONFIG_ALLOW_SYSTEM_LIBS=1 pkg-config --libs xpresent else bst_skip libxpresent/pc-deps-present \ "xpresent.pc requires presentproto and (privately) x11; one is missing on this image" fi else bst_skip libxpresent/pkg-config-present "pkg-config not installed on this image" fi # --------------------------------------------------------------------------- # 3. THE PACKAGING -- dpkg's own file lists. # # Section 1 asserted that the files are THERE; this one asserts they arrived as # the packages the recipe declares, which is the ticket's "the .deb installs on # target with dpkg and the files land where the packaging says". A file that # reached the image by some other route satisfies section 1 and does not satisfy # this. # # Three packages, and the split is the port's own decision: -dev has the header # and the .pc, -staticdev the archive (this distro's whole delivery), -tests the # probe in its own package so an image can take the library without it. nl=' ' haspkgline() { # $1 case id, $2 path, $3 the list contents case "$nl$3$nl" in *"$nl$2$nl"*) bst_ok "$1" ;; *) bst_notok "$1" "not listed: $2" ;; esac } for pkg in libxpresent-dev libxpresent-staticdev libxpresent-tests; do if [ -s "$DPKG/$pkg.list" ]; then bst_ok "libxpresent/pkg-list-$pkg" else bst_notok "libxpresent/pkg-list-$pkg" \ "no $DPKG/$pkg.list -- the package did not install through dpkg" fi done devlist=$(cat "$DPKG/libxpresent-dev.list" 2>/dev/null) haspkgline libxpresent/pkg-dev-header /usr/include/X11/extensions/Xpresent.h "$devlist" haspkgline libxpresent/pkg-dev-pc /usr/lib/pkgconfig/xpresent.pc "$devlist" stalist=$(cat "$DPKG/libxpresent-staticdev.list" 2>/dev/null) haspkgline libxpresent/pkg-static-archive /usr/lib/libXpresent.a "$stalist" tstlist=$(cat "$DPKG/libxpresent-tests.list" 2>/dev/null) haspkgline libxpresent/pkg-tests-probe \ /usr/libexec/libxpresent-tests/libxpresent-probe "$tstlist" # ${PN} IS NEVER WRITTEN ON THIS ARM, so there is no libxpresent.list to find. # Asserting its absence is the packaging half of section 1's no-shared case: a # list here would mean a shared object had been produced and packaged. check_rc libxpresent/pkg-no-runtime 1 test -e "$DPKG/libxpresent.list" # --------------------------------------------------------------------------- # 4. 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/libxpresent-probe if cp "$XT/libxpresent-probe" "$probe" 2>/dev/null && chmod +x "$probe" 2>/dev/null; then "$probe" >"$work/out" 2>"$work/err" rc=$? # Read once, with builtins only -- see xp_line's header for what the forked # spelling of this cost. The `|| [ -n "$_l" ]' keeps a last line that lacks a # trailing newline, and the leading newline is what anchors xp_line's match. XP_OUT= while IFS= read -r _l || [ -n "$_l" ]; do XP_OUT="$XP_OUT$NL$_l" done < "$work/out" # 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. # # It is also the case that would catch the seeded-list section going wrong. If # XPresentCheckExtension had stopped short-circuiting, section 8 would run on # into LockDisplay(token) and fault, and the probe would die HERE rather than # reporting a wrong sentinel. if [ "$rc" -eq 0 ]; then bst_ok libxpresent/probe-runs else bst_notok libxpresent/probe-runs "rc=$rc $(_bst_1line "$(cat "$work/err")")"; fi total=$(sed -n 's/^XPRESENT-TOTAL //p' "$work/out") if [ -n "$total" ]; then bst_ok libxpresent/probe-complete else bst_notok libxpresent/probe-complete \ "no XPRESENT-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 -- every case below greps for what it expects, # so a line that stopped being printed is indistinguishable from one whose # grep simply found nothing. if [ -n "$total" ] && [ "$total" -ge 160 ]; then bst_ok libxpresent/probe-coverage else bst_notok libxpresent/probe-coverage \ "reported ${total:-0} measurements, expected not less than 160"; fi # The version the archive was built from, reported by the binary rather than # read off a path, so a stale probe left behind by an earlier build cannot # pass for this one. xp_line libxpresent/probe-version "XPRESENT-VERSION " "1.0.1" # ------------------------------------------------------------------------- # 5. THE TRAP DID NOT FIRE. # # The probe DEFINES XQueryExtension, so that a seeding failure lands there # instead of in XInitExtension's dereference of the token. Reaching it while # armed prints XPRESENT-ABORT and _exit(0)s -- which section 4's # probe-complete would already have caught, but only as "short", saying # nothing about why. These two cases say why, and they are the reason this # probe is safe to run on an image other tickets share. # # stub-calls is asserted at 0 rather than merely "no ABORT line": a call that # arrived after the trap was disarmed (section 12 disarms it before # XOpenDisplay) would be Xlib's own, would be harmless, and would still mean # something about this image's Xlib had changed. check_rc libxpresent/trap-not-fired 1 grep -q '^XPRESENT-ABORT ' "$work/out" xp_line libxpresent/trap-stub-unused "XPRESENT-FAIL stub-calls " "0" # ------------------------------------------------------------------------- # 6. THE VERSION -- two independent quantities, deliberately kept apart. # # PRESENT_MAJOR and PRESENT_MINOR are xorgproto's and are the PROTOCOL # version; PRESENT_REVISION is Xpresent.h's own; PRESENT_VERSION is the # formula over the three. XPresentVersion() returns that formula as compiled # INTO libXpresent.a, so `agree' compares the archive with the sysroot: a # mismatch means the .a and the xorgproto here came from different places, and # nothing else in this suite would notice. # # 1.0.1 IS NOT ANY OF THESE. It is the library's own version, and it is # asserted in section 2 through pkg-config and here through the -DLIBXPRESENT_PV # the recipe hands the probe. A suite that expected 1.0.1 out of the header # macros -- which is the obvious reading, and the one the ticket carried -- # would be red against a correct image. xp_line libxpresent/ver-pv "XPRESENT-VER pv " "1.0.1" xp_line libxpresent/ver-major "XPRESENT-VER proto-major " "1" xp_line libxpresent/ver-minor "XPRESENT-VER proto-minor " "3" xp_line libxpresent/ver-revision "XPRESENT-VER proto-revision " "0" # (PRESENT_MAJOR * 10000) + (PRESENT_MINOR * 100) + PRESENT_REVISION. xp_line libxpresent/ver-header "XPRESENT-VER header-version " "10300" xp_line libxpresent/ver-lib "XPRESENT-VER lib-version " "10300" xp_line libxpresent/ver-agree "XPRESENT-VER agree " "yes" # The extension name the library puts on the wire, out of the archive's own # `char XPresentExtensionName[] = PRESENT_NAME;'. xp_line libxpresent/ver-ext-name "XPRESENT-VER ext-name " "Present" # ------------------------------------------------------------------------- # 7. THE EXTENSION LIST (src/Xpresent.c). # # libXpresent does NOT use extutil's list -- it keeps its own, because it has # to cache the negotiated version alongside the codes. The cache is one entry # (extinfo->cur) and a fault in it would surface only with two displays open, # which on this image is never, so it would ship silently and break the first # real X client. # # The Display* is an opaque token the probe never loads through; see the # probe's header for why that is legitimate for these paths and for nothing # else in libXpresent. xp_line libxpresent/list-ndisplays "XPRESENT-LIST seeded-ndisplays " "2" # The fast path: extinfo->cur matches by pointer, so XPresentFindDisplay # returns without reaching XPresentExtAddDisplay and therefore without # XInitExtension. xp_line libxpresent/list-find-cached "XPRESENT-LIST find-cached " "seeded" # The walk, with the cache cleared. It must find the same entry AND re-promote # it -- the promotion is what makes the next call take the fast path, and a # walk that found the entry without promoting it would be a cache that never # warmed. xp_line libxpresent/list-find-walk "XPRESENT-LIST find-walk " "seeded" xp_line libxpresent/list-repromotes "XPRESENT-LIST walk-repromotes " "seeded" # The second entry, keyed on NULL, reached only by the walk. It is the fixture # section 8's NULL-display cases run through. xp_line libxpresent/list-find-null "XPRESENT-LIST find-null " "seeded-null" # The guard every entry point branches on, `((i) && ((i)->codes))'. It is # false here because codes is NULL, which is exactly the state a server # without Present leaves the entry in -- and the state section 8 measures the # consequences of. It is also false for a NULL info. xp_line libxpresent/list-has-ext-seeded "XPRESENT-LIST has-ext-seeded " "0" xp_line libxpresent/list-has-ext-null "XPRESENT-LIST has-ext-null-info " "0" # A display that is not in the list, walked rather than looked up: looking it # up would miss and fall into XPresentExtAddDisplay, which is the path the # trap exists for. xp_line libxpresent/list-absent "XPRESENT-LIST absent-in-list " "0" # -1/-1 is what XPresentExtAddDisplay leaves when the server has no Present, # and it is what XPresentQueryVersion would hand a client that ignored the # return status. xp_line libxpresent/list-unknown-version \ "XPRESENT-LIST unknown-version " "major=-1 minor=-1" # ------------------------------------------------------------------------- # 8. THE HONEST-FAILURE ARM. # # One case per documented arm. This is the arm this image is permanently in, # so these are not error paths in the abstract -- they are what every Present # call on this image does. # XPresentQueryExtension asks XPresentHasExtension itself and returns False, # leaving all three out-parameters UNTOUCHED at the probe's sentinels. That # second case is the part a client actually depends on: a build that wrote # zeroes into them before refusing would look identical here without it. xp_line libxpresent/fail-query-extension "XPRESENT-FAIL query-extension " "0" xp_line libxpresent/fail-query-extension-untouched \ "XPRESENT-FAIL query-extension-untouched " "op=-1 ev=-1 err=-1" # Status 0, via XPresentCheckExtension(dpy, info, 0). The out-parameters are # written only AFTER the guard, so they too stay at the sentinels. xp_line libxpresent/fail-query-version "XPRESENT-FAIL query-version " "0" xp_line libxpresent/fail-query-version-untouched \ "XPRESENT-FAIL query-version-untouched " "major=-1 minor=-1" # XID 0 rather than an allocated id: a refused SelectInput must not burn one, # because the client has no way to free an id it was never told about. xp_line libxpresent/fail-select-input "XPRESENT-FAIL select-input " "0" # uint32_t 0 -- no capabilities, which is the truthful answer for a server # that does not have the extension. xp_line libxpresent/fail-query-capabilities \ "XPRESENT-FAIL query-capabilities " "0" # THE VOID ARM, MEASURED BY REACHING THE NEXT LINE. Not weak: a build in which # XPresentSimpleCheckExtension had stopped short-circuiting would carry on # into LockDisplay(token) and fault, and section 4's probe-runs would go red # instead. xp_line libxpresent/fail-notify-msc "XPRESENT-FAIL notify-msc " "reached" xp_line libxpresent/fail-free-input "XPRESENT-FAIL free-input " "reached" xp_line libxpresent/fail-pixmap "XPRESENT-FAIL pixmap " "reached" # The ticket's zero-count notify list. The guard fires before `notifies' is # read at all, so a NULL pointer with count 0 is refused rather than walked -- # which is what makes the case a statement about ORDERING inside XPresentPixmap # rather than about malloc. xp_line libxpresent/fail-pixmap-zero-notifies \ "XPRESENT-FAIL pixmap-zero-notifies " "reached" # --- the NULL Display ---------------------------------------------------- # # THE LIMIT OF THIS FIXTURE IS STATED RATHER THAN HIDDEN. A raw NULL into an # unseeded libXpresent is undefined behaviour, not a documented failure: the # lookup misses, falls into XPresentExtAddDisplay and XInitExtension # dereferences. The probe seeds a second entry keyed on NULL, so what these # cases measure is the DOCUMENTED refusal with a NULL Display -- the same arm # the token takes, reached by the same comparison. That is the useful half: # it says the refusal does not depend on the Display's identity. xp_line libxpresent/null-query-extension "XPRESENT-NULL query-extension " "0" xp_line libxpresent/null-query-extension-untouched \ "XPRESENT-NULL query-extension-untouched " "op=-1 ev=-1 err=-1" xp_line libxpresent/null-query-capabilities \ "XPRESENT-NULL query-capabilities " "0" xp_line libxpresent/null-select-input "XPRESENT-NULL select-input " "0" xp_line libxpresent/null-notify-msc "XPRESENT-NULL notify-msc " "reached" # ------------------------------------------------------------------------- # 9. THE LAYOUTS -- what this port exists for. # # THE NUMBERS BELOW ARE THIS ARM'S, AND THAT IS THE POINT. wasm32 is ILP32 # with a 64-bit `long long' that aligns to 8: int, Bool, a pointer and the # XID/Window/Pixmap/XSyncFence family are all 4 bytes, while uint64_t is 8 and # drags the struct's own alignment to 8 with it. On the LP64 build host every # one of these would be different. They are stated as the target's answers # because that is the only machine whose answer matters to a client running # here, and they were derived from the compiler rather than from arithmetic: # the same headers, compiled by the same cross compiler this image was built # with (BRINTOS-2495). # --- the arm, first: every offset below follows from these ---------------- xp_line libxpresent/arch-sizeof-long "XPRESENT-ARCH sizeof-long " "4" xp_line libxpresent/arch-sizeof-longlong "XPRESENT-ARCH sizeof-longlong " "8" xp_line libxpresent/arch-sizeof-ptr "XPRESENT-ARCH sizeof-ptr " "4" xp_line libxpresent/arch-sizeof-int "XPRESENT-ARCH sizeof-int " "4" xp_line libxpresent/arch-sizeof-uint64 "XPRESENT-ARCH sizeof-uint64 " "8" # THE ONE THAT IS NOT A RESTATEMENT OF ILP32. uint64_t aligns to 8 here, not # to 4 -- which is what puts four bytes of padding between serial_number and # ust below, and it is the single fact that makes Present's layout on this arm # different from the obvious one. A toolchain that aligned it to 4 (as 32-bit # x86 does) would give a 56-byte complete event and every offset from ust on # would move. xp_line libxpresent/arch-align-uint64 "XPRESENT-ARCH align-uint64 " "8" xp_line libxpresent/arch-align-uint32 "XPRESENT-ARCH align-uint32 " "4" xp_line libxpresent/arch-align-ptr "XPRESENT-ARCH align-ptr " "4" # XID is `unsigned long', so it is 4 here and 8 on the host. Window, Pixmap # and XSyncFence are all XID. xp_line libxpresent/arch-sizeof-xid "XPRESENT-ARCH sizeof-XID " "4" xp_line libxpresent/arch-sizeof-window "XPRESENT-ARCH sizeof-Window " "4" xp_line libxpresent/arch-sizeof-bool "XPRESENT-ARCH sizeof-Bool " "4" xp_line libxpresent/arch-sizeof-xevent "XPRESENT-ARCH sizeof-XEvent " "96" # --- XPresentNotify: the element type of XPresentPixmap's list ------------ # # Eight bytes of two 32-bit fields, and it is the one Present structure whose # size is the SAME here as on the wire (sz_xPresentNotify is 8 too, section # 10) -- because both its members are 32-bit by protocol. XPresentPixmap # passes the array straight to Data32, so a client's array and the wire have # to agree exactly, and this pair of cases is that agreement. xp_line libxpresent/size-notify "XPRESENT-SIZE XPresentNotify " "8" xp_line libxpresent/off-notify-window "XPRESENT-OFF XPresentNotify window " "0" xp_line libxpresent/off-notify-serial "XPRESENT-OFF XPresentNotify serial " "4" xp_line libxpresent/fsz-notify-window "XPRESENT-FSZ XPresentNotify window " "4" xp_line libxpresent/fsz-notify-serial "XPRESENT-FSZ XPresentNotify serial " "4" # --- the common event header --------------------------------------------- # # Six fields shared by every Present event, 24 bytes. A client switching on # ->evtype through the generic type reads them at these offsets whichever # event arrived, so the three HDR cases below assert they have not drifted # apart -- which the per-event offsets alone would not say. xp_line libxpresent/size-event "XPRESENT-SIZE XPresentEvent " "24" xp_line libxpresent/off-ev-type "XPRESENT-OFF XPresentEvent type " "0" xp_line libxpresent/off-ev-serial "XPRESENT-OFF XPresentEvent serial " "4" xp_line libxpresent/off-ev-send "XPRESENT-OFF XPresentEvent send_event " "8" xp_line libxpresent/off-ev-display "XPRESENT-OFF XPresentEvent display " "12" xp_line libxpresent/off-ev-ext "XPRESENT-OFF XPresentEvent extension " "16" xp_line libxpresent/off-ev-evtype "XPRESENT-OFF XPresentEvent evtype " "20" xp_line libxpresent/hdr-complete "XPRESENT-HDR complete " "same" xp_line libxpresent/hdr-idle "XPRESENT-HDR idle " "same" xp_line libxpresent/hdr-configure "XPRESENT-HDR configure " "same" # --- PresentCompleteNotify: the event that carries UST and MSC ------------ # # THE SHARPEST ASSERTION IN THIS SUITE. ust and msc are the 64-bit counters # the whole extension is organised around -- the timestamp a frame was # presented at and the media stream counter it was presented on -- and a # client that read either through a 32-bit `unsigned long' would get a value # that is right for about 71 minutes (ust, microseconds) and then silently # wrong. The offsets say they are where they should be; the FSZ cases say each # is eight bytes wide, which is the assertion the offsets cannot make on their # own: a field that shrank AND a field after it that grew would leave every # offset unchanged. # # NOTE THE GAP AT 36. serial_number ends at 36 and ust begins at 40, because # uint64_t aligns to 8 here. The four bytes between are padding and are NOT a # field this suite has failed to name. xp_line libxpresent/size-complete "XPRESENT-SIZE XPresentCompleteNotifyEvent " "64" xp_line libxpresent/off-c-eid \ "XPRESENT-OFF XPresentCompleteNotifyEvent eid " "24" xp_line libxpresent/off-c-window \ "XPRESENT-OFF XPresentCompleteNotifyEvent window " "28" xp_line libxpresent/off-c-serial \ "XPRESENT-OFF XPresentCompleteNotifyEvent serial_number " "32" xp_line libxpresent/off-c-ust \ "XPRESENT-OFF XPresentCompleteNotifyEvent ust " "40" xp_line libxpresent/off-c-msc \ "XPRESENT-OFF XPresentCompleteNotifyEvent msc " "48" xp_line libxpresent/off-c-kind \ "XPRESENT-OFF XPresentCompleteNotifyEvent kind " "56" xp_line libxpresent/off-c-mode \ "XPRESENT-OFF XPresentCompleteNotifyEvent mode " "57" xp_line libxpresent/fsz-c-ust \ "XPRESENT-FSZ XPresentCompleteNotifyEvent ust " "8" xp_line libxpresent/fsz-c-msc \ "XPRESENT-FSZ XPresentCompleteNotifyEvent msc " "8" xp_line libxpresent/fsz-c-serial \ "XPRESENT-FSZ XPresentCompleteNotifyEvent serial_number " "4" xp_line libxpresent/fsz-c-kind \ "XPRESENT-FSZ XPresentCompleteNotifyEvent kind " "1" xp_line libxpresent/fsz-c-mode \ "XPRESENT-FSZ XPresentCompleteNotifyEvent mode " "1" # --- PresentIdleNotify ---------------------------------------------------- # # No 64-bit field, so it is flat 4-byte members throughout and 44 bytes -- NOT # rounded to 8, because nothing in it demands 8-byte alignment. That # difference from the complete event above is itself worth asserting: it is # what says the 8-byte alignment is coming from the uint64_t members and not # from some blanket struct rule. xp_line libxpresent/size-idle "XPRESENT-SIZE XPresentIdleNotifyEvent " "44" xp_line libxpresent/off-i-eid \ "XPRESENT-OFF XPresentIdleNotifyEvent eid " "24" xp_line libxpresent/off-i-window \ "XPRESENT-OFF XPresentIdleNotifyEvent window " "28" xp_line libxpresent/off-i-serial \ "XPRESENT-OFF XPresentIdleNotifyEvent serial_number " "32" xp_line libxpresent/off-i-pixmap \ "XPRESENT-OFF XPresentIdleNotifyEvent pixmap " "36" xp_line libxpresent/off-i-fence \ "XPRESENT-OFF XPresentIdleNotifyEvent idle_fence " "40" xp_line libxpresent/fsz-i-pixmap \ "XPRESENT-FSZ XPresentIdleNotifyEvent pixmap " "4" xp_line libxpresent/fsz-i-fence \ "XPRESENT-FSZ XPresentIdleNotifyEvent idle_fence " "4" # --- PresentConfigureNotify ----------------------------------------------- # # The widest of the three at 68 bytes, and the one whose members are widened # by the CLIENT struct rather than by the protocol: the wire carries x, y, # width, height, off_x, off_y, pixmap_width and pixmap_height as 16-bit # quantities (section 10: the wire event is 40 bytes) and Xpresent.h declares # them int/unsigned. pixmap_flags is `long', so it is 4 here and would be 8 on # the host -- the one member of any Present event whose width is the arm's # rather than the protocol's, which is why it gets its own FSZ case. xp_line libxpresent/size-configure \ "XPRESENT-SIZE XPresentConfigureNotifyEvent " "68" xp_line libxpresent/off-f-eid \ "XPRESENT-OFF XPresentConfigureNotifyEvent eid " "24" xp_line libxpresent/off-f-window \ "XPRESENT-OFF XPresentConfigureNotifyEvent window " "28" xp_line libxpresent/off-f-x \ "XPRESENT-OFF XPresentConfigureNotifyEvent x " "32" xp_line libxpresent/off-f-y \ "XPRESENT-OFF XPresentConfigureNotifyEvent y " "36" xp_line libxpresent/off-f-width \ "XPRESENT-OFF XPresentConfigureNotifyEvent width " "40" xp_line libxpresent/off-f-height \ "XPRESENT-OFF XPresentConfigureNotifyEvent height " "44" xp_line libxpresent/off-f-off-x \ "XPRESENT-OFF XPresentConfigureNotifyEvent off_x " "48" xp_line libxpresent/off-f-off-y \ "XPRESENT-OFF XPresentConfigureNotifyEvent off_y " "52" xp_line libxpresent/off-f-pixmap-width \ "XPRESENT-OFF XPresentConfigureNotifyEvent pixmap_width " "56" xp_line libxpresent/off-f-pixmap-height \ "XPRESENT-OFF XPresentConfigureNotifyEvent pixmap_height " "60" xp_line libxpresent/off-f-pixmap-flags \ "XPRESENT-OFF XPresentConfigureNotifyEvent pixmap_flags " "64" xp_line libxpresent/fsz-f-pixmap-flags \ "XPRESENT-FSZ XPresentConfigureNotifyEvent pixmap_flags " "4" # --- every event fits an XEvent ------------------------------------------- # # Present events are generic events and arrive through XGenericEventCookie, # but a client that received one into an XEvent-shaped slot -- which is what # XNextEvent writes through -- would be corrupting its own stack if any of # them were larger. 96 against 68 is comfortable today; the case exists # because a future field would eat that margin silently. xp_line libxpresent/fits-complete "XPRESENT-FIT complete " "yes" xp_line libxpresent/fits-idle "XPRESENT-FIT idle " "yes" xp_line libxpresent/fits-configure "XPRESENT-FIT configure " "yes" xp_line libxpresent/fits-header "XPRESENT-FIT header " "yes" # --- the event that is not here ------------------------------------------- # # XPresentRedirectNotifyEvent, PresentRedirectNotify and # PresentRedirectNotifyMask are all behind `#if PRESENT_FUTURE_VERSION', and # nothing defines that macro -- not xorgproto, not libXpresent, not this # distro. So the type does not exist on this image and the compatibility # PRESENT_REDIRECT_NOTIFY_MASK is 0, which means PresentAllEvents is 7 and not # 15 (section 10). # # ASSERTING THE ABSENCE IS THE HONEST EQUIVALENT OF ASSERTING THE OFFSETS, and # it is the case that would notice the day xorgproto turns the macro on: three # of these four flip together, PresentAllEvents changes, and a client # compiled against the new header would start selecting an event this library # cannot decode. A red here is a real change, not a formality. xp_line libxpresent/future-macro-undefined \ "XPRESENT-FUTURE macro " "undefined" xp_line libxpresent/future-event-undefined \ "XPRESENT-FUTURE redirect-event " "undefined" xp_line libxpresent/future-mask-undefined \ "XPRESENT-FUTURE redirect-mask " "undefined" xp_line libxpresent/future-compat-mask-zero \ "XPRESENT-FUTURE redirect-compat-mask " "0" # ------------------------------------------------------------------------- # 10. THE PROTOCOL CONSTANTS. # # Wire values fixed by the Present specification, compiled on THIS image out # of the xorgproto it ships. They are stated here rather than diffed against a # reference build because they have an absolute answer -- which is what makes # a red readable without a second machine. xp_line libxpresent/const-name "XPRESENT-CONST name " "Present" xp_line libxpresent/const-nreq "XPRESENT-CONST number-requests " "5" xp_line libxpresent/const-nevents "XPRESENT-CONST number-events " "0" xp_line libxpresent/const-nerrors "XPRESENT-CONST number-errors " "0" # PresentNumberEvents is 0 and that is not a mistake: Present defines no core # events at all. Its three notifications are GENERIC events, delivered through # the XGE cookie machinery Xpresent.c registers -- which is why the library # calls XESetWireToEventCookie rather than XESetWireToEvent, and why there is # no event-base arithmetic in this suite where 57-libxfixes.test has a whole # section of it. xp_line libxpresent/req-query-version "XPRESENT-REQ query-version " "0" xp_line libxpresent/req-pixmap "XPRESENT-REQ pixmap " "1" xp_line libxpresent/req-notify-msc "XPRESENT-REQ notify-msc " "2" xp_line libxpresent/req-select-input "XPRESENT-REQ select-input " "3" xp_line libxpresent/req-query-caps "XPRESENT-REQ query-capabilities " "4" xp_line libxpresent/opt-none "XPRESENT-OPT none " "0" xp_line libxpresent/opt-async "XPRESENT-OPT async " "1" xp_line libxpresent/opt-copy "XPRESENT-OPT copy " "2" xp_line libxpresent/opt-ust "XPRESENT-OPT ust " "4" xp_line libxpresent/opt-suboptimal "XPRESENT-OPT suboptimal " "8" xp_line libxpresent/opt-may-tear "XPRESENT-OPT async-may-tear " "16" xp_line libxpresent/opt-all "XPRESENT-OPT all " "31" xp_line libxpresent/opt-all-async "XPRESENT-OPT all-async " "17" xp_line libxpresent/cap-none "XPRESENT-CAP none " "0" xp_line libxpresent/cap-async "XPRESENT-CAP async " "1" xp_line libxpresent/cap-fence "XPRESENT-CAP fence " "2" xp_line libxpresent/cap-ust "XPRESENT-CAP ust " "4" xp_line libxpresent/cap-may-tear "XPRESENT-CAP async-may-tear " "8" xp_line libxpresent/cap-all "XPRESENT-CAP all " "15" xp_line libxpresent/cap-all-async "XPRESENT-CAP all-async " "9" # THE CAPABILITY AND OPTION TABLES DO NOT LINE UP, and that is the one thing # about them worth remembering: PresentCapabilityFence is 2 while # PresentOptionCopy is 2, and PresentCapabilityAsyncMayTear is 8 while # PresentOptionSuboptimal is 8. A client that used one table's constant # against the other's field would be silently asking for something else, and # the pair of `all' cases above is what pins both tables at once. xp_line libxpresent/ev-configure "XPRESENT-EV configure " "0" xp_line libxpresent/ev-complete "XPRESENT-EV complete " "1" xp_line libxpresent/ev-idle "XPRESENT-EV idle " "2" xp_line libxpresent/mask-configure "XPRESENT-MASK configure " "1" xp_line libxpresent/mask-complete "XPRESENT-MASK complete " "2" xp_line libxpresent/mask-idle "XPRESENT-MASK idle " "4" # 7, not 15: the redirect bit is compiled out. See section 9's FUTURE cases. xp_line libxpresent/mask-all "XPRESENT-MASK all " "7" # Each mask is 1 << its own event subtype -- the relationship between the two # tables above, stated once as a fact rather than three times as arithmetic # the reader has to check. These are what notice a header edit that broke the # correspondence while leaving both numbers individually plausible. xp_line libxpresent/mask-shift-configure \ "XPRESENT-MASK configure-is-shift " "yes" xp_line libxpresent/mask-shift-complete \ "XPRESENT-MASK complete-is-shift " "yes" xp_line libxpresent/mask-shift-idle "XPRESENT-MASK idle-is-shift " "yes" xp_line libxpresent/kind-pixmap "XPRESENT-KIND pixmap " "0" xp_line libxpresent/kind-notify-msc "XPRESENT-KIND notify-msc " "1" xp_line libxpresent/mode-copy "XPRESENT-MODE copy " "0" xp_line libxpresent/mode-flip "XPRESENT-MODE flip " "1" xp_line libxpresent/mode-skip "XPRESENT-MODE skip " "2" xp_line libxpresent/mode-suboptimal "XPRESENT-MODE suboptimal-copy " "3" # --- the wire structures -------------------------------------------------- # # presentproto.h's, and the other half of section 9's question. The wire # event is 40 bytes where the client struct is 64: the wire packs the header # into 12 bytes and carries the geometry as 16-bit fields, and Xlib's # WireToCookie widens them. What must NOT differ is the counters -- 8 bytes on # the wire and 8 in the struct -- because a mismatch there is exactly the # truncation this port is about, and it would be invisible in the client # struct alone. xp_line libxpresent/wire-notify "XPRESENT-WIRE sizeof-xPresentNotify " "8" xp_line libxpresent/wire-complete "XPRESENT-WIRE sizeof-complete " "40" xp_line libxpresent/wire-idle "XPRESENT-WIRE sizeof-idle " "32" xp_line libxpresent/wire-ust "XPRESENT-WIRE complete-ust " "8" xp_line libxpresent/wire-msc "XPRESENT-WIRE complete-msc " "8" xp_line libxpresent/wire-req-tmsc \ "XPRESENT-WIRE pixmap-req-target-msc " "8" # ------------------------------------------------------------------------- # 11. THE ENTRY-POINT CENSUS. # # Section 12 establishes that no display opens here, which makes the # round-trip half of the API unreachable -- so the only thing left to say # about it is that it EXISTS: that the cross build compiled the module, that # the static linker resolved each name out of libXpresent.a, and that the # address is in the shipped image. Taking the address is what forces the # archive member in, so a symbol the build had silently dropped fails the LINK # in do_compile, naming what went missing. # # Nine names: the eight Xpresent.h declares, plus XPresentFindDisplay, which # is not in the header but has external linkage and is what section 7 drives. # Checked by COUNT and by every name -- the library is one translation unit, # so there is no module coverage to sample and naming all nine costs nothing. # The recipe's own LIBXPRESENT_MIN_ENTRY_POINTS floor is what notices the # probe's table shrinking to match a loss. nsym=$(sed -n 's/^XPRESENT-SYM-COUNT //p' "$work/out") if [ -n "$nsym" ] && [ "$nsym" -ge 9 ]; then bst_ok libxpresent/sym-count else bst_notok libxpresent/sym-count \ "census names ${nsym:-0} entry points, expected not less than 9"; fi xp_line libxpresent/sym-none-null "XPRESENT-SYM-NULL " "0" for s in XPresentQueryExtension XPresentQueryVersion XPresentVersion \ XPresentFindDisplay XPresentPixmap XPresentNotifyMSC \ XPresentSelectInput XPresentFreeInput XPresentQueryCapabilities; do xp_line "libxpresent/sym-$s" "XPRESENT-SYM $s " "linked" done # ------------------------------------------------------------------------- # 12. THE DISPLAY THAT IS NOT THERE. # # The documented no-server path, and the reason section 8 had to seed the list # to say anything at all: libXpresent's API begins at a Display, Xlib is the # only thing that makes one, and on this image it cannot. # # These are the ticket's two DISPLAY fixtures. `no-env' is the probe having # unset DISPLAY -- not a spec of "", because XOpenDisplay("") and # XOpenDisplay(NULL) both fall back to $DISPLAY and would make the case a test # of whatever the runner's environment carried. `unix-99' is DISPLAY pointing # at an address nothing is listening on; a unix-socket spelling deliberately, # because it fails at connect(2) with ENOENT and CANNOT block, where a TCP # spelling would put a resolver and a connect timeout between the call and its # answer. # # What must hold is that XOpenDisplay SAYS so -- NULL, promptly, for each # spelling -- rather than crashing, blocking, or handing back a half-built # Display that the first XPresentQueryVersion would then walk into. The last # case is what distinguishes a clean refusal from a hang: the probe reached # its end. xp_line libxpresent/dpy-no-env "XPRESENT-DPY no-env " "NULL" xp_line libxpresent/dpy-unix-99 "XPRESENT-DPY unix-99 " "NULL" xp_line libxpresent/dpy-end "XPRESENT-DPY reached-end " "yes" else bst_notok libxpresent/probe-stage "cannot stage $XT/libxpresent-probe" fi rm -rf "$work"