# SPDX-License-Identifier: MIT # # Tests for X-Resource extension. import struct import pytest from proto import xres from xclient import Extension, X11Reply @pytest.fixture def xres_xclient_swapped(xclient_swapped): """Provide a byte-swapped xclient with X-Resource initialized.""" ext = xclient_swapped.query_extension(Extension.XRES) if not ext: pytest.skip("X-Resource extension not available") req = xres.QueryVersionRequest(opcode=ext.opcode) xclient_swapped.send_request(req) resp = xclient_swapped.recv_response(timeout=5.0) if not isinstance(resp, X11Reply): pytest.skip("XRes QueryVersion failed") return xclient_swapped, ext.opcode class TestXResQueryClientIds: @pytest.mark.swapped_client def test_query_client_ids_spec_entries_swapped(self, xserver, xres_xclient_swapped): """ SProcXResQueryClientIds was missing byte-swaps for the xXResClientIdSpec entries. Without the swaps, the mask field (e.g. XResClientXIDMask=1) is seen as 0x01000000, which doesn't match any known mask → the reply contains zero results. With the fix, the reply should contain at least one result. Fixed in commit f7b574931544 ("Xext/xres: add missing byte-swap of spec entries in SProcXResQueryClientIds"). """ conn, opcode = xres_xclient_swapped # X_XResClientXIDMask = 1. # client=0 means "all clients" (the wildcard). req = xres.QueryClientIdsRequest( opcode=opcode, specs=[(0, 1)], # mask=1 (XResClientXIDMask) ) conn.send_request(req) resp = conn.recv_response(timeout=5.0) assert xserver.is_alive, "Server crashed" assert isinstance(resp, X11Reply), f"Expected reply, got {resp}" # xXResQueryClientIdsReply: # [8] numIds(4) num_ids = struct.unpack_from(">I", resp.data, 8)[0] assert num_ids > 0, ( "QueryClientIds returned numIds=0, expected >0. " "The spec entry mask was not byte-swapped, so the " "server didn't recognize XResClientXIDMask." ) @pytest.mark.swapped_client def test_construct_client_id_value_swap_check( self, xserver, xres_xclient_swapped, xclient ): """ ConstructClientIdValue used client->swapped instead of sendClient->swapped. When a big-endian client queries a native client's ID, the spec.client field in the reply is not swapped → garbled value. We create an additional native (little-endian) connection. The big-endian connection queries the native client's XID. With the fix, the returned spec.client should match the native client's resource base. Without the fix, it's byte-swapped garbage. Fixed in commit d2d4fb35e798 ("Xext/xres: fix wrong swap check"). """ conn, opcode = xres_xclient_swapped conn_native = xclient # Get the list of clients req = xres.QueryClientsRequest(opcode=opcode) conn.send_request(req) resp = conn.recv_response(timeout=5.0) assert isinstance(resp, X11Reply), f"Expected reply, got {resp}" num_clients = struct.unpack_from(">I", resp.data, 8)[0] assert num_clients >= 2, f"Expected at least 2 clients, got {num_clients}" # Collect all client XIDs from the reply. # Each xXResClient is 8 bytes: resource_base(4) + resource_mask(4) client_xids = [] for i in range(num_clients): offset = 32 + i * 8 if offset + 4 <= len(resp.data): xid = struct.unpack_from(">I", resp.data, offset)[0] client_xids.append(xid) # Find the native client's resource base native_base = conn_native._resource_id_base target_xid = None for xid in client_xids: if (xid & 0xFFE00000) == (native_base & 0xFFE00000): target_xid = xid break if target_xid is None: pytest.skip("Could not find native client in XRes client list") # Query the native client's XID using XResClientXIDMask req = xres.QueryClientIdsRequest( opcode=opcode, specs=[(target_xid, 1)], # XResClientXIDMask ) conn.send_request(req) resp = conn.recv_response(timeout=5.0) assert isinstance(resp, X11Reply), f"Expected reply, got {resp}" num_ids = struct.unpack_from(">I", resp.data, 8)[0] assert num_ids > 0, f"Expected >0 IDs, got {num_ids}" # xXResClientIdValue: # spec.client(4) + spec.mask(4) + length(4) + value(4*length) spec_client = struct.unpack_from(">I", resp.data, 32)[0] assert spec_client == target_xid, ( f"spec.client = {spec_client:#010x}, " f"expected {target_xid:#010x}. " f"The swap check used client->swapped instead of " f"sendClient->swapped." )