libtool
m4/
stamp-h1
+
+# unit tests
+gpiod-unit
AUTOMAKE_OPTIONS = foreign
SUBDIRS = include src
+if WITH_TESTS
+
+SUBDIRS += tests
+
+endif
+
if HAS_DOXYGEN
doc:
AC_CHECK_HEADERS([sys/signalfd.h], [], [HEADER_NOT_FOUND_TOOLS([sys/signalfd.h])])
fi
+AC_ARG_ENABLE([tests],
+ [AC_HELP_STRING([--enable-tests],
+ [enable libgpiod tests [default=no]])],
+ [
+ if test "x$enableval" = xyes
+ then
+ with_tests=true
+ else
+ with_tests=false
+ fi
+ ],
+ [with_tests=false])
+AM_CONDITIONAL([WITH_TESTS], [test "x$with_tests" = xtrue])
+
+AC_DEFUN([HEADER_NOT_FOUND_TESTS],
+ [ERR_NOT_FOUND([$1 header], [tests])])
+
+AC_DEFUN([FUNC_NOT_FOUND_TESTS],
+ [ERR_NOT_FOUND([$1()], [tests])])
+
+if test "x$with_tools" = xtrue
+then
+ AC_CHECK_LIB([kmod], [kmod_module_probe_insert_module], [],
+ [AC_MSG_ERROR([libkmod not found (needed to build tests])])
+ AC_CHECK_HEADERS([libkmod.h], [], [HEADER_NOT_FOUND_TESTS([libkmod.h])])
+ AC_CHECK_FUNC([qsort], [], [FUNC_NOT_FOUND_TESTS([qsort])])
+fi
+
AC_CHECK_PROG([has_doxygen], [doxygen], [true], [false])
AM_CONDITIONAL([HAS_DOXYGEN], [test "x$has_doxygen" = xtrue])
if test "x$has_doxygen" = xfalse
include/Makefile
src/Makefile
src/lib/Makefile
- src/tools/Makefile])
+ src/tools/Makefile
+ tests/Makefile
+ tests/unit/Makefile])
AC_OUTPUT
--- /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.
+#
+
+SUBDIRS = . unit
--- /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_srcdir)/config.h
+AM_CFLAGS += -Wall -Wextra -g
+LDADD = -lgpiod -L$(top_srcdir)/src/lib -lkmod
+DEPENDENCIES = libgpiod.la
+
+check_PROGRAMS = gpiod-unit
+
+gpiod_unit_SOURCES = gpiod-unit.c tests-chip.c
--- /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 <time.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <libkmod.h>
+
+#define NORETURN __attribute__((noreturn))
+
+struct mockup_chip {
+ char *path;
+ char *name;
+ unsigned int number;
+};
+
+struct test_context {
+ struct mockup_chip *chips;
+ size_t num_chips;
+ bool test_failed;
+ struct timeval mod_loaded_ts;
+};
+
+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;
+
+static void vmsg(FILE *stream, const char *hdr, const char *fmt, va_list va)
+{
+ fprintf(stream, "[%.5s] ", hdr);
+ vfprintf(stream, fmt, va);
+ fputc('\n', stream);
+}
+
+void gu_msg(const char *fmt, ...)
+{
+ va_list va;
+
+ va_start(va, fmt);
+ vmsg(stderr, " INFO", fmt, va);
+ va_end(va);
+}
+
+void gu_err(const char *fmt, ...)
+{
+ va_list va;
+
+ va_start(va, fmt);
+ vmsg(stderr, "ERROR", fmt, va);
+ va_end(va);
+}
+
+static void GU_PRINTF(1, 2) NORETURN die(const char *fmt, ...)
+{
+ va_list va;
+
+ va_start(va, fmt);
+ vmsg(stderr, "FATAL", fmt, va);
+ va_end(va);
+
+ exit(EXIT_FAILURE);
+}
+
+static void GU_PRINTF(1, 2) NORETURN die_perr(const char *fmt, ...)
+{
+ va_list va;
+
+ va_start(va, fmt);
+ fprintf(stderr, "[FATAL] ");
+ vfprintf(stderr, fmt, va);
+ fprintf(stderr, ": %s\n", strerror(errno));
+ va_end(va);
+
+ exit(EXIT_FAILURE);
+}
+
+static void check_chip_index(unsigned int index)
+{
+ if (index >= globals.test_ctx.num_chips)
+ die("invalid chip number requested from test code");
+}
+
+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++;
+}
+
+void _gu_set_test_failed(void)
+{
+ globals.test_ctx.test_failed = true;
+}
+
+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)
+{
+ gu_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 check_gpio_mockup(void)
+{
+ const char *modpath;
+ int status;
+
+ gu_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,
+ NULL, 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");
+
+ gu_msg("gpio-mockup ok");
+}
+
+static void test_load_module(struct gu_chip_descr *descr)
+{
+ char *modarg, *tmp_modarg;
+ char **line_sizes;
+ size_t modarg_len;
+ unsigned int i;
+ int status;
+
+ line_sizes = malloc(sizeof(char *) * descr->num_chips);
+ if (!line_sizes)
+ die("out of memory");
+
+ modarg_len = strlen("gpio_mockup_ranges=");
+ for (i = 0; i < descr->num_chips; i++) {
+ status = asprintf(&line_sizes[i],
+ "-1,%u,", descr->num_lines[i]);
+ if (status < 0)
+ die_perr("asprintf");
+
+ modarg_len += status;
+ }
+
+ modarg = malloc(modarg_len + 1);
+ if (!modarg)
+ die("out of memory");
+ tmp_modarg = modarg;
+
+ status = sprintf(tmp_modarg, "gpio_mockup_ranges=");
+ tmp_modarg += status;
+
+ for (i = 0; i < descr->num_chips; i++) {
+ status = sprintf(tmp_modarg, "%s", line_sizes[i]);
+ tmp_modarg += status;
+ }
+
+ /*
+ * TODO Once the support for named lines in the gpio-mockup module
+ * is merged upstream, implement checking the named_lines field of
+ * the test description and setting the corresponding module param.
+ */
+
+ modarg[modarg_len - 1] = '\0'; /* Remove the last comma. */
+
+ gettimeofday(&globals.test_ctx.mod_loaded_ts, NULL);
+ status = kmod_module_probe_insert_module(globals.module, 0,
+ modarg, NULL, NULL, NULL);
+ if (status)
+ die_perr("unable to load gpio-mockup");
+
+ free(modarg);
+ for (i = 0; i < descr->num_chips; i++)
+ free(line_sizes[i]);
+ free(line_sizes);
+}
+
+/*
+ * To see if given chip is a mockup chip, check if it was created after
+ * inserting the gpio-mockup module. It's not too clever, but works well
+ * enough...
+ */
+static bool is_mockup_chip(const char *name)
+{
+ struct timeval gdev_created_ts;
+ struct stat gdev_stat;
+ char *path;
+ int status;
+
+ status = asprintf(&path, "/dev/%s", name);
+ if (status < 0)
+ die("asprintf");
+
+ status = stat(path, &gdev_stat);
+ free(path);
+ if (status < 0)
+ die_perr("stat");
+
+ gdev_created_ts.tv_sec = gdev_stat.st_ctim.tv_sec;
+ gdev_created_ts.tv_usec = gdev_stat.st_ctim.tv_nsec / 1000;
+
+ return timercmp(&globals.test_ctx.mod_loaded_ts, &gdev_created_ts, >=);
+}
+
+static int chipcmp(const void *c1, const void *c2)
+{
+ const struct mockup_chip *chip1 = c1;
+ const struct mockup_chip *chip2 = c2;
+
+ return strcmp(chip1->name, chip2->name);
+}
+
+static void test_prepare(struct gu_chip_descr *descr)
+{
+ struct test_context *ctx;
+ struct mockup_chip *chip;
+ unsigned int current = 0;
+ struct dirent *dentry;
+ int status;
+ DIR *dir;
+
+ ctx = &globals.test_ctx;
+ memset(ctx, 0, sizeof(*ctx));
+
+ test_load_module(descr);
+
+ ctx->num_chips = descr->num_chips;
+ ctx->chips = malloc(sizeof(*ctx->chips) * ctx->num_chips);
+ if (!ctx->chips)
+ die("out of memory");
+
+ dir = opendir("/dev");
+ if (!dir)
+ die_perr("error opening /dev");
+
+ for (dentry = readdir(dir); dentry; dentry = readdir(dir)) {
+ if (strncmp(dentry->d_name, "gpiochip", 8) == 0) {
+ if (!is_mockup_chip(dentry->d_name))
+ continue;
+
+ chip = &ctx->chips[current++];
+
+ chip->name = strdup(dentry->d_name);
+ if (!chip->name)
+ die("out of memory");
+
+ status = asprintf(&chip->path,
+ "/dev/%s", dentry->d_name);
+ if (status < 0)
+ die_perr("asprintf");
+
+ status = sscanf(dentry->d_name,
+ "gpiochip%u", &chip->number);
+ if (status != 1)
+ die("unable to determine the chip number");
+ }
+ }
+
+ qsort(ctx->chips, ctx->num_chips, sizeof(*ctx->chips), chipcmp);
+
+ if (descr->num_chips != current)
+ die("number of requested and detected mockup gpiochips is not the same");
+
+ closedir(dir);
+}
+
+static void test_teardown(void)
+{
+ struct mockup_chip *chip;
+ unsigned int i;
+ int status;
+
+ for (i = 0; i < globals.test_ctx.num_chips; i++) {
+ chip = &globals.test_ctx.chips[i];
+
+ free(chip->path);
+ free(chip->name);
+ }
+
+ 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);
+
+ gu_msg("libgpiod unit-test suite");
+ gu_msg("%u tests registered", globals.num_tests);
+
+ check_gpio_mockup();
+
+ gu_msg("running tests");
+
+ for (test = globals.test_list_head; test; test = test->_next) {
+ test_prepare(&test->chip_descr);
+
+ test->func();
+ gu_msg("test '%s': %s", test->name,
+ globals.test_ctx.test_failed ? "FAILED" : "OK");
+ if (globals.test_ctx.test_failed)
+ globals.tests_failed++;
+
+ test_teardown();
+ }
+
+ if (!globals.tests_failed)
+ gu_msg("all tests passed");
+ else
+ gu_err("%u out of %u tests failed",
+ globals.tests_failed, globals.num_tests);
+
+ return globals.tests_failed ? EXIT_FAILURE : EXIT_SUCCESS;
+}
--- /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 <stdlib.h>
+#include <string.h>
+#include <stdbool.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_set_test_failed(void);
+
+#define GU_REGISTER_TEST(test) \
+ static GU_INIT void _gu_register_##test(void) \
+ { \
+ _gu_register_test(&test); \
+ } \
+ static int _gu_##test##_sentinel GU_UNUSED
+
+#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, \
+ }, \
+ }; \
+ GU_REGISTER_TEST(_##_a_func##_descr)
+
+enum {
+ GU_LINES_UNNAMED = false,
+ GU_LINES_NAMED = true,
+};
+
+void GU_PRINTF(1, 2) gu_msg(const char *fmt, ...);
+void GU_PRINTF(1, 2) gu_err(const char *fmt, ...);
+
+const char * gu_chip_path(unsigned int index);
+const char * gu_chip_name(unsigned int index);
+unsigned int gu_chip_num(unsigned int index);
+
+#define GU_ASSERT(statement) \
+ do { \
+ if (!(statement)) { \
+ gu_err("assertion failed (%s:%d): '%s'", \
+ __FILE__, __LINE__, #statement); \
+ _gu_set_test_failed(); \
+ return; \
+ } \
+ } while (0)
+
+#define GU_ASSERT_NOT_NULL(ptr) GU_ASSERT(ptr != NULL)
+#define GU_ASSERT_NULL(ptr) GU_ASSERT(ptr == NULL)
+#define GU_ASSERT_EQ(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 <gpiod.h>
+
+#include <stdio.h>
+#include <errno.h>
+
+static void close_chip(struct gpiod_chip **chip)
+{
+ if (*chip)
+ gpiod_chip_close(*chip);
+}
+
+static void free_str(char **str)
+{
+ if (*str)
+ free(*str);
+}
+
+static void chip_open_good(void)
+{
+ GU_CLEANUP(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",
+ 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_number_good(void)
+{
+ GU_CLEANUP(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(close_chip) struct gpiod_chip *chip_by_name = NULL;
+ GU_CLEANUP(close_chip) struct gpiod_chip *chip_by_path = NULL;
+ GU_CLEANUP(close_chip) struct gpiod_chip *chip_by_num = NULL;
+ GU_CLEANUP(free_str) char *chip_num;
+
+ GU_ASSERT(asprintf(&chip_num, "%u", gu_chip_num(0)) > 0);
+
+ chip_by_name = gpiod_chip_open_lookup(gu_chip_name(0));
+ chip_by_path = gpiod_chip_open_lookup(gu_chip_path(0));
+ chip_by_num = gpiod_chip_open_lookup(chip_num);
+
+ GU_ASSERT_NOT_NULL(chip_by_name);
+ GU_ASSERT_NOT_NULL(chip_by_path);
+ GU_ASSERT_NOT_NULL(chip_by_num);
+}
+GU_DEFINE_TEST(chip_open_lookup,
+ "gpiod_chip_open_lookup()",
+ GU_LINES_UNNAMED, { 8 });
+
+static void chip_name(void)
+{
+ GU_CLEANUP(close_chip) struct gpiod_chip *chip0 = NULL;
+ GU_CLEANUP(close_chip) struct gpiod_chip *chip1 = NULL;
+ GU_CLEANUP(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(close_chip) struct gpiod_chip *chip0 = NULL;
+ GU_CLEANUP(close_chip) struct gpiod_chip *chip1 = NULL;
+ GU_CLEANUP(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(close_chip) struct gpiod_chip *chip0 = NULL;
+ GU_CLEANUP(close_chip) struct gpiod_chip *chip1 = NULL;
+ GU_CLEANUP(close_chip) struct gpiod_chip *chip2 = NULL;
+ GU_CLEANUP(close_chip) struct gpiod_chip *chip3 = NULL;
+ GU_CLEANUP(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, "chip_num_lines()",
+ GU_LINES_UNNAMED, { 1, 4, 8, 16, 32 });