[client] input: add display server behaviour tests

Extract input policy from the Wayland and X11 protocol handlers.

Exercise both implementations against literal Wayland event traces.
This commit is contained in:
Geoffrey McRae
2026-08-10 17:15:11 +10:00
parent 802c8b5803
commit b8955fd012
14 changed files with 1731 additions and 256 deletions

View File

@@ -47,6 +47,46 @@ set_tests_properties(wayland-motion-tests PROPERTIES
TIMEOUT 10
)
add_executable(displayserver-input-tests
displayserver_input_test.c
../displayservers/Wayland/input_event.c
../displayservers/X11/input_event.c
)
target_include_directories(displayserver-input-tests PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/../displayservers"
)
set(DISPLAYSERVER_INPUT_CASES
pointer-events
pointer-buttons
pointer-release
wheel-steps
relative-motion
keyboard-focus
keyboard-keys
keyboard-state
)
foreach(backend IN ITEMS wayland x11)
foreach(name IN LISTS DISPLAYSERVER_INPUT_CASES)
add_test(NAME display-input-${backend}-${name}
COMMAND displayserver-input-tests ${backend} ${name}
)
set_tests_properties(display-input-${backend}-${name} PROPERTIES
TIMEOUT 10
)
endforeach()
endforeach()
foreach(name IN ITEMS wheel-residual wheel-multiple)
add_test(NAME display-input-wayland-${name}
COMMAND displayserver-input-tests wayland ${name}
)
set_tests_properties(display-input-wayland-${name} PROPERTIES
TIMEOUT 10
)
endforeach()
add_executable(mouse-tests
mouse_test.c
../src/core.c

View File

@@ -0,0 +1,877 @@
/**
* Looking Glass
* Copyright © 2017-2026 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "Wayland/input_event.h"
#include "X11/input_event.h"
#include "test.h"
#include <linux/input-event-codes.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
enum Backend
{
BACKEND_WAYLAND,
BACKEND_X11,
};
enum TraceType
{
TRACE_POSITION,
TRACE_RELATIVE,
TRACE_BUTTON,
TRACE_WHEEL,
TRACE_KEY,
TRACE_TEXT,
TRACE_MODIFIERS,
TRACE_LEDS,
TRACE_ENTER,
TRACE_FOCUS,
};
struct Trace
{
enum TraceType type;
union
{
struct
{
double x;
double y;
double rawX;
double rawY;
}
motion;
struct
{
unsigned int value;
bool pressed;
}
input;
struct
{
bool ctrl;
bool shift;
bool alt;
bool super;
}
modifiers;
struct
{
bool num;
bool caps;
bool scroll;
}
leds;
double wheel;
bool state;
char text[32];
} data;
};
#define POSITION(_x, _y) \
{ \
.type = TRACE_POSITION, \
.data.motion = { .x = (_x), .y = (_y) }, \
}
#define RELATIVE(_x, _y, _rawX, _rawY) \
{ \
.type = TRACE_RELATIVE, \
.data.motion = \
{ .x = (_x), .y = (_y), .rawX = (_rawX), .rawY = (_rawY) }, \
}
#define BUTTON(_button, _pressed) \
{ \
.type = TRACE_BUTTON, \
.data.input = { .value = (_button), .pressed = (_pressed) }, \
}
#define WHEEL(_motion) \
{ \
.type = TRACE_WHEEL, \
.data.wheel = (_motion), \
}
#define KEY(_key, _pressed) \
{ \
.type = TRACE_KEY, \
.data.input = { .value = (_key), .pressed = (_pressed) }, \
}
#define TEXT(_text) \
{ \
.type = TRACE_TEXT, \
.data.text = _text, \
}
#define MODIFIERS(_ctrl, _shift, _alt, _super) \
{ \
.type = TRACE_MODIFIERS, \
.data.modifiers = \
{ .ctrl = (_ctrl), .shift = (_shift), .alt = (_alt), \
.super = (_super) }, \
}
#define LEDS(_num, _caps, _scroll) \
{ \
.type = TRACE_LEDS, \
.data.leds = { .num = (_num), .caps = (_caps), .scroll = (_scroll) }, \
}
#define ENTER(_entered) \
{ \
.type = TRACE_ENTER, \
.data.state = (_entered), \
}
#define FOCUS(_focused) \
{ \
.type = TRACE_FOCUS, \
.data.state = (_focused), \
}
#define ARRAY_LENGTH(_array) (sizeof(_array) / sizeof((_array)[0]))
#define MAX_TRACE 128
struct TraceLog
{
struct Trace values[MAX_TRACE];
size_t count;
};
struct Fixture
{
enum Backend backend;
struct TraceLog trace;
uint32_t time;
union
{
WaylandInput wayland;
X11Input x11;
} input;
};
static struct Trace * pushTrace(struct TraceLog * log, enum TraceType type)
{
CHECK(log->count < ARRAY_LENGTH(log->values));
struct Trace * trace = &log->values[log->count++];
memset(trace, 0, sizeof(*trace));
trace->type = type;
return trace;
}
static void tracePosition(void * opaque, double x, double y)
{
struct Trace * trace = pushTrace(opaque, TRACE_POSITION);
trace->data.motion.x = x;
trace->data.motion.y = y;
}
static void traceRelative(void * opaque, double x, double y,
double rawX, double rawY)
{
struct Trace * trace = pushTrace(opaque, TRACE_RELATIVE);
trace->data.motion.x = x;
trace->data.motion.y = y;
trace->data.motion.rawX = rawX;
trace->data.motion.rawY = rawY;
}
static void traceButton(void * opaque, unsigned int button, bool pressed)
{
struct Trace * trace = pushTrace(opaque, TRACE_BUTTON);
trace->data.input.value = button;
trace->data.input.pressed = pressed;
}
static void traceWheel(void * opaque, double motion)
{
struct Trace * trace = pushTrace(opaque, TRACE_WHEEL);
trace->data.wheel = motion;
}
static void traceKey(void * opaque, int scancode, bool pressed)
{
struct Trace * trace = pushTrace(opaque, TRACE_KEY);
trace->data.input.value = scancode;
trace->data.input.pressed = pressed;
}
static void traceText(void * opaque, const char * text)
{
struct Trace * trace = pushTrace(opaque, TRACE_TEXT);
snprintf(trace->data.text, sizeof(trace->data.text), "%s", text);
}
static void traceModifiers(void * opaque, bool ctrl, bool shift,
bool alt, bool super)
{
struct Trace * trace = pushTrace(opaque, TRACE_MODIFIERS);
trace->data.modifiers.ctrl = ctrl;
trace->data.modifiers.shift = shift;
trace->data.modifiers.alt = alt;
trace->data.modifiers.super = super;
}
static void traceLEDs(void * opaque, bool numLock, bool capsLock,
bool scrollLock)
{
struct Trace * trace = pushTrace(opaque, TRACE_LEDS);
trace->data.leds.num = numLock;
trace->data.leds.caps = capsLock;
trace->data.leds.scroll = scrollLock;
}
static void traceEnter(void * opaque, bool entered)
{
struct Trace * trace = pushTrace(opaque, TRACE_ENTER);
trace->data.state = entered;
}
static void traceFocus(void * opaque, bool focused)
{
struct Trace * trace = pushTrace(opaque, TRACE_FOCUS);
trace->data.state = focused;
}
static const LG_DSInputSink sink =
{
.position = tracePosition,
.relative = traceRelative,
.button = traceButton,
.wheel = traceWheel,
.key = traceKey,
.text = traceText,
.modifiers = traceModifiers,
.leds = traceLEDs,
.enter = traceEnter,
.focus = traceFocus,
};
static void initFixture(struct Fixture * fixture, enum Backend backend)
{
memset(fixture, 0, sizeof(*fixture));
fixture->backend = backend;
fixture->time = 1;
if (backend == BACKEND_WAYLAND)
wlInputInit(&fixture->input.wayland, &sink, &fixture->trace);
else
x11InputInit(&fixture->input.x11, &sink, &fixture->trace);
}
static void clearTrace(struct Fixture * fixture)
{
fixture->trace.count = 0;
}
static bool sameDouble(double a, double b)
{
const double delta = a > b ? a - b : b - a;
return delta < 0.000000001;
}
static bool sameTrace(const struct Trace * actual,
const struct Trace * expected)
{
if (actual->type != expected->type)
return false;
switch (actual->type)
{
case TRACE_POSITION:
return
sameDouble(actual->data.motion.x, expected->data.motion.x) &&
sameDouble(actual->data.motion.y, expected->data.motion.y);
case TRACE_RELATIVE:
return
sameDouble(actual->data.motion.x, expected->data.motion.x) &&
sameDouble(actual->data.motion.y, expected->data.motion.y) &&
sameDouble(actual->data.motion.rawX, expected->data.motion.rawX) &&
sameDouble(actual->data.motion.rawY, expected->data.motion.rawY);
case TRACE_BUTTON:
case TRACE_KEY:
return
actual->data.input.value == expected->data.input.value &&
actual->data.input.pressed == expected->data.input.pressed;
case TRACE_WHEEL:
return sameDouble(actual->data.wheel, expected->data.wheel);
case TRACE_TEXT:
return strcmp(actual->data.text, expected->data.text) == 0;
case TRACE_MODIFIERS:
return
actual->data.modifiers.ctrl == expected->data.modifiers.ctrl &&
actual->data.modifiers.shift == expected->data.modifiers.shift &&
actual->data.modifiers.alt == expected->data.modifiers.alt &&
actual->data.modifiers.super == expected->data.modifiers.super;
case TRACE_LEDS:
return
actual->data.leds.num == expected->data.leds.num &&
actual->data.leds.caps == expected->data.leds.caps &&
actual->data.leds.scroll == expected->data.leds.scroll;
case TRACE_ENTER:
case TRACE_FOCUS:
return actual->data.state == expected->data.state;
}
return false;
}
static const char * traceName(enum TraceType type)
{
static const char * names[] =
{
[TRACE_POSITION ] = "position",
[TRACE_RELATIVE ] = "relative",
[TRACE_BUTTON ] = "button",
[TRACE_WHEEL ] = "wheel",
[TRACE_KEY ] = "key",
[TRACE_TEXT ] = "text",
[TRACE_MODIFIERS] = "modifiers",
[TRACE_LEDS ] = "leds",
[TRACE_ENTER ] = "enter",
[TRACE_FOCUS ] = "focus",
};
return names[type];
}
static void printTrace(const struct Trace * trace)
{
fprintf(stderr, "%s", traceName(trace->type));
switch (trace->type)
{
case TRACE_POSITION:
fprintf(stderr, " %.6f %.6f", trace->data.motion.x,
trace->data.motion.y);
break;
case TRACE_RELATIVE:
fprintf(stderr, " %.6f %.6f %.6f %.6f", trace->data.motion.x,
trace->data.motion.y, trace->data.motion.rawX,
trace->data.motion.rawY);
break;
case TRACE_BUTTON:
case TRACE_KEY:
fprintf(stderr, " %u %s", trace->data.input.value,
trace->data.input.pressed ? "down" : "up");
break;
case TRACE_WHEEL:
fprintf(stderr, " %.6f", trace->data.wheel);
break;
case TRACE_TEXT:
fprintf(stderr, " %s", trace->data.text);
break;
case TRACE_MODIFIERS:
fprintf(stderr, " %d %d %d %d", trace->data.modifiers.ctrl,
trace->data.modifiers.shift, trace->data.modifiers.alt,
trace->data.modifiers.super);
break;
case TRACE_LEDS:
fprintf(stderr, " %d %d %d", trace->data.leds.num,
trace->data.leds.caps, trace->data.leds.scroll);
break;
case TRACE_ENTER:
case TRACE_FOCUS:
fprintf(stderr, " %d", trace->data.state);
break;
}
fputc('\n', stderr);
}
static void expectTrace(const struct Fixture * fixture,
const struct Trace * expected, size_t expectedCount)
{
bool match = fixture->trace.count == expectedCount;
if (match)
for (size_t i = 0; i < expectedCount; ++i)
if (!sameTrace(&fixture->trace.values[i], &expected[i]))
{
match = false;
break;
}
if (match)
return;
fprintf(stderr, "expected trace (%zu events):\n", expectedCount);
for (size_t i = 0; i < expectedCount; ++i)
printTrace(&expected[i]);
fprintf(stderr, "actual trace (%zu events):\n", fixture->trace.count);
for (size_t i = 0; i < fixture->trace.count; ++i)
printTrace(&fixture->trace.values[i]);
exit(EXIT_FAILURE);
}
static void pointerEnter(struct Fixture * fixture, bool mainSurface,
double x, double y)
{
if (fixture->backend == BACKEND_WAYLAND)
{
if (wlInputPointerEnter(&fixture->input.wayland, mainSurface))
wlInputPointerMotion(&fixture->input.wayland, x, y);
}
else
x11InputPointerEnter(&fixture->input.x11, mainSurface, true, x, y);
}
static void pointerLeave(struct Fixture * fixture, bool mainSurface,
double x, double y)
{
if (fixture->backend == BACKEND_WAYLAND)
wlInputPointerLeave(&fixture->input.wayland, mainSurface);
else
x11InputPointerLeave(&fixture->input.x11, mainSurface, true, false,
x, y);
}
static void pointerMotion(struct Fixture * fixture, double x, double y)
{
if (fixture->backend == BACKEND_WAYLAND)
wlInputPointerMotion(&fixture->input.wayland, x, y);
else
x11InputPointerMotion(&fixture->input.x11, x, y, false);
}
static unsigned int nativeButton(enum Backend backend,
unsigned int button)
{
if (backend == BACKEND_X11)
return button > 5 ? button + 2 : button;
static const unsigned int wayland[] =
{
[1 ] = BTN_LEFT,
[2 ] = BTN_MIDDLE,
[3 ] = BTN_RIGHT,
[6 ] = BTN_SIDE,
[7 ] = BTN_EXTRA,
[8 ] = BTN_FORWARD,
[9 ] = BTN_BACK,
[10] = BTN_TASK,
};
CHECK(button < ARRAY_LENGTH(wayland));
return wayland[button];
}
static void pointerButton(struct Fixture * fixture, unsigned int button,
bool pressed)
{
const unsigned int native = nativeButton(fixture->backend, button);
if (fixture->backend == BACKEND_WAYLAND)
wlInputPointerButton(&fixture->input.wayland, native, pressed);
else
x11InputPointerButton(&fixture->input.x11, native, pressed, false,
fixture->time++, true);
}
static void unmappedButton(struct Fixture * fixture)
{
if (fixture->backend == BACKEND_WAYLAND)
wlInputPointerButton(&fixture->input.wayland, BTN_STYLUS, true);
else
x11InputPointerButton(&fixture->input.x11, 6, true, false,
fixture->time++, true);
}
static void wheelStep(struct Fixture * fixture, bool down)
{
if (fixture->backend == BACKEND_WAYLAND)
wlInputPointerAxis(&fixture->input.wayland, true, down ? 15.0 : -15.0);
else
x11InputPointerButton(&fixture->input.x11, down ? 5 : 4, true,
false, fixture->time++, true);
}
static void relativeMotion(struct Fixture * fixture, bool active,
uint32_t time, double x, double y, double rawX, double rawY)
{
if (fixture->backend == BACKEND_WAYLAND)
wlInputRelativeMotion(&fixture->input.wayland, active,
x, y, rawX, rawY);
else
x11InputRelativeMotion(&fixture->input.x11, time,
x, y, rawX, rawY, active);
}
static void keyboardEnter(struct Fixture * fixture, bool mainSurface,
const uint32_t * keys, size_t count)
{
if (fixture->backend == BACKEND_WAYLAND)
wlInputKeyboardEnter(&fixture->input.wayland, mainSurface, keys, count);
else if (mainSurface)
x11InputFocus(&fixture->input.x11, true, 40.0, 30.0, keys, count);
}
static void keyboardLeave(struct Fixture * fixture, bool mainSurface)
{
if (fixture->backend == BACKEND_WAYLAND)
wlInputKeyboardLeave(&fixture->input.wayland, mainSurface);
else if (mainSurface)
x11InputFocus(&fixture->input.x11, false, 40.0, 30.0, NULL, 0);
}
static void keyboardKey(struct Fixture * fixture, unsigned int key,
bool pressed, const char * text, bool inputActive)
{
if (fixture->backend == BACKEND_WAYLAND)
wlInputKeyboardKey(&fixture->input.wayland, key, pressed, text);
else
x11InputKeyboardKey(&fixture->input.x11, key + 8, 8, pressed,
false, inputActive, text);
}
static void keyboardState(struct Fixture * fixture,
bool ctrl, bool shift, bool alt, bool super,
bool numLock, bool capsLock, bool scrollLock)
{
if (fixture->backend == BACKEND_WAYLAND)
wlInputKeyboardState(&fixture->input.wayland,
ctrl, shift, alt, super, numLock, capsLock, scrollLock);
else
x11InputKeyboardState(&fixture->input.x11,
ctrl, shift, alt, super, numLock, capsLock, scrollLock);
}
static void activateInput(struct Fixture * fixture)
{
pointerEnter(fixture, true, 40.0, 30.0);
keyboardEnter(fixture, true, NULL, 0);
clearTrace(fixture);
}
static void activatePointer(struct Fixture * fixture)
{
pointerEnter(fixture, true, 40.0, 30.0);
clearTrace(fixture);
}
static void testPointerEvents(enum Backend backend)
{
struct Fixture fixture;
initFixture(&fixture, backend);
pointerEnter(&fixture, false, 1.0, 2.0);
pointerEnter(&fixture, true, 10.0, 20.0);
pointerMotion(&fixture, 11.5, 21.25);
pointerLeave(&fixture, false, 4.0, 5.0);
pointerLeave(&fixture, true, 12.0, 22.0);
static const struct Trace expected[] =
{
ENTER(true),
POSITION(10.0, 20.0),
POSITION(11.5, 21.25),
ENTER(false),
};
expectTrace(&fixture, expected, ARRAY_LENGTH(expected));
}
static void testPointerButtons(enum Backend backend)
{
struct Fixture fixture;
initFixture(&fixture, backend);
activatePointer(&fixture);
static const unsigned int buttons[] = { 1, 2, 3, 6, 7, 8, 9, 10 };
for (size_t i = 0; i < ARRAY_LENGTH(buttons); ++i)
{
pointerButton(&fixture, buttons[i], true);
pointerButton(&fixture, buttons[i], false);
}
unmappedButton(&fixture);
static const struct Trace expected[] =
{
BUTTON(1 , true), BUTTON(1 , false),
BUTTON(2 , true), BUTTON(2 , false),
BUTTON(3 , true), BUTTON(3 , false),
BUTTON(6 , true), BUTTON(6 , false),
BUTTON(7 , true), BUTTON(7 , false),
BUTTON(8 , true), BUTTON(8 , false),
BUTTON(9 , true), BUTTON(9 , false),
BUTTON(10, true), BUTTON(10, false),
};
expectTrace(&fixture, expected, ARRAY_LENGTH(expected));
}
static void testPointerRelease(enum Backend backend)
{
struct Fixture fixture;
initFixture(&fixture, backend);
activateInput(&fixture);
pointerButton(&fixture, 1, true);
/* Wayland's implicit pointer grab suppresses this native leave. */
if (backend == BACKEND_X11)
pointerLeave(&fixture, true, 100.0, 100.0);
pointerButton(&fixture, 1, false);
static const struct Trace expected[] =
{
BUTTON(1, true),
BUTTON(1, false),
};
expectTrace(&fixture, expected, ARRAY_LENGTH(expected));
}
static void testWheelSteps(enum Backend backend)
{
struct Fixture fixture;
initFixture(&fixture, backend);
activateInput(&fixture);
wheelStep(&fixture, true);
wheelStep(&fixture, false);
static const struct Trace expected[] =
{
BUTTON(5, true),
BUTTON(5, false),
WHEEL(1.0),
BUTTON(4, true),
BUTTON(4, false),
WHEEL(-1.0),
};
expectTrace(&fixture, expected, ARRAY_LENGTH(expected));
}
static void testWheelResidual(enum Backend backend)
{
CHECK(backend == BACKEND_WAYLAND);
struct Fixture fixture;
initFixture(&fixture, backend);
wlInputPointerAxis(&fixture.input.wayland, false, 100.0);
wlInputPointerAxis(&fixture.input.wayland, true, 5.0);
wlInputPointerAxis(&fixture.input.wayland, true, 5.0);
wlInputPointerAxis(&fixture.input.wayland, true, -2.0);
wlInputPointerAxis(&fixture.input.wayland, true, -1.0);
static const struct Trace expected[] =
{
WHEEL(1.0 / 3.0),
BUTTON(5, true),
BUTTON(5, false),
WHEEL(1.0 / 3.0),
WHEEL(-2.0 / 15.0),
BUTTON(4, true),
BUTTON(4, false),
WHEEL(-1.0 / 15.0),
};
expectTrace(&fixture, expected, ARRAY_LENGTH(expected));
}
static void testWheelMultiple(enum Backend backend)
{
CHECK(backend == BACKEND_WAYLAND);
struct Fixture fixture;
initFixture(&fixture, backend);
wlInputPointerAxis(&fixture.input.wayland, true, 45.0);
static const struct Trace expected[] =
{
BUTTON(5, true), BUTTON(5, false),
BUTTON(5, true), BUTTON(5, false),
BUTTON(5, true), BUTTON(5, false),
WHEEL(3.0),
};
expectTrace(&fixture, expected, ARRAY_LENGTH(expected));
}
static void testRelativeMotion(enum Backend backend)
{
struct Fixture fixture;
initFixture(&fixture, backend);
activateInput(&fixture);
relativeMotion(&fixture, false, 50, 1.0, 2.0, 3.0, 4.0);
relativeMotion(&fixture, true, 51, 1.0, 2.0, 3.0, 4.0);
relativeMotion(&fixture, true, 51, 1.0, 2.0, 3.0, 4.0);
static const struct Trace expected[] =
{
RELATIVE(1.0, 2.0, 3.0, 4.0),
RELATIVE(1.0, 2.0, 3.0, 4.0),
};
expectTrace(&fixture, expected, ARRAY_LENGTH(expected));
}
static void testKeyboardFocus(enum Backend backend)
{
struct Fixture fixture;
initFixture(&fixture, backend);
static const uint32_t held[] = { 30, 42 };
keyboardEnter(&fixture, false, held, ARRAY_LENGTH(held));
keyboardEnter(&fixture, true, held, ARRAY_LENGTH(held));
keyboardLeave(&fixture, false);
keyboardLeave(&fixture, true);
static const struct Trace expected[] =
{
FOCUS(true),
KEY(30, true),
KEY(42, true),
FOCUS(false),
};
expectTrace(&fixture, expected, ARRAY_LENGTH(expected));
}
static void testKeyboardKeys(enum Backend backend)
{
struct Fixture fixture;
initFixture(&fixture, backend);
keyboardKey(&fixture, 30, true, "ignored", true);
keyboardKey(&fixture, 30, false, "ignored", true);
CHECK(fixture.trace.count == 0);
keyboardEnter(&fixture, true, NULL, 0);
clearTrace(&fixture);
keyboardKey(&fixture, 30, true, "a", true);
keyboardKey(&fixture, 30, false, "ignored", true);
static const struct Trace expected[] =
{
KEY(30, true),
TEXT("a"),
KEY(30, false),
};
expectTrace(&fixture, expected, ARRAY_LENGTH(expected));
keyboardLeave(&fixture, true);
clearTrace(&fixture);
keyboardKey(&fixture, 31, true, "s", true);
keyboardKey(&fixture, 31, false, NULL, true);
CHECK(fixture.trace.count == 0);
}
static void testKeyboardState(enum Backend backend)
{
struct Fixture fixture;
initFixture(&fixture, backend);
keyboardEnter(&fixture, true, NULL, 0);
clearTrace(&fixture);
keyboardState(&fixture, true, false, true, false,
true, false, true);
keyboardState(&fixture, false, true, false, true,
false, true, false);
static const struct Trace expected[] =
{
MODIFIERS(true, false, true, false),
LEDS(true, false, true),
MODIFIERS(false, true, false, true),
LEDS(false, true, false),
};
expectTrace(&fixture, expected, ARRAY_LENGTH(expected));
}
struct Test
{
const char * name;
void (*run)(enum Backend backend);
};
static const struct Test tests[] =
{
{ "pointer-events" , testPointerEvents },
{ "pointer-buttons", testPointerButtons },
{ "pointer-release", testPointerRelease },
{ "wheel-steps" , testWheelSteps },
{ "wheel-residual" , testWheelResidual },
{ "wheel-multiple" , testWheelMultiple },
{ "relative-motion", testRelativeMotion },
{ "keyboard-focus" , testKeyboardFocus },
{ "keyboard-keys" , testKeyboardKeys },
{ "keyboard-state" , testKeyboardState },
};
static bool parseBackend(const char * name, enum Backend * backend)
{
if (strcmp(name, "wayland") == 0)
{
*backend = BACKEND_WAYLAND;
return true;
}
if (strcmp(name, "x11") == 0)
{
*backend = BACKEND_X11;
return true;
}
return false;
}
int main(int argc, char ** argv)
{
if (argc != 3)
{
fprintf(stderr, "usage: %s <wayland|x11> <case>\n", argv[0]);
return EXIT_FAILURE;
}
enum Backend backend;
if (!parseBackend(argv[1], &backend))
{
fprintf(stderr, "unknown backend: %s\n", argv[1]);
return EXIT_FAILURE;
}
for (size_t i = 0; i < ARRAY_LENGTH(tests); ++i)
if (strcmp(argv[2], tests[i].name) == 0)
{
tests[i].run(backend);
return 0;
}
fprintf(stderr, "unknown test: %s\n", argv[2]);
return EXIT_FAILURE;
}