From 21356cca571dc6222a78494b0ddd9b5bf745395e Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 26 Jul 2013 14:35:54 +1000 Subject: [PATCH] test: add uinput creation tests Signed-off-by: Peter Hutterer Reviewed-by: Benjamin Tissoires --- test/Makefile.am | 1 + test/test-main.c | 2 + test/test-uinput.c | 345 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 348 insertions(+) create mode 100644 test/test-uinput.c diff --git a/test/Makefile.am b/test/Makefile.am index d3642c6..8454329 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -26,6 +26,7 @@ test_libevdev_SOURCES = \ test-libevdev-has-event.c \ test-int-queue.c \ test-libevdev-events.c \ + test-uinput.c \ $(common_sources) test_libevdev_LDADD = $(CHECK_LIBS) $(GCOV_LDFLAGS) diff --git a/test/test-main.c b/test/test-main.c index 9434808..c6efaf6 100644 --- a/test/test-main.c +++ b/test/test-main.c @@ -28,6 +28,7 @@ extern Suite *libevdev_init_test(void); extern Suite *queue_suite(void); extern Suite *libevdev_has_event_test(void); extern Suite *libevdev_events(void); +extern Suite *uinput_suite(void); int main(int argc, char **argv) { @@ -38,6 +39,7 @@ int main(int argc, char **argv) srunner_add_suite(sr, libevdev_init_test()); srunner_add_suite(sr, queue_suite()); srunner_add_suite(sr, event_name_suite()); + srunner_add_suite(sr, uinput_suite()); srunner_run_all(sr, CK_NORMAL); failed = srunner_ntests_failed(sr); diff --git a/test/test-uinput.c b/test/test-uinput.c new file mode 100644 index 0000000..79f2229 --- /dev/null +++ b/test/test-uinput.c @@ -0,0 +1,345 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include "test-common.h" +#define UINPUT_NODE "/dev/uinput" + +START_TEST(test_uinput_create_device) +{ + struct libevdev *dev, *dev2; + struct libevdev_uinput *uidev; + int fd, uinput_fd; + unsigned int type, code; + int rc; + const char *devnode; + + dev = libevdev_new(); + ck_assert(dev != NULL); + libevdev_set_name(dev, TEST_DEVICE_NAME); + libevdev_enable_event_type(dev, EV_SYN); + libevdev_enable_event_type(dev, EV_REL); + libevdev_enable_event_code(dev, EV_REL, REL_X, NULL); + libevdev_enable_event_code(dev, EV_REL, REL_Y, NULL); + + rc = libevdev_uinput_create_from_device(dev, LIBEVDEV_UINPUT_OPEN_MANAGED, &uidev); + ck_assert_int_eq(rc, 0); + ck_assert(uidev != NULL); + + uinput_fd = libevdev_uinput_get_fd(uidev); + ck_assert_int_gt(uinput_fd, -1); + + devnode = libevdev_uinput_get_devnode(uidev); + ck_assert(devnode != NULL); + + fd = open(devnode, O_RDONLY); + ck_assert_int_gt(fd, -1); + rc = libevdev_new_from_fd(fd, &dev2); + ck_assert_int_eq(rc, 0); + + for (type = 0; type < EV_MAX; type++) { + int max = libevdev_get_event_type_max(type); + if (max == -1) + continue; + + for (code = 0; code < max; code++) { + ck_assert_int_eq(libevdev_has_event_code(dev, type, code), + libevdev_has_event_code(dev2, type, code)); + } + } + + libevdev_free(dev); + libevdev_free(dev2); + libevdev_uinput_destroy(uidev); + close(fd); + + /* uinput fd is managed, so make sure it did get closed */ + ck_assert_int_eq(close(uinput_fd), -1); + ck_assert_int_eq(errno, EBADF); + +} +END_TEST + +START_TEST(test_uinput_create_device_invalid) +{ + struct libevdev *dev; + struct libevdev_uinput *uidev = NULL; + int rc; + + dev = libevdev_new(); + ck_assert(dev != NULL); + libevdev_set_name(dev, TEST_DEVICE_NAME); + libevdev_enable_event_type(dev, EV_SYN); + libevdev_enable_event_type(dev, EV_REL); + libevdev_enable_event_code(dev, EV_REL, REL_X, NULL); + libevdev_enable_event_code(dev, EV_REL, REL_Y, NULL); + + rc = libevdev_uinput_create_from_device(dev, -1, &uidev); + ck_assert_int_eq(rc, -EBADF); + ck_assert(uidev == NULL); + + libevdev_free(dev); +} +END_TEST + +START_TEST(test_uinput_create_device_from_fd) +{ + struct libevdev *dev, *dev2; + struct libevdev_uinput *uidev; + int fd, fd2; + unsigned int type, code; + int rc; + const char *devnode; + + dev = libevdev_new(); + ck_assert(dev != NULL); + libevdev_set_name(dev, TEST_DEVICE_NAME); + libevdev_enable_event_type(dev, EV_SYN); + libevdev_enable_event_type(dev, EV_REL); + libevdev_enable_event_code(dev, EV_REL, REL_X, NULL); + libevdev_enable_event_code(dev, EV_REL, REL_Y, NULL); + + fd = open(UINPUT_NODE, O_RDWR); + ck_assert_int_gt(fd, -1); + + rc = libevdev_uinput_create_from_device(dev, fd, &uidev); + ck_assert_int_eq(rc, 0); + ck_assert(uidev != NULL); + + ck_assert_int_eq(libevdev_uinput_get_fd(uidev), fd); + + devnode = libevdev_uinput_get_devnode(uidev); + ck_assert(devnode != NULL); + + fd2 = open(devnode, O_RDONLY); + ck_assert_int_gt(fd2, -1); + rc = libevdev_new_from_fd(fd2, &dev2); + ck_assert_int_eq(rc, 0); + + for (type = 0; type < EV_MAX; type++) { + int max = libevdev_get_event_type_max(type); + if (max == -1) + continue; + + for (code = 0; code < max; code++) { + ck_assert_int_eq(libevdev_has_event_code(dev, type, code), + libevdev_has_event_code(dev2, type, code)); + } + } + + libevdev_free(dev); + libevdev_free(dev2); + libevdev_uinput_destroy(uidev); + close(fd); + close(fd2); +} +END_TEST + +START_TEST(test_uinput_check_syspath_time) +{ + struct libevdev *dev; + struct libevdev_uinput *uidev, *uidev2; + const char *syspath, *syspath2; + int fd, fd2; + int rc; + + dev = libevdev_new(); + ck_assert(dev != NULL); + libevdev_set_name(dev, TEST_DEVICE_NAME); + libevdev_enable_event_type(dev, EV_SYN); + libevdev_enable_event_type(dev, EV_REL); + libevdev_enable_event_code(dev, EV_REL, REL_X, NULL); + libevdev_enable_event_code(dev, EV_REL, REL_Y, NULL); + + fd = open(UINPUT_NODE, O_RDWR); + ck_assert_int_gt(fd, -1); + fd2 = open(UINPUT_NODE, O_RDWR); + ck_assert_int_gt(fd2, -1); + + rc = libevdev_uinput_create_from_device(dev, fd, &uidev); + ck_assert_int_eq(rc, 0); + + /* sleep for 1.5 seconds. sysfs resolution is 1 second, so + creating both devices without delay means + libevdev_uinput_get_syspath can't actually differ between + them. By waiting, we get different ctime for uidev and uidev2, + and exercise that part of the code. + */ + usleep(1500000); + + /* create a second one to test the syspath time filtering code */ + rc = libevdev_uinput_create_from_device(dev, fd2, &uidev2); + ck_assert_int_eq(rc, 0); + + syspath = libevdev_uinput_get_syspath(uidev); + ck_assert(syspath != NULL); + + /* get syspath twice returns same pointer */ + syspath2 = libevdev_uinput_get_syspath(uidev); + ck_assert(syspath == syspath2); + + /* second dev has different syspath */ + syspath2 = libevdev_uinput_get_syspath(uidev2); + ck_assert(strcmp(syspath, syspath2) != 0); + + libevdev_free(dev); + libevdev_uinput_destroy(uidev); + libevdev_uinput_destroy(uidev2); + + close(fd); + close(fd2); +} +END_TEST + +START_TEST(test_uinput_check_syspath_name) +{ + struct libevdev *dev; + struct libevdev_uinput *uidev, *uidev2; + const char *syspath, *syspath2; + int fd, fd2; + int rc; + + dev = libevdev_new(); + ck_assert(dev != NULL); + libevdev_set_name(dev, TEST_DEVICE_NAME); + libevdev_enable_event_type(dev, EV_SYN); + libevdev_enable_event_type(dev, EV_REL); + libevdev_enable_event_code(dev, EV_REL, REL_X, NULL); + libevdev_enable_event_code(dev, EV_REL, REL_Y, NULL); + + fd = open(UINPUT_NODE, O_RDWR); + ck_assert_int_gt(fd, -1); + fd2 = open(UINPUT_NODE, O_RDWR); + ck_assert_int_gt(fd2, -1); + + rc = libevdev_uinput_create_from_device(dev, fd, &uidev); + ck_assert_int_eq(rc, 0); + + /* create a second one to stress the syspath filtering code */ + libevdev_set_name(dev, TEST_DEVICE_NAME " 2"); + rc = libevdev_uinput_create_from_device(dev, fd2, &uidev2); + ck_assert_int_eq(rc, 0); + + syspath = libevdev_uinput_get_syspath(uidev); + ck_assert(syspath != NULL); + + /* get syspath twice returns same pointer */ + syspath2 = libevdev_uinput_get_syspath(uidev); + ck_assert(syspath == syspath2); + + /* second dev has different syspath */ + syspath2 = libevdev_uinput_get_syspath(uidev2); + ck_assert(strcmp(syspath, syspath2) != 0); + + libevdev_free(dev); + libevdev_uinput_destroy(uidev); + libevdev_uinput_destroy(uidev2); + + close(fd); + close(fd2); +} +END_TEST + +START_TEST(test_uinput_events) +{ + struct libevdev *dev; + struct libevdev_uinput *uidev; + int fd, fd2; + int rc; + const char *devnode; + int i; + const int nevents = 5; + struct input_event events[] = { {{0, 0}, EV_REL, REL_X, 1}, + {{0, 0}, EV_REL, REL_Y, -1}, + {{0, 0}, EV_SYN, SYN_REPORT, 0}, + {{0, 0}, EV_KEY, BTN_LEFT, 1}, + {{0, 0}, EV_SYN, SYN_REPORT, 0}}; + struct input_event events_read[nevents]; + + dev = libevdev_new(); + ck_assert(dev != NULL); + libevdev_set_name(dev, TEST_DEVICE_NAME); + libevdev_enable_event_type(dev, EV_SYN); + libevdev_enable_event_type(dev, EV_REL); + libevdev_enable_event_type(dev, EV_KEY); + 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); + + fd = open(UINPUT_NODE, O_RDWR); + ck_assert_int_gt(fd, -1); + + rc = libevdev_uinput_create_from_device(dev, fd, &uidev); + ck_assert_int_eq(rc, 0); + ck_assert(uidev != NULL); + + devnode = libevdev_uinput_get_devnode(uidev); + ck_assert(devnode != NULL); + + fd2 = open(devnode, O_RDONLY); + + for (i = 0; i < nevents; i++) + libevdev_uinput_write_event(uidev, events[i].type, events[i].code, events[i].value); + + rc = read(fd2, events_read, sizeof(events_read)); + ck_assert_int_eq(rc, sizeof(events_read)); + + for (i = 0; i < nevents; i++) { + ck_assert_int_eq(events[i].type, events_read[i].type); + ck_assert_int_eq(events[i].code, events_read[i].code); + ck_assert_int_eq(events[i].value, events_read[i].value); + } + + libevdev_free(dev); + libevdev_uinput_destroy(uidev); + close(fd); + close(fd2); +} +END_TEST + +Suite * +uinput_suite(void) +{ + Suite *s = suite_create("libevdev uinput device tests"); + + TCase *tc = tcase_create("device creation"); + tcase_add_test(tc, test_uinput_create_device); + tcase_add_test(tc, test_uinput_create_device_invalid); + tcase_add_test(tc, test_uinput_create_device_from_fd); + tcase_add_test(tc, test_uinput_check_syspath_time); + tcase_add_test(tc, test_uinput_check_syspath_name); + suite_add_tcase(s, tc); + + tc = tcase_create("device events"); + tcase_add_test(tc, test_uinput_events); + suite_add_tcase(s, tc); + + return s; +} -- 2.7.4