[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