. "$BST_LIB" # libxcb 1.16 (poky meta/recipes-graphics/xorg-lib) -- the C binding to the X11 # wire protocol, and the fourth member of the X11 client library stack. Xlib 1.8 # is implemented on top of it, so nothing above it can be reached until this one # works. # # THE PACKAGE IS ALMOST ALL GENERATED CODE, WHICH IS WHAT THIS SUITE IS ABOUT. # libxcb's tarball does not contain its protocol layer: c_client.py writes one # .c and one .h per xcb-proto XML description at build time, and each becomes # one archive. So the interesting question is not "did the files arrive" but # "did the generator produce, on a wasm32 cross build, the same protocol tables # it produces natively" -- and that is answerable, because the recipe compiles # ONE probe source twice, once against this target's libxcb.a and once against # libxcb-native's, and ships the build host's run beside the binary as # libxcb-probe.ref. Every fact the probe prints is a property of the X protocol # rather than of a machine, so the two runs must agree byte for byte. # # ORDERING IS LOAD-BEARING, the shape 84-iperf3 established. Packaging first, # then the generated tables, then the display parser, and the CONNECTION LAST. # A red in an early block is a packaging or generator change; a red in the last # one is the transport, and what is expected there is spelled out above it. # # WHAT THIS IMAGE CANNOT DO, STATED ONCE. There is no X server here and there # will not be one, so no connection can succeed. That is not a gap in the port: # the contract libxcb owes a machine with no server is that xcb_connect returns # a CONNECTION rather than NULL, that it reports the documented error code, and # that every subsequent call on it fails cleanly instead of dereferencing a null # setup. That contract is exactly what lets an Xlib-binding package build and # ship here, and it is asserted rather than skipped. require pkg-config libxcb/pkg-config-present XT=/usr/libexec/libxcb-tests LIBD=/usr/lib INC=/usr/include/xcb CENSUS=/usr/share/libxcb/CENSUS work=${TMPDIR:-/tmp}/bst-libxcb.$$ rm -rf "$work"; mkdir -p "$work" hasfile() { if [ -s "$2" ]; then bst_ok "$1"; else bst_notok "$1" "missing or empty: $2"; fi } # The suite reached its first line. Every case below is emitted in order, so a # run that stops early is visible as a missing tail rather than as a short pass. bst_ok libxcb/suite-canary # --- packaging --------------------------------------------------------------- # The distro configures --disable-shared, so FILES:${PN} (${libdir}/libxcb.so.*) # matches nothing, the ${PN} package is never emitted, and PACKAGES_DYNAMIC's # per-extension splits are empty for the same reason. Everything real is in # -staticdev and -dev. Asserting the shared objects ABSENT is a real test of # that arm rather than a formality: libxcb builds twenty-odd libtool libraries # and every one of them asks for a versioned .so by default. check_eq libxcb/no-shared-objects "" "$(ls $LIBD/libxcb*.so* 2>/dev/null)" hasfile libxcb/archive-core "$LIBD/libxcb.a" hasfile libxcb/hdr-xcb "$INC/xcb.h" hasfile libxcb/hdr-xproto "$INC/xproto.h" hasfile libxcb/hdr-bigreq "$INC/bigreq.h" hasfile libxcb/hdr-xcbext "$INC/xcbext.h" hasfile libxcb/probe-shipped "$XT/libxcb-probe" hasfile libxcb/ref-shipped "$XT/libxcb-probe.ref" hasfile libxcb/census-shipped "$CENSUS" # --- the generated code, counted ------------------------------------------- # One archive per xcb-proto protocol description the generator turned into C. # The recipe pins a floor at build time and writes what it installed into # CENSUS; this re-derives the same count from the shipped filesystem, so a # census that disagrees with the tree is itself the finding. n_a=$(ls $LIBD/libxcb*.a 2>/dev/null | wc -l) n_census=$(grep -c '^libxcb.*\.a$' "$CENSUS" 2>/dev/null) check_eq libxcb/census-matches-tree "$n_a" "$n_census" if [ "${n_a:-0}" -ge 20 ]; then bst_ok libxcb/archive-count else bst_notok libxcb/archive-count "$n_a libxcb*.a archives, expected not less than 20" fi check_eq libxcb/census-version "XCB-VERSION 1.16" "$(sed -n 1p "$CENSUS" 2>/dev/null)" # A spread of the extension archives by name. Each one is a whole protocol # description that c_client.py had to accept, so an absence here is a generator # that skipped an extension rather than a packaging slip. missing= for a in libxcb.a libxcb-composite.a libxcb-damage.a libxcb-dpms.a \ libxcb-glx.a libxcb-present.a libxcb-randr.a libxcb-record.a \ libxcb-render.a libxcb-res.a libxcb-screensaver.a libxcb-shape.a \ libxcb-shm.a libxcb-sync.a libxcb-xfixes.a libxcb-xinerama.a \ libxcb-xinput.a libxcb-xkb.a libxcb-xtest.a libxcb-xv.a; do [ -s "$LIBD/$a" ] || missing="$missing $a" done check_eq libxcb/archives-named "" "$missing" # pkg-config is how every consumer of this port will find it, and the -dev # package exists for that one purpose. check_eq libxcb/pc-modversion "1.16" "$(pkg-config --modversion xcb 2>/dev/null)" check_rc libxcb/pc-exists 0 pkg-config --exists xcb pkg-config --validate xcb >"$work/v.out" 2>&1 if [ $? -eq 0 ] && [ ! -s "$work/v.out" ]; then bst_ok libxcb/pc-validate else bst_notok libxcb/pc-validate "$(_bst_1line "$(cat "$work/v.out")")" fi cf=$(PKG_CONFIG_ALLOW_SYSTEM_CFLAGS=1 pkg-config --cflags xcb 2>/dev/null | sed 's/[[:space:]]*$//') check_eq libxcb/pc-cflags "-I/usr/include" "$cf" # --- the probe runs ---------------------------------------------------------- # Copied to tmpfs before exec: an exec image staged from the virtiofs root is # the platform's least reliable path, and a probe that cannot start would # otherwise read as a library that misbehaved. probe=$work/libxcb-probe if cp "$XT/libxcb-probe" "$probe" 2>/dev/null && chmod +x "$probe" 2>/dev/null; then bst_ok libxcb/probe-staged else bst_notok libxcb/probe-staged "cannot copy $XT/libxcb-probe to $work" fi "$probe" > "$work/p.out" 2>"$work/p.err" prc=$? if [ "$prc" -eq 0 ]; then bst_ok libxcb/probe-runs elif bst_execfail "$(cat "$work/p.err")"; then bst_unrun libxcb/probe-runs "$(cat "$work/p.err")" else bst_notok libxcb/probe-runs "rc=$prc $(_bst_1line "$(cat "$work/p.err")")" fi # A probe that stopped part way is a NON-RESULT, and every comparison below it # would read as agreement over the cases that never ran. The terminator is what # separates the two, so it is asserted before anything is compared. tgt_total=$(sed -n 's/^XCB-TOTAL //p' "$work/p.out") ref_total=$(sed -n 's/^XCB-TOTAL //p' "$XT/libxcb-probe.ref") if [ -n "$tgt_total" ]; then bst_ok libxcb/probe-complete else bst_notok libxcb/probe-complete "no XCB-TOTAL line: $(_bst_1line "$(cat "$work/p.out")")" fi check_eq libxcb/probe-total-agrees "$ref_total" "$tgt_total" if [ -n "$tgt_total" ] && [ "$tgt_total" -ge 150 ]; then bst_ok libxcb/probe-coverage else bst_notok libxcb/probe-coverage "covers ${tgt_total:-0} lines, expected not less than 150" fi # --- the generated tables, against the build host --------------------------- # Section by section rather than one whole-file diff, so a disagreement names # what disagreed. Each section is a different claim: SIZE is the ILP32-vs-LP64 # statement about wire structs, OP is the protocol's own constants, EXT is the # extension name table as QueryExtension would ask for it, FN is libxcb's two # hand-written helpers that every generated *_sizeof() calls. for sec in SIZE OP EXT FN PARSE CONN; do grep "^XCB-$sec " "$work/p.out" | sort > "$work/tgt.$sec" grep "^XCB-$sec " "$XT/libxcb-probe.ref" | sort > "$work/ref.$sec" if [ ! -s "$work/ref.$sec" ]; then bst_notok "libxcb/ref-has-$sec" "the shipped reference carries no XCB-$sec lines" elif cmp -s "$work/tgt.$sec" "$work/ref.$sec"; then bst_ok "libxcb/$sec-matches-buildhost" else bst_notok "libxcb/$sec-matches-buildhost" \ "$(_bst_1line "$(diff "$work/ref.$sec" "$work/tgt.$sec" 2>/dev/null | head -6)")" fi done # The diff above is only as good as the two runs sharing a source, so the # protocol constants are ALSO asserted against the X11 specification directly. # Two independent sources agreeing is what makes this more than a self-diff -- # the reading 57-xcb-proto.test takes when it re-derives its census with # xmllint rather than with the generator that wrote it. op() { check_eq "libxcb/op-$1" "$2" "$(sed -n "s/^XCB-OP $3 //p" "$work/p.out")" } op createwindow 1 XCB_CREATE_WINDOW op destroywindow 4 XCB_DESTROY_WINDOW op mapwindow 8 XCB_MAP_WINDOW op getgeometry 14 XCB_GET_GEOMETRY op internatom 16 XCB_INTERN_ATOM op changeproperty 18 XCB_CHANGE_PROPERTY op getproperty 20 XCB_GET_PROPERTY op sendevent 25 XCB_SEND_EVENT op openfont 45 XCB_OPEN_FONT op createpixmap 53 XCB_CREATE_PIXMAP op creategc 55 XCB_CREATE_GC op copyarea 62 XCB_COPY_AREA op putimage 72 XCB_PUT_IMAGE op getimage 73 XCB_GET_IMAGE op createcolormap 78 XCB_CREATE_COLORMAP op alloccolor 84 XCB_ALLOC_COLOR op queryextension 98 XCB_QUERY_EXTENSION op listextensions 99 XCB_LIST_EXTENSIONS op bell 104 XCB_BELL op killclient 113 XCB_KILL_CLIENT op nooperation 127 XCB_NO_OPERATION op keypress 2 XCB_KEY_PRESS op buttonpress 4 XCB_BUTTON_PRESS op motionnotify 6 XCB_MOTION_NOTIFY op expose 12 XCB_EXPOSE op clientmessage 33 XCB_CLIENT_MESSAGE op mappingnotify 34 XCB_MAPPING_NOTIFY op err-request 1 XCB_REQUEST op err-value 2 XCB_VALUE op err-window 3 XCB_WINDOW op err-atom 5 XCB_ATOM op err-impl 17 XCB_IMPLEMENTATION op atom-primary 1 XCB_ATOM_PRIMARY op atom-secondary 2 XCB_ATOM_SECONDARY op atom-string 31 XCB_ATOM_STRING op atom-wmname 39 XCB_ATOM_WM_NAME op atom-wmclass 67 XCB_ATOM_WM_CLASS op conn-err 1 XCB_CONN_ERROR op conn-parse 5 XCB_CONN_CLOSED_PARSE_ERR op conn-screen 6 XCB_CONN_CLOSED_INVALID_SCREEN # The wire structs whose size the protocol fixes outright. These are the cases # that would move if a generated header ever widened a protocol field to the # target's pointer, which is the one cross-compilation failure mode a native # build can never surface. sz() { check_eq "libxcb/size-$1" "$2" "$(sed -n "s/^XCB-SIZE $3 //p" "$work/p.out")" } sz setup 40 xcb_setup_t sz screen 40 xcb_screen_t sz visualtype 24 xcb_visualtype_t sz format 8 xcb_format_t sz depth 8 xcb_depth_t # 36, not the protocol's 32: libxcb appends its own `full_sequence` word to # every event and error struct so a caller can see the widened sequence number. # That is a libxcb property rather than an X one, which is exactly why it is # pinned -- it is the kind of field a generator change would move silently. sz genericevent 36 xcb_generic_event_t sz genericerror 36 xcb_generic_error_t # The reply header alone: response_type, pad, sequence, length. sz genericreply 8 xcb_generic_reply_t sz window 4 xcb_window_t sz atom 4 xcb_atom_t sz timestamp 4 xcb_timestamp_t sz keysym 4 xcb_keysym_t sz keycode 1 xcb_keycode_t sz point 4 xcb_point_t sz rectangle 8 xcb_rectangle_t sz arc 12 xcb_arc_t sz segment 8 xcb_segment_t sz char2b 2 xcb_char2b_t # The extension name table, against the X protocol registry rather than against # the reference. These two strings are what the server is asked for by name. check_eq libxcb/ext-bigreq "BIG-REQUESTS" \ "$(sed -n 's/^XCB-EXT xcb_big_requests_id //p' "$work/p.out")" check_eq libxcb/ext-xcmisc "XC-MISC" \ "$(sed -n 's/^XCB-EXT xcb_xc_misc_id //p' "$work/p.out")" # libxcb's own helpers, executed. Every generated *_sizeof() calls them, so a # wrong answer here is wrong for every variable-length reply in the protocol. check_eq libxcb/fn-popcount "0 1 32 2" "$(sed -n 's/^XCB-FN popcount //p' "$work/p.out")" check_eq libxcb/fn-sumof "0 15" "$(sed -n 's/^XCB-FN sumof //p' "$work/p.out")" # --- the display parser ------------------------------------------------------ # xcb_parse_display needs no server, no socket and no display, so it is fully # exercisable on this image and it is the first thing every caller reaches. The # fixtures are asserted individually as well as diffed, because "the parser # agrees with the build host" and "the parser is right" are different claims and # only the second is about X. pd() { check_eq "libxcb/parse-$1" "$2" "$(sed -n "s/^XCB-PARSE $1 //p" "$work/p.out")" } # DISPLAY is unset by the probe itself, so a NULL or empty name has nothing to # fall back to and the parser must refuse rather than invent a local display. pd null "rc=0 host='' display=-1 screen=-1" pd empty "rc=0 host='' display=-1 screen=-1" pd colon0 "rc=1 host='' display=0 screen=0" pd colon0dot0 "rc=1 host='' display=0 screen=0" pd colon1dot2 "rc=1 host='' display=1 screen=2" pd host0 "rc=1 host='host' display=0 screen=0" pd host0dot5 "rc=1 host='host' display=0 screen=5" pd unixcolon12 "rc=1 host='' display=1 screen=2" pd tcphost23 "rc=1 host='host' display=2 screen=3" pd bigdisplay "rc=1 host='' display=123 screen=45" pd nocolon "rc=0 host='' display=-1 screen=-1" pd barecolon "rc=0 host='' display=-1 screen=-1" pd nondigit "rc=0 host='' display=-1 screen=-1" pd trailingdot "rc=0 host='' display=-1 screen=-1" pd dotnondigit "rc=0 host='' display=-1 screen=-1" # UPSTREAM ACCEPTS A NEGATIVE DISPLAY, and this case pins that rather than # endorsing it. xcb_parse_display takes the number with strtoul(3), which # happily consumes a leading `-` and wraps, so ":-1" parses as display -1 with # screen 0 instead of being refused. It is a finding about libxcb, it is # identical on x86-64, and a change in either direction should be seen. pd negative "rc=1 host='' display=-1 screen=0" # --- the connection, which is the honest failure ----------------------------- # LAST on purpose. Nothing here can succeed and nothing here is expected to: the # claim is that the failure is the DOCUMENTED one and that the connection object # it hands back is safe to use. Two different refusals are distinguished, and # telling them apart is the point -- # # XCB_CONN_CLOSED_PARSE_ERR (5) the name never parsed, so no socket was # attempted. This is what an unset DISPLAY # gets, and it is a statement about the parser. # XCB_CONN_ERROR (1) the name parsed and the SOCKET failed. This # is the AF_UNIX transport reached for real -- # there is no X socket directory on this image # and no server on display 99 -- and it is the # only execution of libxcb's transport half # this machine can offer. # # A red on one of these is not "X does not work here", which is already known; # it is libxcb reporting the wrong reason, which would send the first consumer # of this port looking in the wrong place. # Each connection case emits six lines: the accessor line, then poll, extension # data, a real generated request, the void-request path, and the disconnect. All # five cases must answer identically on every field except the two the name # itself determines, so they are asserted in one loop. fld() { grep "^XCB-CONN $1 " "$work/p.out" | sed -n "s/.*[ ]$2=\\([^ ]*\\).*/\\1/p" | head -1; } for c in nodisplay emptyname garbage unix99 bare99; do check_eq "libxcb/conn-$c-fd" "-1" "$(fld "$c" fd)" # NONNULL, and that is the point rather than a surprise: the connection # libxcb hands back on failure carries a zeroed setup, so a caller that # reads xcb_get_setup(c)->roots_len without checking gets 0 instead of a # segfault. It is the whole reason this port is usable on a machine with no # server, so it is asserted rather than assumed. check_eq "libxcb/conn-$c-setup" "nonnull" "$(fld "$c" setup)" check_eq "libxcb/conn-$c-maxreq" "0" "$(fld "$c" maxreq)" check_eq "libxcb/conn-$c-genid" "0xffffffff" "$(fld "$c" genid)" check_eq "libxcb/conn-$c-flush" "0" "$(fld "$c" flush)" check_eq "libxcb/conn-$c-poll" "null" "$(fld "$c" poll)" check_eq "libxcb/conn-$c-extdata" "null" "$(fld "$c" extdata)" check_eq "libxcb/conn-$c-reply" "seq=0 reply=null err=null" \ "$(sed -n "s/^XCB-CONN $c \\(seq=.*\\)$/\\1/p" "$work/p.out")" check_eq "libxcb/conn-$c-void" "voidseq=0 check=null" \ "$(sed -n "s/^XCB-CONN $c \\(voidseq=.*\\)$/\\1/p" "$work/p.out")" # xcb_disconnect on a connection that never opened is the release path a # caller will actually take here, and it is the one that could crash. check_eq "libxcb/conn-$c-disconnect" "1" "$(fld "$c" disconnected)" done # The two refusal codes, read off the header rather than hard-coded twice: the # op- cases above already pinned XCB_CONN_CLOSED_PARSE_ERR at 5 and # XCB_CONN_ERROR at 1 against the specification. errof() { sed -n "s/^XCB-CONN $1 err=\([0-9]*\) .*/\1/p" "$work/p.out"; } check_eq libxcb/conn-nodisplay-parse-err "5" "$(errof nodisplay)" check_eq libxcb/conn-emptyname-parse-err "5" "$(errof emptyname)" check_eq libxcb/conn-garbage-parse-err "5" "$(errof garbage)" check_eq libxcb/conn-unix99-socket-err "1" "$(errof unix99)" check_eq libxcb/conn-bare99-socket-err "1" "$(errof bare99)" # The screen out-parameter is written by the PARSER, before the socket is tried, # so a connection that failed at the socket still reports the screen the name # named. That is worth pinning: it is the one field a caller may legitimately # read off a failed connection. check_eq libxcb/conn-bare99-screen "7" \ "$(sed -n 's/^XCB-CONN bare99 .*[ ]screen=\([-0-9]*\)$/\1/p' "$work/p.out")" # xcb_connect is documented never to return NULL, and a caller that trusted the # documentation and did not check would segfault here rather than see an error. check_eq libxcb/conn-never-null "" "$(grep '^XCB-CONN .* null=1$' "$work/p.out")" rm -rf "$work"