[PATCH libevdev 1/4] test: add kernel tests for the EVIOCSMASK behavior
Peter Hutterer
peter.hutterer at who-t.net
Thu Apr 21 21:59:40 UTC 2016
The mask behavior is a bit unintuitive, while the mask is technically correct
and set as expected we are not guaranteed that anything higher than the
kernel's FOO_CNT is actually zeroed out. The current implementation always
sets all bits to 1 up to the long boundary.
Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
test/test-kernel.c | 299 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 299 insertions(+)
diff --git a/test/test-kernel.c b/test/test-kernel.c
index 6fba605..984bf88 100644
--- a/test/test-kernel.c
+++ b/test/test-kernel.c
@@ -32,6 +32,7 @@
#include <linux/input.h>
#include <libevdev/libevdev.h>
+#include <libevdev/libevdev-util.h>
#include <libevdev/libevdev-uinput.h>
#include "test-common.h"
@@ -160,6 +161,297 @@ out:
}
END_TEST
+START_TEST(test_mask)
+{
+ struct uinput_device* uidev;
+ struct libevdev *dev, *dev2 = NULL;
+ int rc, fd;
+ unsigned long m[NLONGS(KEY_CNT)] = {0};
+ struct input_mask mask = {
+ mask.type = EV_REL,
+ mask.codes_size = sizeof(m),
+ mask.codes_ptr = (uint64_t)m,
+ };
+ int code;
+
+ 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|O_NONBLOCK);
+ ck_assert_int_gt(fd, -1);
+
+ rc = libevdev_new_from_fd(fd, &dev2);
+ ck_assert_int_eq(rc, 0);
+
+ rc = ioctl(fd, EVIOCGMASK, &mask);
+ if (rc == -1 && errno == EINVAL) {
+ fprintf(stderr, "WARNING: skipping EVIOCGMASK test, not suported by current kernel\n");
+ goto out;
+ }
+ ck_assert_int_eq(rc, 0);
+
+ /* By default all bits are set */
+ for (code = 0; code < REL_CNT; code++)
+ ck_assert(bit_is_set(m, code));
+
+ clear_bit(m, REL_Y);
+
+ rc = ioctl(fd, EVIOCSMASK, &mask);
+ ck_assert_int_eq(rc, 0);
+
+ memset(m, 0, sizeof(m));
+ rc = ioctl(fd, EVIOCGMASK, &mask);
+ ck_assert_int_eq(rc, 0);
+
+ ck_assert(!bit_is_set(m, REL_Y));
+ for (code = 0; code < REL_CNT; code++) {
+ if (code == REL_Y)
+ continue;
+ ck_assert(bit_is_set(m, code));
+ }
+
+out:
+ uinput_device_free(uidev);
+ libevdev_free(dev);
+ libevdev_free(dev2);
+ close(fd);
+}
+END_TEST
+
+START_TEST(test_mask_events)
+{
+ struct uinput_device* uidev;
+ struct libevdev *dev, *dev2 = NULL;
+ int rc, fd;
+ unsigned long m[NLONGS(KEY_CNT)];
+ struct input_mask mask = {
+ mask.type = EV_REL,
+ mask.codes_size = sizeof(m),
+ mask.codes_ptr = (uint64_t)m,
+ };
+ struct input_event ev;
+ int i;
+
+ 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|O_NONBLOCK);
+ ck_assert_int_gt(fd, -1);
+
+ rc = libevdev_new_from_fd(fd, &dev2);
+ ck_assert_int_eq(rc, 0);
+
+ for (i = 0; i < ARRAY_LENGTH(m); i++)
+ m[i] = -1;
+ clear_bit(m, REL_Y);
+
+ rc = ioctl(fd, EVIOCSMASK, &mask);
+ if (rc == -1 && errno == EINVAL) {
+ fprintf(stderr, "WARNING: skipping EVIOCGMASK test, not suported by current kernel\n");
+ goto out;
+ }
+ ck_assert_int_eq(rc, 0);
+
+ /* we disabled the kernel, not the libevdev device */
+ ck_assert(libevdev_has_event_code(dev2, EV_REL, REL_Y));
+
+ uinput_device_event(uidev, EV_REL, REL_X, 1);
+ uinput_device_event(uidev, EV_REL, REL_Y, 1);
+ uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0);
+
+ rc = libevdev_next_event(dev2, LIBEVDEV_READ_FLAG_NORMAL, &ev);
+ ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS);
+ ck_assert(libevdev_event_is_code(&ev, EV_REL, REL_X));
+
+ rc = libevdev_next_event(dev2, LIBEVDEV_READ_FLAG_NORMAL, &ev);
+ ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS);
+ ck_assert(libevdev_event_is_code(&ev, EV_SYN, SYN_REPORT));
+
+out:
+ uinput_device_free(uidev);
+ libevdev_free(dev);
+ libevdev_free(dev2);
+ close(fd);
+}
+END_TEST
+
+START_TEST(test_mask_out_of_range)
+{
+ struct uinput_device* uidev;
+ struct libevdev *dev, *dev2 = NULL;
+ int rc, fd;
+ const unsigned int fake_max = REL_CNT + 13;
+ unsigned long m[NLONGS(KEY_CNT)] = {0};
+ struct input_mask mask = {
+ mask.type = EV_REL,
+ mask.codes_size = sizeof(m),
+ mask.codes_ptr = (uint64_t)m,
+ };
+ int code;
+ _Static_assert(KEY_CNT >= REL_CNT + 13, "KEY_CNT is too low");
+
+ if (sizeof(unsigned long) < 64) {
+ fprintf(stderr, "WARNING: skipping test_mask_out_of_range(), requires sizeof(long) == 64\n");
+ return;
+ }
+
+ 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|O_NONBLOCK);
+ ck_assert_int_gt(fd, -1);
+
+ rc = libevdev_new_from_fd(fd, &dev2);
+ ck_assert_int_eq(rc, 0);
+
+ rc = ioctl(fd, EVIOCGMASK, &mask);
+ if (rc == -1 && errno == EINVAL) {
+ fprintf(stderr, "WARNING: skipping EVIOCGMASK test, not suported by current kernel\n");
+ goto out;
+ }
+ ck_assert_int_eq(rc, 0);
+
+ /* By default all known bits are set.
+ *
+ * The kernel's mask handling works per long (not bit), so the mask
+ * we get is actually initialized up to the next long boundary. If
+ * this test fails here, check if the kernel implementation changed
+ * first.
+ */
+ for (code = 0; code < fake_max; code++)
+ ck_assert(bit_is_set(m, code));
+
+ clear_bit(m, REL_Y);
+
+ rc = ioctl(fd, EVIOCSMASK, &mask);
+ ck_assert_int_eq(rc, 0);
+
+ memset(m, 0, sizeof(m));
+ rc = ioctl(fd, EVIOCGMASK, &mask);
+ ck_assert_int_eq(rc, 0);
+
+ ck_assert(!bit_is_set(m, REL_Y));
+ for (code = 0; code < fake_max; code++) {
+ if (code == REL_Y)
+ continue;
+ ck_assert(bit_is_set(m, code));
+ }
+
+out:
+ uinput_device_free(uidev);
+ libevdev_free(dev);
+ libevdev_free(dev2);
+ close(fd);
+}
+END_TEST
+
+START_TEST(test_mask_out_of_high_range)
+{
+ struct uinput_device* uidev;
+ struct libevdev *dev, *dev2 = NULL;
+ int rc, fd;
+ const unsigned int fake_max = REL_CNT + 64; /* runs into the next long */
+ unsigned long m[NLONGS(KEY_CNT)];
+ struct input_mask mask = {
+ mask.type = EV_REL,
+ mask.codes_size = sizeof(m),
+ mask.codes_ptr = (uint64_t)m,
+ };
+ int code;
+ int i;
+ _Static_assert(KEY_CNT >= REL_CNT + 64, "KEY_CNT is too low");
+
+ if (sizeof(unsigned long) < 64) {
+ fprintf(stderr, "WARNING: skipping test_mask_out_of_range(), requires sizeof(long) == 64\n");
+ return;
+ }
+
+ 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|O_NONBLOCK);
+ ck_assert_int_gt(fd, -1);
+
+ rc = libevdev_new_from_fd(fd, &dev2);
+ ck_assert_int_eq(rc, 0);
+
+ /* Set the mask to the inverse of what we expect back from the
+ * kernel to make sure all bits get touched. */
+ memset(m, 0, sizeof(m));
+ for (i = 0; i < NLONGS(REL_CNT); i++)
+ m[i] = -1;
+
+ rc = ioctl(fd, EVIOCGMASK, &mask);
+ if (rc == -1 && errno == EINVAL) {
+ fprintf(stderr, "WARNING: skipping EVIOCGMASK test, not suported by current kernel\n");
+ goto out;
+ }
+ ck_assert_int_eq(rc, 0);
+
+ /* By default all known bits are set.
+ *
+ * The kernel's mask handling works per long (not bit), so the mask
+ * we get is actually initialized up to the next long boundary. If
+ * this test fails here, check if the kernel implementation changed
+ * first.
+ */
+ for (code = 0; code < LONG_BITS; code++)
+ ck_assert(bit_is_set(m, code));
+ for (code = LONG_BITS; code < fake_max; code++)
+ ck_assert(!bit_is_set(m, code));
+
+ clear_bit(m, REL_Y);
+ clear_bit(m, REL_CNT + 2);
+
+ rc = ioctl(fd, EVIOCSMASK, &mask);
+ ck_assert_int_eq(rc, 0);
+
+ memset(m, 0, sizeof(m));
+ rc = ioctl(fd, EVIOCGMASK, &mask);
+ ck_assert_int_eq(rc, 0);
+
+ ck_assert(!bit_is_set(m, REL_Y));
+ ck_assert(!bit_is_set(m, REL_CNT + 2));
+ for (code = 0; code < LONG_BITS; code++) {
+ if (code == REL_Y || code == REL_CNT + 2)
+ continue;
+ ck_assert(bit_is_set(m, code));
+ }
+ for (code = LONG_BITS; code < fake_max; code++)
+ ck_assert(!bit_is_set(m, code));
+
+out:
+ uinput_device_free(uidev);
+ libevdev_free(dev);
+ libevdev_free(dev2);
+ close(fd);
+}
+END_TEST
+
int main(int argc, char **argv)
{
SRunner *sr;
@@ -175,6 +467,13 @@ int main(int argc, char **argv)
tcase_add_test(tc, test_revoke_fail_after);
suite_add_tcase(s, tc);
+ tc = tcase_create("EVIOCSMASK");
+ tcase_add_test(tc, test_mask);
+ tcase_add_test(tc, test_mask_events);
+ tcase_add_test(tc, test_mask_out_of_range);
+ tcase_add_test(tc, test_mask_out_of_high_range);
+ suite_add_tcase(s, tc);
+
sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
--
2.7.3
More information about the Input-tools
mailing list