Add tests for VKM evdev implementation

virtual-keyboard-mouse
nirenjan 2026-03-27 20:48:25 -07:00
parent 7860156909
commit 8418f9f0ad
4 changed files with 946 additions and 13 deletions

View File

@ -28,6 +28,20 @@ lib_vkm = library('vkm', vkm_files + vkm_platform_files,
dependencies: [vkm_dep],
include_directories: [includes])
if host_machine.system() == 'linux' and dep_evdev.found()
dep_evdev_headers = dep_evdev.partial_dependency(compile_args: true, link_args: false)
vkm_linux_evdev_test = executable('vkm_linux_evdev_test',
files(
'vkm_linux_evdev_test.c',
'vkm_linux_evdev.c',
'vkm_common.c',
),
include_directories: [includes],
dependencies: [dep_cmocka, dep_evdev_headers],
)
test('vkm_linux_evdev', vkm_linux_evdev_test)
endif
install_headers('vkm.h', subdir: 'vkm')
pkgconfig.generate(lib_vkm,
name: 'vkm',

View File

@ -106,7 +106,9 @@ typedef enum {
* This option must be passed a boolean which lets VKM know whether to
* enable or disable high resolution scrolling.
*
* Defaults to true.
* Defaults to false. When enabled together with \ref vkm_start,
* \ref vkm_mouse_scroll emits high-resolution REL_*_HI_RES events (120 units
* per step) in addition to discrete REL_WHEEL / REL_HWHEEL ticks.
*/
VKM_OPT_HI_RES_SCROLL,
@ -324,7 +326,11 @@ vkm_result vkm_mouse_click(vkm_context *ctx, vkm_mouse_button button, vkm_button
/**
* @brief Scroll the mouse wheel
*
* Send a single scroll event to the mouse wheel
* Send a single scroll event to the mouse wheel (one detent in the chosen
* direction). If \ref VKM_OPT_HI_RES_SCROLL was enabled before \ref vkm_start,
* also emits REL_WHEEL_HI_RES / REL_HWHEEL_HI_RES using the standard 120
* units per detent scale before the corresponding discrete REL_WHEEL /
* REL_HWHEEL event.
*
* @param[in] ctx Context pointer
* @param[in] dir Scroll direction

View File

@ -42,6 +42,12 @@ static const int button_value_map[VKM_BUTTON_STATE_MAX] = {
[VKM_BUTTON_RELEASED] = 0,
};
/*
* High-resolution scroll uses the same scale as REL_WHEEL_HI_RES in the kernel
* (120 units per click / detent; aligns with libinput and the Windows convention).
*/
#define VKM_SCROLL_HI_RES_PER_DETENT 120
vkm_result vkm_init(vkm_context **ctx)
{
char name_buf[32];
@ -316,9 +322,8 @@ vkm_result vkm_mouse_click(vkm_context *ctx, vkm_mouse_button button, vkm_button
vkm_result vkm_mouse_scroll(vkm_context *ctx, vkm_mouse_scroll_direction dir)
{
int axis;
int val;
int rc;
int sign;
if (ctx == NULL) {
return VKM_ERROR_INVALID_PARAM;
@ -336,18 +341,30 @@ vkm_result vkm_mouse_scroll(vkm_context *ctx, vkm_mouse_scroll_direction dir)
if (!ctx->mouse_horizontal_scroll) {
return VKM_ERROR_NOT_SUPPORTED;
}
axis = REL_HWHEEL;
val = (dir == VKM_MOUSE_SCROLL_LEFT) ? -1 : +1;
} else {
axis = REL_WHEEL;
val = (dir == VKM_MOUSE_SCROLL_UP) ? +1 : -1;
sign = (dir == VKM_MOUSE_SCROLL_LEFT) ? -1 : 1;
if (ctx->mouse_hi_res_scroll) {
rc = libevdev_uinput_write_event(ctx->uidev, EV_REL, REL_HWHEEL_HI_RES,
sign * VKM_SCROLL_HI_RES_PER_DETENT);
if (rc != 0) {
return VKM_ERROR_EVENT;
}
}
rc = libevdev_uinput_write_event(ctx->uidev, EV_REL, REL_HWHEEL, sign);
if (rc != 0) {
return VKM_ERROR_EVENT;
}
return VKM_SUCCESS;
}
if (val == 0) {
return VKM_ERROR_NO_CHANGE;
sign = (dir == VKM_MOUSE_SCROLL_UP) ? 1 : -1;
if (ctx->mouse_hi_res_scroll) {
rc = libevdev_uinput_write_event(ctx->uidev, EV_REL, REL_WHEEL_HI_RES,
sign * VKM_SCROLL_HI_RES_PER_DETENT);
if (rc != 0) {
return VKM_ERROR_EVENT;
}
}
rc = libevdev_uinput_write_event(ctx->uidev, EV_REL, axis, val);
rc = libevdev_uinput_write_event(ctx->uidev, EV_REL, REL_WHEEL, sign);
if (rc != 0) {
return VKM_ERROR_EVENT;
}

View File

@ -0,0 +1,896 @@
/*
* Saitek virtual keyboard mouse - Linux evdev unit tests
*
* Copyright (C) 2026 Nirenjan Krishnan
*
* SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
*/
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <setjmp.h>
#include <cmocka.h>
#include <libevdev/libevdev-uinput.h>
#include "vkm-internal.h"
/**********************************************************************
* libevdev stubs (linked instead of libevdev)
*********************************************************************/
struct libevdev {
int dummy;
};
struct libevdev_uinput {
int dummy;
};
static struct libevdev dummy_evdev_device;
static struct libevdev_uinput dummy_uinput_device;
/* When zero, uinput creation "succeeds" and *uidev is set; otherwise that rc is returned. */
static int vkm_test_uinput_create_rc;
struct libevdev *libevdev_new(void)
{
function_called();
return mock_ptr_type(struct libevdev *);
}
void libevdev_free(struct libevdev *dev)
{
check_expected_ptr(dev);
}
int libevdev_uinput_create_from_device(const struct libevdev *dev, int uinput_fd,
struct libevdev_uinput **uidev)
{
function_called();
check_expected_ptr(dev);
check_expected(uinput_fd);
if (vkm_test_uinput_create_rc != 0) {
return vkm_test_uinput_create_rc;
}
*uidev = &dummy_uinput_device;
return 0;
}
void libevdev_uinput_destroy(struct libevdev_uinput *uinput_dev)
{
check_expected_ptr(uinput_dev);
}
int libevdev_enable_event_type(struct libevdev *dev, unsigned int type)
{
function_called();
check_expected_ptr(dev);
check_expected(type);
return 0;
}
int libevdev_enable_event_code(struct libevdev *dev, unsigned int type, unsigned int code,
const void *data)
{
function_called();
check_expected_ptr(dev);
check_expected(type);
check_expected(code);
check_expected_ptr(data);
return 0;
}
void libevdev_set_name(struct libevdev *dev, const char *name)
{
function_called();
check_expected_ptr(dev);
check_expected(name);
}
int libevdev_uinput_write_event(const struct libevdev_uinput *uidev, unsigned int type,
unsigned int code, int value)
{
function_called();
check_expected_ptr(uidev);
check_expected(type);
check_expected(code);
check_expected(value);
return mock_type(int);
}
time_t time(time_t *t)
{
const time_t tv = (time_t)0xdeadbeef;
if (t) {
*t = tv;
}
return tv;
}
/**********************************************************************
* Expectation helpers mirror enable_mouse_events() order
*********************************************************************/
static void expect_enable_mouse_events(bool hi_res, bool horiz)
{
expect_function_call(libevdev_enable_event_type);
expect_value(libevdev_enable_event_type, dev, &dummy_evdev_device);
expect_value(libevdev_enable_event_type, type, EV_REL);
#define EXPECT_REL_CODE(evcode) \
do { \
expect_function_call(libevdev_enable_event_code); \
expect_value(libevdev_enable_event_code, dev, &dummy_evdev_device); \
expect_value(libevdev_enable_event_code, type, EV_REL); \
expect_value(libevdev_enable_event_code, code, (evcode)); \
expect_value(libevdev_enable_event_code, data, (void *)NULL); \
} while (0)
EXPECT_REL_CODE(REL_X);
EXPECT_REL_CODE(REL_Y);
EXPECT_REL_CODE(REL_WHEEL);
if (hi_res) {
EXPECT_REL_CODE(REL_WHEEL_HI_RES);
}
if (horiz) {
EXPECT_REL_CODE(REL_HWHEEL);
if (hi_res) {
EXPECT_REL_CODE(REL_HWHEEL_HI_RES);
}
}
#undef EXPECT_REL_CODE
expect_function_call(libevdev_enable_event_type);
expect_value(libevdev_enable_event_type, dev, &dummy_evdev_device);
expect_value(libevdev_enable_event_type, type, EV_KEY);
#define EXPECT_KEY_CODE(keycode) \
do { \
expect_function_call(libevdev_enable_event_code); \
expect_value(libevdev_enable_event_code, dev, &dummy_evdev_device); \
expect_value(libevdev_enable_event_code, type, EV_KEY); \
expect_value(libevdev_enable_event_code, code, (keycode)); \
expect_value(libevdev_enable_event_code, data, (void *)NULL); \
} while (0)
EXPECT_KEY_CODE(BTN_LEFT);
EXPECT_KEY_CODE(BTN_RIGHT);
EXPECT_KEY_CODE(BTN_MIDDLE);
#undef EXPECT_KEY_CODE
}
static void expect_uinput_create_ok(void)
{
vkm_test_uinput_create_rc = 0;
expect_function_call(libevdev_uinput_create_from_device);
expect_value(libevdev_uinput_create_from_device, dev, &dummy_evdev_device);
expect_value(libevdev_uinput_create_from_device, uinput_fd, LIBEVDEV_UINPUT_OPEN_MANAGED);
}
/* First successful vkm_start: create dev, name, enable events, uinput. */
static void expect_first_start_sequence(const char *device_name, bool hi_res, bool horiz)
{
expect_function_call(libevdev_new);
will_return(libevdev_new, &dummy_evdev_device);
expect_function_call(libevdev_set_name);
expect_value(libevdev_set_name, dev, &dummy_evdev_device);
expect_string(libevdev_set_name, name, device_name);
expect_enable_mouse_events(hi_res, horiz);
expect_uinput_create_ok();
}
static int teardown_ctx(void **state)
{
vkm_context *ctx = *state;
vkm_test_uinput_create_rc = 0;
if (ctx != NULL) {
if (vkm_is_ready(ctx)) {
expect_value(libevdev_uinput_destroy, uinput_dev, &dummy_uinput_device);
expect_value(libevdev_free, dev, &dummy_evdev_device);
}
vkm_exit(ctx);
}
*state = NULL;
return 0;
}
/**********************************************************************
* Tests: init / exit / queries
*********************************************************************/
static void test_init_null_out(void **state)
{
(void)state;
assert_int_equal(vkm_init(NULL), VKM_ERROR_INVALID_PARAM);
}
static void test_exit_null(void **state)
{
(void)state;
vkm_exit(NULL);
}
static void test_platform_supported(void **state)
{
(void)state;
assert_true(vkm_platform_supported());
}
static void test_feature_supported(void **state)
{
(void)state;
assert_true(vkm_feature_supported(VKM_FEAT_MOUSE));
assert_false(vkm_feature_supported(VKM_FEAT_KEYBOARD_US));
assert_false(vkm_feature_supported((vkm_feature)99));
}
static void test_is_ready_null(void **state)
{
(void)state;
assert_false(vkm_is_ready(NULL));
}
static void test_is_ready_after_init_only(void **state)
{
vkm_context *ctx = NULL;
(void)state;
assert_int_equal(vkm_init(&ctx), VKM_SUCCESS);
assert_non_null(ctx);
assert_false(vkm_is_ready(ctx));
vkm_exit(ctx);
}
/**********************************************************************
* Tests: vkm_set_option
*********************************************************************/
static void test_set_option_null_ctx(void **state)
{
(void)state;
assert_int_equal(vkm_set_option(NULL, VKM_OPT_HI_RES_SCROLL, 1), VKM_ERROR_INVALID_PARAM);
}
static void test_set_option_unknown(void **state)
{
vkm_context *ctx = NULL;
assert_int_equal(vkm_init(&ctx), VKM_SUCCESS);
*state = ctx;
assert_int_equal(vkm_set_option(ctx, (vkm_option)999, 0), VKM_ERROR_INVALID_PARAM);
}
static void test_set_option_flags_and_name(void **state)
{
vkm_context *ctx = NULL;
assert_int_equal(vkm_init(&ctx), VKM_SUCCESS);
*state = ctx;
assert_int_equal(vkm_set_option(ctx, VKM_OPT_HI_RES_SCROLL, 1), VKM_SUCCESS);
assert_int_equal(vkm_set_option(ctx, VKM_OPT_HORIZONTAL_SCROLL, 1), VKM_SUCCESS);
assert_int_equal(vkm_set_option(ctx, VKM_OPT_DEVICE_NAME, "custom-name"), VKM_SUCCESS);
expect_first_start_sequence("custom-name", true, true);
assert_int_equal(vkm_start(ctx), VKM_SUCCESS);
}
/**********************************************************************
* Tests: vkm_start
*********************************************************************/
static void test_start_null(void **state)
{
(void)state;
assert_int_equal(vkm_start(NULL), VKM_ERROR_INVALID_PARAM);
}
static void test_start_libevdev_new_fails(void **state)
{
vkm_context *ctx = NULL;
assert_int_equal(vkm_init(&ctx), VKM_SUCCESS);
*state = ctx;
expect_function_call(libevdev_new);
will_return(libevdev_new, (struct libevdev *)(NULL));
assert_int_equal(vkm_start(ctx), VKM_ERROR_DEV_FAILURE);
}
static void test_start_uinput_fails(void **state)
{
vkm_context *ctx = NULL;
assert_int_equal(vkm_init(&ctx), VKM_SUCCESS);
*state = ctx;
vkm_test_uinput_create_rc = -1;
expect_function_call(libevdev_new);
will_return(libevdev_new, &dummy_evdev_device);
expect_function_call(libevdev_set_name);
expect_value(libevdev_set_name, dev, &dummy_evdev_device);
expect_string(libevdev_set_name, name, "VKM virtual device @deadbeef");
expect_enable_mouse_events(false, false);
expect_function_call(libevdev_uinput_create_from_device);
expect_value(libevdev_uinput_create_from_device, dev, &dummy_evdev_device);
expect_value(libevdev_uinput_create_from_device, uinput_fd, LIBEVDEV_UINPUT_OPEN_MANAGED);
expect_value(libevdev_free, dev, &dummy_evdev_device);
assert_int_equal(vkm_start(ctx), VKM_ERROR_DEV_FAILURE);
}
static void test_start_default_name_success(void **state)
{
vkm_context *ctx = NULL;
assert_int_equal(vkm_init(&ctx), VKM_SUCCESS);
*state = ctx;
expect_first_start_sequence("VKM virtual device @deadbeef", false, false);
assert_int_equal(vkm_start(ctx), VKM_SUCCESS);
assert_true(vkm_is_ready(ctx));
}
static void test_start_hi_res_scroll_only(void **state)
{
vkm_context *ctx = NULL;
assert_int_equal(vkm_init(&ctx), VKM_SUCCESS);
assert_int_equal(vkm_set_option(ctx, VKM_OPT_HI_RES_SCROLL, 1), VKM_SUCCESS);
*state = ctx;
expect_first_start_sequence("VKM virtual device @deadbeef", true, false);
assert_int_equal(vkm_start(ctx), VKM_SUCCESS);
}
static void test_start_horizontal_scroll_only(void **state)
{
vkm_context *ctx = NULL;
assert_int_equal(vkm_init(&ctx), VKM_SUCCESS);
assert_int_equal(vkm_set_option(ctx, VKM_OPT_HORIZONTAL_SCROLL, 1), VKM_SUCCESS);
*state = ctx;
expect_first_start_sequence("VKM virtual device @deadbeef", false, true);
assert_int_equal(vkm_start(ctx), VKM_SUCCESS);
}
static void test_start_twice_idempotent(void **state)
{
vkm_context *ctx = NULL;
assert_int_equal(vkm_init(&ctx), VKM_SUCCESS);
*state = ctx;
expect_first_start_sequence("VKM virtual device @deadbeef", false, false);
assert_int_equal(vkm_start(ctx), VKM_SUCCESS);
assert_int_equal(vkm_start(ctx), VKM_SUCCESS);
}
/**********************************************************************
* Tests: mouse move
*********************************************************************/
static void test_mouse_move_param_and_ready(void **state)
{
assert_int_equal(vkm_mouse_move(NULL, 1, 1), VKM_ERROR_INVALID_PARAM);
vkm_context *ctx = NULL;
assert_int_equal(vkm_init(&ctx), VKM_SUCCESS);
*state = ctx;
assert_int_equal(vkm_mouse_move(ctx, 1, 1), VKM_ERROR_NOT_READY);
}
static void test_mouse_move_no_change(void **state)
{
vkm_context *ctx = NULL;
assert_int_equal(vkm_init(&ctx), VKM_SUCCESS);
*state = ctx;
expect_first_start_sequence("VKM virtual device @deadbeef", false, false);
assert_int_equal(vkm_start(ctx), VKM_SUCCESS);
assert_int_equal(vkm_mouse_move(ctx, 0, 0), VKM_ERROR_NO_CHANGE);
}
static void test_mouse_move_dx_then_dy(void **state)
{
vkm_context *ctx = NULL;
assert_int_equal(vkm_init(&ctx), VKM_SUCCESS);
*state = ctx;
expect_first_start_sequence("VKM virtual device @deadbeef", false, false);
assert_int_equal(vkm_start(ctx), VKM_SUCCESS);
expect_function_call(libevdev_uinput_write_event);
expect_value(libevdev_uinput_write_event, uidev, &dummy_uinput_device);
expect_value(libevdev_uinput_write_event, type, EV_REL);
expect_value(libevdev_uinput_write_event, code, REL_X);
expect_value(libevdev_uinput_write_event, value, 3);
will_return(libevdev_uinput_write_event, 0);
expect_function_call(libevdev_uinput_write_event);
expect_value(libevdev_uinput_write_event, uidev, &dummy_uinput_device);
expect_value(libevdev_uinput_write_event, type, EV_REL);
expect_value(libevdev_uinput_write_event, code, REL_Y);
expect_value(libevdev_uinput_write_event, value, -2);
will_return(libevdev_uinput_write_event, 0);
assert_int_equal(vkm_mouse_move(ctx, 3, -2), VKM_SUCCESS);
}
static void test_mouse_move_dx_only(void **state)
{
vkm_context *ctx = NULL;
assert_int_equal(vkm_init(&ctx), VKM_SUCCESS);
*state = ctx;
expect_first_start_sequence("VKM virtual device @deadbeef", false, false);
assert_int_equal(vkm_start(ctx), VKM_SUCCESS);
expect_function_call(libevdev_uinput_write_event);
expect_value(libevdev_uinput_write_event, uidev, &dummy_uinput_device);
expect_value(libevdev_uinput_write_event, type, EV_REL);
expect_value(libevdev_uinput_write_event, code, REL_X);
expect_value(libevdev_uinput_write_event, value, -9);
will_return(libevdev_uinput_write_event, 0);
assert_int_equal(vkm_mouse_move(ctx, -9, 0), VKM_SUCCESS);
}
static void test_mouse_move_write_dx_fails(void **state)
{
vkm_context *ctx = NULL;
assert_int_equal(vkm_init(&ctx), VKM_SUCCESS);
*state = ctx;
expect_first_start_sequence("VKM virtual device @deadbeef", false, false);
assert_int_equal(vkm_start(ctx), VKM_SUCCESS);
expect_function_call(libevdev_uinput_write_event);
expect_value(libevdev_uinput_write_event, uidev, &dummy_uinput_device);
expect_value(libevdev_uinput_write_event, type, EV_REL);
expect_value(libevdev_uinput_write_event, code, REL_X);
expect_value(libevdev_uinput_write_event, value, 5);
will_return(libevdev_uinput_write_event, -1);
assert_int_equal(vkm_mouse_move(ctx, 5, 0), VKM_ERROR_EVENT);
}
static void test_mouse_move_write_dy_fails(void **state)
{
vkm_context *ctx = NULL;
assert_int_equal(vkm_init(&ctx), VKM_SUCCESS);
*state = ctx;
expect_first_start_sequence("VKM virtual device @deadbeef", false, false);
assert_int_equal(vkm_start(ctx), VKM_SUCCESS);
expect_function_call(libevdev_uinput_write_event);
expect_value(libevdev_uinput_write_event, uidev, &dummy_uinput_device);
expect_value(libevdev_uinput_write_event, type, EV_REL);
expect_value(libevdev_uinput_write_event, code, REL_Y);
expect_value(libevdev_uinput_write_event, value, 7);
will_return(libevdev_uinput_write_event, -1);
assert_int_equal(vkm_mouse_move(ctx, 0, 7), VKM_ERROR_EVENT);
}
/**********************************************************************
* Tests: mouse click
*********************************************************************/
static void test_mouse_click_invalid(void **state)
{
vkm_context *ctx = NULL;
assert_int_equal(vkm_init(&ctx), VKM_SUCCESS);
*state = ctx;
expect_first_start_sequence("VKM virtual device @deadbeef", false, false);
assert_int_equal(vkm_start(ctx), VKM_SUCCESS);
assert_int_equal(vkm_mouse_click(NULL, VKM_MOUSE_BTN_LEFT, VKM_BUTTON_PRESSED),
VKM_ERROR_INVALID_PARAM);
assert_int_equal(vkm_mouse_click(ctx, VKM_MOUSE_BTN_MAX, VKM_BUTTON_PRESSED),
VKM_ERROR_INVALID_PARAM);
assert_int_equal(vkm_mouse_click(ctx, VKM_MOUSE_BTN_LEFT, VKM_BUTTON_STATE_MAX),
VKM_ERROR_INVALID_PARAM);
}
static void test_mouse_click_not_ready(void **state)
{
vkm_context *ctx = NULL;
assert_int_equal(vkm_init(&ctx), VKM_SUCCESS);
*state = ctx;
assert_int_equal(vkm_mouse_click(ctx, VKM_MOUSE_BTN_LEFT, VKM_BUTTON_PRESSED),
VKM_ERROR_NOT_READY);
}
static void test_mouse_click_no_change(void **state)
{
vkm_context *ctx = NULL;
assert_int_equal(vkm_init(&ctx), VKM_SUCCESS);
*state = ctx;
expect_first_start_sequence("VKM virtual device @deadbeef", false, false);
assert_int_equal(vkm_start(ctx), VKM_SUCCESS);
expect_function_call(libevdev_uinput_write_event);
expect_value(libevdev_uinput_write_event, uidev, &dummy_uinput_device);
expect_value(libevdev_uinput_write_event, type, EV_KEY);
expect_value(libevdev_uinput_write_event, code, BTN_LEFT);
expect_value(libevdev_uinput_write_event, value, 1);
will_return(libevdev_uinput_write_event, 0);
assert_int_equal(vkm_mouse_click(ctx, VKM_MOUSE_BTN_LEFT, VKM_BUTTON_PRESSED), VKM_SUCCESS);
assert_int_equal(vkm_mouse_click(ctx, VKM_MOUSE_BTN_LEFT, VKM_BUTTON_PRESSED),
VKM_ERROR_NO_CHANGE);
}
static void test_mouse_click_success_release(void **state)
{
vkm_context *ctx = NULL;
assert_int_equal(vkm_init(&ctx), VKM_SUCCESS);
*state = ctx;
expect_first_start_sequence("VKM virtual device @deadbeef", false, false);
assert_int_equal(vkm_start(ctx), VKM_SUCCESS);
expect_function_call(libevdev_uinput_write_event);
expect_value(libevdev_uinput_write_event, uidev, &dummy_uinput_device);
expect_value(libevdev_uinput_write_event, type, EV_KEY);
expect_value(libevdev_uinput_write_event, code, BTN_RIGHT);
expect_value(libevdev_uinput_write_event, value, 1);
will_return(libevdev_uinput_write_event, 0);
assert_int_equal(vkm_mouse_click(ctx, VKM_MOUSE_BTN_RIGHT, VKM_BUTTON_PRESSED), VKM_SUCCESS);
expect_function_call(libevdev_uinput_write_event);
expect_value(libevdev_uinput_write_event, uidev, &dummy_uinput_device);
expect_value(libevdev_uinput_write_event, type, EV_KEY);
expect_value(libevdev_uinput_write_event, code, BTN_RIGHT);
expect_value(libevdev_uinput_write_event, value, 0);
will_return(libevdev_uinput_write_event, 0);
assert_int_equal(vkm_mouse_click(ctx, VKM_MOUSE_BTN_RIGHT, VKM_BUTTON_RELEASED), VKM_SUCCESS);
}
static void test_mouse_click_write_fails(void **state)
{
vkm_context *ctx = NULL;
assert_int_equal(vkm_init(&ctx), VKM_SUCCESS);
*state = ctx;
expect_first_start_sequence("VKM virtual device @deadbeef", false, false);
assert_int_equal(vkm_start(ctx), VKM_SUCCESS);
expect_function_call(libevdev_uinput_write_event);
expect_value(libevdev_uinput_write_event, uidev, &dummy_uinput_device);
expect_value(libevdev_uinput_write_event, type, EV_KEY);
expect_value(libevdev_uinput_write_event, code, BTN_MIDDLE);
expect_value(libevdev_uinput_write_event, value, 1);
will_return(libevdev_uinput_write_event, -1);
assert_int_equal(vkm_mouse_click(ctx, VKM_MOUSE_BTN_MIDDLE, VKM_BUTTON_PRESSED),
VKM_ERROR_EVENT);
}
/**********************************************************************
* Tests: scroll
*********************************************************************/
static void test_mouse_scroll_param_ready(void **state)
{
assert_int_equal(vkm_mouse_scroll(NULL, VKM_MOUSE_SCROLL_UP), VKM_ERROR_INVALID_PARAM);
vkm_context *ctx = NULL;
assert_int_equal(vkm_init(&ctx), VKM_SUCCESS);
*state = ctx;
assert_int_equal(vkm_mouse_scroll(ctx, VKM_MOUSE_SCROLL_MAX), VKM_ERROR_INVALID_PARAM);
assert_int_equal(vkm_mouse_scroll(ctx, VKM_MOUSE_SCROLL_UP), VKM_ERROR_NOT_READY);
}
static void test_mouse_scroll_horizontal_disabled(void **state)
{
vkm_context *ctx = NULL;
assert_int_equal(vkm_init(&ctx), VKM_SUCCESS);
*state = ctx;
expect_first_start_sequence("VKM virtual device @deadbeef", false, false);
assert_int_equal(vkm_start(ctx), VKM_SUCCESS);
assert_int_equal(vkm_mouse_scroll(ctx, VKM_MOUSE_SCROLL_LEFT), VKM_ERROR_NOT_SUPPORTED);
assert_int_equal(vkm_mouse_scroll(ctx, VKM_MOUSE_SCROLL_RIGHT), VKM_ERROR_NOT_SUPPORTED);
}
static void test_mouse_scroll_vertical(void **state)
{
vkm_context *ctx = NULL;
assert_int_equal(vkm_init(&ctx), VKM_SUCCESS);
*state = ctx;
expect_first_start_sequence("VKM virtual device @deadbeef", false, false);
assert_int_equal(vkm_start(ctx), VKM_SUCCESS);
expect_function_call(libevdev_uinput_write_event);
expect_value(libevdev_uinput_write_event, uidev, &dummy_uinput_device);
expect_value(libevdev_uinput_write_event, type, EV_REL);
expect_value(libevdev_uinput_write_event, code, REL_WHEEL);
expect_value(libevdev_uinput_write_event, value, 1);
will_return(libevdev_uinput_write_event, 0);
assert_int_equal(vkm_mouse_scroll(ctx, VKM_MOUSE_SCROLL_UP), VKM_SUCCESS);
expect_function_call(libevdev_uinput_write_event);
expect_value(libevdev_uinput_write_event, uidev, &dummy_uinput_device);
expect_value(libevdev_uinput_write_event, type, EV_REL);
expect_value(libevdev_uinput_write_event, code, REL_WHEEL);
expect_value(libevdev_uinput_write_event, value, -1);
will_return(libevdev_uinput_write_event, 0);
assert_int_equal(vkm_mouse_scroll(ctx, VKM_MOUSE_SCROLL_DOWN), VKM_SUCCESS);
}
static void test_mouse_scroll_vertical_hi_res(void **state)
{
vkm_context *ctx = NULL;
assert_int_equal(vkm_init(&ctx), VKM_SUCCESS);
assert_int_equal(vkm_set_option(ctx, VKM_OPT_HI_RES_SCROLL, 1), VKM_SUCCESS);
*state = ctx;
expect_first_start_sequence("VKM virtual device @deadbeef", true, false);
assert_int_equal(vkm_start(ctx), VKM_SUCCESS);
expect_function_call(libevdev_uinput_write_event);
expect_value(libevdev_uinput_write_event, uidev, &dummy_uinput_device);
expect_value(libevdev_uinput_write_event, type, EV_REL);
expect_value(libevdev_uinput_write_event, code, REL_WHEEL_HI_RES);
expect_value(libevdev_uinput_write_event, value, 120);
will_return(libevdev_uinput_write_event, 0);
expect_function_call(libevdev_uinput_write_event);
expect_value(libevdev_uinput_write_event, uidev, &dummy_uinput_device);
expect_value(libevdev_uinput_write_event, type, EV_REL);
expect_value(libevdev_uinput_write_event, code, REL_WHEEL);
expect_value(libevdev_uinput_write_event, value, 1);
will_return(libevdev_uinput_write_event, 0);
assert_int_equal(vkm_mouse_scroll(ctx, VKM_MOUSE_SCROLL_UP), VKM_SUCCESS);
expect_function_call(libevdev_uinput_write_event);
expect_value(libevdev_uinput_write_event, uidev, &dummy_uinput_device);
expect_value(libevdev_uinput_write_event, type, EV_REL);
expect_value(libevdev_uinput_write_event, code, REL_WHEEL_HI_RES);
expect_value(libevdev_uinput_write_event, value, -120);
will_return(libevdev_uinput_write_event, 0);
expect_function_call(libevdev_uinput_write_event);
expect_value(libevdev_uinput_write_event, uidev, &dummy_uinput_device);
expect_value(libevdev_uinput_write_event, type, EV_REL);
expect_value(libevdev_uinput_write_event, code, REL_WHEEL);
expect_value(libevdev_uinput_write_event, value, -1);
will_return(libevdev_uinput_write_event, 0);
assert_int_equal(vkm_mouse_scroll(ctx, VKM_MOUSE_SCROLL_DOWN), VKM_SUCCESS);
}
static void test_mouse_scroll_horizontal_enabled(void **state)
{
vkm_context *ctx = NULL;
assert_int_equal(vkm_init(&ctx), VKM_SUCCESS);
assert_int_equal(vkm_set_option(ctx, VKM_OPT_HORIZONTAL_SCROLL, 1), VKM_SUCCESS);
*state = ctx;
expect_first_start_sequence("VKM virtual device @deadbeef", false, true);
assert_int_equal(vkm_start(ctx), VKM_SUCCESS);
expect_function_call(libevdev_uinput_write_event);
expect_value(libevdev_uinput_write_event, uidev, &dummy_uinput_device);
expect_value(libevdev_uinput_write_event, type, EV_REL);
expect_value(libevdev_uinput_write_event, code, REL_HWHEEL);
expect_value(libevdev_uinput_write_event, value, -1);
will_return(libevdev_uinput_write_event, 0);
assert_int_equal(vkm_mouse_scroll(ctx, VKM_MOUSE_SCROLL_LEFT), VKM_SUCCESS);
expect_function_call(libevdev_uinput_write_event);
expect_value(libevdev_uinput_write_event, uidev, &dummy_uinput_device);
expect_value(libevdev_uinput_write_event, type, EV_REL);
expect_value(libevdev_uinput_write_event, code, REL_HWHEEL);
expect_value(libevdev_uinput_write_event, value, 1);
will_return(libevdev_uinput_write_event, 0);
assert_int_equal(vkm_mouse_scroll(ctx, VKM_MOUSE_SCROLL_RIGHT), VKM_SUCCESS);
}
static void test_mouse_scroll_horizontal_hi_res(void **state)
{
vkm_context *ctx = NULL;
assert_int_equal(vkm_init(&ctx), VKM_SUCCESS);
assert_int_equal(vkm_set_option(ctx, VKM_OPT_HI_RES_SCROLL, 1), VKM_SUCCESS);
assert_int_equal(vkm_set_option(ctx, VKM_OPT_HORIZONTAL_SCROLL, 1), VKM_SUCCESS);
*state = ctx;
expect_first_start_sequence("VKM virtual device @deadbeef", true, true);
assert_int_equal(vkm_start(ctx), VKM_SUCCESS);
expect_function_call(libevdev_uinput_write_event);
expect_value(libevdev_uinput_write_event, uidev, &dummy_uinput_device);
expect_value(libevdev_uinput_write_event, type, EV_REL);
expect_value(libevdev_uinput_write_event, code, REL_HWHEEL_HI_RES);
expect_value(libevdev_uinput_write_event, value, -120);
will_return(libevdev_uinput_write_event, 0);
expect_function_call(libevdev_uinput_write_event);
expect_value(libevdev_uinput_write_event, uidev, &dummy_uinput_device);
expect_value(libevdev_uinput_write_event, type, EV_REL);
expect_value(libevdev_uinput_write_event, code, REL_HWHEEL);
expect_value(libevdev_uinput_write_event, value, -1);
will_return(libevdev_uinput_write_event, 0);
assert_int_equal(vkm_mouse_scroll(ctx, VKM_MOUSE_SCROLL_LEFT), VKM_SUCCESS);
expect_function_call(libevdev_uinput_write_event);
expect_value(libevdev_uinput_write_event, uidev, &dummy_uinput_device);
expect_value(libevdev_uinput_write_event, type, EV_REL);
expect_value(libevdev_uinput_write_event, code, REL_HWHEEL_HI_RES);
expect_value(libevdev_uinput_write_event, value, 120);
will_return(libevdev_uinput_write_event, 0);
expect_function_call(libevdev_uinput_write_event);
expect_value(libevdev_uinput_write_event, uidev, &dummy_uinput_device);
expect_value(libevdev_uinput_write_event, type, EV_REL);
expect_value(libevdev_uinput_write_event, code, REL_HWHEEL);
expect_value(libevdev_uinput_write_event, value, 1);
will_return(libevdev_uinput_write_event, 0);
assert_int_equal(vkm_mouse_scroll(ctx, VKM_MOUSE_SCROLL_RIGHT), VKM_SUCCESS);
}
static void test_mouse_scroll_write_fails(void **state)
{
vkm_context *ctx = NULL;
assert_int_equal(vkm_init(&ctx), VKM_SUCCESS);
*state = ctx;
expect_first_start_sequence("VKM virtual device @deadbeef", false, false);
assert_int_equal(vkm_start(ctx), VKM_SUCCESS);
expect_function_call(libevdev_uinput_write_event);
expect_value(libevdev_uinput_write_event, uidev, &dummy_uinput_device);
expect_value(libevdev_uinput_write_event, type, EV_REL);
expect_value(libevdev_uinput_write_event, code, REL_WHEEL);
expect_value(libevdev_uinput_write_event, value, 1);
will_return(libevdev_uinput_write_event, -1);
assert_int_equal(vkm_mouse_scroll(ctx, VKM_MOUSE_SCROLL_UP), VKM_ERROR_EVENT);
}
static void test_mouse_scroll_write_fails_hi_res(void **state)
{
vkm_context *ctx = NULL;
assert_int_equal(vkm_init(&ctx), VKM_SUCCESS);
assert_int_equal(vkm_set_option(ctx, VKM_OPT_HI_RES_SCROLL, 1), VKM_SUCCESS);
*state = ctx;
expect_first_start_sequence("VKM virtual device @deadbeef", true, false);
assert_int_equal(vkm_start(ctx), VKM_SUCCESS);
expect_function_call(libevdev_uinput_write_event);
expect_value(libevdev_uinput_write_event, uidev, &dummy_uinput_device);
expect_value(libevdev_uinput_write_event, type, EV_REL);
expect_value(libevdev_uinput_write_event, code, REL_WHEEL_HI_RES);
expect_value(libevdev_uinput_write_event, value, 120);
will_return(libevdev_uinput_write_event, -1);
assert_int_equal(vkm_mouse_scroll(ctx, VKM_MOUSE_SCROLL_UP), VKM_ERROR_EVENT);
}
/**********************************************************************
* Tests: sync
*********************************************************************/
static void test_sync_null(void **state)
{
(void)state;
assert_int_equal(vkm_sync(NULL), VKM_ERROR_INVALID_PARAM);
}
static void test_sync_not_ready(void **state)
{
vkm_context *ctx = NULL;
assert_int_equal(vkm_init(&ctx), VKM_SUCCESS);
*state = ctx;
assert_int_equal(vkm_sync(ctx), VKM_ERROR_NOT_READY);
}
static void test_sync_success(void **state)
{
vkm_context *ctx = NULL;
assert_int_equal(vkm_init(&ctx), VKM_SUCCESS);
*state = ctx;
expect_first_start_sequence("VKM virtual device @deadbeef", false, false);
assert_int_equal(vkm_start(ctx), VKM_SUCCESS);
expect_function_call(libevdev_uinput_write_event);
expect_value(libevdev_uinput_write_event, uidev, &dummy_uinput_device);
expect_value(libevdev_uinput_write_event, type, EV_SYN);
expect_value(libevdev_uinput_write_event, code, SYN_REPORT);
expect_value(libevdev_uinput_write_event, value, 0);
will_return(libevdev_uinput_write_event, 0);
assert_int_equal(vkm_sync(ctx), VKM_SUCCESS);
}
static void test_sync_write_fails(void **state)
{
vkm_context *ctx = NULL;
assert_int_equal(vkm_init(&ctx), VKM_SUCCESS);
*state = ctx;
expect_first_start_sequence("VKM virtual device @deadbeef", false, false);
assert_int_equal(vkm_start(ctx), VKM_SUCCESS);
expect_function_call(libevdev_uinput_write_event);
expect_value(libevdev_uinput_write_event, uidev, &dummy_uinput_device);
expect_value(libevdev_uinput_write_event, type, EV_SYN);
expect_value(libevdev_uinput_write_event, code, SYN_REPORT);
expect_value(libevdev_uinput_write_event, value, 0);
will_return(libevdev_uinput_write_event, -1);
assert_int_equal(vkm_sync(ctx), VKM_ERROR_EVENT);
}
/**********************************************************************
* Tests: vkm_exit destroys uinput + dev
*********************************************************************/
static void test_exit_after_start(void **state)
{
vkm_context *ctx = NULL;
assert_int_equal(vkm_init(&ctx), VKM_SUCCESS);
*state = ctx;
expect_first_start_sequence("VKM virtual device @deadbeef", false, false);
assert_int_equal(vkm_start(ctx), VKM_SUCCESS);
expect_value(libevdev_uinput_destroy, uinput_dev, &dummy_uinput_device);
expect_value(libevdev_free, dev, &dummy_evdev_device);
vkm_exit(ctx);
*state = NULL;
}
int main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test(test_init_null_out),
cmocka_unit_test(test_exit_null),
cmocka_unit_test(test_platform_supported),
cmocka_unit_test(test_feature_supported),
cmocka_unit_test(test_is_ready_null),
cmocka_unit_test(test_is_ready_after_init_only),
cmocka_unit_test(test_set_option_null_ctx),
cmocka_unit_test_setup_teardown(test_set_option_unknown, NULL, teardown_ctx),
cmocka_unit_test_setup_teardown(test_set_option_flags_and_name, NULL, teardown_ctx),
cmocka_unit_test(test_start_null),
cmocka_unit_test_setup_teardown(test_start_libevdev_new_fails, NULL, teardown_ctx),
cmocka_unit_test_setup_teardown(test_start_uinput_fails, NULL, teardown_ctx),
cmocka_unit_test_setup_teardown(test_start_default_name_success, NULL, teardown_ctx),
cmocka_unit_test_setup_teardown(test_start_hi_res_scroll_only, NULL, teardown_ctx),
cmocka_unit_test_setup_teardown(test_start_horizontal_scroll_only, NULL, teardown_ctx),
cmocka_unit_test_setup_teardown(test_start_twice_idempotent, NULL, teardown_ctx),
cmocka_unit_test_setup_teardown(test_mouse_move_param_and_ready, NULL, teardown_ctx),
cmocka_unit_test_setup_teardown(test_mouse_move_no_change, NULL, teardown_ctx),
cmocka_unit_test_setup_teardown(test_mouse_move_dx_then_dy, NULL, teardown_ctx),
cmocka_unit_test_setup_teardown(test_mouse_move_dx_only, NULL, teardown_ctx),
cmocka_unit_test_setup_teardown(test_mouse_move_write_dx_fails, NULL, teardown_ctx),
cmocka_unit_test_setup_teardown(test_mouse_move_write_dy_fails, NULL, teardown_ctx),
cmocka_unit_test_setup_teardown(test_mouse_click_invalid, NULL, teardown_ctx),
cmocka_unit_test_setup_teardown(test_mouse_click_not_ready, NULL, teardown_ctx),
cmocka_unit_test_setup_teardown(test_mouse_click_no_change, NULL, teardown_ctx),
cmocka_unit_test_setup_teardown(test_mouse_click_success_release, NULL, teardown_ctx),
cmocka_unit_test_setup_teardown(test_mouse_click_write_fails, NULL, teardown_ctx),
cmocka_unit_test_setup_teardown(test_mouse_scroll_param_ready, NULL, teardown_ctx),
cmocka_unit_test_setup_teardown(test_mouse_scroll_horizontal_disabled, NULL, teardown_ctx),
cmocka_unit_test_setup_teardown(test_mouse_scroll_vertical, NULL, teardown_ctx),
cmocka_unit_test_setup_teardown(test_mouse_scroll_vertical_hi_res, NULL, teardown_ctx),
cmocka_unit_test_setup_teardown(test_mouse_scroll_horizontal_enabled, NULL, teardown_ctx),
cmocka_unit_test_setup_teardown(test_mouse_scroll_horizontal_hi_res, NULL, teardown_ctx),
cmocka_unit_test_setup_teardown(test_mouse_scroll_write_fails, NULL, teardown_ctx),
cmocka_unit_test_setup_teardown(test_mouse_scroll_write_fails_hi_res, NULL, teardown_ctx),
cmocka_unit_test(test_sync_null),
cmocka_unit_test_setup_teardown(test_sync_not_ready, NULL, teardown_ctx),
cmocka_unit_test_setup_teardown(test_sync_success, NULL, teardown_ctx),
cmocka_unit_test_setup_teardown(test_sync_write_fails, NULL, teardown_ctx),
cmocka_unit_test(test_exit_after_start),
};
cmocka_set_message_output(CM_OUTPUT_TAP);
return cmocka_run_group_tests(tests, NULL, NULL);
}