// SPDX-License-Identifier: MIT /* * Copyright © 2013 Red Hat, Inc. */ #include "config.h" #include #include #include #include #include #include #include #include #include "test-common.h" START_TEST(test_new_device) { struct libevdev *dev; dev = libevdev_new(); ck_assert(dev != NULL); libevdev_free(dev); } END_TEST START_TEST(test_free_device) { libevdev_free(NULL); } END_TEST START_TEST(test_init_from_invalid_fd) { int rc; struct libevdev *dev = NULL; rc = libevdev_new_from_fd(-1, &dev); ck_assert(dev == NULL); ck_assert_int_eq(rc, -EBADF); rc = libevdev_new_from_fd(STDIN_FILENO, &dev); ck_assert(dev == NULL); ck_assert_int_eq(rc, -ENOTTY); } END_TEST START_TEST(test_init_and_change_fd) { struct uinput_device* uidev; struct libevdev *dev; int rc; dev = libevdev_new(); ck_assert(dev != NULL); ck_assert_int_eq(libevdev_set_fd(dev, -1), -EBADF); libevdev_set_log_function(test_logfunc_ignore_error, NULL); ck_assert_int_eq(libevdev_change_fd(dev, -1), -1); libevdev_set_log_function(test_logfunc_abort_on_error, NULL); rc = uinput_device_new_with_events(&uidev, TEST_DEVICE_NAME, DEFAULT_IDS, EV_SYN, SYN_REPORT, EV_REL, REL_X, EV_REL, REL_Y, EV_REL, REL_WHEEL, EV_KEY, BTN_LEFT, EV_KEY, BTN_MIDDLE, EV_KEY, BTN_RIGHT, -1); ck_assert_msg(rc == 0, "Failed to create uinput device: %s", strerror(-rc)); ck_assert_int_eq(libevdev_set_fd(dev, uinput_device_get_fd(uidev)), 0); libevdev_set_log_function(test_logfunc_ignore_error, NULL); ck_assert_int_eq(libevdev_set_fd(dev, 0), -EBADF); libevdev_set_log_function(test_logfunc_abort_on_error, NULL); ck_assert_int_eq(libevdev_get_fd(dev), uinput_device_get_fd(uidev)); ck_assert_int_eq(libevdev_change_fd(dev, 0), 0); ck_assert_int_eq(libevdev_get_fd(dev), 0); uinput_device_free(uidev); libevdev_free(dev); } END_TEST static int log_fn_called = 0; static char *logdata = "test"; static void logfunc(enum libevdev_log_priority priority, void *data, const char *file, int line, const char *func, const char *f, va_list args) { ck_assert_int_eq(strcmp(logdata, data), 0); log_fn_called++; } START_TEST(test_log_init) { struct libevdev *dev = NULL; enum libevdev_log_priority old; old = libevdev_get_log_priority(); libevdev_set_log_priority(LIBEVDEV_LOG_DEBUG); libevdev_set_log_function(logfunc, NULL); libevdev_set_log_function(NULL, NULL); dev = libevdev_new(); ck_assert(dev != NULL); libevdev_set_log_function(logfunc, logdata); libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, NULL); libevdev_set_log_function(NULL, NULL); libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, NULL); libevdev_set_log_function(logfunc, logdata); libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, NULL); /* libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, NULL) should trigger a log message. We called it three times, but only twice with the logfunc set, thus, ensure we only called the logfunc twice */ ck_assert_int_eq(log_fn_called, 2); libevdev_free(dev); libevdev_set_log_function(test_logfunc_abort_on_error, NULL); log_fn_called = 0; libevdev_set_log_priority(old); } END_TEST START_TEST(test_log_default_priority) { ck_assert_int_eq(libevdev_get_log_priority(), LIBEVDEV_LOG_INFO); } END_TEST START_TEST(test_log_set_get_priority) { enum libevdev_log_priority pri; enum libevdev_log_priority old; old = libevdev_get_log_priority(); pri = LIBEVDEV_LOG_DEBUG; libevdev_set_log_priority(pri); ck_assert_int_eq(libevdev_get_log_priority(), pri); pri = LIBEVDEV_LOG_INFO; libevdev_set_log_priority(pri); ck_assert_int_eq(libevdev_get_log_priority(), pri); pri = LIBEVDEV_LOG_ERROR; libevdev_set_log_priority(pri); ck_assert_int_eq(libevdev_get_log_priority(), pri); /* debug and above is clamped */ pri = LIBEVDEV_LOG_DEBUG + 1; libevdev_set_log_priority(pri); ck_assert_int_eq(libevdev_get_log_priority(), LIBEVDEV_LOG_DEBUG); /* error and below is not clamped, we need this for another test */ pri = LIBEVDEV_LOG_ERROR - 1; libevdev_set_log_priority(pri); ck_assert_int_eq(libevdev_get_log_priority(), pri); libevdev_set_log_priority(old); } END_TEST START_TEST(test_log_priority) { struct libevdev *dev = NULL; enum libevdev_log_priority old; old = libevdev_get_log_priority(); libevdev_set_log_function(logfunc, logdata); dev = libevdev_new(); ck_assert(dev != NULL); libevdev_set_log_priority(LIBEVDEV_LOG_DEBUG); libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, NULL); ck_assert_int_eq(log_fn_called, 1); libevdev_set_log_priority(LIBEVDEV_LOG_INFO); libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, NULL); ck_assert_int_eq(log_fn_called, 2); libevdev_set_log_priority(LIBEVDEV_LOG_ERROR); libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, NULL); ck_assert_int_eq(log_fn_called, 3); /* we don't have any log msgs > ERROR at the moment, so test it by setting an invalid priority. */ libevdev_set_log_priority(LIBEVDEV_LOG_ERROR - 1); libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, NULL); ck_assert_int_eq(log_fn_called, 3); libevdev_free(dev); libevdev_set_log_function(test_logfunc_abort_on_error, NULL); log_fn_called = 0; libevdev_set_log_priority(old); } END_TEST static char *logdata_1 = "foo"; static char *logdata_2 = "bar"; static int log_data_fn_called = 0; static void logfunc_data(enum libevdev_log_priority priority, void *data, const char *file, int line, const char *func, const char *f, va_list args) { switch(log_data_fn_called) { case 0: ck_assert(data == logdata_1); break; case 1: ck_assert(data == logdata_2); break; case 2: ck_assert(data == NULL); break; default: ck_abort(); } log_data_fn_called++; } START_TEST(test_log_data) { struct libevdev *dev = NULL; dev = libevdev_new(); ck_assert(dev != NULL); libevdev_set_log_function(logfunc_data, logdata_1); libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, NULL); libevdev_set_log_function(logfunc_data, logdata_2); libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, NULL); libevdev_set_log_function(logfunc_data, NULL); libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, NULL); libevdev_free(dev); } END_TEST struct libevdev *devlogdata; static int dev_log_fn_called = 0; static void devlogfunc(const struct libevdev *dev, enum libevdev_log_priority priority, void *data, const char *file, int line, const char *func, const char *f, va_list args) { ck_assert(dev == data); dev_log_fn_called++; } START_TEST(test_device_log_init) { struct libevdev *dev = NULL; enum libevdev_log_priority old; old = libevdev_get_log_priority(); libevdev_set_log_priority(LIBEVDEV_LOG_DEBUG); libevdev_set_log_function(logfunc, logdata); /* error for NULL device */ libevdev_set_device_log_function(NULL, NULL, LIBEVDEV_LOG_ERROR, NULL); ck_assert_int_eq(log_fn_called, 1); /* error for NULL device */ libevdev_set_device_log_function(NULL, devlogfunc, LIBEVDEV_LOG_ERROR, NULL); ck_assert_int_eq(log_fn_called, 2); log_fn_called = 0; dev = libevdev_new(); ck_assert(dev != NULL); libevdev_set_device_log_function(dev, NULL, LIBEVDEV_LOG_ERROR, NULL); /* libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, NULL) should trigger a log message. */ /* expect global handler triggered */ libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, NULL); ck_assert_int_eq(log_fn_called, 1); ck_assert_int_eq(dev_log_fn_called, 0); /* expect device handler triggered */ libevdev_set_device_log_function(dev, devlogfunc, LIBEVDEV_LOG_ERROR, dev); libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, NULL); ck_assert_int_eq(log_fn_called, 1); ck_assert_int_eq(dev_log_fn_called, 1); /* device handler set, but priority filters. don't expect any log handler to be called. we don't have any log msgs > ERROR at the moment, so test it by setting an invalid priority. */ libevdev_set_device_log_function(dev, devlogfunc, LIBEVDEV_LOG_ERROR - 1, dev); libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, NULL); ck_assert_int_eq(log_fn_called, 1); ck_assert_int_eq(dev_log_fn_called, 1); libevdev_free(dev); log_fn_called = 0; libevdev_set_log_priority(old); libevdev_set_log_function(test_logfunc_abort_on_error, NULL); } END_TEST START_TEST(test_device_init) { struct uinput_device* uidev; struct libevdev *dev; int rc; rc = uinput_device_new_with_events(&uidev, TEST_DEVICE_NAME, DEFAULT_IDS, EV_SYN, SYN_REPORT, EV_REL, REL_X, EV_REL, REL_Y, EV_REL, REL_WHEEL, EV_KEY, BTN_LEFT, EV_KEY, BTN_MIDDLE, EV_KEY, BTN_RIGHT, -1); ck_assert_msg(rc == 0, "Failed to create uinput device: %s", strerror(-rc)); dev = libevdev_new(); ck_assert(dev != NULL); rc = libevdev_set_fd(dev, uinput_device_get_fd(uidev)); ck_assert_msg(rc == 0, "Failed to init device: %s", strerror(-rc));; uinput_device_free(uidev); libevdev_free(dev); } END_TEST START_TEST(test_device_init_from_fd) { struct uinput_device* uidev; struct libevdev *dev; int rc; rc = uinput_device_new_with_events(&uidev, TEST_DEVICE_NAME, DEFAULT_IDS, EV_SYN, SYN_REPORT, EV_REL, REL_X, EV_REL, REL_Y, EV_REL, REL_WHEEL, EV_KEY, BTN_LEFT, EV_KEY, BTN_MIDDLE, EV_KEY, BTN_RIGHT, -1); ck_assert_msg(rc == 0, "Failed to create uinput device: %s", strerror(-rc)); rc = libevdev_new_from_fd(uinput_device_get_fd(uidev), &dev); ck_assert_msg(rc == 0, "Failed to init device: %s", strerror(-rc));; uinput_device_free(uidev); libevdev_free(dev); } END_TEST START_TEST(test_device_grab) { struct uinput_device* uidev; struct libevdev *dev; int rc; test_create_device(&uidev, &dev, EV_SYN, SYN_REPORT, EV_REL, REL_X, EV_REL, REL_Y, EV_REL, REL_WHEEL, EV_KEY, BTN_LEFT, EV_KEY, BTN_MIDDLE, EV_KEY, BTN_RIGHT, -1); libevdev_set_log_function(test_logfunc_ignore_error, NULL); rc = libevdev_grab(dev, 0); ck_assert_int_eq(rc, -EINVAL); rc = libevdev_grab(dev, 1); ck_assert_int_eq(rc, -EINVAL); libevdev_set_log_function(test_logfunc_abort_on_error, NULL); rc = libevdev_grab(dev, LIBEVDEV_UNGRAB); ck_assert_int_eq(rc, 0); rc = libevdev_grab(dev, LIBEVDEV_GRAB); ck_assert_int_eq(rc, 0); rc = libevdev_grab(dev, LIBEVDEV_GRAB); ck_assert_int_eq(rc, 0); rc = libevdev_grab(dev, LIBEVDEV_UNGRAB); ck_assert_int_eq(rc, 0); uinput_device_free(uidev); libevdev_free(dev); } END_TEST START_TEST(test_device_grab_invalid_fd) { struct uinput_device* uidev; struct libevdev *dev; int rc; libevdev_set_log_function(test_logfunc_ignore_error, NULL); dev = libevdev_new(); rc = libevdev_grab(dev, 0); ck_assert_int_eq(rc, -EBADF); libevdev_free(dev); test_create_device(&uidev, &dev, EV_SYN, SYN_REPORT, EV_REL, REL_X, EV_REL, REL_Y, EV_REL, REL_WHEEL, EV_KEY, BTN_LEFT, EV_KEY, BTN_MIDDLE, EV_KEY, BTN_RIGHT, -1); libevdev_change_fd(dev, -2); rc = libevdev_grab(dev, 0); ck_assert_int_eq(rc, -EBADF); libevdev_set_log_function(test_logfunc_abort_on_error, NULL); uinput_device_free(uidev); libevdev_free(dev); } END_TEST START_TEST(test_device_grab_change_fd) { struct libevdev_uinput *uidev; struct libevdev *dev, *other; struct input_event e; int rc; int other_fd; int dev_fd; dev = libevdev_new(); libevdev_set_name(dev, "libevdev test device"); libevdev_enable_event_code(dev, EV_REL, REL_X, NULL); libevdev_enable_event_code(dev, EV_REL, REL_Y, NULL); libevdev_enable_event_code(dev, EV_KEY, BTN_LEFT, NULL); rc = libevdev_uinput_create_from_device(dev, LIBEVDEV_UINPUT_OPEN_MANAGED, &uidev); ck_assert_int_eq(rc, 0); libevdev_free(dev); dev_fd = open(libevdev_uinput_get_devnode(uidev), O_RDONLY|O_NONBLOCK); ck_assert_int_ne(dev_fd, -1); rc = libevdev_new_from_fd(dev_fd, &dev); ck_assert_int_eq(rc, 0); other_fd = open(libevdev_uinput_get_devnode(uidev), O_RDONLY|O_NONBLOCK); ck_assert_int_ne(other_fd, -1); rc = libevdev_new_from_fd(other_fd, &other); ck_assert_int_eq(rc, 0); /* check we're getting the events before the grab */ libevdev_uinput_write_event(uidev, EV_REL, REL_X, -1); libevdev_uinput_write_event(uidev, EV_SYN, SYN_REPORT, 0); rc = libevdev_next_event(other, LIBEVDEV_READ_FLAG_NORMAL, &e); ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS); rc = libevdev_next_event(other, LIBEVDEV_READ_FLAG_NORMAL, &e); ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS); rc = libevdev_next_event(other, LIBEVDEV_READ_FLAG_NORMAL, &e); ck_assert_int_eq(rc, -EAGAIN); /* no events after the grab */ rc = libevdev_grab(dev, LIBEVDEV_GRAB); ck_assert_int_eq(rc, 0); libevdev_uinput_write_event(uidev, EV_REL, REL_X, -1); libevdev_uinput_write_event(uidev, EV_SYN, SYN_REPORT, 0); rc = libevdev_grab(dev, LIBEVDEV_GRAB); ck_assert_int_eq(rc, 0); rc = libevdev_next_event(other, LIBEVDEV_READ_FLAG_NORMAL, &e); ck_assert_int_eq(rc, -EAGAIN); /* swapping the fd removes the grab */ close(dev_fd); dev_fd = open(libevdev_uinput_get_devnode(uidev), O_RDONLY|O_NONBLOCK); ck_assert_int_ne(dev_fd, -1); rc = libevdev_change_fd(dev, dev_fd); ck_assert_int_eq(rc, 0); /* check we're getting the events again */ libevdev_uinput_write_event(uidev, EV_REL, REL_X, -1); libevdev_uinput_write_event(uidev, EV_SYN, SYN_REPORT, 0); rc = libevdev_next_event(other, LIBEVDEV_READ_FLAG_NORMAL, &e); ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS); rc = libevdev_next_event(other, LIBEVDEV_READ_FLAG_NORMAL, &e); ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS); rc = libevdev_next_event(other, LIBEVDEV_READ_FLAG_NORMAL, &e); ck_assert_int_eq(rc, -EAGAIN); /* no events after the grab */ rc = libevdev_grab(dev, LIBEVDEV_GRAB); ck_assert_int_eq(rc, 0); libevdev_uinput_write_event(uidev, EV_REL, REL_X, -1); libevdev_uinput_write_event(uidev, EV_SYN, SYN_REPORT, 0); rc = libevdev_next_event(other, LIBEVDEV_READ_FLAG_NORMAL, &e); ck_assert_int_eq(rc, -EAGAIN); libevdev_uinput_destroy(uidev); libevdev_free(dev); libevdev_free(other); close(dev_fd); close(other_fd); } END_TEST START_TEST(test_set_clock_id) { struct uinput_device* uidev; struct libevdev *dev; int clockid; int rc; test_create_device(&uidev, &dev, EV_SYN, SYN_REPORT, EV_REL, REL_X, EV_REL, REL_Y, EV_REL, REL_WHEEL, EV_KEY, BTN_LEFT, EV_KEY, BTN_MIDDLE, EV_KEY, BTN_RIGHT, -1); rc = libevdev_set_clock_id(dev, CLOCK_REALTIME); ck_assert_int_eq(rc, 0); rc = libevdev_set_clock_id(dev, CLOCK_MONOTONIC); ck_assert_int_eq(rc, 0); #ifdef __FreeBSD__ clockid = CLOCK_MONOTONIC_FAST; #else clockid = CLOCK_MONOTONIC_RAW; #endif rc = libevdev_set_clock_id(dev, clockid); ck_assert_int_eq(rc, -EINVAL); uinput_device_free(uidev); libevdev_free(dev); } END_TEST START_TEST(test_set_clock_id_invalid_fd) { struct uinput_device* uidev; struct libevdev *dev; int rc; libevdev_set_log_function(test_logfunc_ignore_error, NULL); dev = libevdev_new(); rc = libevdev_set_clock_id(dev, CLOCK_MONOTONIC); ck_assert_int_eq(rc, -EBADF); libevdev_free(dev); test_create_device(&uidev, &dev, EV_SYN, SYN_REPORT, EV_REL, REL_X, EV_REL, REL_Y, EV_REL, REL_WHEEL, EV_KEY, BTN_LEFT, EV_KEY, BTN_MIDDLE, EV_KEY, BTN_RIGHT, -1); libevdev_change_fd(dev, -2); rc = libevdev_set_clock_id(dev, CLOCK_MONOTONIC); ck_assert_int_eq(rc, -EBADF); uinput_device_free(uidev); libevdev_free(dev); } END_TEST START_TEST(test_clock_id_events) { struct uinput_device* uidev; struct libevdev *dev, *dev2; int rc, fd; struct input_event ev1, ev2; struct timespec t1_real, t2_real; struct timespec t1_mono, t2_mono; int64_t t1, t2; test_create_device(&uidev, &dev, EV_SYN, SYN_REPORT, EV_REL, REL_X, EV_REL, REL_Y, EV_REL, REL_WHEEL, EV_KEY, BTN_LEFT, EV_KEY, BTN_MIDDLE, EV_KEY, BTN_RIGHT, -1); fd = open(uinput_device_get_devnode(uidev), O_RDONLY); ck_assert_int_gt(fd, -1); rc = libevdev_new_from_fd(fd, &dev2); ck_assert_msg(rc == 0, "Failed to create second device: %s", strerror(-rc)); rc = libevdev_set_clock_id(dev2, CLOCK_MONOTONIC); ck_assert_int_eq(rc, 0); clock_gettime(CLOCK_REALTIME, &t1_real); clock_gettime(CLOCK_MONOTONIC, &t1_mono); uinput_device_event(uidev, EV_REL, REL_X, 1); uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0); clock_gettime(CLOCK_REALTIME, &t2_real); clock_gettime(CLOCK_MONOTONIC, &t2_mono); rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev1); ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS); rc = libevdev_next_event(dev2, LIBEVDEV_READ_FLAG_NORMAL, &ev2); ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS); ck_assert_int_eq(ev1.type, ev2.type); ck_assert_int_eq(ev1.code, ev2.code); ck_assert_int_eq(ev1.value, ev2.value); t1 = ev1.input_event_sec * 1000000LL + ev1.input_event_usec; t2 = ev2.input_event_sec * 1000000LL + ev2.input_event_usec; ck_assert_int_ne(t1, t2); ck_assert_int_ge(ev1.input_event_sec, t1_real.tv_sec); ck_assert_int_ge(ev1.input_event_usec, t1_real.tv_nsec/1000); ck_assert_int_le(ev1.input_event_sec, t2_real.tv_sec); ck_assert_int_le(ev1.input_event_usec, t2_real.tv_nsec/1000); ck_assert_int_ge(ev2.input_event_sec, t1_mono.tv_sec); ck_assert_int_ge(ev2.input_event_usec, t1_mono.tv_nsec/1000); ck_assert_int_le(ev2.input_event_sec, t2_mono.tv_sec); ck_assert_int_le(ev2.input_event_usec, t2_mono.tv_nsec/1000); uinput_device_free(uidev); libevdev_free(dev); libevdev_free(dev2); close(fd); } END_TEST TEST_SUITE_ROOT_PRIVILEGES(libevdev_init_test) { Suite *s = suite_create("libevdev init tests"); add_test(s, test_new_device); add_test(s, test_free_device); add_test(s, test_init_from_invalid_fd); add_test(s, test_init_and_change_fd); add_test(s, test_log_init); add_test(s, test_log_priority); add_test(s, test_log_set_get_priority); add_test(s, test_log_default_priority); add_test(s, test_log_data); add_test(s, test_device_log_init); add_test(s, test_device_init); add_test(s, test_device_init_from_fd); add_test(s, test_device_grab); add_test(s, test_device_grab_invalid_fd); add_test(s, test_device_grab_change_fd); add_test(s, test_set_clock_id); add_test(s, test_set_clock_id_invalid_fd); add_test(s, test_clock_id_events); return s; }