From ee202f1f99246808e59c4f75c894674fc585d7de Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 2 May 2017 23:50:28 +0200 Subject: [PATCH] tests: extend the testing framework with support for event injecting Add a routine allowing tests to specify a single event to be injected at regular intervals. Signed-off-by: Bartosz Golaszewski --- tests/unit/Makefile.am | 1 + tests/unit/gpiod-unit.c | 133 ++++++++++++++++++++++++++++++++++++++++ tests/unit/gpiod-unit.h | 9 +++ 3 files changed, 143 insertions(+) diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index 7288183..4051bed 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -8,6 +8,7 @@ AM_CFLAGS = -I$(top_srcdir)/include/ -include $(top_builddir)/config.h AM_CFLAGS += -Wall -Wextra -g $(KMOD_CFLAGS) $(UDEV_CFLAGS) +AM_LDFLAGS = -pthread LDADD = ../../src/lib/libgpiod.la $(KMOD_LIBS) $(UDEV_LIBS) check_PROGRAMS = gpiod-unit diff --git a/tests/unit/gpiod-unit.c b/tests/unit/gpiod-unit.c index 8c3a1da..24fab67 100644 --- a/tests/unit/gpiod-unit.c +++ b/tests/unit/gpiod-unit.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #define NORETURN __attribute__((noreturn)) @@ -31,11 +33,24 @@ struct mockup_chip { unsigned int number; }; +struct event_thread { + pthread_t thread_id; + pthread_mutex_t lock; + pthread_cond_t cond; + bool running; + bool should_stop; + unsigned int chip_index; + unsigned int line_offset; + unsigned int freq; + int event_type; +}; + struct test_context { struct mockup_chip **chips; size_t num_chips; bool test_failed; char *failed_msg; + struct event_thread event; }; static struct { @@ -229,6 +244,74 @@ static void module_cleanup(void) kmod_unref(globals.module_ctx); } +static void event_lock(void) +{ + pthread_mutex_lock(&globals.test_ctx.event.lock); +} + +static void event_unlock(void) +{ + pthread_mutex_unlock(&globals.test_ctx.event.lock); +} + +static void * event_worker(void *data GU_UNUSED) +{ + struct event_thread *ev = &globals.test_ctx.event; + struct timeval tv_now, tv_add, tv_res; + struct timespec ts; + int status, i, fd; + char *path; + ssize_t rd; + char buf; + + for (i = 0;; i++) { + event_lock(); + if (ev->should_stop) { + event_unlock(); + break; + } + + gettimeofday(&tv_now, NULL); + tv_add.tv_sec = 0; + tv_add.tv_usec = ev->freq * 1000; + timeradd(&tv_now, &tv_add, &tv_res); + ts.tv_sec = tv_res.tv_sec; + ts.tv_nsec = tv_res.tv_usec * 1000; + + status = pthread_cond_timedwait(&ev->cond, &ev->lock, &ts); + if (status != 0 && status != ETIMEDOUT) + die("error waiting for conditional variable: %s", + strerror(status)); + + path = xappend(NULL, + "/sys/kernel/debug/gpio-mockup-event/gpio-mockup-%c/%u", + 'A' + ev->chip_index, ev->line_offset); + + fd = open(path, O_RDWR); + free(path); + if (fd < 0) + die_perr("error opening gpio event file"); + + if (ev->event_type == GU_EVENT_RISING) + buf = '1'; + else if (ev->event_type == GU_EVENT_FALLING) + buf = '0'; + else + buf = i % 2 == 0 ? '1' : '0'; + + rd = write(fd, &buf, 1); + close(fd); + if (rd < 0) + die_perr("error writing to gpio event file"); + else if (rd != 1) + die("invalid write size to gpio event file"); + + event_unlock(); + } + + return NULL; +} + static void check_kernel(void) { int status, version, patchlevel; @@ -336,6 +419,8 @@ static void test_prepare(struct _gu_chip_descr *descr) memset(ctx, 0, sizeof(*ctx)); ctx->num_chips = descr->num_chips; ctx->chips = xzalloc(sizeof(*ctx->chips) * ctx->num_chips); + pthread_mutex_init(&ctx->event.lock, NULL); + pthread_cond_init(&ctx->event.cond, NULL); /* * We'll setup the udev monitor, insert the module and wait for the @@ -414,9 +499,29 @@ static void test_prepare(struct _gu_chip_descr *descr) static void test_teardown(void) { struct mockup_chip *chip; + struct event_thread *ev; unsigned int i; int status; + event_lock(); + ev = &globals.test_ctx.event; + + if (ev->running) { + ev->should_stop = true; + pthread_cond_broadcast(&ev->cond); + event_unlock(); + + status = pthread_join(globals.test_ctx.event.thread_id, NULL); + if (status != 0) + die("error joining event thread: %s", + strerror(status)); + + pthread_mutex_destroy(&globals.test_ctx.event.lock); + pthread_cond_destroy(&globals.test_ctx.event.cond); + } else { + event_unlock(); + } + for (i = 0; i < globals.test_ctx.num_chips; i++) { chip = globals.test_ctx.chips[i]; @@ -557,3 +662,31 @@ GU_PRINTF(1, 2) void _gu_test_failed(const char *fmt, ...) globals.test_ctx.test_failed = true; } + +void gu_set_event(unsigned int chip_index, + unsigned int line_offset, int event_type, unsigned int freq) +{ + struct event_thread *ev = &globals.test_ctx.event; + int status; + + event_lock(); + + if (!ev->running) { + status = pthread_create(&ev->thread_id, NULL, + event_worker, NULL); + if (status != 0) + die("error creating event thread: %s", + strerror(status)); + + ev->running = true; + } + + ev->chip_index = chip_index; + ev->line_offset = line_offset; + ev->event_type = event_type; + ev->freq = freq; + + pthread_cond_broadcast(&ev->cond); + + event_unlock(); +} diff --git a/tests/unit/gpiod-unit.h b/tests/unit/gpiod-unit.h index 4a79ebf..343a70b 100644 --- a/tests/unit/gpiod-unit.h +++ b/tests/unit/gpiod-unit.h @@ -91,6 +91,15 @@ const char * gu_chip_path(unsigned int index); const char * gu_chip_name(unsigned int index); unsigned int gu_chip_num(unsigned int index); +enum { + GU_EVENT_FALLING, + GU_EVENT_RISING, + GU_EVENT_ALTERNATING, +}; + +void gu_set_event(unsigned int chip_index, + unsigned int line_offset, int event_type, unsigned int freq); + /* * Every GU_ASSERT_*() macro expansion can make a test function return, so it * would be quite difficult to keep track of every resource allocation. At -- 2.30.2