. "$BST_LIB" # BST-TIMEOUT: 300 # One probe run -- a 540-layout sweep and 298 conversion # round trips, milliseconds of work -- plus ~70 shell # extractions. Same shape and same figure as # 57-xcb-util.test and 57-xcb-util-keysyms.test. # xcb-util-image 0.4.1 -- the XCB image convenience library (poky # recipes-graphics/xorg-lib). See xcb-util-image_%.bbappend for what the port # adds and for the static-link closure it needed. # # --------------------------------------------------------------------------- # WHAT IS MEASURED HERE, AND WHY IT IS MORE THAN ITS SIBLINGS MANAGE # # For most members of this round the honest measurement is a guard being # reached, because the package cannot do its work without a server. This one is # the family's exception. Of the 15 symbols libxcb-image.a defines, EIGHT take # no xcb_connection_t, and those eight are not a corner of the library -- they # are its subject: bit order, byte order, scanline pad, scanline unit, depth # and stride arithmetic over a buffer the caller supplies. Every one of them # runs end to end here, doing real work. # # The other seven take a connection and are measured as documented refusals on # a connection with no server. That is the weaker claim and it is labelled as # one below. All 15 are called, so the link proves the whole ABI present. # # --------------------------------------------------------------------------- # EVERY EXPECTATION BELOW WAS MEASURED, NOT PREDICTED -- BUT MEASURED ON # x86-64, AND THAT DISTINCTION IS THE ONE A READER NEEDS # # On 2026-09-12, by building this exact probe source as xcb-util-image-native # and running it (the bbappend carries BBCLASSEXTEND = "native" so that run # stays reproducible from the shipped tree: `bitbake xcb-util-image-native', # then run xcb-util-image-probe). Nothing here was measured on wasm32 before it # was committed, because a recipe-scope build produces no rootfs to run it on. # # The values are expected to carry across the word size, and the reason is not # hope: every figure asserted below is a property of explicitly-sized data. # The strides and sizes are uint32_t arithmetic over a uint16_t width and # uint8_t bpp/pad/unit; the pixel encodings are byte stores at computed # offsets; the buffer digests are FNV-1a over those bytes. None of it reads a # pointer, a long, or a struct whose layout differs between the two. # # THE ONE FIGURE THAT DOES NOT CARRY IS NAMED RATHER THAN ASSERTED. # sizeof(xcb_image_t) is 48 on x86-64 and is expected to be 40 on wasm32 -- # the struct ends in two pointers -- so it is REPORTED below and checked only # for being one of the two, never for equality with the native run. A suite # that asserted 48 would go red on the arm it is written for. # # --------------------------------------------------------------------------- # THE SWEEP FOUND SOMETHING, AND IT IS THE POINT OF THE SUITE # # 540 layouts are offered to xcb_image_create; 312 are accepted. Over the # accepted ones, 298 conversion round trips run at CONSTANT depth (a conversion # across depths is lossy by construction, so round-tripping one would be # asserting the wrong thing). 264 agree byte for byte. 34 do not -- and all 34 # are explained by ONE rule, with none left over: # # scanline PAD < scanline UNIT, and byte_order != bit_order # # That is not a wasm32 finding. It reproduces exactly on x86-64, and it is a # property of the library: xy_image_byte() swizzles the byte index within the # scanline UNIT (x ^ 1 for unit 16, x ^ 3 for unit 32) whenever the two orders # differ, while xcb_image_annotate() computes the stride from the scanline PAD. # format_valid() requires neither to bound the other, so a pad < unit layout is # accepted and its own accessors then index up to unit/8 - 1 bytes PAST the # scanline the stride defines -- past the whole allocation, on the last row of # the last plane. The probe measures that directly: it owns every buffer, sizes # each one with slack, and counts the cases that wrote into the slack. 14 did. # # So the suite does not assert "34 failures" as a number to be tolerated. It # asserts that the number of failures the rule does NOT explain is ZERO, which # is a regression signal that stays meaningful if the library changes: a new # failure outside the rule turns it red, and a fix inside the rule turns the # explained count down without touching it. # # WE FILE NOTHING AGAINST THIRD-PARTY PUBLIC REPOSITORIES # (docs/conventions/no-public-upstream-reports.md), so this is recorded here # and in the design doc rather than sent upstream. # # --------------------------------------------------------------------------- # TWO MORE UPSTREAM FINDINGS ARE PINNED AS FINDINGS, NOT AS PASSES # # xcb_image_shm_get RETURNS 1 -- success -- on a connection with no server. # The reply never arrives and no error is delivered either, so the `if (err)' # arm is not taken and the else arm frees a NULL reply and reports success. # A caller cannot tell that from a completed read. Asserted as 1 below # BECAUSE that is what it does; a change in either direction should be seen. # # xcb_image_put_pixel's bounds guard is `x > image->width', where # xcb_image_get_pixel's is `x < image->width', so a write at exactly # x == width is admitted and lands one pixel past the row. NOT exercised: # it is an out-of-bounds write, and a suite that drove it would be measuring # whether the overflow happens to be survivable rather than measuring the # library. It is named so the next reader finds it already read. # # --------------------------------------------------------------------------- # NOTHING IS ASSERTED ABOUT A SHARED OBJECT, IN EITHER DIRECTION # # xcb-util-image is not in brintos.conf's BRINTOS_PACKAGE_SHARED_ALLOW, so on # the arm shipping today FILES:${PN} (${libdir}/libxcb-image.so.*) matches # nothing and pkgdata records PKGSIZE:xcb-util-image 0 -- the main package is # never emitted, measured on the 2026-09-11 build where only -dev, -staticdev # and -tests carry a .packaged marker. Asserting the .so absent would go red # the day xcb-util-image is allow-listed, and asserting it present is false # today. So neither is asserted, bst_lib_shape carries the arm-independent # half, and libxcb-image.a is the artefact this suite is about. Same reading, # and the same reason, as xcb-util's and xcb-util-keysyms'. PROBE=${BST_XCB_UTIL_IMAGE_PROBE:-/usr/libexec/xcb-util-image-tests/xcb-util-image-probe} ARCHIVE=/usr/lib/libxcb-image.a INCDIR=/usr/include/xcb PCDIR=/usr/lib/pkgconfig VERSION=0.4.1 tmp=${TMPDIR:-/tmp} out=$tmp/bst-xcb-util-image-probe.$$.out # The first BST result line of a suite is dropped by the guest console, so the # suite opens with one that costs nothing to lose. bst_ok xcb-util-image/suite-start # --- installed surface ------------------------------------------------------ check xcb-util-image/archive-present test -s "$ARCHIVE" bst_lib_shape xcb-util-image/lib-shape "/usr/lib/libxcb-image.so*" # All three headers the recipe promises. xcb_bitops.h is named explicitly # because it is the one a reader is most likely to think is internal -- it # holds xcb_roundup and the host-byte-order helper the clang.patch touches -- # and xcbinclude_HEADERS installs it, so it is part of the deliverable. for h in xcb_image.h xcb_pixel.h xcb_bitops.h; do check "xcb-util-image/header-$h" test -s "$INCDIR/$h" done check "xcb-util-image/pc-present" test -s "$PCDIR/xcb-image.pc" if command -v pkg-config >/dev/null 2>&1; then check xcb-util-image/pc-exists pkg-config --exists xcb-image check xcb-util-image/pc-validate pkg-config --validate xcb-image # The three literals the ticket asks to agree: the version the recipe pins, # the version the installed .pc advertises, and the version in the archive's # own path. There is no soname to compare against -- see the shared-object # note in the header above -- so the archive stands in for it, which is the # artefact that actually ships. check_eq xcb-util-image/pc-modversion "$VERSION" \ "$(pkg-config --modversion xcb-image 2>/dev/null)" # -lxcb-image is the whole point of the .pc: a consumer handed an empty Libs # line would link nothing and fail at the first xcb_image_* symbol. check_out xcb-util-image/pc-libs -lxcb-image pkg-config --libs xcb-image # Requires names BOTH of this library's own dependencies. xcb-shm is the one # worth asserting: libxcb-image.a references xcb_shm_put_image and # xcb_shm_get_image, which live in a SEPARATE archive from libxcb, so a .pc # that lost this line would leave every consumer under-linked in exactly the # way this port had to repair for the package's own test programs. check_out xcb-util-image/pc-requires-xcb "xcb" \ grep "^Requires:" "$PCDIR/xcb-image.pc" check_out xcb-util-image/pc-requires-xcb-shm "xcb-shm" \ grep "^Requires:" "$PCDIR/xcb-image.pc" else bst_skip xcb-util-image/pkg-config-present "pkg-config not installed on this image" fi check xcb-util-image/probe-present test -x "$PROBE" # --- the probe runs --------------------------------------------------------- if ! "$PROBE" > "$out" 2>&1; then bst_notok xcb-util-image/probe-runs "probe exited $? : $(head -c 200 "$out")" exit 0 fi if ! grep -q '^==XCBIMAGE-PROBE-END==$' "$out"; then bst_notok xcb-util-image/probe-runs "probe produced no end sentinel -- truncated run" exit 0 fi bst_ok xcb-util-image/probe-runs field() { grep "^$1 " "$out" | head -1 | cut -d" " -f2-; } # --- the ABI figure that is arm-dependent, reported not asserted ------------- # # 48 on x86-64, 40 on wasm32: the struct ends in `void *base' and `uint8_t # *data'. Checked for being one of the two rather than for equality with the # native run, so this stays a real assertion (a struct that grew a field would # fail it) without encoding the wrong word size. case "$(field abi.sizeof-xcb-image-t)" in 40|48) bst_ok xcb-util-image/abi-sizeof ;; *) bst_notok xcb-util-image/abi-sizeof \ "sizeof(xcb_image_t) is $(field abi.sizeof-xcb-image-t), expected 40 (wasm32) or 48 (x86-64)" ;; esac # And the arm this actually ran on, so a reader of a red above can tell which # of the two figures should have appeared. check_eq xcb-util-image/abi-pointer-bits "32" "$(field abi.pointer-bits)" # --- the validity matrix ---------------------------------------------------- # # Every documented combination of format, depth, bit order, byte order, # scanline pad and scanline unit offered to xcb_image_create, with the # accept/reject verdict of each folded into one FNV-1a digest. One literal # therefore pins all 540 answers, and the three counts beside it say WHERE a # digest mismatch went wrong -- a reader seeing only a changed hash would know # nothing else. check_eq xcb-util-image/matrix-total "540" "$(field matrix.total)" check_eq xcb-util-image/matrix-accepted "312" "$(field matrix.accepted)" check_eq xcb-util-image/matrix-rejected "228" "$(field matrix.rejected)" check_eq xcb-util-image/matrix-hash "0x7d801f2d" "$(field matrix.hash)" # --- the conversion round trip ---------------------------------------------- check_eq xcb-util-image/roundtrip-run "298" "$(field roundtrip.run)" check_eq xcb-util-image/roundtrip-ok "264" "$(field roundtrip.ok)" # THE ASSERTION THAT MATTERS. Not the failure count -- the count of failures # the pad < unit rule does not account for. Zero is the claim: every layout # whose pad bounds its unit round-trips byte for byte, and every one that does # not is the documented addressing inconsistency and nothing else. A new # failure outside the rule turns this red; a fix inside the rule turns # roundtrip-bad-explained down and leaves this alone. check_eq xcb-util-image/roundtrip-no-unexplained-failures "0" \ "$(field roundtrip.bad-unexplained)" check_eq xcb-util-image/roundtrip-bad-explained "34" "$(field roundtrip.bad-explained)" # The overrun measured in the act: cases in which the library wrote past the # scanline its own stride defines, caught in slack the PROBE owns rather than # on the allocator's heap. Asserted as a number because it is a measurement of # upstream behaviour, not a tolerance -- if it drops, the library changed. check_eq xcb-util-image/roundtrip-overrun "14" "$(field roundtrip.overrun)" # --- three-pixel placement -------------------------------------------------- # # The first, last and a middle pixel of eleven layouts, written with # xcb_image_put_pixel and read back with xcb_image_get_pixel. Each case asserts # the stride, the size, the three read-back values AND a digest of the # resulting buffer. The digest is the one that earns its place: a wrong stride # shared by both accessors cancels in the read-back and does not cancel in the # bytes, which is exactly the off-by-one the ticket asks to catch rather than # let cancel out. The middle pixel is (4,1) and not (1,0) for the same reason # -- a stride error shows up as a wrong ROW, so the case has to cross a row # boundary to see one. pixel_case() { check_eq "xcb-util-image/pixel-$1" "$2" "$(field "pixel.$1")" } pixel_case zpixmap.d8.pad8.unit8.byte-lsb.bit-lsb \ "8 24 0x00000001 0x000000a5 0x000000ef 0x3fa91dec overrun=0" pixel_case zpixmap.d16.pad8.unit16.byte-lsb.bit-lsb \ "16 48 0x00000001 0x0000a5a5 0x0000beef 0xd7e5584b overrun=0" pixel_case zpixmap.d16.pad8.unit16.byte-msb.bit-lsb \ "16 48 0x00000001 0x0000a5a5 0x0000beef 0x1fa5f6f7 overrun=0" pixel_case zpixmap.d24.pad32.unit24.byte-lsb.bit-lsb \ "24 72 0x00000001 0x00a5a5a5 0x00adbeef 0xa01f010f overrun=0" pixel_case zpixmap.d24.pad32.unit24.byte-msb.bit-lsb \ "24 72 0x00000001 0x00a5a5a5 0x00adbeef 0x0d4f4117 overrun=0" pixel_case zpixmap.d32.pad32.unit32.byte-lsb.bit-lsb \ "32 96 0x00000001 0xa5a5a5a5 0xdeadbeef 0x6f2fb3a6 overrun=0" pixel_case zpixmap.d32.pad32.unit32.byte-msb.bit-lsb \ "32 96 0x00000001 0xa5a5a5a5 0xdeadbeef 0x34f6725c overrun=0" # The two 16-bit cases and the two 24-bit ones differ ONLY in byte order, and # their digests differ while their read-back pixels agree. That pair is the # suite's direct evidence that byte order reaches the stored bytes and is not # merely cancelling in the accessors -- which is the whole hazard on a fresh # arm, where a wrong pixel is not an error. pixel_case xypixmap.d1.pad8.unit32.byte-lsb.bit-lsb \ "1 3 0x00000001 0x00000001 0x00000001 0xebf1217c overrun=0" # The one three-pixel case that IS a pad < unit layout with differing orders, # kept deliberately: it is the finding above reduced to a single named case, # and its overrun is 3 -- unit 32 swizzles the byte index by ^3 into a # one-byte scanline. Contained in probe-owned slack, measured, not suffered. pixel_case xypixmap.d1.pad8.unit32.byte-lsb.bit-msb \ "1 3 0x00000001 0x00000001 0x00000001 0x4ab0f7b7 overrun=3" pixel_case xypixmap.d1.pad32.unit32.byte-msb.bit-msb \ "4 12 0x00000001 0x00000001 0x00000001 0xb6e3042c overrun=0" pixel_case xybitmap.d1.pad8.unit32.byte-lsb.bit-lsb \ "1 3 0x00000001 0x00000001 0x00000001 0xebf1217c overrun=0" # --- the error paths -------------------------------------------------------- # # Each pins what the library DID, not a pass/fail, because three of these do # something other than the refusal a reader would guess and the point is that a # change in any of them is visible. # A zero width or height is NOT refused: xcb_image_create has no dimension # guard at all, and annotate() computes the cached figures from them. A caller # has to be ready for a zero-size image, and the two differ -- zero width also # zeroes the stride, zero height leaves the stride at its full value with the # size at zero. check_eq xcb-util-image/err-zero-width "created stride=0 size=0" "$(field err.zero-width)" check_eq xcb-util-image/err-zero-height "created stride=8 size=0" "$(field err.zero-height)" # A depth the library does not support, refused two different ways: # format_valid() rejects bpp 7 for Z_PIXMAP outright, and rejects depth > bpp # before it reaches the format switch at all. check_eq xcb-util-image/err-bad-depth-7 "null" "$(field err.bad-depth-7)" check_eq xcb-util-image/err-depth-over-bpp "null" "$(field err.depth-over-bpp)" # A scanline pad that is not 8, 16 or 32. THE ASYMMETRY IS THE FINDING: it is # refused for an XY format and accepted for Z_PIXMAP, because format_valid() # checks xpad only on the XY branch. The accepted one then carries pad 12 into # the stride round-up and produces stride 9 for an 8-pixel 8-bpp row, which is # the arithmetic doing exactly what it was told with an input nothing checked. check_eq xcb-util-image/err-xy-pad-12 "null" "$(field err.xy-pad-12)" check_eq xcb-util-image/err-z-pad-12 "created stride=9 size=27" "$(field err.z-pad-12)" # A plane mask with no bits set. Not a create-time argument -- xcb_image_create # sets the field from the depth -- so the probe clears it afterwards, which is # the only way a caller reaches this state. Both directions are measured: the # same pixel reads 0xf with the mask intact and 0x0 with it cleared, so the # mask is shown to be what suppressed it rather than the write having failed. check_eq xcb-util-image/err-plane-mask-zero "with=0xf without=0x0" \ "$(field err.plane-mask-zero)" # And with the mask cleared BEFORE the write, so the suppression is measured on # the write path too. The digest is of an all-zero buffer: the write took no # plane, which is the documented behaviour and not a no-op that happened to # leave the buffer as it found it -- the buffer was memset first either way, so # the assertion is that nothing was stored. check_eq xcb-util-image/err-plane-mask-zero-write "buffer=0xe23c62b5" \ "$(field err.plane-mask-zero-write)" # A buffer shorter than the stride implies must be refused rather than handed # back as an image whose size exceeds the storage. Asserted together with the # SAME call given enough storage, so the refusal is attributable to the length # and not to anything else about the call. check_eq xcb-util-image/err-short-buffer "null" "$(field err.short-buffer)" check_eq xcb-util-image/err-long-enough-buffer "created size=96" \ "$(field err.long-enough-buffer)" # xcb_image_convert between images of different width -- the one precondition # it states and does check. check_eq xcb-util-image/err-convert-width-mismatch "null" \ "$(field err.convert-width-mismatch)" # --- subimage, the xbm constructor, annotate -------------------------------- # A 4x2 window taken at (2,1) out of an 8x3 image, asserted by the BYTES it # produced as well as its dimensions -- a subimage lifted from the wrong row # has the right shape and the wrong content. check_eq xcb-util-image/sub-4x2 "4 2 8 8 stride=4 size=8 data=0x837578e5" \ "$(field sub.4x2)" check_eq xcb-util-image/sub-out-of-bounds "null" "$(field sub.out-of-bounds)" # xcb_image_create_from_bitmap_data pins the layout it hard-codes (XY_PIXMAP, # depth 1, 8-bit unit and pad, both orders LSB-first -- the xbm format) and two # facts about ownership: base is null, and data IS the caller's array rather # than a copy. That pair matters because it means xcb_image_destroy must not # free the fixture, which the next line then confirms. check_eq xcb-util-image/bitmap-8x6 \ "8 6 d=1 bpp=1 unit=8 pad=8 byte-lsb bit-lsb stride=1 size=6 base-null=1 data-is-fixture=1" \ "$(field bitmap.8x6)" check_eq xcb-util-image/bitmap-8x6-pixels "1 0 0 1" "$(field bitmap.8x6.pixels)" # The fixture read back AFTER the image was destroyed. 0x0f 0x33 0x55 0xff # 0xaa 0xcc, unchanged -- so destroy freed the struct and not the caller's # storage, which is the contract create_from_bitmap_data establishes by passing # base 0. check_eq xcb-util-image/bitmap-fixture-intact "0xc7d8159d" \ "$(field bitmap.fixture-intact)" # xcb_image_annotate after the caller raises the scanline pad from 8 to 32. # The cached figures DO NOT MOVE, and that is the measured answer rather than # the expected one: annotate() recomputes the Z_PIXMAP stride as # roundup(width * bpp, scanline_pad) >> 3, and this image's 8 x 8bpp scanline # is 64 bits, which is already a multiple of 32. So the pad rises and the # stride cannot. What it pins is that the stride is a function of width * bpp # and not of the pad alone -- an annotate that started computing from `unit', # or that took the pad as the stride, moves this line. # # The first draft asserted "8 24 -> 32 96" on the reasoning that the stride # follows the pad. That was arithmetic, never a measurement, and it is wrong on # both arms: built against libxcb 1.16 and this same xcb_image.c on x86-64, pad # 8/16/32/64 all give 8 24, pad 128 gives 16 48 and pad 255 gives 31 93 -- the # wasm32 result here is identical to the x86-64 one. # # Driving annotate to a genuinely different answer needs pad >= 128 in the # probe, which is a recipe change and not a suite one; BRINTOS-2498's test-plan # entry records it as the stronger form of this case. check_eq xcb-util-image/annotate "8 24 -> 8 24" "$(field annotate)" # --- the seven connection-bearing entry points ------------------------------ # # THIS IS THE WEAKER HALF OF THE SUITE AND IS LABELLED AS SUCH. There is no X # server on this image and there is not going to be one, so these are measured # as documented refusals inside a bounded run rather than as work done. What # that is worth on a fresh arm is still real: the claim is that each returns # its documented failure value instead of trapping. # # The whole construction rests on one libxcb behaviour this image has already # pinned as a finding -- xcb_get_setup() on a FAILED connection returns # non-NULL, handing back a zeroed setup instead of segfaulting (57-libxcb's # reference records it). xcb_image_create_native and xcb_image_native both # dereference that setup on entry, so if it were ever NULL these would trap. # The probe asserts it BEFORE relying on it, and the suite asserts the # assertion, so a change in libxcb surfaces here as a red rather than as a # guest that stops mid-suite. check_eq xcb-util-image/conn-nonnull "1" "$(field conn.nonnull)" # XCB_CONN_CLOSED_PARSE_ERR (5), not XCB_CONN_ERROR (1), and the distinction is # the name the probe passes rather than anything about this arm. libxcb 1.16's # _xcb_parse_display sends any name beginning with `/' to # _xcb_parse_display_path_to_socket, which stat(2)s it as a socket path; the # probe's "/nonexistent-brintos-2498:0" does not exist and carries no `.' # suffix, so the parse returns 0 and the connection is closed before a socket is # ever attempted. Measured identical on x86-64 against the same libxcb, and # consistent with 57-libxcb, which pins 5 for a name that never parsed and 1 for # one that parsed and failed at the socket. # # What the entry points below need is a connection that is IN ERROR and still # safe to read, and 5 gives them that exactly as 1 would. The first draft # asserted 1 on the assumption that the name would reach the transport; it # cannot. check_eq xcb-util-image/conn-has-error "5" "$(field conn.has-error)" check_eq xcb-util-image/conn-setup-nonnull "1" "$(field conn.setup-nonnull)" # Zero pixmap formats is what makes the refusals below happen: every one of # them is find_format_by_depth() walking an empty list. check_eq xcb-util-image/conn-setup-formats-len "0" "$(field conn.setup-formats-len)" check_eq xcb-util-image/conn-create-native "null" "$(field conn.create-native)" check_eq xcb-util-image/conn-native-check "null" "$(field conn.native-check)" check_eq xcb-util-image/conn-native-convert "null" "$(field conn.native-convert)" check_eq xcb-util-image/conn-get "null" "$(field conn.get)" # xcb_image_put has no failure value -- it returns a cookie. On a connection # with an error libxcb discards the request and hands back sequence 0, which is # what "the request did not go out" looks like to a caller. check_eq xcb-util-image/conn-put-sequence "0" "$(field conn.put-sequence)" # shm_put refuses before issuing any request, because xcb_image_native says no # first -- so this is the create_native refusal reached through a second door. check_eq xcb-util-image/conn-shm-put "null" "$(field conn.shm-put)" # THE FINDING, asserted as what it does. 1 is SUCCESS: the reply never arrived # and no error was delivered, so `if (err)' was not taken and the else arm # freed a NULL reply and reported a completed read. This is upstream's contract # as written; it is pinned so a change in either direction is visible, and it # is NOT evidence that anything worked. check_eq xcb-util-image/conn-shm-get-reports-success "1" "$(field conn.shm-get)" # 0 is XCB_NONE -- no pixmap. xcb_image_native refuses the conversion against # the zeroed setup, so the function frees its image and returns without ever # reaching xcb_generate_id. check_eq xcb-util-image/conn-pixmap-from-bitmap "0" "$(field conn.pixmap-from-bitmap)" # The probe survived all seven and shut the connection down cleanly. This is # the case that would be missing, rather than red, if one of them had trapped. check_eq xcb-util-image/conn-disconnected "1" "$(field conn.disconnected)" rm -f "$out"