From b5f0f3c3f4efdeb4daadc597a2500e3adee7adca Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 23 Feb 2017 15:17:29 +0100 Subject: [PATCH] tests: wait for gpiochips before running tests We're currently using an ugly hack to check if gpiochip files are actually mockup chips. Remove it and use libudev instead to poll for uevents until all mockup GPIO devices appear in /dev. Signed-off-by: Bartosz Golaszewski --- configure.ac | 3 + tests/unit/Makefile.am | 2 +- tests/unit/gpiod-unit.c | 141 ++++++++++++++++++++-------------------- 3 files changed, 74 insertions(+), 72 deletions(-) diff --git a/configure.ac b/configure.ac index 4572987..9b79797 100644 --- a/configure.ac +++ b/configure.ac @@ -106,7 +106,10 @@ if test "x$with_tools" = xtrue then AC_CHECK_LIB([kmod], [kmod_module_probe_insert_module], [], [AC_MSG_ERROR([libkmod not found (needed to build tests])]) + AC_CHECK_LIB([udev], [udev_monitor_new_from_netlink], [], + [AC_MSG_ERROR([libudev not found (needed to build tests])]) AC_CHECK_HEADERS([libkmod.h], [], [HEADER_NOT_FOUND_TESTS([libkmod.h])]) + AC_CHECK_HEADERS([libudev.h], [], [HEADER_NOT_FOUND_TESTS([libudev.h])]) AC_CHECK_FUNC([qsort], [], [FUNC_NOT_FOUND_TESTS([qsort])]) fi diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index 3e216cc..6d72d16 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -8,7 +8,7 @@ AM_CFLAGS = -I$(top_srcdir)/include/ -include $(top_srcdir)/config.h AM_CFLAGS += -Wall -Wextra -g -LDADD = -lgpiod -L$(top_srcdir)/src/lib -lkmod +LDADD = -lgpiod -L$(top_srcdir)/src/lib -lkmod -ludev DEPENDENCIES = libgpiod.la check_PROGRAMS = gpiod-unit diff --git a/tests/unit/gpiod-unit.c b/tests/unit/gpiod-unit.c index 4e0e6cc..5512ae4 100644 --- a/tests/unit/gpiod-unit.c +++ b/tests/unit/gpiod-unit.c @@ -14,16 +14,15 @@ #include #include #include -#include -#include #include -#include -#include -#include +#include #include +#include #define NORETURN __attribute__((noreturn)) +static const char mockup_devpath[] = "/devices/platform/gpio-mockup/gpiochip"; + struct mockup_chip { char *path; char *name; @@ -34,7 +33,6 @@ struct test_context { struct mockup_chip **chips; size_t num_chips; bool test_failed; - struct timeval mod_loaded_ts; }; static struct { @@ -120,21 +118,6 @@ static char * xstrdup(const char *str) return ret; } -static GU_PRINTF(1, 2) char * xasprintf(const char *fmt, ...) -{ - int status; - va_list va; - char *ret; - - va_start(va, fmt); - status = vasprintf(&ret, fmt, va); - if (status < 0) - die_perr("asprintf"); - va_end(va); - - return ret; -} - static GU_PRINTF(2, 3) char * xcasprintf(size_t *count, const char *fmt, ...) { int status; @@ -296,7 +279,6 @@ static void test_load_module(struct gu_chip_descr *descr) modarg[modarg_len - 1] = '\0'; /* Remove the last comma. */ - gettimeofday(&globals.test_ctx.mod_loaded_ts, NULL); status = kmod_module_probe_insert_module(globals.module, 0, modarg, NULL, NULL, NULL); if (status) @@ -308,31 +290,6 @@ static void test_load_module(struct gu_chip_descr *descr) free(line_sizes); } -/* - * To see if given chip is a mockup chip, check if it was created after - * inserting the gpio-mockup module. It's not too clever, but works well - * enough... - */ -static bool is_mockup_chip(const char *name) -{ - struct timeval gdev_created_ts; - struct stat gdev_stat; - char *path; - int status; - - path = xasprintf("/dev/%s", name); - - status = stat(path, &gdev_stat); - free(path); - if (status < 0) - die_perr("stat"); - - gdev_created_ts.tv_sec = gdev_stat.st_ctim.tv_sec; - gdev_created_ts.tv_usec = gdev_stat.st_ctim.tv_nsec / 1000; - - return timercmp(&globals.test_ctx.mod_loaded_ts, &gdev_created_ts, >=); -} - static int chipcmp(const void *c1, const void *c2) { const struct mockup_chip *chip1 = *(const struct mockup_chip **)c1; @@ -341,48 +298,90 @@ static int chipcmp(const void *c1, const void *c2) return strcmp(chip1->name, chip2->name); } +static bool devpath_is_mockup(const char *devpath) +{ + return !strncmp(devpath, mockup_devpath, sizeof(mockup_devpath) - 1); +} + static void test_prepare(struct gu_chip_descr *descr) { + const char *devpath, *devnode, *sysname; + struct udev_monitor *monitor; + unsigned int detected = 0; struct test_context *ctx; struct mockup_chip *chip; - unsigned int current = 0; - struct dirent *dentry; + struct udev_device *dev; + struct udev *udev_ctx; + struct pollfd pfd; int status; - DIR *dir; ctx = &globals.test_ctx; memset(ctx, 0, sizeof(*ctx)); + ctx->num_chips = descr->num_chips; + ctx->chips = xzalloc(sizeof(*ctx->chips) * ctx->num_chips); + + /* + * We'll setup the udev monitor, insert the module and wait for the + * mockup gpiochips to appear. + */ + + udev_ctx = udev_new(); + if (!udev_ctx) + die_perr("error creating udev context"); + + monitor = udev_monitor_new_from_netlink(udev_ctx, "udev"); + if (!monitor) + die_perr("error creating udev monitor"); + + status = udev_monitor_filter_add_match_subsystem_devtype(monitor, + "gpio", NULL); + if (status < 0) + die_perr("error adding udev filters"); + + status = udev_monitor_enable_receiving(monitor); + if (status < 0) + die_perr("error enabling udev event receiving"); test_load_module(descr); - ctx->num_chips = descr->num_chips; - ctx->chips = xzalloc(sizeof(*ctx->chips) * ctx->num_chips); + pfd.fd = udev_monitor_get_fd(monitor); + pfd.events = POLLIN | POLLPRI; + + while (ctx->num_chips > detected) { + status = poll(&pfd, 1, 5000); + if (status < 0) + die_perr("error polling for uevents"); + else if (status == 0) + die("timeout waiting for gpiochips"); + + dev = udev_monitor_receive_device(monitor); + if (!dev) + die_perr("error reading device info"); - dir = opendir("/dev"); - if (!dir) - die_perr("error opening /dev"); + devpath = udev_device_get_devpath(dev); + devnode = udev_device_get_devnode(dev); + sysname = udev_device_get_sysname(dev); + if (!devpath || !devnode || !sysname) + goto cont; - for (dentry = readdir(dir); dentry; dentry = readdir(dir)) { - if (strncmp(dentry->d_name, "gpiochip", 8) == 0) { - if (!is_mockup_chip(dentry->d_name)) - continue; + if (!devpath_is_mockup(devpath)) + goto cont; - chip = xzalloc(sizeof(*chip)); - ctx->chips[current++] = chip; + chip = xzalloc(sizeof(*chip)); + chip->name = xstrdup(sysname); + chip->path = xstrdup(devnode); + status = sscanf(sysname, "gpiochip%u", &chip->number); + if (status != 1) + die("unable to determine chip number"); - chip->name = xstrdup(dentry->d_name); - chip->path = xasprintf("/dev/%s", dentry->d_name); + ctx->chips[detected++] = chip; - status = sscanf(dentry->d_name, - "gpiochip%u", &chip->number); - if (status != 1) - die("unable to determine the chip number"); - } +cont: + udev_device_unref(dev); } - closedir(dir); - if (descr->num_chips != current) - die("number of requested and detected mockup gpiochips is not the same"); + udev_monitor_unref(monitor); + udev_unref(udev_ctx); qsort(ctx->chips, ctx->num_chips, sizeof(*ctx->chips), chipcmp); } -- 2.30.2