tests: remove the unit subdirectory
authorBartosz Golaszewski <bartekgola@gmail.com>
Thu, 11 May 2017 10:59:08 +0000 (12:59 +0200)
committerBartosz Golaszewski <bartekgola@gmail.com>
Sun, 14 May 2017 11:09:36 +0000 (13:09 +0200)
Initially we planned to have different subdirectories for different
kinds of tests (unit, tools etc.) but since it was decided that we'll
reuse the already existing framework for all tests (in order to avoid
having to reimplement the same functionality) we can get rid of the
unit subdirectory.

Signed-off-by: Bartosz Golaszewski <bartekgola@gmail.com>
20 files changed:
README.md
configure.ac
tests/Makefile.am
tests/gpiod-unit.c [new file with mode: 0644]
tests/gpiod-unit.h [new file with mode: 0644]
tests/tests-chip.c [new file with mode: 0644]
tests/tests-event.c [new file with mode: 0644]
tests/tests-iter.c [new file with mode: 0644]
tests/tests-line.c [new file with mode: 0644]
tests/tests-misc.c [new file with mode: 0644]
tests/tests-simple-api.c [new file with mode: 0644]
tests/unit/Makefile.am [deleted file]
tests/unit/gpiod-unit.c [deleted file]
tests/unit/gpiod-unit.h [deleted file]
tests/unit/tests-chip.c [deleted file]
tests/unit/tests-event.c [deleted file]
tests/unit/tests-iter.c [deleted file]
tests/unit/tests-line.c [deleted file]
tests/unit/tests-misc.c [deleted file]
tests/unit/tests-simple-api.c [deleted file]

index 9443355c197846f08cb3527a146e65b8b3302a50..5860c068c768b0cb3edf608403fa5ea12b884107 100644 (file)
--- a/README.md
+++ b/README.md
@@ -119,7 +119,7 @@ As opposed to standard autotools projects, libgpiod doesn't execute any tests
 when invoking 'make check'. Instead the user must run them manually with
 superuser privileges:
 
-    sudo ./tests/unit/gpiod-unit
+    sudo ./tests/gpiod-unit
 
 CONTRIBUTING
 ------------
index f08d00ddbd3c8e37f2834a77d87b58bc55e18e2b..02144ddfb831f2968c548159dce4bd9093f1a595 100644 (file)
@@ -120,7 +120,6 @@ AC_CONFIG_FILES([Makefile
                 src/Makefile
                 src/lib/Makefile
                 src/tools/Makefile
-                tests/Makefile
-                tests/unit/Makefile])
+                tests/Makefile])
 
 AC_OUTPUT
index fe02e4a408e701a9ad15e14f6f0d8b4192263706..47722164b595c97cf818aa5b39f80c4bc55f3e7b 100644 (file)
@@ -6,4 +6,30 @@
 # as published by the Free Software Foundation.
 #
 
-SUBDIRS = . unit
+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
+
+gpiod_unit_SOURCES =   gpiod-unit.c \
+                       gpiod-unit.h \
+                       tests-chip.c \
+                       tests-event.c \
+                       tests-iter.c \
+                       tests-line.c \
+                       tests-misc.c \
+                       tests-simple-api.c
+
+check: check-am
+       @echo " ********************************************************"
+       @echo " * Unit tests have been built as tests/gpio-unit.       *"
+       @echo " *                                                      *"
+       @echo " * They require linux kernel version >=v4.11 and the    *"
+       @echo " * gpio-mockup module (must not be built-in).           *"
+       @echo " *                                                      *"
+       @echo " * Run the test executable with superuser privileges or *"
+       @echo " * make sure /dev/gpiochipX files are readable and      *"
+       @echo " * writable by normal users.                            *"
+       @echo " ********************************************************"
diff --git a/tests/gpiod-unit.c b/tests/gpiod-unit.c
new file mode 100644 (file)
index 0000000..100e8ca
--- /dev/null
@@ -0,0 +1,696 @@
+/*
+ * Unit testing framework for libgpiod.
+ *
+ * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2.1 of the GNU Lesser General Public License
+ * as published by the Free Software Foundation.
+ */
+
+#include "gpiod-unit.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <unistd.h>
+#include <poll.h>
+#include <libkmod.h>
+#include <libudev.h>
+#include <pthread.h>
+#include <sys/time.h>
+#include <sys/utsname.h>
+
+#define NORETURN       __attribute__((noreturn))
+#define MALLOC         __attribute__((malloc))
+
+static const char mockup_devpath[] = "/devices/platform/gpio-mockup/gpiochip";
+
+struct mockup_chip {
+       char *path;
+       char *name;
+       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 {
+       struct _gu_test *test_list_head;
+       struct _gu_test *test_list_tail;
+       unsigned int num_tests;
+       unsigned int tests_failed;
+       struct kmod_ctx *module_ctx;
+       struct kmod_module *module;
+       struct test_context test_ctx;
+} globals;
+
+enum {
+       CNORM = 0,
+       CGREEN,
+       CRED,
+       CREDBOLD,
+       CYELLOW,
+};
+
+static const char *term_colors[] = {
+       "\033[0m",
+       "\033[32m",
+       "\033[31m",
+       "\033[1m\033[31m",
+       "\033[33m",
+};
+
+static void set_color(int color)
+{
+       fprintf(stderr, "%s", term_colors[color]);
+}
+
+static void reset_color(void)
+{
+       fprintf(stderr, "%s", term_colors[CNORM]);
+}
+
+static void pr_raw_v(const char *fmt, va_list va)
+{
+       vfprintf(stderr, fmt, va);
+}
+
+static GU_PRINTF(1, 2) void pr_raw(const char *fmt, ...)
+{
+       va_list va;
+
+       va_start(va, fmt);
+       pr_raw_v(fmt, va);
+       va_end(va);
+}
+
+static void print_header(const char *hdr, int color)
+{
+       set_color(color);
+       pr_raw("[%-5s] ", hdr);
+       reset_color();
+}
+
+static void vmsgn(const char *hdr, int hdr_clr, const char *fmt, va_list va)
+{
+       print_header(hdr, hdr_clr);
+       pr_raw_v(fmt, va);
+}
+
+static void vmsg(const char *hdr, int hdr_clr, const char *fmt, va_list va)
+{
+       vmsgn(hdr, hdr_clr, fmt, va);
+       pr_raw("\n");
+}
+
+static GU_PRINTF(1, 2) void msg(const char *fmt, ...)
+{
+       va_list va;
+
+       va_start(va, fmt);
+       vmsg("INFO", CGREEN, fmt, va);
+       va_end(va);
+}
+
+static GU_PRINTF(1, 2) void err(const char *fmt, ...)
+{
+       va_list va;
+
+       va_start(va, fmt);
+       vmsg("ERROR", CRED, fmt, va);
+       va_end(va);
+}
+
+static GU_PRINTF(1, 2) NORETURN void die(const char *fmt, ...)
+{
+       va_list va;
+
+       va_start(va, fmt);
+       vmsg("FATAL", CRED, fmt, va);
+       va_end(va);
+
+       exit(EXIT_FAILURE);
+}
+
+static GU_PRINTF(1, 2) NORETURN void die_perr(const char *fmt, ...)
+{
+       va_list va;
+
+       va_start(va, fmt);
+       vmsgn("FATAL", CRED, fmt, va);
+       pr_raw(": %s\n", strerror(errno));
+       va_end(va);
+
+       exit(EXIT_FAILURE);
+}
+
+static MALLOC void * xzalloc(size_t size)
+{
+       void *ptr;
+
+       ptr = malloc(size);
+       if (!ptr)
+               die("out of memory");
+
+       memset(ptr, 0, size);
+
+       return ptr;
+}
+
+static MALLOC char * xstrdup(const char *str)
+{
+       char *ret;
+
+       ret = strdup(str);
+       if (!ret)
+               die("out of memory");
+
+       return ret;
+}
+
+static MALLOC GU_PRINTF(2, 3) char * xappend(char *str, const char *fmt, ...)
+{
+       char *new, *ret;
+       va_list va;
+       int status;
+
+       va_start(va, fmt);
+       status = vasprintf(&new, fmt, va);
+       va_end(va);
+       if (status < 0)
+               die_perr("vasprintf");
+
+       if (!str)
+               return new;
+
+       ret = realloc(str, strlen(str) + strlen(new) + 1);
+       if (!ret)
+               die("out of memory");
+
+       strcat(ret, new);
+       free(new);
+
+       return ret;
+}
+
+static void check_chip_index(unsigned int index)
+{
+       if (index >= globals.test_ctx.num_chips)
+               die("invalid chip number requested from test code");
+}
+
+static bool mockup_loaded(void)
+{
+       int state;
+
+       if (!globals.module_ctx || !globals.module)
+               return false;
+
+       state = kmod_module_get_initstate(globals.module);
+
+       return state == KMOD_MODULE_LIVE;
+}
+
+static void module_cleanup(void)
+{
+       msg("cleaning up");
+
+       if (mockup_loaded())
+               kmod_module_remove_module(globals.module, 0);
+
+       if (globals.module)
+               kmod_module_unref(globals.module);
+
+       if (globals.module_ctx)
+               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;
+       struct utsname un;
+
+       msg("checking the linux kernel version");
+
+       status = uname(&un);
+       if (status)
+               die_perr("uname");
+
+       status = sscanf(un.release, "%d.%d", &version, &patchlevel);
+       if (status != 2)
+               die("error reading kernel release version");
+
+       if (version < 4 || patchlevel < 11)
+               die("linux kernel version must be at least v4.11 - got v%d.%d",
+                   version, patchlevel);
+
+       msg("kernel release is v%d.%d - ok to run tests", version, patchlevel);
+}
+
+static void check_gpio_mockup(void)
+{
+       const char *modpath;
+       int status;
+
+       msg("checking gpio-mockup availability");
+
+       globals.module_ctx = kmod_new(NULL, NULL);
+       if (!globals.module_ctx)
+               die_perr("error creating kernel module context");
+
+       status = kmod_module_new_from_name(globals.module_ctx,
+                                          "gpio-mockup", &globals.module);
+       if (status)
+               die_perr("error allocating module info");
+
+       /* First see if we can find the module. */
+       modpath = kmod_module_get_path(globals.module);
+       if (!modpath)
+               die("the gpio-mockup module does not exist in the system or is built into the kernel");
+
+       /* Then see if we can freely load and unload it. */
+       status = kmod_module_probe_insert_module(globals.module, 0,
+                                                "gpio_mockup_ranges=-1,4",
+                                                NULL, NULL, NULL);
+       if (status)
+               die_perr("unable to load gpio-mockup");
+
+       status = kmod_module_remove_module(globals.module, 0);
+       if (status)
+               die_perr("unable to remove gpio-mockup");
+
+       msg("gpio-mockup ok");
+}
+
+static void test_load_module(struct _gu_chip_descr *descr)
+{
+       unsigned int i;
+       char *modarg;
+       int status;
+
+       modarg = xappend(NULL, "gpio_mockup_ranges=");
+       for (i = 0; i < descr->num_chips; i++)
+               modarg = xappend(modarg, "-1,%u,", descr->num_lines[i]);
+       modarg[strlen(modarg) - 1] = '\0'; /* Remove the last comma. */
+
+       if (descr->named_lines)
+               modarg = xappend(modarg, " gpio_mockup_named_lines");
+
+       status = kmod_module_probe_insert_module(globals.module, 0,
+                                                modarg, NULL, NULL, NULL);
+       if (status)
+               die_perr("unable to load gpio-mockup");
+
+       free(modarg);
+}
+
+static int chipcmp(const void *c1, const void *c2)
+{
+       const struct mockup_chip *chip1 = *(const struct mockup_chip **)c1;
+       const struct mockup_chip *chip2 = *(const struct mockup_chip **)c2;
+
+       return chip1->number > chip2->number;
+}
+
+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;
+       struct udev_device *dev;
+       struct udev *udev_ctx;
+       struct pollfd pfd;
+       int status;
+
+       ctx = &globals.test_ctx;
+       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
+        * 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);
+
+       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");
+
+               devpath = udev_device_get_devpath(dev);
+               devnode = udev_device_get_devnode(dev);
+               sysname = udev_device_get_sysname(dev);
+
+               if (!devpath || !devnode || !sysname ||
+                   !devpath_is_mockup(devpath)) {
+                       udev_device_unref(dev);
+                       continue;
+               }
+
+               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");
+
+               ctx->chips[detected++] = chip;
+               udev_device_unref(dev);
+       }
+
+       udev_monitor_unref(monitor);
+       udev_unref(udev_ctx);
+
+       /*
+        * We can't assume that the order in which the mockup gpiochip
+        * devices are created will be deterministic, yet we want the
+        * index passed to the gu_chip_*() functions to correspond with the
+        * order in which the chips were defined in the GU_DEFINE_TEST()
+        * macro.
+        *
+        * Once all gpiochips are there, sort them by chip number.
+        */
+       qsort(ctx->chips, ctx->num_chips, sizeof(*ctx->chips), chipcmp);
+}
+
+static void test_run(struct _gu_test *test)
+{
+       print_header("TEST", CYELLOW);
+       pr_raw("'%s': ", test->name);
+
+       test->func();
+
+       if (globals.test_ctx.test_failed) {
+               globals.tests_failed++;
+               set_color(CREDBOLD);
+               pr_raw("FAILED:");
+               reset_color();
+               set_color(CRED);
+               pr_raw("\n\t\t'%s': %s\n",
+                      test->name, globals.test_ctx.failed_msg);
+               reset_color();
+               free(globals.test_ctx.failed_msg);
+       } else {
+               set_color(CGREEN);
+               pr_raw("OK\n");
+               reset_color();
+       }
+}
+
+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];
+
+               free(chip->path);
+               free(chip->name);
+               free(chip);
+       }
+
+       free(globals.test_ctx.chips);
+
+       status = kmod_module_remove_module(globals.module, 0);
+       if (status)
+               die_perr("unable to remove gpio-mockup");
+}
+
+int main(int argc GU_UNUSED, char **argv GU_UNUSED)
+{
+       struct _gu_test *test;
+
+       atexit(module_cleanup);
+
+       msg("libgpiod unit-test suite");
+       msg("%u tests registered", globals.num_tests);
+
+       check_kernel();
+       check_gpio_mockup();
+
+       msg("running tests");
+
+       for (test = globals.test_list_head; test; test = test->_next) {
+               test_prepare(&test->chip_descr);
+               test_run(test);
+               test_teardown();
+       }
+
+       if (!globals.tests_failed)
+               msg("all tests passed");
+       else
+               err("%u out of %u tests failed",
+                   globals.tests_failed, globals.num_tests);
+
+       return globals.tests_failed ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
+void gu_close_chip(struct gpiod_chip **chip)
+{
+       if (*chip)
+               gpiod_chip_close(*chip);
+}
+
+void gu_free_str(char **str)
+{
+       if (*str)
+               free(*str);
+}
+
+void gu_free_chip_iter(struct gpiod_chip_iter **iter)
+{
+       if (*iter)
+               gpiod_chip_iter_free(*iter);
+}
+
+void gu_free_chip_iter_noclose(struct gpiod_chip_iter **iter)
+{
+       if (*iter)
+               gpiod_chip_iter_free_noclose(*iter);
+}
+
+const char * gu_chip_path(unsigned int index)
+{
+       check_chip_index(index);
+
+       return globals.test_ctx.chips[index]->path;
+}
+
+const char * gu_chip_name(unsigned int index)
+{
+       check_chip_index(index);
+
+       return globals.test_ctx.chips[index]->name;
+}
+
+unsigned int gu_chip_num(unsigned int index)
+{
+       check_chip_index(index);
+
+       return globals.test_ctx.chips[index]->number;
+}
+
+void _gu_register_test(struct _gu_test *test)
+{
+       struct _gu_test *tmp;
+
+       if (!globals.test_list_head) {
+               globals.test_list_head = globals.test_list_tail = test;
+               test->_next = NULL;
+       } else {
+               tmp = globals.test_list_tail;
+               globals.test_list_tail = test;
+               test->_next = NULL;
+               tmp->_next = test;
+       }
+
+       globals.num_tests++;
+}
+
+GU_PRINTF(1, 2) void _gu_test_failed(const char *fmt, ...)
+{
+       int status;
+       va_list va;
+
+       va_start(va, fmt);
+       status = vasprintf(&globals.test_ctx.failed_msg, fmt, va);
+       va_end(va);
+       if (status < 0)
+               die_perr("vasprintf");
+
+       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/gpiod-unit.h b/tests/gpiod-unit.h
new file mode 100644 (file)
index 0000000..a1883e0
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Unit testing framework for libgpiod.
+ *
+ * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2.1 of the GNU Lesser General Public License
+ * as published by the Free Software Foundation.
+ */
+
+#ifndef __GPIOD_UNIT_H__
+#define __GPIOD_UNIT_H__
+
+#include <gpiod.h>
+#include <string.h>
+
+#define GU_INIT                        __attribute__((constructor))
+#define GU_UNUSED              __attribute__((unused))
+#define GU_PRINTF(fmt, arg)    __attribute__((format(printf, fmt, arg)))
+#define GU_CLEANUP(func)       __attribute__((cleanup(func)))
+
+#define GU_ARRAY_SIZE(x)       (sizeof(x) / sizeof(*(x)))
+
+typedef void (*gu_test_func)(void);
+
+struct _gu_chip_descr {
+       unsigned int num_chips;
+       unsigned int *num_lines;
+       bool named_lines;
+};
+
+struct _gu_test {
+       struct _gu_test *_next;
+
+       const char *name;
+       gu_test_func func;
+
+       struct _gu_chip_descr chip_descr;
+};
+
+void _gu_register_test(struct _gu_test *test);
+void _gu_test_failed(const char *fmt, ...);
+
+/*
+ * This macro should be used for code brevity instead of manually declaring
+ * the gu_test structure.
+ *
+ * The macro accepts the following arguments:
+ *   _a_func: name of the test function
+ *   _a_name: name of the test case (will be shown to user)
+ *   _a_named_lines: indicate whether we want the GPIO lines to be named
+ *
+ * The last argument must be an array of unsigned integers specifying the
+ * number of GPIO lines in each subsequent mockup chip. The size of this
+ * array will at the same time specify the number of gpiochips to create.
+ */
+#define GU_DEFINE_TEST(_a_func, _a_name, _a_named_lines, ...)          \
+       static unsigned int _##_a_func##_lines[] = __VA_ARGS__;         \
+       static struct _gu_test _##_a_func##_descr = {                   \
+               .name = _a_name,                                        \
+               .func = _a_func,                                        \
+               .chip_descr = {                                         \
+                       .num_chips = GU_ARRAY_SIZE(_##_a_func##_lines), \
+                       .num_lines = _##_a_func##_lines,                \
+                       .named_lines = _a_named_lines,                  \
+               },                                                      \
+       };                                                              \
+       static GU_INIT void _gu_register_##_a_func##_test(void)         \
+       {                                                               \
+               _gu_register_test(&_##_a_func##_descr);                 \
+       }                                                               \
+       static int _gu_##_a_func##_sentinel GU_UNUSED
+
+enum {
+       GU_LINES_UNNAMED = false,
+       GU_LINES_NAMED = true,
+};
+
+/*
+ * We want libgpiod tests to co-exist with gpiochips created by other GPIO
+ * drivers. For that reason we can't just use hardcoded device file paths or
+ * gpiochip names.
+ *
+ * The test suite detects the chips that were exported by the gpio-mockup
+ * module and stores them in the internal test context structure. Test cases
+ * should use the routines declared below to access the gpiochip path, name
+ * or number by index corresponding with the order in which the mockup chips
+ * were requested in the GU_DEFINE_TEST() macro.
+ */
+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
+ * the same time we don't want any deliberate memory leaks as we also use this
+ * test suite to find actual memory leaks in the library with valgrind.
+ *
+ * For this reason, the tests should generally always use the GU_CLEANUP()
+ * macro for dynamically allocated variables and objects that need closing.
+ *
+ * The functions below can be reused by different tests for common use cases.
+ */
+void gu_close_chip(struct gpiod_chip **chip);
+void gu_free_str(char **str);
+void gu_free_chip_iter(struct gpiod_chip_iter **iter);
+void gu_free_chip_iter_noclose(struct gpiod_chip_iter **iter);
+
+#define GU_ASSERT(statement)                                           \
+       do {                                                            \
+               if (!(statement)) {                                     \
+                       _gu_test_failed(                                \
+                               "assertion failed (%s:%d): '%s'",       \
+                               __FILE__, __LINE__, #statement);        \
+                       return;                                         \
+               }                                                       \
+       } while (0)
+
+#define GU_ASSERT_FALSE(statement)     GU_ASSERT(!(statement))
+#define GU_ASSERT_NOT_NULL(ptr)                GU_ASSERT(ptr != NULL)
+#define GU_ASSERT_RET_OK(status)       GU_ASSERT(status == 0)
+#define GU_ASSERT_NULL(ptr)            GU_ASSERT(ptr == NULL)
+#define GU_ASSERT_EQ(a1, a2)           GU_ASSERT(a1 == a2)
+#define GU_ASSERT_NOTEQ(a1, a2)                GU_ASSERT(a1 != a2)
+#define GU_ASSERT_STR_EQ(s1, s2)       GU_ASSERT(strcmp(s1, s2) == 0)
+
+#endif /* __GPIOD_UNIT_H__ */
diff --git a/tests/tests-chip.c b/tests/tests-chip.c
new file mode 100644 (file)
index 0000000..d94eaa8
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * GPIO chip test cases for libgpiod.
+ *
+ * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2.1 of the GNU Lesser General Public License
+ * as published by the Free Software Foundation.
+ */
+
+#include "gpiod-unit.h"
+
+#include <stdio.h>
+#include <errno.h>
+
+static void chip_open_good(void)
+{
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
+
+       chip = gpiod_chip_open(gu_chip_path(0));
+       GU_ASSERT_NOT_NULL(chip);
+}
+GU_DEFINE_TEST(chip_open_good,
+              "gpiod_chip_open() - good",
+              GU_LINES_UNNAMED, { 8 });
+
+static void chip_open_nonexistent(void)
+{
+       struct gpiod_chip *chip;
+
+       chip = gpiod_chip_open("/dev/nonexistent_gpiochip");
+       GU_ASSERT_NULL(chip);
+       GU_ASSERT_EQ(gpiod_errno(), ENOENT);
+}
+GU_DEFINE_TEST(chip_open_nonexistent,
+              "gpiod_chip_open() - nonexistent chip",
+              GU_LINES_UNNAMED, { 8 });
+
+static void chip_open_notty(void)
+{
+       struct gpiod_chip *chip;
+
+       chip = gpiod_chip_open("/dev/null");
+       GU_ASSERT_NULL(chip);
+       GU_ASSERT_EQ(gpiod_errno(), ENOTTY);
+}
+GU_DEFINE_TEST(chip_open_notty,
+              "gpiod_chip_open() - notty",
+              GU_LINES_UNNAMED, { 8 });
+
+static void chip_open_by_name_good(void)
+{
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
+
+       chip = gpiod_chip_open_by_name(gu_chip_name(0));
+       GU_ASSERT_NOT_NULL(chip);
+}
+GU_DEFINE_TEST(chip_open_by_name_good,
+              "gpiod_chip_open_by_name() - good",
+              GU_LINES_UNNAMED, { 8 });
+
+static void chip_open_by_number_good(void)
+{
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
+
+       chip = gpiod_chip_open_by_number(gu_chip_num(0));
+       GU_ASSERT_NOT_NULL(chip);
+}
+GU_DEFINE_TEST(chip_open_by_number_good,
+              "gpiod_chip_open_by_number() - good",
+              GU_LINES_UNNAMED, { 8 });
+
+static void chip_open_lookup(void)
+{
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip_by_label = NULL;
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip_by_name = NULL;
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip_by_path = NULL;
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip_by_num = NULL;
+       GU_CLEANUP(gu_free_str) char *chip_num;
+
+       GU_ASSERT(asprintf(&chip_num, "%u", gu_chip_num(1)) > 0);
+
+       chip_by_name = gpiod_chip_open_lookup(gu_chip_name(1));
+       chip_by_path = gpiod_chip_open_lookup(gu_chip_path(1));
+       chip_by_num = gpiod_chip_open_lookup(chip_num);
+       chip_by_label = gpiod_chip_open_lookup("gpio-mockup-B");
+
+       GU_ASSERT_NOT_NULL(chip_by_name);
+       GU_ASSERT_NOT_NULL(chip_by_path);
+       GU_ASSERT_NOT_NULL(chip_by_num);
+       GU_ASSERT_NOT_NULL(chip_by_label);
+
+       GU_ASSERT_STR_EQ(gpiod_chip_name(chip_by_name), gu_chip_name(1));
+       GU_ASSERT_STR_EQ(gpiod_chip_name(chip_by_path), gu_chip_name(1));
+       GU_ASSERT_STR_EQ(gpiod_chip_name(chip_by_num), gu_chip_name(1));
+       GU_ASSERT_STR_EQ(gpiod_chip_name(chip_by_label), gu_chip_name(1));
+}
+GU_DEFINE_TEST(chip_open_lookup,
+              "gpiod_chip_open_lookup() - good",
+              GU_LINES_UNNAMED, { 8, 8, 8 });
+
+static void chip_open_by_label_good(void)
+{
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
+
+       chip = gpiod_chip_open_by_label("gpio-mockup-D");
+       GU_ASSERT_NOT_NULL(chip);
+       GU_ASSERT_STR_EQ(gpiod_chip_name(chip), gu_chip_name(3));
+}
+GU_DEFINE_TEST(chip_open_by_label_good,
+              "gpiod_chip_open_by_label() - good",
+              GU_LINES_UNNAMED, { 4, 4, 4, 4, 4 });
+
+static void chip_open_by_label_bad(void)
+{
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
+
+       chip = gpiod_chip_open_by_label("nonexistent_gpio_chip");
+       GU_ASSERT_NULL(chip);
+}
+GU_DEFINE_TEST(chip_open_by_label_bad,
+              "gpiod_chip_open_by_label() - bad",
+              GU_LINES_UNNAMED, { 4, 4, 4, 4, 4 });
+
+static void chip_name(void)
+{
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip0 = NULL;
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip1 = NULL;
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip2 = NULL;
+
+       chip0 = gpiod_chip_open(gu_chip_path(0));
+       chip1 = gpiod_chip_open(gu_chip_path(1));
+       chip2 = gpiod_chip_open(gu_chip_path(2));
+       GU_ASSERT_NOT_NULL(chip0);
+       GU_ASSERT_NOT_NULL(chip1);
+       GU_ASSERT_NOT_NULL(chip2);
+
+       GU_ASSERT_STR_EQ(gpiod_chip_name(chip0), gu_chip_name(0));
+       GU_ASSERT_STR_EQ(gpiod_chip_name(chip1), gu_chip_name(1));
+       GU_ASSERT_STR_EQ(gpiod_chip_name(chip2), gu_chip_name(2));
+}
+GU_DEFINE_TEST(chip_name,
+              "gpiod_chip_name()",
+              GU_LINES_UNNAMED, { 8, 8, 8 });
+
+static void chip_label(void)
+{
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip0 = NULL;
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip1 = NULL;
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip2 = NULL;
+
+       chip0 = gpiod_chip_open(gu_chip_path(0));
+       chip1 = gpiod_chip_open(gu_chip_path(1));
+       chip2 = gpiod_chip_open(gu_chip_path(2));
+       GU_ASSERT_NOT_NULL(chip0);
+       GU_ASSERT_NOT_NULL(chip1);
+       GU_ASSERT_NOT_NULL(chip2);
+
+       GU_ASSERT_STR_EQ(gpiod_chip_label(chip0), "gpio-mockup-A");
+       GU_ASSERT_STR_EQ(gpiod_chip_label(chip1), "gpio-mockup-B");
+       GU_ASSERT_STR_EQ(gpiod_chip_label(chip2), "gpio-mockup-C");
+}
+GU_DEFINE_TEST(chip_label,
+              "gpiod_chip_label()",
+              GU_LINES_UNNAMED, { 8, 8, 8 });
+
+static void chip_num_lines(void)
+{
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip0 = NULL;
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip1 = NULL;
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip2 = NULL;
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip3 = NULL;
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip4 = NULL;
+
+       chip0 = gpiod_chip_open(gu_chip_path(0));
+       chip1 = gpiod_chip_open(gu_chip_path(1));
+       chip2 = gpiod_chip_open(gu_chip_path(2));
+       chip3 = gpiod_chip_open(gu_chip_path(3));
+       chip4 = gpiod_chip_open(gu_chip_path(4));
+       GU_ASSERT_NOT_NULL(chip0);
+       GU_ASSERT_NOT_NULL(chip1);
+       GU_ASSERT_NOT_NULL(chip2);
+       GU_ASSERT_NOT_NULL(chip3);
+       GU_ASSERT_NOT_NULL(chip4);
+
+       GU_ASSERT_EQ(gpiod_chip_num_lines(chip0), 1);
+       GU_ASSERT_EQ(gpiod_chip_num_lines(chip1), 4);
+       GU_ASSERT_EQ(gpiod_chip_num_lines(chip2), 8);
+       GU_ASSERT_EQ(gpiod_chip_num_lines(chip3), 16);
+       GU_ASSERT_EQ(gpiod_chip_num_lines(chip4), 32);
+}
+GU_DEFINE_TEST(chip_num_lines,
+              "gpiod_chip_num_lines()",
+              GU_LINES_UNNAMED, { 1, 4, 8, 16, 32 });
diff --git a/tests/tests-event.c b/tests/tests-event.c
new file mode 100644 (file)
index 0000000..74d2f41
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * Line event test cases for libgpiod.
+ *
+ * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2.1 of the GNU Lesser General Public License
+ * as published by the Free Software Foundation.
+ */
+
+#include "gpiod-unit.h"
+
+static void event_rising_edge_good(void)
+{
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
+       struct timespec ts = { 1, 0 };
+       struct gpiod_line_event ev;
+       struct gpiod_line *line;
+       int status;
+
+       chip = gpiod_chip_open(gu_chip_path(0));
+       GU_ASSERT_NOT_NULL(chip);
+
+       line = gpiod_chip_get_line(chip, 7);
+       GU_ASSERT_NOT_NULL(line);
+
+       status = gpiod_line_event_request_rising(line, "gpiod-unit", false);
+       GU_ASSERT_RET_OK(status);
+
+       gu_set_event(0, 7, GU_EVENT_RISING, 100);
+
+       status = gpiod_line_event_wait(line, &ts);
+       GU_ASSERT_EQ(status, 1);
+
+       status = gpiod_line_event_read(line, &ev);
+       GU_ASSERT_RET_OK(status);
+
+       GU_ASSERT_EQ(ev.event_type, GPIOD_EVENT_RISING_EDGE);
+}
+GU_DEFINE_TEST(event_rising_edge_good,
+              "events - receive single rising edge event",
+              GU_LINES_UNNAMED, { 8 });
+
+static void event_falling_edge_good(void)
+{
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
+       struct timespec ts = { 1, 0 };
+       struct gpiod_line_event ev;
+       struct gpiod_line *line;
+       int status;
+
+       chip = gpiod_chip_open(gu_chip_path(0));
+       GU_ASSERT_NOT_NULL(chip);
+
+       line = gpiod_chip_get_line(chip, 7);
+       GU_ASSERT_NOT_NULL(line);
+
+       status = gpiod_line_event_request_falling(line, "gpiod-unit", false);
+       GU_ASSERT_RET_OK(status);
+
+       gu_set_event(0, 7, GU_EVENT_FALLING, 100);
+
+       status = gpiod_line_event_wait(line, &ts);
+       GU_ASSERT_EQ(status, 1);
+
+       status = gpiod_line_event_read(line, &ev);
+       GU_ASSERT_RET_OK(status);
+
+       GU_ASSERT_EQ(ev.event_type, GPIOD_EVENT_FALLING_EDGE);
+}
+GU_DEFINE_TEST(event_falling_edge_good,
+              "events - receive single falling edge event",
+              GU_LINES_UNNAMED, { 8 });
+
+static void event_rising_edge_ignore_falling(void)
+{
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
+       struct timespec ts = { 0, 300 };
+       struct gpiod_line *line;
+       int status;
+
+       chip = gpiod_chip_open(gu_chip_path(0));
+       GU_ASSERT_NOT_NULL(chip);
+
+       line = gpiod_chip_get_line(chip, 7);
+       GU_ASSERT_NOT_NULL(line);
+
+       status = gpiod_line_event_request_rising(line, "gpiod-unit", false);
+       GU_ASSERT_RET_OK(status);
+
+       gu_set_event(0, 7, GU_EVENT_FALLING, 100);
+
+       status = gpiod_line_event_wait(line, &ts);
+       GU_ASSERT_EQ(status, 0);
+}
+GU_DEFINE_TEST(event_rising_edge_ignore_falling,
+              "events - request rising edge & ignore falling edge events",
+              GU_LINES_UNNAMED, { 8 });
+
+static void event_rising_edge_active_low(void)
+{
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
+       struct timespec ts = { 1, 0 };
+       struct gpiod_line_event ev;
+       struct gpiod_line *line;
+       int status;
+
+       chip = gpiod_chip_open(gu_chip_path(0));
+       GU_ASSERT_NOT_NULL(chip);
+
+       line = gpiod_chip_get_line(chip, 7);
+       GU_ASSERT_NOT_NULL(line);
+
+       status = gpiod_line_event_request_rising(line, "gpiod-unit", true);
+       GU_ASSERT_RET_OK(status);
+
+       gu_set_event(0, 7, GU_EVENT_RISING, 100);
+
+       status = gpiod_line_event_wait(line, &ts);
+       GU_ASSERT_EQ(status, 1);
+
+       status = gpiod_line_event_read(line, &ev);
+       GU_ASSERT_RET_OK(status);
+
+       GU_ASSERT_EQ(ev.event_type, GPIOD_EVENT_RISING_EDGE);
+}
+GU_DEFINE_TEST(event_rising_edge_active_low,
+              "events - single rising edge event with low active state",
+              GU_LINES_UNNAMED, { 8 });
+
+static void event_get_value(void)
+{
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
+       struct timespec ts = { 1, 0 };
+       struct gpiod_line_event ev;
+       struct gpiod_line *line;
+       int status;
+
+       chip = gpiod_chip_open(gu_chip_path(0));
+       GU_ASSERT_NOT_NULL(chip);
+
+       line = gpiod_chip_get_line(chip, 7);
+       GU_ASSERT_NOT_NULL(line);
+
+       status = gpiod_line_event_request_rising(line, "gpiod-unit", false);
+       GU_ASSERT_RET_OK(status);
+
+       status = gpiod_line_get_value(line);
+       GU_ASSERT_EQ(status, 0);
+
+       gu_set_event(0, 7, GU_EVENT_RISING, 100);
+
+       status = gpiod_line_event_wait(line, &ts);
+       GU_ASSERT_EQ(status, 1);
+
+       status = gpiod_line_event_read(line, &ev);
+       GU_ASSERT_RET_OK(status);
+
+       GU_ASSERT_EQ(ev.event_type, GPIOD_EVENT_RISING_EDGE);
+
+       status = gpiod_line_get_value(line);
+       GU_ASSERT_EQ(status, 1);
+}
+GU_DEFINE_TEST(event_get_value,
+              "events - mixing events and gpiod_line_get_value()",
+              GU_LINES_UNNAMED, { 8 });
diff --git a/tests/tests-iter.c b/tests/tests-iter.c
new file mode 100644 (file)
index 0000000..00d63ba
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Iterator test cases for libgpiod.
+ *
+ * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2.1 of the GNU Lesser General Public License
+ * as published by the Free Software Foundation.
+ */
+
+#include "gpiod-unit.h"
+
+static void chip_iter(void)
+{
+       GU_CLEANUP(gu_free_chip_iter) struct gpiod_chip_iter *iter = NULL;
+       struct gpiod_chip *chip;
+       bool A, B, C;
+
+       A = B = C = false;
+
+       iter = gpiod_chip_iter_new();
+       GU_ASSERT_NOT_NULL(iter);
+
+       gpiod_foreach_chip(iter, chip) {
+               GU_ASSERT(!gpiod_chip_iter_err(iter));
+
+               if (strcmp(gpiod_chip_label(chip), "gpio-mockup-A") == 0)
+                       A = true;
+               else if (strcmp(gpiod_chip_label(chip), "gpio-mockup-B") == 0)
+                       B = true;
+               else if (strcmp(gpiod_chip_label(chip), "gpio-mockup-C") == 0)
+                       C = true;
+       }
+
+       GU_ASSERT(A);
+       GU_ASSERT(B);
+       GU_ASSERT(C);
+}
+GU_DEFINE_TEST(chip_iter,
+              "gpiod_chip_iter - simple loop",
+              GU_LINES_UNNAMED, { 8, 8, 8 });
+
+static void chip_iter_noclose(void)
+{
+       GU_CLEANUP(gu_free_chip_iter_noclose)
+                       struct gpiod_chip_iter *iter = NULL;
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chipA;
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chipB;
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chipC;
+       struct gpiod_chip *chip;
+       bool A, B, C;
+
+       A = B = C = false;
+
+       iter = gpiod_chip_iter_new();
+       GU_ASSERT_NOT_NULL(iter);
+
+       gpiod_foreach_chip_noclose(iter, chip) {
+               GU_ASSERT(!gpiod_chip_iter_err(iter));
+
+               if (strcmp(gpiod_chip_label(chip), "gpio-mockup-A") == 0) {
+                       A = true;
+                       chipA = chip;
+               } else if (strcmp(gpiod_chip_label(chip),
+                                 "gpio-mockup-B") == 0) {
+                       B = true;
+                       chipB = chip;
+               } else if (strcmp(gpiod_chip_label(chip),
+                                 "gpio-mockup-C") == 0) {
+                       C = true;
+                       chipC = chip;
+               }
+       }
+
+       GU_ASSERT(A);
+       GU_ASSERT(B);
+       GU_ASSERT(C);
+
+       gpiod_chip_iter_free_noclose(iter);
+       iter = NULL;
+
+       /* See if the chips are still open and usable. */
+       GU_ASSERT_STR_EQ(gpiod_chip_label(chipA), "gpio-mockup-A");
+       GU_ASSERT_STR_EQ(gpiod_chip_label(chipB), "gpio-mockup-B");
+       GU_ASSERT_STR_EQ(gpiod_chip_label(chipC), "gpio-mockup-C");
+}
+GU_DEFINE_TEST(chip_iter_noclose,
+              "gpiod_chip_iter - simple loop, noclose variant",
+              GU_LINES_UNNAMED, { 8, 8, 8 });
+
+static void chip_iter_break(void)
+{
+       GU_CLEANUP(gu_free_chip_iter) struct gpiod_chip_iter *iter = NULL;
+       struct gpiod_chip *chip;
+       int i = 0;
+
+       iter = gpiod_chip_iter_new();
+       GU_ASSERT_NOT_NULL(iter);
+
+       gpiod_foreach_chip(iter, chip) {
+               GU_ASSERT(!gpiod_chip_iter_err(iter));
+
+               if ((strcmp(gpiod_chip_label(chip), "gpio-mockup-A") == 0) ||
+                   (strcmp(gpiod_chip_label(chip), "gpio-mockup-B") == 0) ||
+                   (strcmp(gpiod_chip_label(chip), "gpio-mockup-C") == 0))
+                       i++;
+
+               if (i == 3)
+                       break;
+       }
+
+       gpiod_chip_iter_free(iter);
+       iter = NULL;
+
+       GU_ASSERT_EQ(i, 3);
+}
+GU_DEFINE_TEST(chip_iter_break,
+              "gpiod_chip_iter - break",
+              GU_LINES_UNNAMED, { 8, 8, 8, 8, 8 });
+
+static void line_iter(void)
+{
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
+       struct gpiod_line_iter iter;
+       struct gpiod_line *line;
+       unsigned int i = 0;
+
+       chip = gpiod_chip_open(gu_chip_path(0));
+       GU_ASSERT_NOT_NULL(chip);
+
+       gpiod_line_iter_init(&iter, chip);
+
+       gpiod_foreach_line(&iter, line) {
+               GU_ASSERT(!gpiod_line_iter_err(&iter));
+               GU_ASSERT_EQ(i, gpiod_line_offset(line));
+               i++;
+       }
+
+       GU_ASSERT_EQ(8, i);
+}
+GU_DEFINE_TEST(line_iter,
+              "gpiod_line_iter - simple loop, check offsets",
+              GU_LINES_UNNAMED, { 8 });
+
+static void line_iter_static_initializer(void)
+{
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
+       struct gpiod_line *line;
+       unsigned int i = 0;
+
+       chip = gpiod_chip_open(gu_chip_path(0));
+       GU_ASSERT_NOT_NULL(chip);
+
+       {
+               struct gpiod_line_iter iter = GPIOD_LINE_ITER_INITIALIZER(chip);
+
+               gpiod_foreach_line(&iter, line) {
+                       GU_ASSERT(!gpiod_line_iter_err(&iter));
+                       GU_ASSERT_EQ(i, gpiod_line_offset(line));
+                       i++;
+               }
+       }
+
+       GU_ASSERT_EQ(8, i);
+}
+GU_DEFINE_TEST(line_iter_static_initializer,
+              "gpiod_line_iter - simple loop, static initializer",
+              GU_LINES_UNNAMED, { 8 });
diff --git a/tests/tests-line.c b/tests/tests-line.c
new file mode 100644 (file)
index 0000000..4f430f4
--- /dev/null
@@ -0,0 +1,366 @@
+/*
+ * GPIO line test cases for libgpiod.
+ *
+ * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2.1 of the GNU Lesser General Public License
+ * as published by the Free Software Foundation.
+ */
+
+#include "gpiod-unit.h"
+
+static void line_request_output(void)
+{
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
+       struct gpiod_line *line_0;
+       struct gpiod_line *line_1;
+       int status;
+
+       chip = gpiod_chip_open(gu_chip_path(0));
+       GU_ASSERT_NOT_NULL(chip);
+
+       line_0 = gpiod_chip_get_line(chip, 2);
+       line_1 = gpiod_chip_get_line(chip, 5);
+       GU_ASSERT_NOT_NULL(line_0);
+       GU_ASSERT_NOT_NULL(line_1);
+
+       status = gpiod_line_request_output(line_0, "gpiod-unit", false, 0);
+       GU_ASSERT_RET_OK(status);
+       status = gpiod_line_request_output(line_1, "gpiod-unit", false, 1);
+       GU_ASSERT_RET_OK(status);
+
+       GU_ASSERT_EQ(gpiod_line_get_value(line_0), 0);
+       GU_ASSERT_EQ(gpiod_line_get_value(line_1), 1);
+
+       gpiod_line_release(line_0);
+       gpiod_line_release(line_1);
+}
+GU_DEFINE_TEST(line_request_output,
+              "gpiod_line_request_output() - good",
+              GU_LINES_UNNAMED, { 8 });
+
+static void line_request_already_requested(void)
+{
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
+       struct gpiod_line *line;
+       int status;
+
+       chip = gpiod_chip_open(gu_chip_path(0));
+       GU_ASSERT_NOT_NULL(chip);
+
+       line = gpiod_chip_get_line(chip, 0);
+       GU_ASSERT_NOT_NULL(line);
+
+       status = gpiod_line_request_input(line, "gpiod-unit", false);
+       GU_ASSERT_RET_OK(status);
+
+       status = gpiod_line_request_input(line, "gpiod-unit", false);
+       GU_ASSERT_NOTEQ(status, 0);
+       GU_ASSERT_EQ(gpiod_errno(), GPIOD_ELINEBUSY);
+}
+GU_DEFINE_TEST(line_request_already_requested,
+              "gpiod_line_request() - already requested",
+              GU_LINES_UNNAMED, { 8 });
+
+static void line_consumer(void)
+{
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
+       struct gpiod_line *line;
+       int status;
+
+       chip = gpiod_chip_open(gu_chip_path(0));
+       GU_ASSERT_NOT_NULL(chip);
+
+       line = gpiod_chip_get_line(chip, 0);
+       GU_ASSERT_NOT_NULL(line);
+
+       GU_ASSERT_NULL(gpiod_line_consumer(line));
+
+       status = gpiod_line_request_input(line, "gpiod-unit", false);
+       GU_ASSERT_RET_OK(status);
+
+       GU_ASSERT(!gpiod_line_needs_update(line));
+       GU_ASSERT_STR_EQ(gpiod_line_consumer(line), "gpiod-unit");
+}
+GU_DEFINE_TEST(line_consumer,
+              "gpiod_line_consumer() - good",
+              GU_LINES_UNNAMED, { 8 });
+
+static void line_request_bulk_output(void)
+{
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chipA = NULL;
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chipB = NULL;
+       struct gpiod_line *lineA0;
+       struct gpiod_line *lineA1;
+       struct gpiod_line *lineA2;
+       struct gpiod_line *lineA3;
+       struct gpiod_line *lineB0;
+       struct gpiod_line *lineB1;
+       struct gpiod_line *lineB2;
+       struct gpiod_line *lineB3;
+       struct gpiod_line_bulk bulkA;
+       struct gpiod_line_bulk bulkB = GPIOD_LINE_BULK_INITIALIZER;
+       int status;
+       int valA[4], valB[4];
+
+       chipA = gpiod_chip_open(gu_chip_path(0));
+       chipB = gpiod_chip_open(gu_chip_path(1));
+       GU_ASSERT_NOT_NULL(chipA);
+       GU_ASSERT_NOT_NULL(chipB);
+
+       gpiod_line_bulk_init(&bulkA);
+
+       lineA0 = gpiod_chip_get_line(chipA, 0);
+       lineA1 = gpiod_chip_get_line(chipA, 1);
+       lineA2 = gpiod_chip_get_line(chipA, 2);
+       lineA3 = gpiod_chip_get_line(chipA, 3);
+       lineB0 = gpiod_chip_get_line(chipB, 0);
+       lineB1 = gpiod_chip_get_line(chipB, 1);
+       lineB2 = gpiod_chip_get_line(chipB, 2);
+       lineB3 = gpiod_chip_get_line(chipB, 3);
+
+       GU_ASSERT_NOT_NULL(lineA0);
+       GU_ASSERT_NOT_NULL(lineA1);
+       GU_ASSERT_NOT_NULL(lineA2);
+       GU_ASSERT_NOT_NULL(lineA3);
+       GU_ASSERT_NOT_NULL(lineB0);
+       GU_ASSERT_NOT_NULL(lineB1);
+       GU_ASSERT_NOT_NULL(lineB2);
+       GU_ASSERT_NOT_NULL(lineB3);
+
+       gpiod_line_bulk_add(&bulkA, lineA0);
+       gpiod_line_bulk_add(&bulkA, lineA1);
+       gpiod_line_bulk_add(&bulkA, lineA2);
+       gpiod_line_bulk_add(&bulkA, lineA3);
+       gpiod_line_bulk_add(&bulkB, lineB0);
+       gpiod_line_bulk_add(&bulkB, lineB1);
+       gpiod_line_bulk_add(&bulkB, lineB2);
+       gpiod_line_bulk_add(&bulkB, lineB3);
+
+       valA[0] = 1;
+       valA[1] = 0;
+       valA[2] = 0;
+       valA[3] = 1;
+       status = gpiod_line_request_bulk_output(&bulkA, "gpiod-unit",
+                                               false, valA);
+       GU_ASSERT_RET_OK(status);
+
+       valB[0] = 0;
+       valB[1] = 1;
+       valB[2] = 0;
+       valB[3] = 1;
+       status = gpiod_line_request_bulk_output(&bulkB, "gpiod-unit",
+                                               false, valB);
+       GU_ASSERT_RET_OK(status);
+
+       memset(valA, 0, sizeof(valA));
+       memset(valB, 0, sizeof(valB));
+
+       status = gpiod_line_get_value_bulk(&bulkA, valA);
+       GU_ASSERT_RET_OK(status);
+       GU_ASSERT_EQ(valA[0], 1);
+       GU_ASSERT_EQ(valA[1], 0);
+       GU_ASSERT_EQ(valA[2], 0);
+       GU_ASSERT_EQ(valA[3], 1);
+
+       status = gpiod_line_get_value_bulk(&bulkB, valB);
+       GU_ASSERT_RET_OK(status);
+       GU_ASSERT_EQ(valB[0], 0);
+       GU_ASSERT_EQ(valB[1], 1);
+       GU_ASSERT_EQ(valB[2], 0);
+       GU_ASSERT_EQ(valB[3], 1);
+
+       gpiod_line_release_bulk(&bulkA);
+       gpiod_line_release_bulk(&bulkB);
+}
+GU_DEFINE_TEST(line_request_bulk_output,
+              "gpiod_line_request_bulk_output() - good",
+              GU_LINES_UNNAMED, { 8, 8 });
+
+static void line_request_bulk_different_chips(void)
+{
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chipA = NULL;
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chipB = NULL;
+       struct gpiod_line_request_config req;
+       struct gpiod_line *lineA0;
+       struct gpiod_line *lineA1;
+       struct gpiod_line *lineB0;
+       struct gpiod_line *lineB1;
+       struct gpiod_line_bulk bulk;
+       int status;
+
+       chipA = gpiod_chip_open(gu_chip_path(0));
+       chipB = gpiod_chip_open(gu_chip_path(1));
+       GU_ASSERT_NOT_NULL(chipA);
+       GU_ASSERT_NOT_NULL(chipB);
+
+       lineA0 = gpiod_chip_get_line(chipA, 0);
+       lineA1 = gpiod_chip_get_line(chipA, 1);
+       lineB0 = gpiod_chip_get_line(chipB, 0);
+       lineB1 = gpiod_chip_get_line(chipB, 1);
+
+       GU_ASSERT_NOT_NULL(lineA0);
+       GU_ASSERT_NOT_NULL(lineA1);
+       GU_ASSERT_NOT_NULL(lineB0);
+       GU_ASSERT_NOT_NULL(lineB1);
+
+       gpiod_line_bulk_init(&bulk);
+       gpiod_line_bulk_add(&bulk, lineA0);
+       gpiod_line_bulk_add(&bulk, lineA1);
+       gpiod_line_bulk_add(&bulk, lineB0);
+       gpiod_line_bulk_add(&bulk, lineB1);
+
+       req.consumer = "gpiod-unit";
+       req.direction = GPIOD_DIRECTION_INPUT;
+       req.active_state = GPIOD_ACTIVE_STATE_HIGH;
+
+       status = gpiod_line_request_bulk(&bulk, &req, NULL);
+       GU_ASSERT_NOTEQ(status, 0);
+       GU_ASSERT_EQ(gpiod_errno(), GPIOD_EBULKINCOH);
+}
+GU_DEFINE_TEST(line_request_bulk_different_chips,
+              "gpiod_line_request_bulk() - different chips",
+              GU_LINES_UNNAMED, { 8, 8 });
+
+static void line_set_value(void)
+{
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
+       struct gpiod_line *line;
+       int status;
+
+       chip = gpiod_chip_open(gu_chip_path(0));
+       GU_ASSERT_NOT_NULL(chip);
+
+       line = gpiod_chip_get_line(chip, 2);
+       GU_ASSERT_NOT_NULL(line);
+
+       status = gpiod_line_request_output(line, "gpiod-unit", false, 0);
+       GU_ASSERT_RET_OK(status);
+
+       GU_ASSERT_RET_OK(gpiod_line_set_value(line, 1));
+       GU_ASSERT_EQ(gpiod_line_get_value(line), 1);
+       GU_ASSERT_RET_OK(gpiod_line_set_value(line, 0));
+       GU_ASSERT_EQ(gpiod_line_get_value(line), 0);
+
+       gpiod_line_release(line);
+}
+GU_DEFINE_TEST(line_set_value,
+              "gpiod_line_set_value() - good",
+              GU_LINES_UNNAMED, { 8 });
+
+static void line_find_by_name_good(void)
+{
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
+       struct gpiod_line *line;
+
+       line = gpiod_line_find_by_name("gpio-mockup-C-12");
+       GU_ASSERT_NOT_NULL(line);
+       chip = gpiod_line_get_chip(line);
+
+       GU_ASSERT_STR_EQ(gpiod_chip_label(chip), "gpio-mockup-C");
+       GU_ASSERT_EQ(gpiod_line_offset(line), 12);
+}
+GU_DEFINE_TEST(line_find_by_name_good,
+              "gpiod_line_find_by_name() - good",
+              GU_LINES_NAMED, { 16, 16, 32, 16 });
+
+static void line_direction(void)
+{
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
+       struct gpiod_line *line;
+       int status;
+
+       chip = gpiod_chip_open(gu_chip_path(0));
+       GU_ASSERT_NOT_NULL(chip);
+
+       line = gpiod_chip_get_line(chip, 5);
+       GU_ASSERT_NOT_NULL(line);
+
+       status = gpiod_line_request_output(line, "gpiod-unit", false, 0);
+       GU_ASSERT_RET_OK(status);
+
+       GU_ASSERT_EQ(gpiod_line_direction(line), GPIOD_DIRECTION_OUTPUT);
+
+       gpiod_line_release(line);
+
+       status = gpiod_line_request_input(line, "gpiod-unit", false);
+       GU_ASSERT_RET_OK(status);
+
+       GU_ASSERT_EQ(gpiod_line_direction(line), GPIOD_DIRECTION_INPUT);
+}
+GU_DEFINE_TEST(line_direction,
+              "gpiod_line_direction() - set & get",
+              GU_LINES_UNNAMED, { 8 });
+
+static void line_active_state(void)
+{
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
+       struct gpiod_line *line;
+       int status;
+
+       chip = gpiod_chip_open(gu_chip_path(0));
+       GU_ASSERT_NOT_NULL(chip);
+
+       line = gpiod_chip_get_line(chip, 5);
+       GU_ASSERT_NOT_NULL(line);
+
+       status = gpiod_line_request_input(line, "gpiod-unit", false);
+       GU_ASSERT_RET_OK(status);
+
+       GU_ASSERT_EQ(gpiod_line_active_state(line), GPIOD_ACTIVE_STATE_HIGH);
+
+       gpiod_line_release(line);
+
+       status = gpiod_line_request_input(line, "gpiod-unit", true);
+       GU_ASSERT_RET_OK(status);
+
+       GU_ASSERT_EQ(gpiod_line_direction(line), GPIOD_ACTIVE_STATE_LOW);
+}
+GU_DEFINE_TEST(line_active_state,
+              "gpiod_line_active_state() - set & get",
+              GU_LINES_UNNAMED, { 8 });
+
+static void line_misc_flags(void)
+{
+       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
+       struct gpiod_line_request_config config;
+       struct gpiod_line *line;
+       int status;
+
+       chip = gpiod_chip_open(gu_chip_path(0));
+       GU_ASSERT_NOT_NULL(chip);
+
+       line = gpiod_chip_get_line(chip, 2);
+       GU_ASSERT_NOT_NULL(line);
+
+       GU_ASSERT_FALSE(gpiod_line_is_used_by_kernel(line));
+       GU_ASSERT_FALSE(gpiod_line_is_open_drain(line));
+       GU_ASSERT_FALSE(gpiod_line_is_open_source(line));
+
+       config.direction = GPIOD_DIRECTION_INPUT;
+       config.consumer = "gpiod-unit";
+       config.active_state = GPIOD_ACTIVE_STATE_HIGH;
+       config.flags = GPIOD_REQUEST_OPEN_DRAIN;
+
+       status = gpiod_line_request(line, &config, 0);
+       GU_ASSERT_RET_OK(status);
+
+       GU_ASSERT(gpiod_line_is_used_by_kernel(line));
+       GU_ASSERT(gpiod_line_is_open_drain(line));
+       GU_ASSERT_FALSE(gpiod_line_is_open_source(line));
+
+       gpiod_line_release(line);
+
+       config.flags = GPIOD_REQUEST_OPEN_SOURCE;
+
+       status = gpiod_line_request(line, &config, 0);
+       GU_ASSERT_RET_OK(status);
+
+       GU_ASSERT(gpiod_line_is_used_by_kernel(line));
+       GU_ASSERT_FALSE(gpiod_line_is_open_drain(line));
+       GU_ASSERT(gpiod_line_is_open_source(line));
+}
+GU_DEFINE_TEST(line_misc_flags,
+              "gpiod_line - misc flags",
+              GU_LINES_UNNAMED, { 8 });
diff --git a/tests/tests-misc.c b/tests/tests-misc.c
new file mode 100644 (file)
index 0000000..72e7f4d
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Misc test cases for libgpiod.
+ *
+ * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2.1 of the GNU Lesser General Public License
+ * as published by the Free Software Foundation.
+ */
+
+#include "gpiod-unit.h"
+
+#include <errno.h>
+
+static void version_string(void)
+{
+       /* Check that gpiod_version_string() returns an actual string. */
+       GU_ASSERT_NOT_NULL(gpiod_version_string());
+       GU_ASSERT(strlen(gpiod_version_string()) > 0);
+}
+GU_DEFINE_TEST(version_string,
+              "gpiod_version_string()",
+              GU_LINES_UNNAMED, { 1 });
+
+static void error_handling(void)
+{
+       struct gpiod_chip *chip;
+       int err;
+
+       chip = gpiod_chip_open("/dev/nonexistent_gpiochip");
+       GU_ASSERT_NULL(chip);
+
+       err = gpiod_errno();
+       GU_ASSERT_EQ(err, ENOENT);
+
+       GU_ASSERT_NOT_NULL(gpiod_strerror(err));
+       GU_ASSERT(strlen(gpiod_strerror(err)) > 0);
+       GU_ASSERT_STR_EQ(gpiod_strerror(err), gpiod_last_strerror());
+}
+GU_DEFINE_TEST(error_handling,
+              "error handling",
+              GU_LINES_UNNAMED, { 1 });
diff --git a/tests/tests-simple-api.c b/tests/tests-simple-api.c
new file mode 100644 (file)
index 0000000..e8d8727
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Simple API test cases for libgpiod.
+ *
+ * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2.1 of the GNU Lesser General Public License
+ * as published by the Free Software Foundation.
+ */
+
+#include "gpiod-unit.h"
+
+static void simple_set_get_value(void)
+{
+       int ret;
+
+       ret = gpiod_simple_get_value("gpiod-unit", gu_chip_name(0), 3, false);
+       GU_ASSERT_EQ(ret, 0);
+
+       ret = gpiod_simple_set_value("gpiod-unit", gu_chip_name(0),
+                                    3, 1, false, NULL, NULL);
+       GU_ASSERT_RET_OK(ret);
+
+       ret = gpiod_simple_get_value("gpiod-unit", gu_chip_name(0), 3, false);
+       GU_ASSERT_EQ(ret, 1);
+}
+GU_DEFINE_TEST(simple_set_get_value,
+              "simple set/get value - single line",
+              GU_LINES_UNNAMED, { 8 });
+
+static void simple_set_get_value_multiple(void)
+{
+       unsigned int offsets[] = { 0, 1, 2, 3, 4, 5, 6, 12, 13, 15 };
+       int values[10], ret;
+
+       ret = gpiod_simple_get_value_multiple("gpiod-unit", gu_chip_name(0),
+                                             offsets, values, 10, false);
+       GU_ASSERT_RET_OK(ret);
+
+       GU_ASSERT_EQ(values[0], 0);
+       GU_ASSERT_EQ(values[1], 0);
+       GU_ASSERT_EQ(values[2], 0);
+       GU_ASSERT_EQ(values[3], 0);
+       GU_ASSERT_EQ(values[4], 0);
+       GU_ASSERT_EQ(values[5], 0);
+       GU_ASSERT_EQ(values[6], 0);
+       GU_ASSERT_EQ(values[7], 0);
+       GU_ASSERT_EQ(values[8], 0);
+       GU_ASSERT_EQ(values[9], 0);
+
+       values[0] = 1;
+       values[1] = 1;
+       values[2] = 1;
+       values[3] = 0;
+       values[4] = 0;
+       values[5] = 1;
+       values[6] = 0;
+       values[7] = 1;
+       values[8] = 0;
+       values[9] = 0;
+
+       ret = gpiod_simple_set_value_multiple("gpiod-unit", gu_chip_name(0),
+                                             offsets, values, 10, false,
+                                             NULL, NULL);
+       GU_ASSERT_RET_OK(ret);
+
+       ret = gpiod_simple_get_value_multiple("gpiod-unit", gu_chip_name(0),
+                                             offsets, values, 10, false);
+       GU_ASSERT_RET_OK(ret);
+
+       GU_ASSERT_EQ(values[0], 1);
+       GU_ASSERT_EQ(values[1], 1);
+       GU_ASSERT_EQ(values[2], 1);
+       GU_ASSERT_EQ(values[3], 0);
+       GU_ASSERT_EQ(values[4], 0);
+       GU_ASSERT_EQ(values[5], 1);
+       GU_ASSERT_EQ(values[6], 0);
+       GU_ASSERT_EQ(values[7], 1);
+       GU_ASSERT_EQ(values[8], 0);
+       GU_ASSERT_EQ(values[9], 0);
+}
+GU_DEFINE_TEST(simple_set_get_value_multiple,
+              "simple set/get value - multiple lines",
+              GU_LINES_UNNAMED, { 16 });
+
+static void simple_get_value_multiple_max_lines(void)
+{
+       unsigned int offsets[GPIOD_REQUEST_MAX_LINES + 1];
+       int values[GPIOD_REQUEST_MAX_LINES + 1], ret;
+
+       ret = gpiod_simple_get_value_multiple("gpiod-unit", gu_chip_name(0),
+                                             offsets, values,
+                                             GPIOD_REQUEST_MAX_LINES + 1,
+                                             false);
+       GU_ASSERT_NOTEQ(ret, 0);
+       GU_ASSERT_EQ(gpiod_errno(), GPIOD_ELINEMAX);
+}
+GU_DEFINE_TEST(simple_get_value_multiple_max_lines,
+              "gpiod_simple_get_value_multiple() exceed max lines",
+              GU_LINES_UNNAMED, { 128 });
+
+static void simple_set_value_multiple_max_lines(void)
+{
+       unsigned int offsets[GPIOD_REQUEST_MAX_LINES + 1];
+       int values[GPIOD_REQUEST_MAX_LINES + 1], ret;
+
+       ret = gpiod_simple_set_value_multiple("gpiod-unit", gu_chip_name(0),
+                                             offsets, values,
+                                             GPIOD_REQUEST_MAX_LINES + 1,
+                                             false, NULL, NULL);
+       GU_ASSERT_NOTEQ(ret, 0);
+       GU_ASSERT_EQ(gpiod_errno(), GPIOD_ELINEMAX);
+}
+GU_DEFINE_TEST(simple_set_value_multiple_max_lines,
+              "gpiod_simple_set_value_multiple() exceed max lines",
+              GU_LINES_UNNAMED, { 128 });
+
+struct simple_event_data {
+       bool got_event;
+};
+
+static int simple_event_cb(int evtype GU_UNUSED,
+                          const struct timespec *ts GU_UNUSED,
+                          void *data)
+{
+       struct simple_event_data *evdata = data;
+
+       evdata->got_event = true;
+
+       return GPIOD_EVENT_CB_STOP;
+}
+
+static void simple_event_loop(void)
+{
+       struct simple_event_data evdata = { false };
+       struct timespec ts = { 1, 0 };
+       int status;
+
+       gu_set_event(0, 3, GU_EVENT_ALTERNATING, 100);
+
+       status = gpiod_simple_event_loop("gpiod-unit", gu_chip_name(0), 3,
+                                        false, &ts, simple_event_cb, &evdata);
+
+       GU_ASSERT_RET_OK(status);
+       GU_ASSERT(evdata.got_event);
+}
+GU_DEFINE_TEST(simple_event_loop,
+              "gpiod_simple_event_loop() - single event",
+              GU_LINES_UNNAMED, { 8 });
diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am
deleted file mode 100644 (file)
index 33847ef..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-#
-# Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
-#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms of version 2.1 of the GNU Lesser General Public License
-# as published by the Free Software Foundation.
-#
-
-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
-
-gpiod_unit_SOURCES =   gpiod-unit.c \
-                       gpiod-unit.h \
-                       tests-chip.c \
-                       tests-event.c \
-                       tests-iter.c \
-                       tests-line.c \
-                       tests-misc.c \
-                       tests-simple-api.c
-
-check: check-am
-       @echo " ********************************************************"
-       @echo " * Unit tests have been built as tests/unit/gpio-unit.  *"
-       @echo " *                                                      *"
-       @echo " * They require linux kernel version >=v4.11 and the    *"
-       @echo " * gpio-mockup module (must not be built-in).           *"
-       @echo " *                                                      *"
-       @echo " * Run the test executable with superuser privileges or *"
-       @echo " * make sure /dev/gpiochipX files are readable and      *"
-       @echo " * writable by normal users.                            *"
-       @echo " ********************************************************"
diff --git a/tests/unit/gpiod-unit.c b/tests/unit/gpiod-unit.c
deleted file mode 100644 (file)
index 100e8ca..0000000
+++ /dev/null
@@ -1,696 +0,0 @@
-/*
- * Unit testing framework for libgpiod.
- *
- * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2.1 of the GNU Lesser General Public License
- * as published by the Free Software Foundation.
- */
-
-#include "gpiod-unit.h"
-
-#include <stdio.h>
-#include <string.h>
-#include <stdarg.h>
-#include <errno.h>
-#include <unistd.h>
-#include <poll.h>
-#include <libkmod.h>
-#include <libudev.h>
-#include <pthread.h>
-#include <sys/time.h>
-#include <sys/utsname.h>
-
-#define NORETURN       __attribute__((noreturn))
-#define MALLOC         __attribute__((malloc))
-
-static const char mockup_devpath[] = "/devices/platform/gpio-mockup/gpiochip";
-
-struct mockup_chip {
-       char *path;
-       char *name;
-       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 {
-       struct _gu_test *test_list_head;
-       struct _gu_test *test_list_tail;
-       unsigned int num_tests;
-       unsigned int tests_failed;
-       struct kmod_ctx *module_ctx;
-       struct kmod_module *module;
-       struct test_context test_ctx;
-} globals;
-
-enum {
-       CNORM = 0,
-       CGREEN,
-       CRED,
-       CREDBOLD,
-       CYELLOW,
-};
-
-static const char *term_colors[] = {
-       "\033[0m",
-       "\033[32m",
-       "\033[31m",
-       "\033[1m\033[31m",
-       "\033[33m",
-};
-
-static void set_color(int color)
-{
-       fprintf(stderr, "%s", term_colors[color]);
-}
-
-static void reset_color(void)
-{
-       fprintf(stderr, "%s", term_colors[CNORM]);
-}
-
-static void pr_raw_v(const char *fmt, va_list va)
-{
-       vfprintf(stderr, fmt, va);
-}
-
-static GU_PRINTF(1, 2) void pr_raw(const char *fmt, ...)
-{
-       va_list va;
-
-       va_start(va, fmt);
-       pr_raw_v(fmt, va);
-       va_end(va);
-}
-
-static void print_header(const char *hdr, int color)
-{
-       set_color(color);
-       pr_raw("[%-5s] ", hdr);
-       reset_color();
-}
-
-static void vmsgn(const char *hdr, int hdr_clr, const char *fmt, va_list va)
-{
-       print_header(hdr, hdr_clr);
-       pr_raw_v(fmt, va);
-}
-
-static void vmsg(const char *hdr, int hdr_clr, const char *fmt, va_list va)
-{
-       vmsgn(hdr, hdr_clr, fmt, va);
-       pr_raw("\n");
-}
-
-static GU_PRINTF(1, 2) void msg(const char *fmt, ...)
-{
-       va_list va;
-
-       va_start(va, fmt);
-       vmsg("INFO", CGREEN, fmt, va);
-       va_end(va);
-}
-
-static GU_PRINTF(1, 2) void err(const char *fmt, ...)
-{
-       va_list va;
-
-       va_start(va, fmt);
-       vmsg("ERROR", CRED, fmt, va);
-       va_end(va);
-}
-
-static GU_PRINTF(1, 2) NORETURN void die(const char *fmt, ...)
-{
-       va_list va;
-
-       va_start(va, fmt);
-       vmsg("FATAL", CRED, fmt, va);
-       va_end(va);
-
-       exit(EXIT_FAILURE);
-}
-
-static GU_PRINTF(1, 2) NORETURN void die_perr(const char *fmt, ...)
-{
-       va_list va;
-
-       va_start(va, fmt);
-       vmsgn("FATAL", CRED, fmt, va);
-       pr_raw(": %s\n", strerror(errno));
-       va_end(va);
-
-       exit(EXIT_FAILURE);
-}
-
-static MALLOC void * xzalloc(size_t size)
-{
-       void *ptr;
-
-       ptr = malloc(size);
-       if (!ptr)
-               die("out of memory");
-
-       memset(ptr, 0, size);
-
-       return ptr;
-}
-
-static MALLOC char * xstrdup(const char *str)
-{
-       char *ret;
-
-       ret = strdup(str);
-       if (!ret)
-               die("out of memory");
-
-       return ret;
-}
-
-static MALLOC GU_PRINTF(2, 3) char * xappend(char *str, const char *fmt, ...)
-{
-       char *new, *ret;
-       va_list va;
-       int status;
-
-       va_start(va, fmt);
-       status = vasprintf(&new, fmt, va);
-       va_end(va);
-       if (status < 0)
-               die_perr("vasprintf");
-
-       if (!str)
-               return new;
-
-       ret = realloc(str, strlen(str) + strlen(new) + 1);
-       if (!ret)
-               die("out of memory");
-
-       strcat(ret, new);
-       free(new);
-
-       return ret;
-}
-
-static void check_chip_index(unsigned int index)
-{
-       if (index >= globals.test_ctx.num_chips)
-               die("invalid chip number requested from test code");
-}
-
-static bool mockup_loaded(void)
-{
-       int state;
-
-       if (!globals.module_ctx || !globals.module)
-               return false;
-
-       state = kmod_module_get_initstate(globals.module);
-
-       return state == KMOD_MODULE_LIVE;
-}
-
-static void module_cleanup(void)
-{
-       msg("cleaning up");
-
-       if (mockup_loaded())
-               kmod_module_remove_module(globals.module, 0);
-
-       if (globals.module)
-               kmod_module_unref(globals.module);
-
-       if (globals.module_ctx)
-               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;
-       struct utsname un;
-
-       msg("checking the linux kernel version");
-
-       status = uname(&un);
-       if (status)
-               die_perr("uname");
-
-       status = sscanf(un.release, "%d.%d", &version, &patchlevel);
-       if (status != 2)
-               die("error reading kernel release version");
-
-       if (version < 4 || patchlevel < 11)
-               die("linux kernel version must be at least v4.11 - got v%d.%d",
-                   version, patchlevel);
-
-       msg("kernel release is v%d.%d - ok to run tests", version, patchlevel);
-}
-
-static void check_gpio_mockup(void)
-{
-       const char *modpath;
-       int status;
-
-       msg("checking gpio-mockup availability");
-
-       globals.module_ctx = kmod_new(NULL, NULL);
-       if (!globals.module_ctx)
-               die_perr("error creating kernel module context");
-
-       status = kmod_module_new_from_name(globals.module_ctx,
-                                          "gpio-mockup", &globals.module);
-       if (status)
-               die_perr("error allocating module info");
-
-       /* First see if we can find the module. */
-       modpath = kmod_module_get_path(globals.module);
-       if (!modpath)
-               die("the gpio-mockup module does not exist in the system or is built into the kernel");
-
-       /* Then see if we can freely load and unload it. */
-       status = kmod_module_probe_insert_module(globals.module, 0,
-                                                "gpio_mockup_ranges=-1,4",
-                                                NULL, NULL, NULL);
-       if (status)
-               die_perr("unable to load gpio-mockup");
-
-       status = kmod_module_remove_module(globals.module, 0);
-       if (status)
-               die_perr("unable to remove gpio-mockup");
-
-       msg("gpio-mockup ok");
-}
-
-static void test_load_module(struct _gu_chip_descr *descr)
-{
-       unsigned int i;
-       char *modarg;
-       int status;
-
-       modarg = xappend(NULL, "gpio_mockup_ranges=");
-       for (i = 0; i < descr->num_chips; i++)
-               modarg = xappend(modarg, "-1,%u,", descr->num_lines[i]);
-       modarg[strlen(modarg) - 1] = '\0'; /* Remove the last comma. */
-
-       if (descr->named_lines)
-               modarg = xappend(modarg, " gpio_mockup_named_lines");
-
-       status = kmod_module_probe_insert_module(globals.module, 0,
-                                                modarg, NULL, NULL, NULL);
-       if (status)
-               die_perr("unable to load gpio-mockup");
-
-       free(modarg);
-}
-
-static int chipcmp(const void *c1, const void *c2)
-{
-       const struct mockup_chip *chip1 = *(const struct mockup_chip **)c1;
-       const struct mockup_chip *chip2 = *(const struct mockup_chip **)c2;
-
-       return chip1->number > chip2->number;
-}
-
-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;
-       struct udev_device *dev;
-       struct udev *udev_ctx;
-       struct pollfd pfd;
-       int status;
-
-       ctx = &globals.test_ctx;
-       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
-        * 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);
-
-       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");
-
-               devpath = udev_device_get_devpath(dev);
-               devnode = udev_device_get_devnode(dev);
-               sysname = udev_device_get_sysname(dev);
-
-               if (!devpath || !devnode || !sysname ||
-                   !devpath_is_mockup(devpath)) {
-                       udev_device_unref(dev);
-                       continue;
-               }
-
-               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");
-
-               ctx->chips[detected++] = chip;
-               udev_device_unref(dev);
-       }
-
-       udev_monitor_unref(monitor);
-       udev_unref(udev_ctx);
-
-       /*
-        * We can't assume that the order in which the mockup gpiochip
-        * devices are created will be deterministic, yet we want the
-        * index passed to the gu_chip_*() functions to correspond with the
-        * order in which the chips were defined in the GU_DEFINE_TEST()
-        * macro.
-        *
-        * Once all gpiochips are there, sort them by chip number.
-        */
-       qsort(ctx->chips, ctx->num_chips, sizeof(*ctx->chips), chipcmp);
-}
-
-static void test_run(struct _gu_test *test)
-{
-       print_header("TEST", CYELLOW);
-       pr_raw("'%s': ", test->name);
-
-       test->func();
-
-       if (globals.test_ctx.test_failed) {
-               globals.tests_failed++;
-               set_color(CREDBOLD);
-               pr_raw("FAILED:");
-               reset_color();
-               set_color(CRED);
-               pr_raw("\n\t\t'%s': %s\n",
-                      test->name, globals.test_ctx.failed_msg);
-               reset_color();
-               free(globals.test_ctx.failed_msg);
-       } else {
-               set_color(CGREEN);
-               pr_raw("OK\n");
-               reset_color();
-       }
-}
-
-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];
-
-               free(chip->path);
-               free(chip->name);
-               free(chip);
-       }
-
-       free(globals.test_ctx.chips);
-
-       status = kmod_module_remove_module(globals.module, 0);
-       if (status)
-               die_perr("unable to remove gpio-mockup");
-}
-
-int main(int argc GU_UNUSED, char **argv GU_UNUSED)
-{
-       struct _gu_test *test;
-
-       atexit(module_cleanup);
-
-       msg("libgpiod unit-test suite");
-       msg("%u tests registered", globals.num_tests);
-
-       check_kernel();
-       check_gpio_mockup();
-
-       msg("running tests");
-
-       for (test = globals.test_list_head; test; test = test->_next) {
-               test_prepare(&test->chip_descr);
-               test_run(test);
-               test_teardown();
-       }
-
-       if (!globals.tests_failed)
-               msg("all tests passed");
-       else
-               err("%u out of %u tests failed",
-                   globals.tests_failed, globals.num_tests);
-
-       return globals.tests_failed ? EXIT_FAILURE : EXIT_SUCCESS;
-}
-
-void gu_close_chip(struct gpiod_chip **chip)
-{
-       if (*chip)
-               gpiod_chip_close(*chip);
-}
-
-void gu_free_str(char **str)
-{
-       if (*str)
-               free(*str);
-}
-
-void gu_free_chip_iter(struct gpiod_chip_iter **iter)
-{
-       if (*iter)
-               gpiod_chip_iter_free(*iter);
-}
-
-void gu_free_chip_iter_noclose(struct gpiod_chip_iter **iter)
-{
-       if (*iter)
-               gpiod_chip_iter_free_noclose(*iter);
-}
-
-const char * gu_chip_path(unsigned int index)
-{
-       check_chip_index(index);
-
-       return globals.test_ctx.chips[index]->path;
-}
-
-const char * gu_chip_name(unsigned int index)
-{
-       check_chip_index(index);
-
-       return globals.test_ctx.chips[index]->name;
-}
-
-unsigned int gu_chip_num(unsigned int index)
-{
-       check_chip_index(index);
-
-       return globals.test_ctx.chips[index]->number;
-}
-
-void _gu_register_test(struct _gu_test *test)
-{
-       struct _gu_test *tmp;
-
-       if (!globals.test_list_head) {
-               globals.test_list_head = globals.test_list_tail = test;
-               test->_next = NULL;
-       } else {
-               tmp = globals.test_list_tail;
-               globals.test_list_tail = test;
-               test->_next = NULL;
-               tmp->_next = test;
-       }
-
-       globals.num_tests++;
-}
-
-GU_PRINTF(1, 2) void _gu_test_failed(const char *fmt, ...)
-{
-       int status;
-       va_list va;
-
-       va_start(va, fmt);
-       status = vasprintf(&globals.test_ctx.failed_msg, fmt, va);
-       va_end(va);
-       if (status < 0)
-               die_perr("vasprintf");
-
-       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
deleted file mode 100644 (file)
index a1883e0..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Unit testing framework for libgpiod.
- *
- * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2.1 of the GNU Lesser General Public License
- * as published by the Free Software Foundation.
- */
-
-#ifndef __GPIOD_UNIT_H__
-#define __GPIOD_UNIT_H__
-
-#include <gpiod.h>
-#include <string.h>
-
-#define GU_INIT                        __attribute__((constructor))
-#define GU_UNUSED              __attribute__((unused))
-#define GU_PRINTF(fmt, arg)    __attribute__((format(printf, fmt, arg)))
-#define GU_CLEANUP(func)       __attribute__((cleanup(func)))
-
-#define GU_ARRAY_SIZE(x)       (sizeof(x) / sizeof(*(x)))
-
-typedef void (*gu_test_func)(void);
-
-struct _gu_chip_descr {
-       unsigned int num_chips;
-       unsigned int *num_lines;
-       bool named_lines;
-};
-
-struct _gu_test {
-       struct _gu_test *_next;
-
-       const char *name;
-       gu_test_func func;
-
-       struct _gu_chip_descr chip_descr;
-};
-
-void _gu_register_test(struct _gu_test *test);
-void _gu_test_failed(const char *fmt, ...);
-
-/*
- * This macro should be used for code brevity instead of manually declaring
- * the gu_test structure.
- *
- * The macro accepts the following arguments:
- *   _a_func: name of the test function
- *   _a_name: name of the test case (will be shown to user)
- *   _a_named_lines: indicate whether we want the GPIO lines to be named
- *
- * The last argument must be an array of unsigned integers specifying the
- * number of GPIO lines in each subsequent mockup chip. The size of this
- * array will at the same time specify the number of gpiochips to create.
- */
-#define GU_DEFINE_TEST(_a_func, _a_name, _a_named_lines, ...)          \
-       static unsigned int _##_a_func##_lines[] = __VA_ARGS__;         \
-       static struct _gu_test _##_a_func##_descr = {                   \
-               .name = _a_name,                                        \
-               .func = _a_func,                                        \
-               .chip_descr = {                                         \
-                       .num_chips = GU_ARRAY_SIZE(_##_a_func##_lines), \
-                       .num_lines = _##_a_func##_lines,                \
-                       .named_lines = _a_named_lines,                  \
-               },                                                      \
-       };                                                              \
-       static GU_INIT void _gu_register_##_a_func##_test(void)         \
-       {                                                               \
-               _gu_register_test(&_##_a_func##_descr);                 \
-       }                                                               \
-       static int _gu_##_a_func##_sentinel GU_UNUSED
-
-enum {
-       GU_LINES_UNNAMED = false,
-       GU_LINES_NAMED = true,
-};
-
-/*
- * We want libgpiod tests to co-exist with gpiochips created by other GPIO
- * drivers. For that reason we can't just use hardcoded device file paths or
- * gpiochip names.
- *
- * The test suite detects the chips that were exported by the gpio-mockup
- * module and stores them in the internal test context structure. Test cases
- * should use the routines declared below to access the gpiochip path, name
- * or number by index corresponding with the order in which the mockup chips
- * were requested in the GU_DEFINE_TEST() macro.
- */
-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
- * the same time we don't want any deliberate memory leaks as we also use this
- * test suite to find actual memory leaks in the library with valgrind.
- *
- * For this reason, the tests should generally always use the GU_CLEANUP()
- * macro for dynamically allocated variables and objects that need closing.
- *
- * The functions below can be reused by different tests for common use cases.
- */
-void gu_close_chip(struct gpiod_chip **chip);
-void gu_free_str(char **str);
-void gu_free_chip_iter(struct gpiod_chip_iter **iter);
-void gu_free_chip_iter_noclose(struct gpiod_chip_iter **iter);
-
-#define GU_ASSERT(statement)                                           \
-       do {                                                            \
-               if (!(statement)) {                                     \
-                       _gu_test_failed(                                \
-                               "assertion failed (%s:%d): '%s'",       \
-                               __FILE__, __LINE__, #statement);        \
-                       return;                                         \
-               }                                                       \
-       } while (0)
-
-#define GU_ASSERT_FALSE(statement)     GU_ASSERT(!(statement))
-#define GU_ASSERT_NOT_NULL(ptr)                GU_ASSERT(ptr != NULL)
-#define GU_ASSERT_RET_OK(status)       GU_ASSERT(status == 0)
-#define GU_ASSERT_NULL(ptr)            GU_ASSERT(ptr == NULL)
-#define GU_ASSERT_EQ(a1, a2)           GU_ASSERT(a1 == a2)
-#define GU_ASSERT_NOTEQ(a1, a2)                GU_ASSERT(a1 != a2)
-#define GU_ASSERT_STR_EQ(s1, s2)       GU_ASSERT(strcmp(s1, s2) == 0)
-
-#endif /* __GPIOD_UNIT_H__ */
diff --git a/tests/unit/tests-chip.c b/tests/unit/tests-chip.c
deleted file mode 100644 (file)
index d94eaa8..0000000
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * GPIO chip test cases for libgpiod.
- *
- * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2.1 of the GNU Lesser General Public License
- * as published by the Free Software Foundation.
- */
-
-#include "gpiod-unit.h"
-
-#include <stdio.h>
-#include <errno.h>
-
-static void chip_open_good(void)
-{
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
-
-       chip = gpiod_chip_open(gu_chip_path(0));
-       GU_ASSERT_NOT_NULL(chip);
-}
-GU_DEFINE_TEST(chip_open_good,
-              "gpiod_chip_open() - good",
-              GU_LINES_UNNAMED, { 8 });
-
-static void chip_open_nonexistent(void)
-{
-       struct gpiod_chip *chip;
-
-       chip = gpiod_chip_open("/dev/nonexistent_gpiochip");
-       GU_ASSERT_NULL(chip);
-       GU_ASSERT_EQ(gpiod_errno(), ENOENT);
-}
-GU_DEFINE_TEST(chip_open_nonexistent,
-              "gpiod_chip_open() - nonexistent chip",
-              GU_LINES_UNNAMED, { 8 });
-
-static void chip_open_notty(void)
-{
-       struct gpiod_chip *chip;
-
-       chip = gpiod_chip_open("/dev/null");
-       GU_ASSERT_NULL(chip);
-       GU_ASSERT_EQ(gpiod_errno(), ENOTTY);
-}
-GU_DEFINE_TEST(chip_open_notty,
-              "gpiod_chip_open() - notty",
-              GU_LINES_UNNAMED, { 8 });
-
-static void chip_open_by_name_good(void)
-{
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
-
-       chip = gpiod_chip_open_by_name(gu_chip_name(0));
-       GU_ASSERT_NOT_NULL(chip);
-}
-GU_DEFINE_TEST(chip_open_by_name_good,
-              "gpiod_chip_open_by_name() - good",
-              GU_LINES_UNNAMED, { 8 });
-
-static void chip_open_by_number_good(void)
-{
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
-
-       chip = gpiod_chip_open_by_number(gu_chip_num(0));
-       GU_ASSERT_NOT_NULL(chip);
-}
-GU_DEFINE_TEST(chip_open_by_number_good,
-              "gpiod_chip_open_by_number() - good",
-              GU_LINES_UNNAMED, { 8 });
-
-static void chip_open_lookup(void)
-{
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip_by_label = NULL;
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip_by_name = NULL;
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip_by_path = NULL;
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip_by_num = NULL;
-       GU_CLEANUP(gu_free_str) char *chip_num;
-
-       GU_ASSERT(asprintf(&chip_num, "%u", gu_chip_num(1)) > 0);
-
-       chip_by_name = gpiod_chip_open_lookup(gu_chip_name(1));
-       chip_by_path = gpiod_chip_open_lookup(gu_chip_path(1));
-       chip_by_num = gpiod_chip_open_lookup(chip_num);
-       chip_by_label = gpiod_chip_open_lookup("gpio-mockup-B");
-
-       GU_ASSERT_NOT_NULL(chip_by_name);
-       GU_ASSERT_NOT_NULL(chip_by_path);
-       GU_ASSERT_NOT_NULL(chip_by_num);
-       GU_ASSERT_NOT_NULL(chip_by_label);
-
-       GU_ASSERT_STR_EQ(gpiod_chip_name(chip_by_name), gu_chip_name(1));
-       GU_ASSERT_STR_EQ(gpiod_chip_name(chip_by_path), gu_chip_name(1));
-       GU_ASSERT_STR_EQ(gpiod_chip_name(chip_by_num), gu_chip_name(1));
-       GU_ASSERT_STR_EQ(gpiod_chip_name(chip_by_label), gu_chip_name(1));
-}
-GU_DEFINE_TEST(chip_open_lookup,
-              "gpiod_chip_open_lookup() - good",
-              GU_LINES_UNNAMED, { 8, 8, 8 });
-
-static void chip_open_by_label_good(void)
-{
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
-
-       chip = gpiod_chip_open_by_label("gpio-mockup-D");
-       GU_ASSERT_NOT_NULL(chip);
-       GU_ASSERT_STR_EQ(gpiod_chip_name(chip), gu_chip_name(3));
-}
-GU_DEFINE_TEST(chip_open_by_label_good,
-              "gpiod_chip_open_by_label() - good",
-              GU_LINES_UNNAMED, { 4, 4, 4, 4, 4 });
-
-static void chip_open_by_label_bad(void)
-{
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
-
-       chip = gpiod_chip_open_by_label("nonexistent_gpio_chip");
-       GU_ASSERT_NULL(chip);
-}
-GU_DEFINE_TEST(chip_open_by_label_bad,
-              "gpiod_chip_open_by_label() - bad",
-              GU_LINES_UNNAMED, { 4, 4, 4, 4, 4 });
-
-static void chip_name(void)
-{
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip0 = NULL;
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip1 = NULL;
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip2 = NULL;
-
-       chip0 = gpiod_chip_open(gu_chip_path(0));
-       chip1 = gpiod_chip_open(gu_chip_path(1));
-       chip2 = gpiod_chip_open(gu_chip_path(2));
-       GU_ASSERT_NOT_NULL(chip0);
-       GU_ASSERT_NOT_NULL(chip1);
-       GU_ASSERT_NOT_NULL(chip2);
-
-       GU_ASSERT_STR_EQ(gpiod_chip_name(chip0), gu_chip_name(0));
-       GU_ASSERT_STR_EQ(gpiod_chip_name(chip1), gu_chip_name(1));
-       GU_ASSERT_STR_EQ(gpiod_chip_name(chip2), gu_chip_name(2));
-}
-GU_DEFINE_TEST(chip_name,
-              "gpiod_chip_name()",
-              GU_LINES_UNNAMED, { 8, 8, 8 });
-
-static void chip_label(void)
-{
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip0 = NULL;
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip1 = NULL;
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip2 = NULL;
-
-       chip0 = gpiod_chip_open(gu_chip_path(0));
-       chip1 = gpiod_chip_open(gu_chip_path(1));
-       chip2 = gpiod_chip_open(gu_chip_path(2));
-       GU_ASSERT_NOT_NULL(chip0);
-       GU_ASSERT_NOT_NULL(chip1);
-       GU_ASSERT_NOT_NULL(chip2);
-
-       GU_ASSERT_STR_EQ(gpiod_chip_label(chip0), "gpio-mockup-A");
-       GU_ASSERT_STR_EQ(gpiod_chip_label(chip1), "gpio-mockup-B");
-       GU_ASSERT_STR_EQ(gpiod_chip_label(chip2), "gpio-mockup-C");
-}
-GU_DEFINE_TEST(chip_label,
-              "gpiod_chip_label()",
-              GU_LINES_UNNAMED, { 8, 8, 8 });
-
-static void chip_num_lines(void)
-{
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip0 = NULL;
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip1 = NULL;
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip2 = NULL;
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip3 = NULL;
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip4 = NULL;
-
-       chip0 = gpiod_chip_open(gu_chip_path(0));
-       chip1 = gpiod_chip_open(gu_chip_path(1));
-       chip2 = gpiod_chip_open(gu_chip_path(2));
-       chip3 = gpiod_chip_open(gu_chip_path(3));
-       chip4 = gpiod_chip_open(gu_chip_path(4));
-       GU_ASSERT_NOT_NULL(chip0);
-       GU_ASSERT_NOT_NULL(chip1);
-       GU_ASSERT_NOT_NULL(chip2);
-       GU_ASSERT_NOT_NULL(chip3);
-       GU_ASSERT_NOT_NULL(chip4);
-
-       GU_ASSERT_EQ(gpiod_chip_num_lines(chip0), 1);
-       GU_ASSERT_EQ(gpiod_chip_num_lines(chip1), 4);
-       GU_ASSERT_EQ(gpiod_chip_num_lines(chip2), 8);
-       GU_ASSERT_EQ(gpiod_chip_num_lines(chip3), 16);
-       GU_ASSERT_EQ(gpiod_chip_num_lines(chip4), 32);
-}
-GU_DEFINE_TEST(chip_num_lines,
-              "gpiod_chip_num_lines()",
-              GU_LINES_UNNAMED, { 1, 4, 8, 16, 32 });
diff --git a/tests/unit/tests-event.c b/tests/unit/tests-event.c
deleted file mode 100644 (file)
index 74d2f41..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Line event test cases for libgpiod.
- *
- * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2.1 of the GNU Lesser General Public License
- * as published by the Free Software Foundation.
- */
-
-#include "gpiod-unit.h"
-
-static void event_rising_edge_good(void)
-{
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
-       struct timespec ts = { 1, 0 };
-       struct gpiod_line_event ev;
-       struct gpiod_line *line;
-       int status;
-
-       chip = gpiod_chip_open(gu_chip_path(0));
-       GU_ASSERT_NOT_NULL(chip);
-
-       line = gpiod_chip_get_line(chip, 7);
-       GU_ASSERT_NOT_NULL(line);
-
-       status = gpiod_line_event_request_rising(line, "gpiod-unit", false);
-       GU_ASSERT_RET_OK(status);
-
-       gu_set_event(0, 7, GU_EVENT_RISING, 100);
-
-       status = gpiod_line_event_wait(line, &ts);
-       GU_ASSERT_EQ(status, 1);
-
-       status = gpiod_line_event_read(line, &ev);
-       GU_ASSERT_RET_OK(status);
-
-       GU_ASSERT_EQ(ev.event_type, GPIOD_EVENT_RISING_EDGE);
-}
-GU_DEFINE_TEST(event_rising_edge_good,
-              "events - receive single rising edge event",
-              GU_LINES_UNNAMED, { 8 });
-
-static void event_falling_edge_good(void)
-{
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
-       struct timespec ts = { 1, 0 };
-       struct gpiod_line_event ev;
-       struct gpiod_line *line;
-       int status;
-
-       chip = gpiod_chip_open(gu_chip_path(0));
-       GU_ASSERT_NOT_NULL(chip);
-
-       line = gpiod_chip_get_line(chip, 7);
-       GU_ASSERT_NOT_NULL(line);
-
-       status = gpiod_line_event_request_falling(line, "gpiod-unit", false);
-       GU_ASSERT_RET_OK(status);
-
-       gu_set_event(0, 7, GU_EVENT_FALLING, 100);
-
-       status = gpiod_line_event_wait(line, &ts);
-       GU_ASSERT_EQ(status, 1);
-
-       status = gpiod_line_event_read(line, &ev);
-       GU_ASSERT_RET_OK(status);
-
-       GU_ASSERT_EQ(ev.event_type, GPIOD_EVENT_FALLING_EDGE);
-}
-GU_DEFINE_TEST(event_falling_edge_good,
-              "events - receive single falling edge event",
-              GU_LINES_UNNAMED, { 8 });
-
-static void event_rising_edge_ignore_falling(void)
-{
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
-       struct timespec ts = { 0, 300 };
-       struct gpiod_line *line;
-       int status;
-
-       chip = gpiod_chip_open(gu_chip_path(0));
-       GU_ASSERT_NOT_NULL(chip);
-
-       line = gpiod_chip_get_line(chip, 7);
-       GU_ASSERT_NOT_NULL(line);
-
-       status = gpiod_line_event_request_rising(line, "gpiod-unit", false);
-       GU_ASSERT_RET_OK(status);
-
-       gu_set_event(0, 7, GU_EVENT_FALLING, 100);
-
-       status = gpiod_line_event_wait(line, &ts);
-       GU_ASSERT_EQ(status, 0);
-}
-GU_DEFINE_TEST(event_rising_edge_ignore_falling,
-              "events - request rising edge & ignore falling edge events",
-              GU_LINES_UNNAMED, { 8 });
-
-static void event_rising_edge_active_low(void)
-{
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
-       struct timespec ts = { 1, 0 };
-       struct gpiod_line_event ev;
-       struct gpiod_line *line;
-       int status;
-
-       chip = gpiod_chip_open(gu_chip_path(0));
-       GU_ASSERT_NOT_NULL(chip);
-
-       line = gpiod_chip_get_line(chip, 7);
-       GU_ASSERT_NOT_NULL(line);
-
-       status = gpiod_line_event_request_rising(line, "gpiod-unit", true);
-       GU_ASSERT_RET_OK(status);
-
-       gu_set_event(0, 7, GU_EVENT_RISING, 100);
-
-       status = gpiod_line_event_wait(line, &ts);
-       GU_ASSERT_EQ(status, 1);
-
-       status = gpiod_line_event_read(line, &ev);
-       GU_ASSERT_RET_OK(status);
-
-       GU_ASSERT_EQ(ev.event_type, GPIOD_EVENT_RISING_EDGE);
-}
-GU_DEFINE_TEST(event_rising_edge_active_low,
-              "events - single rising edge event with low active state",
-              GU_LINES_UNNAMED, { 8 });
-
-static void event_get_value(void)
-{
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
-       struct timespec ts = { 1, 0 };
-       struct gpiod_line_event ev;
-       struct gpiod_line *line;
-       int status;
-
-       chip = gpiod_chip_open(gu_chip_path(0));
-       GU_ASSERT_NOT_NULL(chip);
-
-       line = gpiod_chip_get_line(chip, 7);
-       GU_ASSERT_NOT_NULL(line);
-
-       status = gpiod_line_event_request_rising(line, "gpiod-unit", false);
-       GU_ASSERT_RET_OK(status);
-
-       status = gpiod_line_get_value(line);
-       GU_ASSERT_EQ(status, 0);
-
-       gu_set_event(0, 7, GU_EVENT_RISING, 100);
-
-       status = gpiod_line_event_wait(line, &ts);
-       GU_ASSERT_EQ(status, 1);
-
-       status = gpiod_line_event_read(line, &ev);
-       GU_ASSERT_RET_OK(status);
-
-       GU_ASSERT_EQ(ev.event_type, GPIOD_EVENT_RISING_EDGE);
-
-       status = gpiod_line_get_value(line);
-       GU_ASSERT_EQ(status, 1);
-}
-GU_DEFINE_TEST(event_get_value,
-              "events - mixing events and gpiod_line_get_value()",
-              GU_LINES_UNNAMED, { 8 });
diff --git a/tests/unit/tests-iter.c b/tests/unit/tests-iter.c
deleted file mode 100644 (file)
index 00d63ba..0000000
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Iterator test cases for libgpiod.
- *
- * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2.1 of the GNU Lesser General Public License
- * as published by the Free Software Foundation.
- */
-
-#include "gpiod-unit.h"
-
-static void chip_iter(void)
-{
-       GU_CLEANUP(gu_free_chip_iter) struct gpiod_chip_iter *iter = NULL;
-       struct gpiod_chip *chip;
-       bool A, B, C;
-
-       A = B = C = false;
-
-       iter = gpiod_chip_iter_new();
-       GU_ASSERT_NOT_NULL(iter);
-
-       gpiod_foreach_chip(iter, chip) {
-               GU_ASSERT(!gpiod_chip_iter_err(iter));
-
-               if (strcmp(gpiod_chip_label(chip), "gpio-mockup-A") == 0)
-                       A = true;
-               else if (strcmp(gpiod_chip_label(chip), "gpio-mockup-B") == 0)
-                       B = true;
-               else if (strcmp(gpiod_chip_label(chip), "gpio-mockup-C") == 0)
-                       C = true;
-       }
-
-       GU_ASSERT(A);
-       GU_ASSERT(B);
-       GU_ASSERT(C);
-}
-GU_DEFINE_TEST(chip_iter,
-              "gpiod_chip_iter - simple loop",
-              GU_LINES_UNNAMED, { 8, 8, 8 });
-
-static void chip_iter_noclose(void)
-{
-       GU_CLEANUP(gu_free_chip_iter_noclose)
-                       struct gpiod_chip_iter *iter = NULL;
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chipA;
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chipB;
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chipC;
-       struct gpiod_chip *chip;
-       bool A, B, C;
-
-       A = B = C = false;
-
-       iter = gpiod_chip_iter_new();
-       GU_ASSERT_NOT_NULL(iter);
-
-       gpiod_foreach_chip_noclose(iter, chip) {
-               GU_ASSERT(!gpiod_chip_iter_err(iter));
-
-               if (strcmp(gpiod_chip_label(chip), "gpio-mockup-A") == 0) {
-                       A = true;
-                       chipA = chip;
-               } else if (strcmp(gpiod_chip_label(chip),
-                                 "gpio-mockup-B") == 0) {
-                       B = true;
-                       chipB = chip;
-               } else if (strcmp(gpiod_chip_label(chip),
-                                 "gpio-mockup-C") == 0) {
-                       C = true;
-                       chipC = chip;
-               }
-       }
-
-       GU_ASSERT(A);
-       GU_ASSERT(B);
-       GU_ASSERT(C);
-
-       gpiod_chip_iter_free_noclose(iter);
-       iter = NULL;
-
-       /* See if the chips are still open and usable. */
-       GU_ASSERT_STR_EQ(gpiod_chip_label(chipA), "gpio-mockup-A");
-       GU_ASSERT_STR_EQ(gpiod_chip_label(chipB), "gpio-mockup-B");
-       GU_ASSERT_STR_EQ(gpiod_chip_label(chipC), "gpio-mockup-C");
-}
-GU_DEFINE_TEST(chip_iter_noclose,
-              "gpiod_chip_iter - simple loop, noclose variant",
-              GU_LINES_UNNAMED, { 8, 8, 8 });
-
-static void chip_iter_break(void)
-{
-       GU_CLEANUP(gu_free_chip_iter) struct gpiod_chip_iter *iter = NULL;
-       struct gpiod_chip *chip;
-       int i = 0;
-
-       iter = gpiod_chip_iter_new();
-       GU_ASSERT_NOT_NULL(iter);
-
-       gpiod_foreach_chip(iter, chip) {
-               GU_ASSERT(!gpiod_chip_iter_err(iter));
-
-               if ((strcmp(gpiod_chip_label(chip), "gpio-mockup-A") == 0) ||
-                   (strcmp(gpiod_chip_label(chip), "gpio-mockup-B") == 0) ||
-                   (strcmp(gpiod_chip_label(chip), "gpio-mockup-C") == 0))
-                       i++;
-
-               if (i == 3)
-                       break;
-       }
-
-       gpiod_chip_iter_free(iter);
-       iter = NULL;
-
-       GU_ASSERT_EQ(i, 3);
-}
-GU_DEFINE_TEST(chip_iter_break,
-              "gpiod_chip_iter - break",
-              GU_LINES_UNNAMED, { 8, 8, 8, 8, 8 });
-
-static void line_iter(void)
-{
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
-       struct gpiod_line_iter iter;
-       struct gpiod_line *line;
-       unsigned int i = 0;
-
-       chip = gpiod_chip_open(gu_chip_path(0));
-       GU_ASSERT_NOT_NULL(chip);
-
-       gpiod_line_iter_init(&iter, chip);
-
-       gpiod_foreach_line(&iter, line) {
-               GU_ASSERT(!gpiod_line_iter_err(&iter));
-               GU_ASSERT_EQ(i, gpiod_line_offset(line));
-               i++;
-       }
-
-       GU_ASSERT_EQ(8, i);
-}
-GU_DEFINE_TEST(line_iter,
-              "gpiod_line_iter - simple loop, check offsets",
-              GU_LINES_UNNAMED, { 8 });
-
-static void line_iter_static_initializer(void)
-{
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
-       struct gpiod_line *line;
-       unsigned int i = 0;
-
-       chip = gpiod_chip_open(gu_chip_path(0));
-       GU_ASSERT_NOT_NULL(chip);
-
-       {
-               struct gpiod_line_iter iter = GPIOD_LINE_ITER_INITIALIZER(chip);
-
-               gpiod_foreach_line(&iter, line) {
-                       GU_ASSERT(!gpiod_line_iter_err(&iter));
-                       GU_ASSERT_EQ(i, gpiod_line_offset(line));
-                       i++;
-               }
-       }
-
-       GU_ASSERT_EQ(8, i);
-}
-GU_DEFINE_TEST(line_iter_static_initializer,
-              "gpiod_line_iter - simple loop, static initializer",
-              GU_LINES_UNNAMED, { 8 });
diff --git a/tests/unit/tests-line.c b/tests/unit/tests-line.c
deleted file mode 100644 (file)
index 4f430f4..0000000
+++ /dev/null
@@ -1,366 +0,0 @@
-/*
- * GPIO line test cases for libgpiod.
- *
- * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2.1 of the GNU Lesser General Public License
- * as published by the Free Software Foundation.
- */
-
-#include "gpiod-unit.h"
-
-static void line_request_output(void)
-{
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
-       struct gpiod_line *line_0;
-       struct gpiod_line *line_1;
-       int status;
-
-       chip = gpiod_chip_open(gu_chip_path(0));
-       GU_ASSERT_NOT_NULL(chip);
-
-       line_0 = gpiod_chip_get_line(chip, 2);
-       line_1 = gpiod_chip_get_line(chip, 5);
-       GU_ASSERT_NOT_NULL(line_0);
-       GU_ASSERT_NOT_NULL(line_1);
-
-       status = gpiod_line_request_output(line_0, "gpiod-unit", false, 0);
-       GU_ASSERT_RET_OK(status);
-       status = gpiod_line_request_output(line_1, "gpiod-unit", false, 1);
-       GU_ASSERT_RET_OK(status);
-
-       GU_ASSERT_EQ(gpiod_line_get_value(line_0), 0);
-       GU_ASSERT_EQ(gpiod_line_get_value(line_1), 1);
-
-       gpiod_line_release(line_0);
-       gpiod_line_release(line_1);
-}
-GU_DEFINE_TEST(line_request_output,
-              "gpiod_line_request_output() - good",
-              GU_LINES_UNNAMED, { 8 });
-
-static void line_request_already_requested(void)
-{
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
-       struct gpiod_line *line;
-       int status;
-
-       chip = gpiod_chip_open(gu_chip_path(0));
-       GU_ASSERT_NOT_NULL(chip);
-
-       line = gpiod_chip_get_line(chip, 0);
-       GU_ASSERT_NOT_NULL(line);
-
-       status = gpiod_line_request_input(line, "gpiod-unit", false);
-       GU_ASSERT_RET_OK(status);
-
-       status = gpiod_line_request_input(line, "gpiod-unit", false);
-       GU_ASSERT_NOTEQ(status, 0);
-       GU_ASSERT_EQ(gpiod_errno(), GPIOD_ELINEBUSY);
-}
-GU_DEFINE_TEST(line_request_already_requested,
-              "gpiod_line_request() - already requested",
-              GU_LINES_UNNAMED, { 8 });
-
-static void line_consumer(void)
-{
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
-       struct gpiod_line *line;
-       int status;
-
-       chip = gpiod_chip_open(gu_chip_path(0));
-       GU_ASSERT_NOT_NULL(chip);
-
-       line = gpiod_chip_get_line(chip, 0);
-       GU_ASSERT_NOT_NULL(line);
-
-       GU_ASSERT_NULL(gpiod_line_consumer(line));
-
-       status = gpiod_line_request_input(line, "gpiod-unit", false);
-       GU_ASSERT_RET_OK(status);
-
-       GU_ASSERT(!gpiod_line_needs_update(line));
-       GU_ASSERT_STR_EQ(gpiod_line_consumer(line), "gpiod-unit");
-}
-GU_DEFINE_TEST(line_consumer,
-              "gpiod_line_consumer() - good",
-              GU_LINES_UNNAMED, { 8 });
-
-static void line_request_bulk_output(void)
-{
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chipA = NULL;
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chipB = NULL;
-       struct gpiod_line *lineA0;
-       struct gpiod_line *lineA1;
-       struct gpiod_line *lineA2;
-       struct gpiod_line *lineA3;
-       struct gpiod_line *lineB0;
-       struct gpiod_line *lineB1;
-       struct gpiod_line *lineB2;
-       struct gpiod_line *lineB3;
-       struct gpiod_line_bulk bulkA;
-       struct gpiod_line_bulk bulkB = GPIOD_LINE_BULK_INITIALIZER;
-       int status;
-       int valA[4], valB[4];
-
-       chipA = gpiod_chip_open(gu_chip_path(0));
-       chipB = gpiod_chip_open(gu_chip_path(1));
-       GU_ASSERT_NOT_NULL(chipA);
-       GU_ASSERT_NOT_NULL(chipB);
-
-       gpiod_line_bulk_init(&bulkA);
-
-       lineA0 = gpiod_chip_get_line(chipA, 0);
-       lineA1 = gpiod_chip_get_line(chipA, 1);
-       lineA2 = gpiod_chip_get_line(chipA, 2);
-       lineA3 = gpiod_chip_get_line(chipA, 3);
-       lineB0 = gpiod_chip_get_line(chipB, 0);
-       lineB1 = gpiod_chip_get_line(chipB, 1);
-       lineB2 = gpiod_chip_get_line(chipB, 2);
-       lineB3 = gpiod_chip_get_line(chipB, 3);
-
-       GU_ASSERT_NOT_NULL(lineA0);
-       GU_ASSERT_NOT_NULL(lineA1);
-       GU_ASSERT_NOT_NULL(lineA2);
-       GU_ASSERT_NOT_NULL(lineA3);
-       GU_ASSERT_NOT_NULL(lineB0);
-       GU_ASSERT_NOT_NULL(lineB1);
-       GU_ASSERT_NOT_NULL(lineB2);
-       GU_ASSERT_NOT_NULL(lineB3);
-
-       gpiod_line_bulk_add(&bulkA, lineA0);
-       gpiod_line_bulk_add(&bulkA, lineA1);
-       gpiod_line_bulk_add(&bulkA, lineA2);
-       gpiod_line_bulk_add(&bulkA, lineA3);
-       gpiod_line_bulk_add(&bulkB, lineB0);
-       gpiod_line_bulk_add(&bulkB, lineB1);
-       gpiod_line_bulk_add(&bulkB, lineB2);
-       gpiod_line_bulk_add(&bulkB, lineB3);
-
-       valA[0] = 1;
-       valA[1] = 0;
-       valA[2] = 0;
-       valA[3] = 1;
-       status = gpiod_line_request_bulk_output(&bulkA, "gpiod-unit",
-                                               false, valA);
-       GU_ASSERT_RET_OK(status);
-
-       valB[0] = 0;
-       valB[1] = 1;
-       valB[2] = 0;
-       valB[3] = 1;
-       status = gpiod_line_request_bulk_output(&bulkB, "gpiod-unit",
-                                               false, valB);
-       GU_ASSERT_RET_OK(status);
-
-       memset(valA, 0, sizeof(valA));
-       memset(valB, 0, sizeof(valB));
-
-       status = gpiod_line_get_value_bulk(&bulkA, valA);
-       GU_ASSERT_RET_OK(status);
-       GU_ASSERT_EQ(valA[0], 1);
-       GU_ASSERT_EQ(valA[1], 0);
-       GU_ASSERT_EQ(valA[2], 0);
-       GU_ASSERT_EQ(valA[3], 1);
-
-       status = gpiod_line_get_value_bulk(&bulkB, valB);
-       GU_ASSERT_RET_OK(status);
-       GU_ASSERT_EQ(valB[0], 0);
-       GU_ASSERT_EQ(valB[1], 1);
-       GU_ASSERT_EQ(valB[2], 0);
-       GU_ASSERT_EQ(valB[3], 1);
-
-       gpiod_line_release_bulk(&bulkA);
-       gpiod_line_release_bulk(&bulkB);
-}
-GU_DEFINE_TEST(line_request_bulk_output,
-              "gpiod_line_request_bulk_output() - good",
-              GU_LINES_UNNAMED, { 8, 8 });
-
-static void line_request_bulk_different_chips(void)
-{
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chipA = NULL;
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chipB = NULL;
-       struct gpiod_line_request_config req;
-       struct gpiod_line *lineA0;
-       struct gpiod_line *lineA1;
-       struct gpiod_line *lineB0;
-       struct gpiod_line *lineB1;
-       struct gpiod_line_bulk bulk;
-       int status;
-
-       chipA = gpiod_chip_open(gu_chip_path(0));
-       chipB = gpiod_chip_open(gu_chip_path(1));
-       GU_ASSERT_NOT_NULL(chipA);
-       GU_ASSERT_NOT_NULL(chipB);
-
-       lineA0 = gpiod_chip_get_line(chipA, 0);
-       lineA1 = gpiod_chip_get_line(chipA, 1);
-       lineB0 = gpiod_chip_get_line(chipB, 0);
-       lineB1 = gpiod_chip_get_line(chipB, 1);
-
-       GU_ASSERT_NOT_NULL(lineA0);
-       GU_ASSERT_NOT_NULL(lineA1);
-       GU_ASSERT_NOT_NULL(lineB0);
-       GU_ASSERT_NOT_NULL(lineB1);
-
-       gpiod_line_bulk_init(&bulk);
-       gpiod_line_bulk_add(&bulk, lineA0);
-       gpiod_line_bulk_add(&bulk, lineA1);
-       gpiod_line_bulk_add(&bulk, lineB0);
-       gpiod_line_bulk_add(&bulk, lineB1);
-
-       req.consumer = "gpiod-unit";
-       req.direction = GPIOD_DIRECTION_INPUT;
-       req.active_state = GPIOD_ACTIVE_STATE_HIGH;
-
-       status = gpiod_line_request_bulk(&bulk, &req, NULL);
-       GU_ASSERT_NOTEQ(status, 0);
-       GU_ASSERT_EQ(gpiod_errno(), GPIOD_EBULKINCOH);
-}
-GU_DEFINE_TEST(line_request_bulk_different_chips,
-              "gpiod_line_request_bulk() - different chips",
-              GU_LINES_UNNAMED, { 8, 8 });
-
-static void line_set_value(void)
-{
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
-       struct gpiod_line *line;
-       int status;
-
-       chip = gpiod_chip_open(gu_chip_path(0));
-       GU_ASSERT_NOT_NULL(chip);
-
-       line = gpiod_chip_get_line(chip, 2);
-       GU_ASSERT_NOT_NULL(line);
-
-       status = gpiod_line_request_output(line, "gpiod-unit", false, 0);
-       GU_ASSERT_RET_OK(status);
-
-       GU_ASSERT_RET_OK(gpiod_line_set_value(line, 1));
-       GU_ASSERT_EQ(gpiod_line_get_value(line), 1);
-       GU_ASSERT_RET_OK(gpiod_line_set_value(line, 0));
-       GU_ASSERT_EQ(gpiod_line_get_value(line), 0);
-
-       gpiod_line_release(line);
-}
-GU_DEFINE_TEST(line_set_value,
-              "gpiod_line_set_value() - good",
-              GU_LINES_UNNAMED, { 8 });
-
-static void line_find_by_name_good(void)
-{
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
-       struct gpiod_line *line;
-
-       line = gpiod_line_find_by_name("gpio-mockup-C-12");
-       GU_ASSERT_NOT_NULL(line);
-       chip = gpiod_line_get_chip(line);
-
-       GU_ASSERT_STR_EQ(gpiod_chip_label(chip), "gpio-mockup-C");
-       GU_ASSERT_EQ(gpiod_line_offset(line), 12);
-}
-GU_DEFINE_TEST(line_find_by_name_good,
-              "gpiod_line_find_by_name() - good",
-              GU_LINES_NAMED, { 16, 16, 32, 16 });
-
-static void line_direction(void)
-{
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
-       struct gpiod_line *line;
-       int status;
-
-       chip = gpiod_chip_open(gu_chip_path(0));
-       GU_ASSERT_NOT_NULL(chip);
-
-       line = gpiod_chip_get_line(chip, 5);
-       GU_ASSERT_NOT_NULL(line);
-
-       status = gpiod_line_request_output(line, "gpiod-unit", false, 0);
-       GU_ASSERT_RET_OK(status);
-
-       GU_ASSERT_EQ(gpiod_line_direction(line), GPIOD_DIRECTION_OUTPUT);
-
-       gpiod_line_release(line);
-
-       status = gpiod_line_request_input(line, "gpiod-unit", false);
-       GU_ASSERT_RET_OK(status);
-
-       GU_ASSERT_EQ(gpiod_line_direction(line), GPIOD_DIRECTION_INPUT);
-}
-GU_DEFINE_TEST(line_direction,
-              "gpiod_line_direction() - set & get",
-              GU_LINES_UNNAMED, { 8 });
-
-static void line_active_state(void)
-{
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
-       struct gpiod_line *line;
-       int status;
-
-       chip = gpiod_chip_open(gu_chip_path(0));
-       GU_ASSERT_NOT_NULL(chip);
-
-       line = gpiod_chip_get_line(chip, 5);
-       GU_ASSERT_NOT_NULL(line);
-
-       status = gpiod_line_request_input(line, "gpiod-unit", false);
-       GU_ASSERT_RET_OK(status);
-
-       GU_ASSERT_EQ(gpiod_line_active_state(line), GPIOD_ACTIVE_STATE_HIGH);
-
-       gpiod_line_release(line);
-
-       status = gpiod_line_request_input(line, "gpiod-unit", true);
-       GU_ASSERT_RET_OK(status);
-
-       GU_ASSERT_EQ(gpiod_line_direction(line), GPIOD_ACTIVE_STATE_LOW);
-}
-GU_DEFINE_TEST(line_active_state,
-              "gpiod_line_active_state() - set & get",
-              GU_LINES_UNNAMED, { 8 });
-
-static void line_misc_flags(void)
-{
-       GU_CLEANUP(gu_close_chip) struct gpiod_chip *chip = NULL;
-       struct gpiod_line_request_config config;
-       struct gpiod_line *line;
-       int status;
-
-       chip = gpiod_chip_open(gu_chip_path(0));
-       GU_ASSERT_NOT_NULL(chip);
-
-       line = gpiod_chip_get_line(chip, 2);
-       GU_ASSERT_NOT_NULL(line);
-
-       GU_ASSERT_FALSE(gpiod_line_is_used_by_kernel(line));
-       GU_ASSERT_FALSE(gpiod_line_is_open_drain(line));
-       GU_ASSERT_FALSE(gpiod_line_is_open_source(line));
-
-       config.direction = GPIOD_DIRECTION_INPUT;
-       config.consumer = "gpiod-unit";
-       config.active_state = GPIOD_ACTIVE_STATE_HIGH;
-       config.flags = GPIOD_REQUEST_OPEN_DRAIN;
-
-       status = gpiod_line_request(line, &config, 0);
-       GU_ASSERT_RET_OK(status);
-
-       GU_ASSERT(gpiod_line_is_used_by_kernel(line));
-       GU_ASSERT(gpiod_line_is_open_drain(line));
-       GU_ASSERT_FALSE(gpiod_line_is_open_source(line));
-
-       gpiod_line_release(line);
-
-       config.flags = GPIOD_REQUEST_OPEN_SOURCE;
-
-       status = gpiod_line_request(line, &config, 0);
-       GU_ASSERT_RET_OK(status);
-
-       GU_ASSERT(gpiod_line_is_used_by_kernel(line));
-       GU_ASSERT_FALSE(gpiod_line_is_open_drain(line));
-       GU_ASSERT(gpiod_line_is_open_source(line));
-}
-GU_DEFINE_TEST(line_misc_flags,
-              "gpiod_line - misc flags",
-              GU_LINES_UNNAMED, { 8 });
diff --git a/tests/unit/tests-misc.c b/tests/unit/tests-misc.c
deleted file mode 100644 (file)
index 72e7f4d..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Misc test cases for libgpiod.
- *
- * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2.1 of the GNU Lesser General Public License
- * as published by the Free Software Foundation.
- */
-
-#include "gpiod-unit.h"
-
-#include <errno.h>
-
-static void version_string(void)
-{
-       /* Check that gpiod_version_string() returns an actual string. */
-       GU_ASSERT_NOT_NULL(gpiod_version_string());
-       GU_ASSERT(strlen(gpiod_version_string()) > 0);
-}
-GU_DEFINE_TEST(version_string,
-              "gpiod_version_string()",
-              GU_LINES_UNNAMED, { 1 });
-
-static void error_handling(void)
-{
-       struct gpiod_chip *chip;
-       int err;
-
-       chip = gpiod_chip_open("/dev/nonexistent_gpiochip");
-       GU_ASSERT_NULL(chip);
-
-       err = gpiod_errno();
-       GU_ASSERT_EQ(err, ENOENT);
-
-       GU_ASSERT_NOT_NULL(gpiod_strerror(err));
-       GU_ASSERT(strlen(gpiod_strerror(err)) > 0);
-       GU_ASSERT_STR_EQ(gpiod_strerror(err), gpiod_last_strerror());
-}
-GU_DEFINE_TEST(error_handling,
-              "error handling",
-              GU_LINES_UNNAMED, { 1 });
diff --git a/tests/unit/tests-simple-api.c b/tests/unit/tests-simple-api.c
deleted file mode 100644 (file)
index e8d8727..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Simple API test cases for libgpiod.
- *
- * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2.1 of the GNU Lesser General Public License
- * as published by the Free Software Foundation.
- */
-
-#include "gpiod-unit.h"
-
-static void simple_set_get_value(void)
-{
-       int ret;
-
-       ret = gpiod_simple_get_value("gpiod-unit", gu_chip_name(0), 3, false);
-       GU_ASSERT_EQ(ret, 0);
-
-       ret = gpiod_simple_set_value("gpiod-unit", gu_chip_name(0),
-                                    3, 1, false, NULL, NULL);
-       GU_ASSERT_RET_OK(ret);
-
-       ret = gpiod_simple_get_value("gpiod-unit", gu_chip_name(0), 3, false);
-       GU_ASSERT_EQ(ret, 1);
-}
-GU_DEFINE_TEST(simple_set_get_value,
-              "simple set/get value - single line",
-              GU_LINES_UNNAMED, { 8 });
-
-static void simple_set_get_value_multiple(void)
-{
-       unsigned int offsets[] = { 0, 1, 2, 3, 4, 5, 6, 12, 13, 15 };
-       int values[10], ret;
-
-       ret = gpiod_simple_get_value_multiple("gpiod-unit", gu_chip_name(0),
-                                             offsets, values, 10, false);
-       GU_ASSERT_RET_OK(ret);
-
-       GU_ASSERT_EQ(values[0], 0);
-       GU_ASSERT_EQ(values[1], 0);
-       GU_ASSERT_EQ(values[2], 0);
-       GU_ASSERT_EQ(values[3], 0);
-       GU_ASSERT_EQ(values[4], 0);
-       GU_ASSERT_EQ(values[5], 0);
-       GU_ASSERT_EQ(values[6], 0);
-       GU_ASSERT_EQ(values[7], 0);
-       GU_ASSERT_EQ(values[8], 0);
-       GU_ASSERT_EQ(values[9], 0);
-
-       values[0] = 1;
-       values[1] = 1;
-       values[2] = 1;
-       values[3] = 0;
-       values[4] = 0;
-       values[5] = 1;
-       values[6] = 0;
-       values[7] = 1;
-       values[8] = 0;
-       values[9] = 0;
-
-       ret = gpiod_simple_set_value_multiple("gpiod-unit", gu_chip_name(0),
-                                             offsets, values, 10, false,
-                                             NULL, NULL);
-       GU_ASSERT_RET_OK(ret);
-
-       ret = gpiod_simple_get_value_multiple("gpiod-unit", gu_chip_name(0),
-                                             offsets, values, 10, false);
-       GU_ASSERT_RET_OK(ret);
-
-       GU_ASSERT_EQ(values[0], 1);
-       GU_ASSERT_EQ(values[1], 1);
-       GU_ASSERT_EQ(values[2], 1);
-       GU_ASSERT_EQ(values[3], 0);
-       GU_ASSERT_EQ(values[4], 0);
-       GU_ASSERT_EQ(values[5], 1);
-       GU_ASSERT_EQ(values[6], 0);
-       GU_ASSERT_EQ(values[7], 1);
-       GU_ASSERT_EQ(values[8], 0);
-       GU_ASSERT_EQ(values[9], 0);
-}
-GU_DEFINE_TEST(simple_set_get_value_multiple,
-              "simple set/get value - multiple lines",
-              GU_LINES_UNNAMED, { 16 });
-
-static void simple_get_value_multiple_max_lines(void)
-{
-       unsigned int offsets[GPIOD_REQUEST_MAX_LINES + 1];
-       int values[GPIOD_REQUEST_MAX_LINES + 1], ret;
-
-       ret = gpiod_simple_get_value_multiple("gpiod-unit", gu_chip_name(0),
-                                             offsets, values,
-                                             GPIOD_REQUEST_MAX_LINES + 1,
-                                             false);
-       GU_ASSERT_NOTEQ(ret, 0);
-       GU_ASSERT_EQ(gpiod_errno(), GPIOD_ELINEMAX);
-}
-GU_DEFINE_TEST(simple_get_value_multiple_max_lines,
-              "gpiod_simple_get_value_multiple() exceed max lines",
-              GU_LINES_UNNAMED, { 128 });
-
-static void simple_set_value_multiple_max_lines(void)
-{
-       unsigned int offsets[GPIOD_REQUEST_MAX_LINES + 1];
-       int values[GPIOD_REQUEST_MAX_LINES + 1], ret;
-
-       ret = gpiod_simple_set_value_multiple("gpiod-unit", gu_chip_name(0),
-                                             offsets, values,
-                                             GPIOD_REQUEST_MAX_LINES + 1,
-                                             false, NULL, NULL);
-       GU_ASSERT_NOTEQ(ret, 0);
-       GU_ASSERT_EQ(gpiod_errno(), GPIOD_ELINEMAX);
-}
-GU_DEFINE_TEST(simple_set_value_multiple_max_lines,
-              "gpiod_simple_set_value_multiple() exceed max lines",
-              GU_LINES_UNNAMED, { 128 });
-
-struct simple_event_data {
-       bool got_event;
-};
-
-static int simple_event_cb(int evtype GU_UNUSED,
-                          const struct timespec *ts GU_UNUSED,
-                          void *data)
-{
-       struct simple_event_data *evdata = data;
-
-       evdata->got_event = true;
-
-       return GPIOD_EVENT_CB_STOP;
-}
-
-static void simple_event_loop(void)
-{
-       struct simple_event_data evdata = { false };
-       struct timespec ts = { 1, 0 };
-       int status;
-
-       gu_set_event(0, 3, GU_EVENT_ALTERNATING, 100);
-
-       status = gpiod_simple_event_loop("gpiod-unit", gu_chip_name(0), 3,
-                                        false, &ts, simple_event_cb, &evdata);
-
-       GU_ASSERT_RET_OK(status);
-       GU_ASSERT(evdata.got_event);
-}
-GU_DEFINE_TEST(simple_event_loop,
-              "gpiod_simple_event_loop() - single event",
-              GU_LINES_UNNAMED, { 8 });