diff --git a/configure.ac b/configure.ac index 39e42d0f..803d5733 100644 --- a/configure.ac +++ b/configure.ac @@ -34,6 +34,7 @@ LIBINPUT_LT_VERSION=8:0:1 AC_SUBST(LIBINPUT_LT_VERSION) AM_SILENT_RULES([yes]) +AC_USE_SYSTEM_EXTENSIONS # Check for programs AC_PROG_CC_C99 diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 94cbc136..ae37ab17 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -1107,12 +1107,6 @@ tp_tag_device(struct evdev_device *device, if (udev_device_get_property_value(udev_device, "TOUCHPAD_HAS_TRACKPOINT_BUTTONS")) device->tags |= EVDEV_TAG_TOUCHPAD_TRACKPOINT; - - /* Magic version tag: used by the litest device. Should never be set - * in real life but allows us to test for these features without - * requiring custom udev rules during make check */ - if (libevdev_get_id_version(device->evdev) == 0xfffa) - device->tags |= EVDEV_TAG_TOUCHPAD_TRACKPOINT; } static struct evdev_dispatch_interface tp_interface = { diff --git a/test/litest-int.h b/test/litest-int.h index 95bc2483..12746f1b 100644 --- a/test/litest-int.h +++ b/test/litest-int.h @@ -69,6 +69,8 @@ struct litest_test_device { */ struct input_absinfo *absinfo; struct litest_device_interface *interface; + + const char *udev_rule; }; struct litest_device_interface { diff --git a/test/litest-synaptics-x1-carbon-3rd.c b/test/litest-synaptics-x1-carbon-3rd.c index 8be29e03..67d6f467 100644 --- a/test/litest-synaptics-x1-carbon-3rd.c +++ b/test/litest-synaptics-x1-carbon-3rd.c @@ -65,7 +65,6 @@ static struct input_id input_id = { .bustype = 0x11, .vendor = 0x2, .product = 0x7, - .version = 0xfffa, /* Magic value, used to detect this test device */ }; static int events[] = { @@ -97,6 +96,16 @@ static struct input_absinfo absinfo[] = { { .value = -1 } }; +static const char udev_rule[] = +"ACTION==\"remove\", GOTO=\"touchpad_end\"\n" +"KERNEL!=\"event*\", GOTO=\"touchpad_end\"\n" +"ENV{ID_INPUT_TOUCHPAD}==\"\", GOTO=\"touchpad_end\"\n" +"\n" +"ATTRS{name}==\"litest*X1C3rd*\",\\\n" +" ENV{TOUCHPAD_HAS_TRACKPOINT_BUTTONS}=\"1\"\n" +"\n" +"LABEL=\"touchpad_end\""; + struct litest_test_device litest_synaptics_carbon3rd_device = { .type = LITEST_SYNAPTICS_TRACKPOINT_BUTTONS, .features = LITEST_TOUCHPAD | LITEST_CLICKPAD | LITEST_BUTTON, @@ -104,8 +113,9 @@ struct litest_test_device litest_synaptics_carbon3rd_device = { .setup = litest_synaptics_carbon3rd_setup, .interface = &interface, - .name = "SynPS/2 Synaptics TouchPad", + .name = "SynPS/2 Synaptics TouchPad X1C3rd", .id = &input_id, .events = events, .absinfo = absinfo, + .udev_rule = udev_rule, }; diff --git a/test/litest.c b/test/litest.c index 2a336e9a..b220b2fe 100644 --- a/test/litest.c +++ b/test/litest.c @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -44,6 +45,9 @@ #include "litest-int.h" #include "libinput-util.h" +#define UDEV_RULES_D "/run/udev/rules.d" +#define UDEV_RULE_PREFIX "99-litest-" + static int in_debugger = -1; static int verbose = 0; @@ -114,6 +118,55 @@ struct litest_test_device* devices[] = { static struct list all_tests; +static void +litest_reload_udev_rules(void) +{ + system("udevadm control --reload-rules"); +} + +static int +litest_udev_rule_filter(const struct dirent *entry) +{ + return strncmp(entry->d_name, + UDEV_RULE_PREFIX, + strlen(UDEV_RULE_PREFIX)) == 0; +} + +static void +litest_drop_udev_rules(void) +{ + int n; + int rc; + struct dirent **entries; + char path[PATH_MAX]; + + n = scandir(UDEV_RULES_D, + &entries, + litest_udev_rule_filter, + alphasort); + if (n < 0) + return; + + while (n--) { + rc = snprintf(path, sizeof(path), + "%s/%s", + UDEV_RULES_D, + entries[n]->d_name); + if (rc > 0 && + (size_t)rc == strlen(UDEV_RULES_D) + + strlen(entries[n]->d_name) + 1) + unlink(path); + else + fprintf(stderr, + "Failed to delete %s. Remaining tests are unreliable\n", + entries[n]->d_name); + free(entries[n]); + } + free(entries); + + litest_reload_udev_rules(); +} + static void litest_add_tcase_for_device(struct suite *suite, void *func, @@ -134,6 +187,13 @@ litest_add_tcase_for_device(struct suite *suite, t->name = strdup(test_name); t->tc = tcase_create(test_name); list_insert(&suite->tests, &t->node); + /* we can't guarantee that we clean up properly if a test fails, the + udev rules used for a previous test may still be in place. Add an + unchecked fixture to always clean up all rules before/after a + test case completes */ + tcase_add_unchecked_fixture(t->tc, + litest_drop_udev_rules, + litest_drop_udev_rules); tcase_add_checked_fixture(t->tc, dev->setup, dev->teardown ? dev->teardown : litest_generic_device_teardown); tcase_add_test(t->tc, func); @@ -464,6 +524,40 @@ merge_events(const int *orig, const int *override) return events; } +static char * +litest_init_udev_rules(struct litest_test_device *dev) +{ + int rc; + FILE *f; + char *path; + + if (!dev->udev_rule) + return NULL; + + rc = mkdir(UDEV_RULES_D, 0755); + if (rc == -1 && errno != EEXIST) + ck_abort_msg("Failed to create udev rules directory (%s)\n", + strerror(errno)); + + rc = asprintf(&path, + "%s/%s%s.rules", + UDEV_RULES_D, + UDEV_RULE_PREFIX, + dev->shortname); + ck_assert_int_eq(rc, + strlen(UDEV_RULES_D) + + strlen(UDEV_RULE_PREFIX) + + strlen(dev->shortname) + 7); + f = fopen(path, "w"); + ck_assert_notnull(f); + ck_assert_int_ge(fputs(dev->udev_rule, f), 0); + fclose(f); + + litest_reload_udev_rules(); + + return path; +} + static struct litest_device * litest_create(enum litest_device_type which, const char *name_override, @@ -477,6 +571,7 @@ litest_create(enum litest_device_type which, const struct input_id *id; struct input_absinfo *abs; int *events; + char *udev_file; dev = devices; while (*dev) { @@ -491,12 +586,17 @@ litest_create(enum litest_device_type which, d = zalloc(sizeof(*d)); ck_assert(d != NULL); + udev_file = litest_init_udev_rules(*dev); + /* device has custom create method */ if ((*dev)->create) { (*dev)->create(d); - if (abs_override || events_override) + if (abs_override || events_override) { + if (udev_file) + unlink(udev_file); ck_abort_msg("Custom create cannot" "be overridden"); + } return d; } @@ -511,6 +611,7 @@ litest_create(enum litest_device_type which, abs, events); d->interface = (*dev)->interface; + d->udev_rule_file = udev_file; free(abs); free(events); @@ -629,6 +730,12 @@ litest_delete_device(struct litest_device *d) if (!d) return; + if (d->udev_rule_file) { + unlink(d->udev_rule_file); + free(d->udev_rule_file); + d->udev_rule_file = NULL; + } + libinput_device_unref(d->libinput_device); libinput_path_remove_device(d->libinput_device); if (d->owns_context) diff --git a/test/litest.h b/test/litest.h index 0625a71e..60ec4587 100644 --- a/test/litest.h +++ b/test/litest.h @@ -84,6 +84,8 @@ struct litest_device { bool skip_ev_syn; void *private; /* device-specific data */ + + char *udev_rule_file; }; struct libinput *litest_create_context(void);