xserver/test/pyxtest/test_xres.py
Peter Hutterer acbc46e708 pyxtest: add tests for the byteswapping patches
Not a full list since not all can easily be tested but hey, better than
nothing.
See https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/2181

Assisted-by: Claude:claude-claude-opus-4-6
Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/2187>
2026-05-10 23:42:44 +00:00

139 lines
5 KiB
Python

# 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.to_bytes(">"))
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.to_bytes(">"))
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.to_bytes(">"))
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.to_bytes(">"))
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."
)