test: parallel-safe suite (xdist), shared globals session, KICK_DUMP_DIR

Make ./test.sh -n4 work (~47s vs ~156s serial):

- kick.c: KICK_DUMP_DIR env override for frame-dump .ppm paths
  (default /tmp); per-boot dump_seq counters no longer collide
  across concurrent compositors.
- tests: every spawn gets a unique log file and a per-session dump
  directory owned by the Session (close removes the tree instead of
  globbing /tmp, which raced parallel workers' live dumps).
- 14 protocol-globals tests share one module-scoped keeper session
  via short-lived --globals-out clients instead of 14 boots.
- Two presence-waits converted from fixed sleeps to event waits.
- Harden the fullscreen round-trip: wait for seat keyboard focus
  ([diag] kbd-enter, new debug line in window_enter) before tapping
  F -- map alone does not guarantee enter under load and the tap was
  silently dropped.

Serial remains the deterministic gate (documented in test.sh and
README): a few pre-existing timing-sensitive tests can still flake
under parallel load; they pass alone and serially.
This commit is contained in:
Andrew Hamilton 2026-09-08 11:50:45 +10:00
parent a593d83b37
commit 7e2bb18a85
4 changed files with 107 additions and 71 deletions

40
kick.c
View file

@ -894,6 +894,14 @@ static void bridge_attach_seat(void);
static volatile sig_atomic_t dump_scene_req, dump_host_req;
static int dump_seq;
static bool client_dumped = false;
/* Directory for dump .ppm files (KICK_DUMP_DIR, default /tmp): parallel
test workers point each compositor at its own directory so per-boot
dump_seq counters cannot collide on filenames. */
static char dump_dir[512] = "/tmp";
static void dump_path(char *buf, size_t sz, const char *kind, int seq) {
snprintf(buf, sz, "%s/%s_%d.ppm", dump_dir, kind, seq);
}
/* Set by sigchld_handler whenever a child exits; the main loop clears it and
runs sweep_dead_startup_groups() to drop black tabs whose command exited
@ -3000,6 +3008,12 @@ static void window_enter(struct kick_window *w) {
if (!kbd) kbd = &server.keyboard;
wlr_seat_keyboard_notify_enter(server.seat, surface,
kbd->keycodes, kbd->num_keycodes, &kbd->modifiers);
/* Seat keyboard focus is what routes keys to the client; map alone
does not guarantee it has arrived (enter can lag map under
load), so tests tapping keys wait on this line, not the map. */
if (kick_log_level() >= KICK_LOG_DEBUG)
kick_log(KICK_LOG_DEBUG, "[diag] kbd-enter app=%d\n",
tab_index(tab));
}
show_window(w->tree ? w : NULL);
kick_request_redraw();
@ -3500,9 +3514,8 @@ static void handle_toplevel_commit(struct wl_listener *listener, void *data) {
"nonblack=%lu/%lu mean=%lu\n", fmt, nonblack,
(unsigned long)bw * bh,
lum / ((unsigned long)bw * bh + 1));
char cpath[64];
snprintf(cpath, sizeof(cpath), "/tmp/client_%d.ppm",
dump_seq);
char cpath[512];
dump_path(cpath, sizeof(cpath), "client", dump_seq);
FILE *cf = fopen(cpath, "wb");
if (cf) {
fprintf(cf, "P6\n%d %d\n255\n", bw, bh);
@ -8081,8 +8094,8 @@ static void dump_scene_buffer(struct wlr_buffer *buf) {
kick_log(KICK_LOG_INFO, "[dump] scene %dx%d fmt=0x%x "
"nonblack=%lu/%lu mean=%lu\n", w, h, format, nonblack,
(unsigned long)w * h, lum / ((unsigned long)w * h + 1));
char path[64];
snprintf(path, sizeof(path), "/tmp/scene_%d.ppm", dump_seq);
char path[512];
dump_path(path, sizeof(path), "scene", dump_seq);
FILE *f = fopen(path, "wb");
if (f) {
int w = buf->width, h = buf->height;
@ -8124,8 +8137,8 @@ static void dump_host_back(void) {
kick_log(KICK_LOG_INFO, "[dump] host %dx%d nonblack=%lu/%lu mean=%lu\n",
w, h, nonblack, (unsigned long)w * h,
lum / ((unsigned long)w * h + 1));
char path[64];
snprintf(path, sizeof(path), "/tmp/host_%d.ppm", dump_seq);
char path[512];
dump_path(path, sizeof(path), "host", dump_seq);
FILE *f = fopen(path, "wb");
if (f) {
fprintf(f, "P6\n%d %d\n255\n", w, h);
@ -8195,8 +8208,8 @@ static void dump_standalone_buffer(struct wlr_buffer *buf) {
kick_log(KICK_LOG_INFO, "[dump] host %dx%d nonblack=%lu/%lu mean=%lu\n",
w, h, nonblack, (unsigned long)w * h,
lum / ((unsigned long)w * h + 1));
char path[64];
snprintf(path, sizeof(path), "/tmp/host_%d.ppm", dump_seq);
char path[512];
dump_path(path, sizeof(path), "host", dump_seq);
FILE *f = fopen(path, "wb");
if (f) {
fprintf(f, "P6\n%d %d\n255\n", w, h);
@ -11174,6 +11187,15 @@ int main(int argc, char *argv[]) {
level without touching non-async-signal-safe functions. */
kick_log_level_init();
/* KICK_DUMP_DIR: where frame-dump .ppm files land (default /tmp).
Test workers point each compositor at its own directory so the
per-boot dump_seq counters cannot collide on filenames. */
{
const char *d = getenv("KICK_DUMP_DIR");
if (d && d[0])
snprintf(dump_dir, sizeof(dump_dir), "%s", d);
}
/* KICK_SYNTH_INPUT: keep real host keyboard events out of the test
channel so the stdin feed is the only source of key/modifier input. */
{