when invoking 'make check'. Instead the user must run them manually with
superuser privileges:
- sudo ./tests/unit/gpiod-unit
+ sudo ./tests/gpiod-unit
CONTRIBUTING
------------
src/Makefile
src/lib/Makefile
src/tools/Makefile
- tests/Makefile
- tests/unit/Makefile])
+ tests/Makefile])
AC_OUTPUT
# 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 " ********************************************************"
--- /dev/null
+/*
+ * 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();
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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 });
--- /dev/null
+/*
+ * 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 });
--- /dev/null
+/*
+ * 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 });
--- /dev/null
+/*
+ * 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 });
--- /dev/null
+/*
+ * 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 });
--- /dev/null
+/*
+ * 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 });
+++ /dev/null
-#
-# 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 " ********************************************************"
+++ /dev/null
-/*
- * 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();
-}
+++ /dev/null
-/*
- * 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__ */
+++ /dev/null
-/*
- * 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 });
+++ /dev/null
-/*
- * 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 });
+++ /dev/null
-/*
- * 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 });
+++ /dev/null
-/*
- * 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 });
+++ /dev/null
-/*
- * 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 });
+++ /dev/null
-/*
- * 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 });