tests: split out reusable test code into a local static library
authorBartosz Golaszewski <bartosz.golaszewski@linaro.org>
Mon, 12 Aug 2024 08:22:22 +0000 (10:22 +0200)
committerBartosz Golaszewski <bartosz.golaszewski@linaro.org>
Tue, 13 Aug 2024 08:38:40 +0000 (10:38 +0200)
In order to allow the upcoming GLib and DBus bindings to reuse the test
code, let's put all common elements into reusable libtool objects and
export the relevant symbols in internal headers.

Tested-by: Alexander Sverdlin <alexander.sverdlin@siemens.com>
Link: https://lore.kernel.org/r/20240812-dbus-v5-1-ead288509217@linaro.org
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
27 files changed:
configure.ac
tests/Makefile.am
tests/gpiod-test-helpers.c [deleted file]
tests/gpiod-test-helpers.h [deleted file]
tests/gpiod-test-sim.c [deleted file]
tests/gpiod-test-sim.h [deleted file]
tests/gpiod-test.c [deleted file]
tests/gpiod-test.h [deleted file]
tests/gpiosim-glib/Makefile.am [new file with mode: 0644]
tests/gpiosim-glib/gpiosim-glib.c [new file with mode: 0644]
tests/gpiosim-glib/gpiosim-glib.h [new file with mode: 0644]
tests/harness/Makefile.am [new file with mode: 0644]
tests/harness/gpiod-test-common.h [new file with mode: 0644]
tests/harness/gpiod-test.c [new file with mode: 0644]
tests/harness/gpiod-test.h [new file with mode: 0644]
tests/helpers.h [new file with mode: 0644]
tests/tests-chip-info.c
tests/tests-chip.c
tests/tests-edge-event.c
tests/tests-info-event.c
tests/tests-kernel-uapi.c
tests/tests-line-config.c
tests/tests-line-info.c
tests/tests-line-request.c
tests/tests-line-settings.c
tests/tests-misc.c
tests/tests-request-config.c

index b86eee0a4756a42790d568d27424218ea080fcb8..d1f49aca34a86de0f3dbbdcfadd4273e2a827c91 100644 (file)
@@ -275,6 +275,8 @@ AC_CONFIG_FILES([Makefile
                 tools/Makefile
                 tests/Makefile
                 tests/gpiosim/Makefile
+                tests/gpiosim-glib/Makefile
+                tests/harness/Makefile
                 bindings/cxx/libgpiodcxx.pc
                 bindings/Makefile
                 bindings/cxx/Makefile
index a5e1fe04fb87c88d251e67bad12c2f7d445a44c8..c89fd8daa2c60a40b57b78d2b6cebba2f4d90f2d 100644 (file)
@@ -1,25 +1,23 @@
 # SPDX-License-Identifier: GPL-2.0-or-later
 # SPDX-FileCopyrightText: 2017-2022 Bartosz Golaszewski <brgl@bgdev.pl>
 
-SUBDIRS = gpiosim
+SUBDIRS = gpiosim gpiosim-glib harness
 
-AM_CFLAGS = -I$(top_srcdir)/include/ -I$(top_srcdir)/tests/gpiosim/
+AM_CFLAGS = -I$(top_srcdir)/include/ -I$(top_srcdir)/tests/gpiosim-glib/
+AM_CFLAGS += -I$(top_srcdir)/tests/harness/
 AM_CFLAGS += -include $(top_builddir)/config.h
 AM_CFLAGS += -Wall -Wextra -g -std=gnu89 $(GLIB_CFLAGS) $(GIO_CFLAGS)
 AM_CFLAGS += -DG_LOG_DOMAIN=\"gpiod-test\"
 LDADD = $(top_builddir)/lib/libgpiod.la
 LDADD += $(top_builddir)/tests/gpiosim/libgpiosim.la
+LDADD += $(top_builddir)/tests/gpiosim-glib/libgpiosim-glib.la
+LDADD += $(top_builddir)/tests/harness/libgpiod-test-harness.la
 LDADD += $(GLIB_LIBS) $(GIO_LIBS)
 
 noinst_PROGRAMS = gpiod-test
 
 gpiod_test_SOURCES = \
-       gpiod-test.c \
-       gpiod-test.h \
-       gpiod-test-helpers.c \
-       gpiod-test-helpers.h \
-       gpiod-test-sim.c \
-       gpiod-test-sim.h \
+       helpers.h \
        tests-chip.c \
        tests-chip-info.c \
        tests-edge-event.c \
diff --git a/tests/gpiod-test-helpers.c b/tests/gpiod-test-helpers.c
deleted file mode 100644 (file)
index 7e5b396..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* SPDX-FileCopyrightText: 2017-2022 Bartosz Golaszewski <brgl@bgdev.pl> */
-
-/*
- * Testing framework for the core library.
- *
- * This file contains functions and definitions extending the GLib unit testing
- * framework with functionalities necessary to test the libgpiod core C API as
- * well as the kernel-to-user-space interface.
- */
-
-#include "gpiod-test-helpers.h"
-
-GVariant *
-gpiod_test_package_line_names(const GPIOSimLineName *names)
-{
-       g_autoptr(GVariantBuilder) builder = NULL;
-       const GPIOSimLineName *name;
-
-       builder = g_variant_builder_new(G_VARIANT_TYPE("a(us)"));
-
-       for (name = &names[0]; name->name; name++)
-               g_variant_builder_add(builder, "(us)",
-                                     name->offset, name->name);
-
-       return g_variant_ref_sink(g_variant_new("a(us)", builder));
-}
-
-GVariant *gpiod_test_package_hogs(const GPIOSimHog *hogs)
-{
-       g_autoptr(GVariantBuilder) builder = NULL;
-       const GPIOSimHog *hog;
-
-       builder = g_variant_builder_new(G_VARIANT_TYPE("a(usi)"));
-
-       for (hog = &hogs[0]; hog->name; hog++)
-               g_variant_builder_add(builder, "(usi)",
-                                     hog->offset, hog->name, hog->direction);
-
-       return g_variant_ref_sink(g_variant_new("a(usi)", builder));
-}
diff --git a/tests/gpiod-test-helpers.h b/tests/gpiod-test-helpers.h
deleted file mode 100644 (file)
index 41791a3..0000000
+++ /dev/null
@@ -1,203 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <brgl@bgdev.pl> */
-
-#ifndef __GPIOD_TEST_HELPERS_H__
-#define __GPIOD_TEST_HELPERS_H__
-
-#include <errno.h>
-#include <glib.h>
-#include <gpiod.h>
-
-#include "gpiod-test-sim.h"
-
-/*
- * These typedefs are needed to make g_autoptr work - it doesn't accept
- * regular 'struct typename' syntax.
- */
-
-typedef struct gpiod_chip struct_gpiod_chip;
-G_DEFINE_AUTOPTR_CLEANUP_FUNC(struct_gpiod_chip, gpiod_chip_close);
-
-typedef struct gpiod_chip_info struct_gpiod_chip_info;
-G_DEFINE_AUTOPTR_CLEANUP_FUNC(struct_gpiod_chip_info, gpiod_chip_info_free);
-
-typedef struct gpiod_line_info struct_gpiod_line_info;
-G_DEFINE_AUTOPTR_CLEANUP_FUNC(struct_gpiod_line_info, gpiod_line_info_free);
-
-typedef struct gpiod_info_event struct_gpiod_info_event;
-G_DEFINE_AUTOPTR_CLEANUP_FUNC(struct_gpiod_info_event, gpiod_info_event_free);
-
-typedef struct gpiod_line_config struct_gpiod_line_config;
-G_DEFINE_AUTOPTR_CLEANUP_FUNC(struct_gpiod_line_config, gpiod_line_config_free);
-
-typedef struct gpiod_line_settings struct_gpiod_line_settings;
-G_DEFINE_AUTOPTR_CLEANUP_FUNC(struct_gpiod_line_settings,
-                             gpiod_line_settings_free);
-
-typedef struct gpiod_request_config struct_gpiod_request_config;
-G_DEFINE_AUTOPTR_CLEANUP_FUNC(struct_gpiod_request_config,
-                             gpiod_request_config_free);
-
-typedef struct gpiod_line_request struct_gpiod_line_request;
-G_DEFINE_AUTOPTR_CLEANUP_FUNC(struct_gpiod_line_request,
-                             gpiod_line_request_release);
-
-typedef struct gpiod_edge_event struct_gpiod_edge_event;
-G_DEFINE_AUTOPTR_CLEANUP_FUNC(struct_gpiod_edge_event, gpiod_edge_event_free);
-
-typedef struct gpiod_edge_event_buffer struct_gpiod_edge_event_buffer;
-G_DEFINE_AUTOPTR_CLEANUP_FUNC(struct_gpiod_edge_event_buffer,
-                             gpiod_edge_event_buffer_free);
-
-#define gpiod_test_return_if_failed() \
-       do { \
-               if (g_test_failed()) \
-                       return; \
-       } while (0)
-
-#define gpiod_test_join_thread_and_return_if_failed(_thread) \
-       do { \
-               if (g_test_failed()) { \
-                       g_thread_join(_thread); \
-                       return; \
-               } \
-       } while (0)
-
-#define gpiod_test_open_chip_or_fail(_path) \
-       ({ \
-               struct gpiod_chip *_chip = gpiod_chip_open(_path); \
-               g_assert_nonnull(_chip); \
-               gpiod_test_return_if_failed(); \
-               _chip; \
-       })
-
-#define gpiod_test_chip_get_info_or_fail(_chip) \
-       ({ \
-               struct gpiod_chip_info *_info = gpiod_chip_get_info(_chip); \
-               g_assert_nonnull(_info); \
-               gpiod_test_return_if_failed(); \
-               _info; \
-       })
-
-#define gpiod_test_chip_get_line_info_or_fail(_chip, _offset) \
-       ({ \
-               struct gpiod_line_info *_info = \
-                               gpiod_chip_get_line_info(_chip, _offset); \
-               g_assert_nonnull(_info); \
-               gpiod_test_return_if_failed(); \
-               _info; \
-       })
-
-#define gpiod_test_chip_watch_line_info_or_fail(_chip, _offset) \
-       ({ \
-               struct gpiod_line_info *_info = \
-                               gpiod_chip_watch_line_info(_chip, _offset); \
-               g_assert_nonnull(_info); \
-               gpiod_test_return_if_failed(); \
-               _info; \
-       })
-
-#define gpiod_test_create_line_settings_or_fail() \
-       ({ \
-               struct gpiod_line_settings *_settings = \
-                               gpiod_line_settings_new(); \
-               g_assert_nonnull(_settings); \
-               gpiod_test_return_if_failed(); \
-               _settings; \
-       })
-
-#define gpiod_test_create_line_config_or_fail() \
-       ({ \
-               struct gpiod_line_config *_config = \
-                               gpiod_line_config_new(); \
-               g_assert_nonnull(_config); \
-               gpiod_test_return_if_failed(); \
-               _config; \
-       })
-
-#define gpiod_test_create_edge_event_buffer_or_fail(_capacity) \
-       ({ \
-               struct gpiod_edge_event_buffer *_buffer = \
-                               gpiod_edge_event_buffer_new(_capacity); \
-               g_assert_nonnull(_buffer); \
-               gpiod_test_return_if_failed(); \
-               _buffer; \
-       })
-
-#define gpiod_test_line_config_add_line_settings_or_fail(_line_cfg, _offsets, \
-                                                        _num_offsets, \
-                                                        _settings) \
-       do { \
-               gint _ret = gpiod_line_config_add_line_settings(_line_cfg, \
-                                                               _offsets,  \
-                                                               _num_offsets, \
-                                                               _settings); \
-               g_assert_cmpint(_ret, ==, 0); \
-               gpiod_test_return_if_failed(); \
-       } while (0)
-
-#define gpiod_test_line_config_get_line_settings_or_fail(_line_cfg, _offset) \
-       ({ \
-               struct gpiod_line_settings *_settings = \
-                       gpiod_line_config_get_line_settings(_line_cfg, \
-                                                           _offset); \
-               g_assert_nonnull(_settings); \
-               gpiod_test_return_if_failed(); \
-               _settings; \
-       })
-
-#define gpiod_test_line_config_set_output_values_or_fail(_line_cfg, _values, \
-                                                        _num_values) \
-       do { \
-               gint _ret = gpiod_line_config_set_output_values(_line_cfg, \
-                                                               _values, \
-                                                               _num_values); \
-               g_assert_cmpint(_ret, ==, 0); \
-               gpiod_test_return_if_failed(); \
-       } while (0)
-
-#define gpiod_test_create_request_config_or_fail() \
-       ({ \
-               struct gpiod_request_config *_config = \
-                               gpiod_request_config_new(); \
-               g_assert_nonnull(_config); \
-               gpiod_test_return_if_failed(); \
-               _config; \
-       })
-
-#define gpiod_test_chip_request_lines_or_fail(_chip, _req_cfg, _line_cfg) \
-       ({ \
-               struct gpiod_line_request *_request = \
-                       gpiod_chip_request_lines(_chip, \
-                                                _req_cfg, _line_cfg); \
-               g_assert_nonnull(_request); \
-               gpiod_test_return_if_failed(); \
-               _request; \
-       })
-
-#define gpiod_test_line_request_reconfigure_lines_or_fail(_request, _line_cfg) \
-       do { \
-               gint _ret = gpiod_line_request_reconfigure_lines(_request, \
-                                                                _line_cfg); \
-               g_assert_cmpint(_ret, ==, 0); \
-               gpiod_test_return_if_failed(); \
-       } while (0)
-
-#define gpiod_test_expect_errno(_expected) \
-       g_assert_cmpint(_expected, ==, errno)
-
-typedef struct {
-       guint offset;
-       const gchar *name;
-} GPIOSimLineName;
-
-typedef struct {
-       guint offset;
-       const gchar *name;
-       GPIOSimDirection direction;
-} GPIOSimHog;
-
-GVariant *gpiod_test_package_line_names(const GPIOSimLineName *names);
-GVariant *gpiod_test_package_hogs(const GPIOSimHog *hogs);
-
-#endif /* __GPIOD_TEST_HELPERS_H__ */
diff --git a/tests/gpiod-test-sim.c b/tests/gpiod-test-sim.c
deleted file mode 100644 (file)
index ac6c71a..0000000
+++ /dev/null
@@ -1,464 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/* SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <brgl@bgdev.pl> */
-
-#include <errno.h>
-#include <gpiosim.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "gpiod-test-sim.h"
-
-G_DEFINE_QUARK(g-gpiosim-error, g_gpiosim_error);
-
-struct _GPIOSimChip {
-       GObject parent_instance;
-       struct gpiosim_bank *bank;
-       GError *construct_err;
-       guint num_lines;
-       gchar *label;
-       GVariant *line_names;
-       GVariant *hogs;
-};
-
-enum {
-       G_GPIOSIM_CHIP_PROP_DEV_PATH = 1,
-       G_GPIOSIM_CHIP_PROP_NAME,
-       G_GPIOSIM_CHIP_PROP_NUM_LINES,
-       G_GPIOSIM_CHIP_PROP_LABEL,
-       G_GPIOSIM_CHIP_PROP_LINE_NAMES,
-       G_GPIOSIM_CHIP_PROP_HOGS,
-};
-
-static struct gpiosim_ctx *sim_ctx;
-
-static gboolean
-g_gpiosim_chip_initable_init(GInitable *initable,
-                            GCancellable *cancellable G_GNUC_UNUSED,
-                            GError **err)
-{
-       GPIOSimChip *self = G_GPIOSIM_CHIP_OBJ(initable);
-
-       if (self->construct_err) {
-               g_propagate_error(err, self->construct_err);
-               self->construct_err = NULL;
-               return FALSE;
-       }
-
-       return TRUE;
-}
-
-static void g_gpiosim_chip_initable_iface_init(GInitableIface *iface)
-{
-       iface->init = g_gpiosim_chip_initable_init;
-}
-
-G_DEFINE_TYPE_WITH_CODE(GPIOSimChip, g_gpiosim_chip, G_TYPE_OBJECT,
-                       G_IMPLEMENT_INTERFACE(
-                               G_TYPE_INITABLE,
-                               g_gpiosim_chip_initable_iface_init));
-
-static void g_gpiosim_ctx_unref(void)
-{
-       gpiosim_ctx_unref(sim_ctx);
-}
-
-static gboolean g_gpiosim_chip_apply_line_names(GPIOSimChip *self)
-{
-       g_autoptr(GVariantIter) iter = NULL;
-       guint offset;
-       gchar *name;
-       int ret;
-
-       if (!self->line_names)
-               return TRUE;
-
-       iter = g_variant_iter_new(self->line_names);
-
-       while (g_variant_iter_loop(iter, "(us)", &offset, &name)) {
-               ret = gpiosim_bank_set_line_name(self->bank, offset, name);
-               if (ret) {
-                       g_set_error(&self->construct_err, G_GPIOSIM_ERROR,
-                                   G_GPIOSIM_ERR_CHIP_INIT_FAILED,
-                                   "Unable to set the name of the simulated GPIO line: %s",
-                                   g_strerror(errno));
-                       return FALSE;
-               }
-       }
-
-       return TRUE;
-}
-
-static gboolean g_gpiosim_chip_apply_hogs(GPIOSimChip *self)
-{
-       g_autoptr(GVariantIter) iter = NULL;
-       enum gpiosim_direction dir;
-       guint offset;
-       gchar *name;
-       gint vdir;
-       int ret;
-
-       if (!self->hogs)
-               return TRUE;
-
-       iter = g_variant_iter_new(self->hogs);
-
-       while (g_variant_iter_loop(iter, "(usi)", &offset, &name, &vdir)) {
-               switch (vdir) {
-               case G_GPIOSIM_DIRECTION_INPUT:
-                       dir = GPIOSIM_DIRECTION_INPUT;
-                       break;
-               case G_GPIOSIM_DIRECTION_OUTPUT_HIGH:
-                       dir = GPIOSIM_DIRECTION_OUTPUT_HIGH;
-                       break;
-               case G_GPIOSIM_DIRECTION_OUTPUT_LOW:
-                       dir = GPIOSIM_DIRECTION_OUTPUT_LOW;
-                       break;
-               default:
-                       g_error("Invalid hog direction value: %d", vdir);
-               }
-
-               ret = gpiosim_bank_hog_line(self->bank, offset, name, dir);
-               if (ret) {
-                       g_set_error(&self->construct_err, G_GPIOSIM_ERROR,
-                                   G_GPIOSIM_ERR_CHIP_INIT_FAILED,
-                                   "Unable to hog the simulated GPIO line: %s",
-                                   g_strerror(errno));
-                       return FALSE;
-               }
-       }
-
-       return TRUE;
-}
-
-static gboolean g_gpiosim_chip_apply_properties(GPIOSimChip *self)
-{
-       int ret;
-
-       ret = gpiosim_bank_set_num_lines(self->bank, self->num_lines);
-       if (ret) {
-               g_set_error(&self->construct_err, G_GPIOSIM_ERROR,
-                           G_GPIOSIM_ERR_CHIP_INIT_FAILED,
-                           "Unable to set the number of lines exposed by the simulated chip: %s",
-                           g_strerror(errno));
-               return FALSE;
-       }
-
-       if (self->label) {
-               ret = gpiosim_bank_set_label(self->bank, self->label);
-               if (ret) {
-                       g_set_error(&self->construct_err, G_GPIOSIM_ERROR,
-                                   G_GPIOSIM_ERR_CHIP_INIT_FAILED,
-                                   "Unable to set the label of the simulated chip: %s",
-                                   g_strerror(errno));
-                       return FALSE;
-               }
-       }
-
-       ret = g_gpiosim_chip_apply_line_names(self);
-       if (!ret)
-               return FALSE;
-
-       return g_gpiosim_chip_apply_hogs(self);
-}
-
-static gboolean g_gpiosim_chip_enable(GPIOSimChip *self)
-{
-       struct gpiosim_dev *dev;
-       int ret;
-
-       dev = gpiosim_bank_get_dev(self->bank);
-       ret = gpiosim_dev_enable(dev);
-       gpiosim_dev_unref(dev);
-       if (ret) {
-               g_set_error(&self->construct_err, G_GPIOSIM_ERROR,
-                           G_GPIOSIM_ERR_CHIP_ENABLE_FAILED,
-                           "Error while trying to enable the simulated GPIO device: %s",
-                           g_strerror(errno));
-               return FALSE;
-       }
-
-       return TRUE;
-}
-
-static gboolean g_gpiosim_ctx_init(GError **err)
-{
-       sim_ctx = gpiosim_ctx_new();
-       if (!sim_ctx) {
-               g_set_error(err, G_GPIOSIM_ERROR,
-                           G_GPIOSIM_ERR_CTX_INIT_FAILED,
-                           "Unable to initialize libgpiosim: %s",
-                           g_strerror(errno));
-               return FALSE;
-       }
-
-       atexit(g_gpiosim_ctx_unref);
-
-       return TRUE;
-}
-
-static void g_gpiosim_chip_constructed(GObject *obj)
-{
-       GPIOSimChip *self = G_GPIOSIM_CHIP(obj);
-       struct gpiosim_dev *dev;
-       gboolean ret;
-
-       if (!sim_ctx) {
-               ret = g_gpiosim_ctx_init(&self->construct_err);
-               if (!ret)
-                       return;
-       }
-
-       dev = gpiosim_dev_new(sim_ctx);
-       if (!dev) {
-               g_set_error(&self->construct_err, G_GPIOSIM_ERROR,
-                           G_GPIOSIM_ERR_CHIP_INIT_FAILED,
-                           "Unable to instantiate new GPIO device: %s",
-                           g_strerror(errno));
-               return;
-       }
-
-       self->bank = gpiosim_bank_new(dev);
-       gpiosim_dev_unref(dev);
-       if (!self->bank) {
-               g_set_error(&self->construct_err, G_GPIOSIM_ERROR,
-                           G_GPIOSIM_ERR_CHIP_INIT_FAILED,
-                           "Unable to instantiate new GPIO bank: %s",
-                           g_strerror(errno));
-               return;
-       }
-
-       ret = g_gpiosim_chip_apply_properties(self);
-       if (!ret)
-               return;
-
-       ret = g_gpiosim_chip_enable(self);
-       if (!ret)
-               return;
-
-       G_OBJECT_CLASS(g_gpiosim_chip_parent_class)->constructed(obj);
-}
-
-static void g_gpiosim_chip_get_property(GObject *obj, guint prop_id,
-                                       GValue *val, GParamSpec *pspec)
-{
-       GPIOSimChip *self = G_GPIOSIM_CHIP(obj);
-
-       switch (prop_id) {
-       case G_GPIOSIM_CHIP_PROP_DEV_PATH:
-               g_value_set_static_string(val,
-                               gpiosim_bank_get_dev_path(self->bank));
-               break;
-       case G_GPIOSIM_CHIP_PROP_NAME:
-               g_value_set_static_string(val,
-                               gpiosim_bank_get_chip_name(self->bank));
-               break;
-       default:
-               G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
-               break;
-       }
-}
-
-static void set_variant_prop(GVariant **prop, const GValue *val)
-{
-       GVariant *variant = g_value_get_variant(val);
-
-       g_clear_pointer(prop, g_variant_unref);
-       if (variant)
-               *prop = g_variant_ref(variant);
-}
-
-static void g_gpiosim_chip_set_property(GObject *obj, guint prop_id,
-                                       const GValue *val, GParamSpec *pspec)
-{
-       GPIOSimChip *self = G_GPIOSIM_CHIP(obj);
-       const gchar *label;
-
-       switch (prop_id) {
-       case G_GPIOSIM_CHIP_PROP_NUM_LINES:
-               self->num_lines = g_value_get_uint(val);
-               break;
-       case G_GPIOSIM_CHIP_PROP_LABEL:
-               g_clear_pointer(&self->label, g_free);
-               label = g_value_get_string(val);
-               if (label)
-                       self->label = g_strdup(label);
-               break;
-       case G_GPIOSIM_CHIP_PROP_LINE_NAMES:
-               set_variant_prop(&self->line_names, val);
-               break;
-       case G_GPIOSIM_CHIP_PROP_HOGS:
-               set_variant_prop(&self->hogs, val);
-               break;
-       default:
-               G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
-               break;
-       }
-}
-
-static void g_gpiosim_chip_dispose(GObject *obj)
-{
-       GPIOSimChip *self = G_GPIOSIM_CHIP(obj);
-
-       g_clear_pointer(&self->line_names, g_variant_unref);
-       g_clear_pointer(&self->hogs, g_variant_unref);
-
-       G_OBJECT_CLASS(g_gpiosim_chip_parent_class)->dispose(obj);
-}
-
-static void g_gpiosim_disable_and_cleanup(struct gpiosim_bank *bank)
-{
-       struct gpiosim_dev *dev;
-       gint ret;
-
-       dev = gpiosim_bank_get_dev(bank);
-
-       if (gpiosim_dev_is_live(dev)) {
-               ret = gpiosim_dev_disable(dev);
-               if (ret)
-                       g_warning("Error while trying to disable the simulated GPIO device: %s",
-                                 g_strerror(errno));
-       }
-
-       gpiosim_dev_unref(dev);
-       gpiosim_bank_unref(bank);
-}
-
-static void g_gpiosim_chip_finalize(GObject *obj)
-{
-       GPIOSimChip *self = G_GPIOSIM_CHIP(obj);
-
-       g_clear_error(&self->construct_err);
-       g_clear_pointer(&self->label, g_free);
-       g_clear_pointer(&self->bank, g_gpiosim_disable_and_cleanup);
-
-       G_OBJECT_CLASS(g_gpiosim_chip_parent_class)->finalize(obj);
-}
-
-static void g_gpiosim_chip_class_init(GPIOSimChipClass *chip_class)
-{
-       GObjectClass *class = G_OBJECT_CLASS(chip_class);
-
-       class->constructed = g_gpiosim_chip_constructed;
-       class->get_property = g_gpiosim_chip_get_property;
-       class->set_property = g_gpiosim_chip_set_property;
-       class->dispose = g_gpiosim_chip_dispose;
-       class->finalize = g_gpiosim_chip_finalize;
-
-       g_object_class_install_property(
-               class, G_GPIOSIM_CHIP_PROP_DEV_PATH,
-               g_param_spec_string("dev-path", "Device path",
-                                   "Character device filesystem path.", NULL,
-                                   G_PARAM_READABLE));
-
-       g_object_class_install_property(
-               class, G_GPIOSIM_CHIP_PROP_NAME,
-               g_param_spec_string(
-                       "name", "Chip name",
-                       "Name of this chip device as set by the kernel.", NULL,
-                       G_PARAM_READABLE));
-
-       g_object_class_install_property(
-               class, G_GPIOSIM_CHIP_PROP_NUM_LINES,
-               g_param_spec_uint("num-lines", "Number of lines",
-                                 "Number of lines this simulated chip exposes.",
-                                 1, G_MAXUINT, 1,
-                                 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
-
-       g_object_class_install_property(
-               class, G_GPIOSIM_CHIP_PROP_LABEL,
-               g_param_spec_string("label", "Chip label",
-                                   "Label of this simulated chip.", NULL,
-                                   G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
-
-       g_object_class_install_property(
-               class, G_GPIOSIM_CHIP_PROP_LINE_NAMES,
-               g_param_spec_variant(
-                       "line-names", "Line names",
-                       "List of names of the lines exposed by this chip",
-                       (GVariantType *)"a(us)", NULL,
-                       G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
-
-       g_object_class_install_property(
-               class, G_GPIOSIM_CHIP_PROP_HOGS,
-               g_param_spec_variant(
-                       "hogs", "Line hogs",
-                       "List of hogged lines and their directions.",
-                       (GVariantType *)"a(usi)", NULL,
-                       G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
-}
-
-static void g_gpiosim_chip_init(GPIOSimChip *self)
-{
-       self->construct_err = NULL;
-       self->num_lines = 1;
-       self->label = NULL;
-       self->line_names = NULL;
-       self->hogs = NULL;
-}
-
-static const gchar *
-g_gpiosim_chip_get_string_prop(GPIOSimChip *self, const gchar *prop)
-{
-       GValue val = G_VALUE_INIT;
-       const gchar *str;
-
-       g_object_get_property(G_OBJECT(self), prop, &val);
-       str = g_value_get_string(&val);
-       g_value_unset(&val);
-
-       return str;
-}
-
-const gchar *g_gpiosim_chip_get_dev_path(GPIOSimChip *self)
-{
-       return g_gpiosim_chip_get_string_prop(self, "dev-path");
-}
-
-const gchar *g_gpiosim_chip_get_name(GPIOSimChip *self)
-{
-       return g_gpiosim_chip_get_string_prop(self, "name");
-}
-
-GPIOSimValue
-_g_gpiosim_chip_get_value(GPIOSimChip *chip, guint offset, GError **err)
-{
-       enum gpiosim_value val;
-
-       val = gpiosim_bank_get_value(chip->bank, offset);
-       switch (val) {
-       case GPIOSIM_VALUE_ERROR:
-               g_set_error(err, G_GPIOSIM_ERROR,
-                           G_GPIOSIM_ERR_GET_VALUE_FAILED,
-                           "Unable to read the line value: %s",
-                           g_strerror(errno));
-               return G_GPIOSIM_VALUE_ERROR;
-       case GPIOSIM_VALUE_INACTIVE:
-               return G_GPIOSIM_VALUE_INACTIVE;
-       case GPIOSIM_VALUE_ACTIVE:
-               return G_GPIOSIM_VALUE_ACTIVE;
-       }
-
-       g_error("Invalid line value returned by gpiosim");
-}
-
-void g_gpiosim_chip_set_pull(GPIOSimChip *chip, guint offset, GPIOSimPull pull)
-{
-       enum gpiosim_pull sim_pull;
-       gint ret;
-
-       switch (pull) {
-       case G_GPIOSIM_PULL_DOWN:
-               sim_pull = GPIOSIM_PULL_DOWN;
-               break;
-       case G_GPIOSIM_PULL_UP:
-               sim_pull = GPIOSIM_PULL_UP;
-               break;
-       default:
-               g_error("invalid pull value");
-       }
-
-       ret = gpiosim_bank_set_pull(chip->bank, offset, sim_pull);
-       if (ret)
-               g_critical("Unable to set the pull setting for simulated line: %s",
-                           g_strerror(errno));
-}
diff --git a/tests/gpiod-test-sim.h b/tests/gpiod-test-sim.h
deleted file mode 100644 (file)
index f6a4bf0..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/* SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <brgl@bgdev.pl> */
-
-#ifndef __GPIOD_TEST_SIM_H__
-#define __GPIOD_TEST_SIM_H__
-
-#include <gio/gio.h>
-#include <glib.h>
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-
-typedef enum {
-       G_GPIOSIM_VALUE_ERROR = -1,
-       G_GPIOSIM_VALUE_INACTIVE = 0,
-       G_GPIOSIM_VALUE_ACTIVE = 1,
-} GPIOSimValue;
-
-typedef enum {
-       G_GPIOSIM_PULL_UP = 1,
-       G_GPIOSIM_PULL_DOWN,
-} GPIOSimPull;
-
-typedef enum {
-       G_GPIOSIM_DIRECTION_INPUT = 1,
-       G_GPIOSIM_DIRECTION_OUTPUT_HIGH,
-       G_GPIOSIM_DIRECTION_OUTPUT_LOW,
-} GPIOSimDirection;
-
-#define G_GPIOSIM_ERROR g_gpiosim_error_quark()
-
-typedef enum {
-       G_GPIOSIM_ERR_CTX_INIT_FAILED = 1,
-       G_GPIOSIM_ERR_CHIP_INIT_FAILED,
-       G_GPIOSIM_ERR_CHIP_ENABLE_FAILED,
-       G_GPIOSIM_ERR_GET_VALUE_FAILED,
-} GPIOSimError;
-
-GQuark g_gpiosim_error_quark(void);
-
-G_DECLARE_FINAL_TYPE(GPIOSimChip, g_gpiosim_chip, G_GPIOSIM, CHIP, GObject);
-
-#define G_GPIOSIM_CHIP_TYPE (g_gpiosim_chip_get_type())
-#define G_GPIOSIM_CHIP_OBJ(obj) \
-       (G_TYPE_CHECK_INSTANCE_CAST((obj), G_GPIOSIM_CHIP_TYPE, GPIOSimChip))
-
-#define g_gpiosim_chip_new(...) \
-       ({ \
-               g_autoptr(GError) _err = NULL; \
-               GPIOSimChip *_chip = G_GPIOSIM_CHIP_OBJ( \
-                                       g_initable_new(G_GPIOSIM_CHIP_TYPE, \
-                                                      NULL, &_err, \
-                                                      __VA_ARGS__)); \
-               g_assert_no_error(_err); \
-               if (g_test_failed()) \
-                       return; \
-               _chip; \
-       })
-
-const gchar *g_gpiosim_chip_get_dev_path(GPIOSimChip *self);
-const gchar *g_gpiosim_chip_get_name(GPIOSimChip *self);
-
-GPIOSimValue
-_g_gpiosim_chip_get_value(GPIOSimChip *self, guint offset, GError **err);
-void g_gpiosim_chip_set_pull(GPIOSimChip *self, guint offset, GPIOSimPull pull);
-
-#define g_gpiosim_chip_get_value(self, offset) \
-       ({ \
-               g_autoptr(GError) _err = NULL; \
-               gint _val = _g_gpiosim_chip_get_value(self, offset, &_err); \
-               g_assert_no_error(_err); \
-               if (g_test_failed()) \
-                       return; \
-               _val; \
-       })
-
-G_END_DECLS
-
-#endif /* __GPIOD_TEST_SIM_H__ */
diff --git a/tests/gpiod-test.c b/tests/gpiod-test.c
deleted file mode 100644 (file)
index 4e65ae2..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-// SPDX-FileCopyrightText: 2017-2022 Bartosz Golaszewski <brgl@bgdev.pl>
-
-#include <errno.h>
-#include <linux/version.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/utsname.h>
-#include <unistd.h>
-
-#include "gpiod-test.h"
-
-#define MIN_KERNEL_MAJOR       5
-#define MIN_KERNEL_MINOR       19
-#define MIN_KERNEL_RELEASE     0
-#define MIN_KERNEL_VERSION     KERNEL_VERSION(MIN_KERNEL_MAJOR, \
-                                              MIN_KERNEL_MINOR, \
-                                              MIN_KERNEL_RELEASE)
-
-static GList *tests;
-
-static gboolean check_kernel(void)
-{
-       guint major, minor, release;
-       struct utsname un;
-       gint ret;
-
-       g_debug("checking linux kernel version");
-
-       ret = uname(&un);
-       if (ret) {
-               g_critical("unable to read the kernel release version: %s",
-                          g_strerror(errno));
-               return FALSE;
-       }
-
-       ret = sscanf(un.release, "%u.%u.%u", &major, &minor, &release);
-       if (ret != 3) {
-               g_critical("error reading kernel release version");
-               return FALSE;
-       }
-
-       if (KERNEL_VERSION(major, minor, release) < MIN_KERNEL_VERSION) {
-               g_critical("linux kernel version must be at least v%u.%u.%u - got v%u.%u.%u",
-                          MIN_KERNEL_MAJOR, MIN_KERNEL_MINOR, MIN_KERNEL_RELEASE,
-                          major, minor, release);
-               return FALSE;
-       }
-
-       g_debug("kernel release is v%u.%u.%u - ok to run tests",
-               major, minor, release);
-
-       return TRUE;
-}
-
-static void test_func_wrapper(gconstpointer data)
-{
-       const struct _gpiod_test_case *test = data;
-
-       test->func();
-}
-
-static void add_test_from_list(gpointer element, gpointer data G_GNUC_UNUSED)
-{
-       struct _gpiod_test_case *test = element;
-
-       g_test_add_data_func(test->path, test, test_func_wrapper);
-}
-
-int main(int argc, char **argv)
-{
-       g_test_init(&argc, &argv, NULL);
-       g_test_set_nonfatal_assertions();
-
-       g_debug("running libgpiod test suite");
-       g_debug("%u tests registered", g_list_length(tests));
-
-       if (!check_kernel())
-               return EXIT_FAILURE;
-
-       g_list_foreach(tests, add_test_from_list, NULL);
-       g_list_free(tests);
-
-       return g_test_run();
-}
-
-void _gpiod_test_register(struct _gpiod_test_case *test)
-{
-       tests = g_list_append(tests, test);
-}
diff --git a/tests/gpiod-test.h b/tests/gpiod-test.h
deleted file mode 100644 (file)
index 6a84162..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* SPDX-FileCopyrightText: 2017-2022 Bartosz Golaszewski <brgl@bgdev.pl> */
-
-/*
- * Testing framework for the core library.
- *
- * This file contains functions and definitions extending the GLib unit testing
- * framework with functionalities necessary to test the libgpiod core C API as
- * well as the kernel-to-user-space interface.
- */
-
-#ifndef __GPIOD_TEST_H__
-#define __GPIOD_TEST_H__
-
-#include <glib.h>
-
-/* These are private definitions and should not be used directly. */
-
-struct _gpiod_test_case {
-       const gchar *path;
-       void (*func)(void);
-};
-
-void _gpiod_test_register(struct _gpiod_test_case *test);
-
-#define _GPIOD_TEST_PATH(_name) \
-               "/gpiod/" GPIOD_TEST_GROUP "/" G_STRINGIFY(_name)
-
-/*
- * Register a test case function.
- */
-#define GPIOD_TEST_CASE(_name) \
-       static void _gpiod_test_func_##_name(void); \
-       static struct _gpiod_test_case _##_name##_test_case = { \
-               .path = _GPIOD_TEST_PATH(_name), \
-               .func = _gpiod_test_func_##_name, \
-       }; \
-       static __attribute__((constructor)) void \
-       _##_name##_test_register(void) \
-       { \
-               _gpiod_test_register(&_##_name##_test_case); \
-       } \
-       static void _gpiod_test_func_##_name(void)
-
-#endif /* __GPIOD_TEST_H__ */
diff --git a/tests/gpiosim-glib/Makefile.am b/tests/gpiosim-glib/Makefile.am
new file mode 100644 (file)
index 0000000..1c01629
--- /dev/null
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+
+noinst_LTLIBRARIES = libgpiosim-glib.la
+libgpiosim_glib_la_SOURCES = \
+       gpiosim-glib.c \
+       gpiosim-glib.h
+
+AM_CFLAGS = -I$(top_srcdir)/tests/gpiosim/
+AM_CFLAGS += -include $(top_builddir)/config.h
+AM_CFLAGS += -Wall -Wextra -g -std=gnu89 $(GLIB_CFLAGS) $(GIO_CFLAGS)
+AM_CFLAGS += -DG_LOG_DOMAIN=\"gpiosim-glib\"
+libgpiosim_glib_la_LDFLAGS = -lgpiosim
diff --git a/tests/gpiosim-glib/gpiosim-glib.c b/tests/gpiosim-glib/gpiosim-glib.c
new file mode 100644 (file)
index 0000000..4eaeace
--- /dev/null
@@ -0,0 +1,492 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <brgl@bgdev.pl> */
+
+#include <errno.h>
+#include <gpiosim.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "gpiosim-glib.h"
+
+G_DEFINE_QUARK(g-gpiosim-error, g_gpiosim_error);
+
+struct _GPIOSimChip {
+       GObject parent_instance;
+       struct gpiosim_bank *bank;
+       GError *construct_err;
+       guint num_lines;
+       gchar *label;
+       GVariant *line_names;
+       GVariant *hogs;
+};
+
+enum {
+       G_GPIOSIM_CHIP_PROP_DEV_PATH = 1,
+       G_GPIOSIM_CHIP_PROP_NAME,
+       G_GPIOSIM_CHIP_PROP_NUM_LINES,
+       G_GPIOSIM_CHIP_PROP_LABEL,
+       G_GPIOSIM_CHIP_PROP_LINE_NAMES,
+       G_GPIOSIM_CHIP_PROP_HOGS,
+};
+
+static struct gpiosim_ctx *sim_ctx;
+
+static gboolean
+g_gpiosim_chip_initable_init(GInitable *initable,
+                            GCancellable *cancellable G_GNUC_UNUSED,
+                            GError **err)
+{
+       GPIOSimChip *self = G_GPIOSIM_CHIP_OBJ(initable);
+
+       if (self->construct_err) {
+               g_propagate_error(err, self->construct_err);
+               self->construct_err = NULL;
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static void g_gpiosim_chip_initable_iface_init(GInitableIface *iface)
+{
+       iface->init = g_gpiosim_chip_initable_init;
+}
+
+G_DEFINE_TYPE_WITH_CODE(GPIOSimChip, g_gpiosim_chip, G_TYPE_OBJECT,
+                       G_IMPLEMENT_INTERFACE(
+                               G_TYPE_INITABLE,
+                               g_gpiosim_chip_initable_iface_init));
+
+static void g_gpiosim_ctx_unref(void)
+{
+       gpiosim_ctx_unref(sim_ctx);
+}
+
+static gboolean g_gpiosim_chip_apply_line_names(GPIOSimChip *self)
+{
+       g_autoptr(GVariantIter) iter = NULL;
+       guint offset;
+       gchar *name;
+       int ret;
+
+       if (!self->line_names)
+               return TRUE;
+
+       iter = g_variant_iter_new(self->line_names);
+
+       while (g_variant_iter_loop(iter, "(us)", &offset, &name)) {
+               ret = gpiosim_bank_set_line_name(self->bank, offset, name);
+               if (ret) {
+                       g_set_error(&self->construct_err, G_GPIOSIM_ERROR,
+                                   G_GPIOSIM_ERR_CHIP_INIT_FAILED,
+                                   "Unable to set the name of the simulated GPIO line: %s",
+                                   g_strerror(errno));
+                       return FALSE;
+               }
+       }
+
+       return TRUE;
+}
+
+static gboolean g_gpiosim_chip_apply_hogs(GPIOSimChip *self)
+{
+       g_autoptr(GVariantIter) iter = NULL;
+       enum gpiosim_direction dir;
+       guint offset;
+       gchar *name;
+       gint vdir;
+       int ret;
+
+       if (!self->hogs)
+               return TRUE;
+
+       iter = g_variant_iter_new(self->hogs);
+
+       while (g_variant_iter_loop(iter, "(usi)", &offset, &name, &vdir)) {
+               switch (vdir) {
+               case G_GPIOSIM_DIRECTION_INPUT:
+                       dir = GPIOSIM_DIRECTION_INPUT;
+                       break;
+               case G_GPIOSIM_DIRECTION_OUTPUT_HIGH:
+                       dir = GPIOSIM_DIRECTION_OUTPUT_HIGH;
+                       break;
+               case G_GPIOSIM_DIRECTION_OUTPUT_LOW:
+                       dir = GPIOSIM_DIRECTION_OUTPUT_LOW;
+                       break;
+               default:
+                       g_error("Invalid hog direction value: %d", vdir);
+               }
+
+               ret = gpiosim_bank_hog_line(self->bank, offset, name, dir);
+               if (ret) {
+                       g_set_error(&self->construct_err, G_GPIOSIM_ERROR,
+                                   G_GPIOSIM_ERR_CHIP_INIT_FAILED,
+                                   "Unable to hog the simulated GPIO line: %s",
+                                   g_strerror(errno));
+                       return FALSE;
+               }
+       }
+
+       return TRUE;
+}
+
+static gboolean g_gpiosim_chip_apply_properties(GPIOSimChip *self)
+{
+       int ret;
+
+       ret = gpiosim_bank_set_num_lines(self->bank, self->num_lines);
+       if (ret) {
+               g_set_error(&self->construct_err, G_GPIOSIM_ERROR,
+                           G_GPIOSIM_ERR_CHIP_INIT_FAILED,
+                           "Unable to set the number of lines exposed by the simulated chip: %s",
+                           g_strerror(errno));
+               return FALSE;
+       }
+
+       if (self->label) {
+               ret = gpiosim_bank_set_label(self->bank, self->label);
+               if (ret) {
+                       g_set_error(&self->construct_err, G_GPIOSIM_ERROR,
+                                   G_GPIOSIM_ERR_CHIP_INIT_FAILED,
+                                   "Unable to set the label of the simulated chip: %s",
+                                   g_strerror(errno));
+                       return FALSE;
+               }
+       }
+
+       ret = g_gpiosim_chip_apply_line_names(self);
+       if (!ret)
+               return FALSE;
+
+       return g_gpiosim_chip_apply_hogs(self);
+}
+
+static gboolean g_gpiosim_chip_enable(GPIOSimChip *self)
+{
+       struct gpiosim_dev *dev;
+       int ret;
+
+       dev = gpiosim_bank_get_dev(self->bank);
+       ret = gpiosim_dev_enable(dev);
+       gpiosim_dev_unref(dev);
+       if (ret) {
+               g_set_error(&self->construct_err, G_GPIOSIM_ERROR,
+                           G_GPIOSIM_ERR_CHIP_ENABLE_FAILED,
+                           "Error while trying to enable the simulated GPIO device: %s",
+                           g_strerror(errno));
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static gboolean g_gpiosim_ctx_init(GError **err)
+{
+       sim_ctx = gpiosim_ctx_new();
+       if (!sim_ctx) {
+               g_set_error(err, G_GPIOSIM_ERROR,
+                           G_GPIOSIM_ERR_CTX_INIT_FAILED,
+                           "Unable to initialize libgpiosim: %s",
+                           g_strerror(errno));
+               return FALSE;
+       }
+
+       atexit(g_gpiosim_ctx_unref);
+
+       return TRUE;
+}
+
+static void g_gpiosim_chip_constructed(GObject *obj)
+{
+       GPIOSimChip *self = G_GPIOSIM_CHIP(obj);
+       struct gpiosim_dev *dev;
+       gboolean ret;
+
+       if (!sim_ctx) {
+               ret = g_gpiosim_ctx_init(&self->construct_err);
+               if (!ret)
+                       return;
+       }
+
+       dev = gpiosim_dev_new(sim_ctx);
+       if (!dev) {
+               g_set_error(&self->construct_err, G_GPIOSIM_ERROR,
+                           G_GPIOSIM_ERR_CHIP_INIT_FAILED,
+                           "Unable to instantiate new GPIO device: %s",
+                           g_strerror(errno));
+               return;
+       }
+
+       self->bank = gpiosim_bank_new(dev);
+       gpiosim_dev_unref(dev);
+       if (!self->bank) {
+               g_set_error(&self->construct_err, G_GPIOSIM_ERROR,
+                           G_GPIOSIM_ERR_CHIP_INIT_FAILED,
+                           "Unable to instantiate new GPIO bank: %s",
+                           g_strerror(errno));
+               return;
+       }
+
+       ret = g_gpiosim_chip_apply_properties(self);
+       if (!ret)
+               return;
+
+       ret = g_gpiosim_chip_enable(self);
+       if (!ret)
+               return;
+
+       G_OBJECT_CLASS(g_gpiosim_chip_parent_class)->constructed(obj);
+}
+
+static void g_gpiosim_chip_get_property(GObject *obj, guint prop_id,
+                                       GValue *val, GParamSpec *pspec)
+{
+       GPIOSimChip *self = G_GPIOSIM_CHIP(obj);
+
+       switch (prop_id) {
+       case G_GPIOSIM_CHIP_PROP_DEV_PATH:
+               g_value_set_static_string(val,
+                               gpiosim_bank_get_dev_path(self->bank));
+               break;
+       case G_GPIOSIM_CHIP_PROP_NAME:
+               g_value_set_static_string(val,
+                               gpiosim_bank_get_chip_name(self->bank));
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
+               break;
+       }
+}
+
+static void set_variant_prop(GVariant **prop, const GValue *val)
+{
+       GVariant *variant = g_value_get_variant(val);
+
+       g_clear_pointer(prop, g_variant_unref);
+       if (variant)
+               *prop = g_variant_ref(variant);
+}
+
+static void g_gpiosim_chip_set_property(GObject *obj, guint prop_id,
+                                       const GValue *val, GParamSpec *pspec)
+{
+       GPIOSimChip *self = G_GPIOSIM_CHIP(obj);
+       const gchar *label;
+
+       switch (prop_id) {
+       case G_GPIOSIM_CHIP_PROP_NUM_LINES:
+               self->num_lines = g_value_get_uint(val);
+               break;
+       case G_GPIOSIM_CHIP_PROP_LABEL:
+               g_clear_pointer(&self->label, g_free);
+               label = g_value_get_string(val);
+               if (label)
+                       self->label = g_strdup(label);
+               break;
+       case G_GPIOSIM_CHIP_PROP_LINE_NAMES:
+               set_variant_prop(&self->line_names, val);
+               break;
+       case G_GPIOSIM_CHIP_PROP_HOGS:
+               set_variant_prop(&self->hogs, val);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
+               break;
+       }
+}
+
+static void g_gpiosim_chip_dispose(GObject *obj)
+{
+       GPIOSimChip *self = G_GPIOSIM_CHIP(obj);
+
+       g_clear_pointer(&self->line_names, g_variant_unref);
+       g_clear_pointer(&self->hogs, g_variant_unref);
+
+       G_OBJECT_CLASS(g_gpiosim_chip_parent_class)->dispose(obj);
+}
+
+static void g_gpiosim_disable_and_cleanup(struct gpiosim_bank *bank)
+{
+       struct gpiosim_dev *dev;
+       gint ret;
+
+       dev = gpiosim_bank_get_dev(bank);
+
+       if (gpiosim_dev_is_live(dev)) {
+               ret = gpiosim_dev_disable(dev);
+               if (ret)
+                       g_warning("Error while trying to disable the simulated GPIO device: %s",
+                                 g_strerror(errno));
+       }
+
+       gpiosim_dev_unref(dev);
+       gpiosim_bank_unref(bank);
+}
+
+static void g_gpiosim_chip_finalize(GObject *obj)
+{
+       GPIOSimChip *self = G_GPIOSIM_CHIP(obj);
+
+       g_clear_error(&self->construct_err);
+       g_clear_pointer(&self->label, g_free);
+       g_clear_pointer(&self->bank, g_gpiosim_disable_and_cleanup);
+
+       G_OBJECT_CLASS(g_gpiosim_chip_parent_class)->finalize(obj);
+}
+
+static void g_gpiosim_chip_class_init(GPIOSimChipClass *chip_class)
+{
+       GObjectClass *class = G_OBJECT_CLASS(chip_class);
+
+       class->constructed = g_gpiosim_chip_constructed;
+       class->get_property = g_gpiosim_chip_get_property;
+       class->set_property = g_gpiosim_chip_set_property;
+       class->dispose = g_gpiosim_chip_dispose;
+       class->finalize = g_gpiosim_chip_finalize;
+
+       g_object_class_install_property(
+               class, G_GPIOSIM_CHIP_PROP_DEV_PATH,
+               g_param_spec_string("dev-path", "Device path",
+                                   "Character device filesystem path.", NULL,
+                                   G_PARAM_READABLE));
+
+       g_object_class_install_property(
+               class, G_GPIOSIM_CHIP_PROP_NAME,
+               g_param_spec_string(
+                       "name", "Chip name",
+                       "Name of this chip device as set by the kernel.", NULL,
+                       G_PARAM_READABLE));
+
+       g_object_class_install_property(
+               class, G_GPIOSIM_CHIP_PROP_NUM_LINES,
+               g_param_spec_uint("num-lines", "Number of lines",
+                                 "Number of lines this simulated chip exposes.",
+                                 1, G_MAXUINT, 1,
+                                 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+
+       g_object_class_install_property(
+               class, G_GPIOSIM_CHIP_PROP_LABEL,
+               g_param_spec_string("label", "Chip label",
+                                   "Label of this simulated chip.", NULL,
+                                   G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+
+       g_object_class_install_property(
+               class, G_GPIOSIM_CHIP_PROP_LINE_NAMES,
+               g_param_spec_variant(
+                       "line-names", "Line names",
+                       "List of names of the lines exposed by this chip",
+                       (GVariantType *)"a(us)", NULL,
+                       G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+
+       g_object_class_install_property(
+               class, G_GPIOSIM_CHIP_PROP_HOGS,
+               g_param_spec_variant(
+                       "hogs", "Line hogs",
+                       "List of hogged lines and their directions.",
+                       (GVariantType *)"a(usi)", NULL,
+                       G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void g_gpiosim_chip_init(GPIOSimChip *self)
+{
+       self->construct_err = NULL;
+       self->num_lines = 1;
+       self->label = NULL;
+       self->line_names = NULL;
+       self->hogs = NULL;
+}
+
+static const gchar *
+g_gpiosim_chip_get_string_prop(GPIOSimChip *self, const gchar *prop)
+{
+       GValue val = G_VALUE_INIT;
+       const gchar *str;
+
+       g_object_get_property(G_OBJECT(self), prop, &val);
+       str = g_value_get_string(&val);
+       g_value_unset(&val);
+
+       return str;
+}
+
+const gchar *g_gpiosim_chip_get_dev_path(GPIOSimChip *self)
+{
+       return g_gpiosim_chip_get_string_prop(self, "dev-path");
+}
+
+const gchar *g_gpiosim_chip_get_name(GPIOSimChip *self)
+{
+       return g_gpiosim_chip_get_string_prop(self, "name");
+}
+
+GPIOSimValue
+_g_gpiosim_chip_get_value(GPIOSimChip *chip, guint offset, GError **err)
+{
+       enum gpiosim_value val;
+
+       val = gpiosim_bank_get_value(chip->bank, offset);
+       switch (val) {
+       case GPIOSIM_VALUE_ERROR:
+               g_set_error(err, G_GPIOSIM_ERROR,
+                           G_GPIOSIM_ERR_GET_VALUE_FAILED,
+                           "Unable to read the line value: %s",
+                           g_strerror(errno));
+               return G_GPIOSIM_VALUE_ERROR;
+       case GPIOSIM_VALUE_INACTIVE:
+               return G_GPIOSIM_VALUE_INACTIVE;
+       case GPIOSIM_VALUE_ACTIVE:
+               return G_GPIOSIM_VALUE_ACTIVE;
+       }
+
+       g_error("Invalid line value returned by gpiosim");
+}
+
+void g_gpiosim_chip_set_pull(GPIOSimChip *chip, guint offset, GPIOSimPull pull)
+{
+       enum gpiosim_pull sim_pull;
+       gint ret;
+
+       switch (pull) {
+       case G_GPIOSIM_PULL_DOWN:
+               sim_pull = GPIOSIM_PULL_DOWN;
+               break;
+       case G_GPIOSIM_PULL_UP:
+               sim_pull = GPIOSIM_PULL_UP;
+               break;
+       default:
+               g_error("invalid pull value");
+       }
+
+       ret = gpiosim_bank_set_pull(chip->bank, offset, sim_pull);
+       if (ret)
+               g_critical("Unable to set the pull setting for simulated line: %s",
+                           g_strerror(errno));
+}
+
+GVariant *g_gpiosim_package_line_names(const GPIOSimLineName *names)
+{
+       g_autoptr(GVariantBuilder) builder = NULL;
+       const GPIOSimLineName *name;
+
+       builder = g_variant_builder_new(G_VARIANT_TYPE("a(us)"));
+
+       for (name = &names[0]; name->name; name++)
+               g_variant_builder_add(builder, "(us)",
+                                     name->offset, name->name);
+
+       return g_variant_ref_sink(g_variant_new("a(us)", builder));
+}
+
+GVariant *g_gpiosim_package_hogs(const GPIOSimHog *hogs)
+{
+       g_autoptr(GVariantBuilder) builder = NULL;
+       const GPIOSimHog *hog;
+
+       builder = g_variant_builder_new(G_VARIANT_TYPE("a(usi)"));
+
+       for (hog = &hogs[0]; hog->name; hog++)
+               g_variant_builder_add(builder, "(usi)",
+                                     hog->offset, hog->name, hog->direction);
+
+       return g_variant_ref_sink(g_variant_new("a(usi)", builder));
+}
diff --git a/tests/gpiosim-glib/gpiosim-glib.h b/tests/gpiosim-glib/gpiosim-glib.h
new file mode 100644 (file)
index 0000000..fa76736
--- /dev/null
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <brgl@bgdev.pl> */
+
+#ifndef __GPIOD_TEST_SIM_H__
+#define __GPIOD_TEST_SIM_H__
+
+#include <gio/gio.h>
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+       G_GPIOSIM_VALUE_ERROR = -1,
+       G_GPIOSIM_VALUE_INACTIVE = 0,
+       G_GPIOSIM_VALUE_ACTIVE = 1,
+} GPIOSimValue;
+
+typedef enum {
+       G_GPIOSIM_PULL_UP = 1,
+       G_GPIOSIM_PULL_DOWN,
+} GPIOSimPull;
+
+typedef enum {
+       G_GPIOSIM_DIRECTION_INPUT = 1,
+       G_GPIOSIM_DIRECTION_OUTPUT_HIGH,
+       G_GPIOSIM_DIRECTION_OUTPUT_LOW,
+} GPIOSimDirection;
+
+#define G_GPIOSIM_ERROR g_gpiosim_error_quark()
+
+typedef enum {
+       G_GPIOSIM_ERR_CTX_INIT_FAILED = 1,
+       G_GPIOSIM_ERR_CHIP_INIT_FAILED,
+       G_GPIOSIM_ERR_CHIP_ENABLE_FAILED,
+       G_GPIOSIM_ERR_GET_VALUE_FAILED,
+} GPIOSimError;
+
+GQuark g_gpiosim_error_quark(void);
+
+G_DECLARE_FINAL_TYPE(GPIOSimChip, g_gpiosim_chip, G_GPIOSIM, CHIP, GObject);
+
+#define G_GPIOSIM_CHIP_TYPE (g_gpiosim_chip_get_type())
+#define G_GPIOSIM_CHIP_OBJ(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST((obj), G_GPIOSIM_CHIP_TYPE, GPIOSimChip))
+
+#define g_gpiosim_chip_new(...) \
+       ({ \
+               g_autoptr(GError) _err = NULL; \
+               GPIOSimChip *_chip = G_GPIOSIM_CHIP_OBJ( \
+                                       g_initable_new(G_GPIOSIM_CHIP_TYPE, \
+                                                      NULL, &_err, \
+                                                      __VA_ARGS__)); \
+               g_assert_no_error(_err); \
+               if (g_test_failed()) \
+                       return; \
+               _chip; \
+       })
+
+const gchar *g_gpiosim_chip_get_dev_path(GPIOSimChip *self);
+const gchar *g_gpiosim_chip_get_name(GPIOSimChip *self);
+
+GPIOSimValue
+_g_gpiosim_chip_get_value(GPIOSimChip *self, guint offset, GError **err);
+void g_gpiosim_chip_set_pull(GPIOSimChip *self, guint offset, GPIOSimPull pull);
+
+#define g_gpiosim_chip_get_value(self, offset) \
+       ({ \
+               g_autoptr(GError) _err = NULL; \
+               gint _val = _g_gpiosim_chip_get_value(self, offset, &_err); \
+               g_assert_no_error(_err); \
+               if (g_test_failed()) \
+                       return; \
+               _val; \
+       })
+
+typedef struct {
+       guint offset;
+       const gchar *name;
+} GPIOSimLineName;
+
+typedef struct {
+       guint offset;
+       const gchar *name;
+       GPIOSimDirection direction;
+} GPIOSimHog;
+
+GVariant *g_gpiosim_package_line_names(const GPIOSimLineName *names);
+GVariant *g_gpiosim_package_hogs(const GPIOSimHog *hogs);
+
+G_END_DECLS
+
+#endif /* __GPIOD_TEST_SIM_H__ */
diff --git a/tests/harness/Makefile.am b/tests/harness/Makefile.am
new file mode 100644 (file)
index 0000000..185c00f
--- /dev/null
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+
+noinst_LTLIBRARIES = libgpiod-test-harness.la
+libgpiod_test_harness_la_SOURCES = \
+       gpiod-test.c \
+       gpiod-test.h \
+       gpiod-test-common.h
+
+AM_CFLAGS = -include $(top_builddir)/config.h
+AM_CFLAGS += -Wall -Wextra -g -std=gnu89 $(GLIB_CFLAGS)
+AM_CFLAGS += -DG_LOG_DOMAIN=\"gpiod-test\"
diff --git a/tests/harness/gpiod-test-common.h b/tests/harness/gpiod-test-common.h
new file mode 100644 (file)
index 0000000..7aaec05
--- /dev/null
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <brgl@bgdev.pl> */
+
+#ifndef __GPIOD_TEST_COMMON_H__
+#define __GPIOD_TEST_COMMON_H__
+
+#include <glib.h>
+
+#define gpiod_test_return_if_failed() \
+       do { \
+               if (g_test_failed()) \
+                       return; \
+       } while (0)
+
+#define gpiod_test_join_thread_and_return_if_failed(_thread) \
+       do { \
+               if (g_test_failed()) { \
+                       g_thread_join(_thread); \
+                       return; \
+               } \
+       } while (0)
+
+#endif /* __GPIOD_TEST_COMMON_H__ */
diff --git a/tests/harness/gpiod-test.c b/tests/harness/gpiod-test.c
new file mode 100644 (file)
index 0000000..4e65ae2
--- /dev/null
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2017-2022 Bartosz Golaszewski <brgl@bgdev.pl>
+
+#include <errno.h>
+#include <linux/version.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+#include "gpiod-test.h"
+
+#define MIN_KERNEL_MAJOR       5
+#define MIN_KERNEL_MINOR       19
+#define MIN_KERNEL_RELEASE     0
+#define MIN_KERNEL_VERSION     KERNEL_VERSION(MIN_KERNEL_MAJOR, \
+                                              MIN_KERNEL_MINOR, \
+                                              MIN_KERNEL_RELEASE)
+
+static GList *tests;
+
+static gboolean check_kernel(void)
+{
+       guint major, minor, release;
+       struct utsname un;
+       gint ret;
+
+       g_debug("checking linux kernel version");
+
+       ret = uname(&un);
+       if (ret) {
+               g_critical("unable to read the kernel release version: %s",
+                          g_strerror(errno));
+               return FALSE;
+       }
+
+       ret = sscanf(un.release, "%u.%u.%u", &major, &minor, &release);
+       if (ret != 3) {
+               g_critical("error reading kernel release version");
+               return FALSE;
+       }
+
+       if (KERNEL_VERSION(major, minor, release) < MIN_KERNEL_VERSION) {
+               g_critical("linux kernel version must be at least v%u.%u.%u - got v%u.%u.%u",
+                          MIN_KERNEL_MAJOR, MIN_KERNEL_MINOR, MIN_KERNEL_RELEASE,
+                          major, minor, release);
+               return FALSE;
+       }
+
+       g_debug("kernel release is v%u.%u.%u - ok to run tests",
+               major, minor, release);
+
+       return TRUE;
+}
+
+static void test_func_wrapper(gconstpointer data)
+{
+       const struct _gpiod_test_case *test = data;
+
+       test->func();
+}
+
+static void add_test_from_list(gpointer element, gpointer data G_GNUC_UNUSED)
+{
+       struct _gpiod_test_case *test = element;
+
+       g_test_add_data_func(test->path, test, test_func_wrapper);
+}
+
+int main(int argc, char **argv)
+{
+       g_test_init(&argc, &argv, NULL);
+       g_test_set_nonfatal_assertions();
+
+       g_debug("running libgpiod test suite");
+       g_debug("%u tests registered", g_list_length(tests));
+
+       if (!check_kernel())
+               return EXIT_FAILURE;
+
+       g_list_foreach(tests, add_test_from_list, NULL);
+       g_list_free(tests);
+
+       return g_test_run();
+}
+
+void _gpiod_test_register(struct _gpiod_test_case *test)
+{
+       tests = g_list_append(tests, test);
+}
diff --git a/tests/harness/gpiod-test.h b/tests/harness/gpiod-test.h
new file mode 100644 (file)
index 0000000..6a84162
--- /dev/null
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* SPDX-FileCopyrightText: 2017-2022 Bartosz Golaszewski <brgl@bgdev.pl> */
+
+/*
+ * Testing framework for the core library.
+ *
+ * This file contains functions and definitions extending the GLib unit testing
+ * framework with functionalities necessary to test the libgpiod core C API as
+ * well as the kernel-to-user-space interface.
+ */
+
+#ifndef __GPIOD_TEST_H__
+#define __GPIOD_TEST_H__
+
+#include <glib.h>
+
+/* These are private definitions and should not be used directly. */
+
+struct _gpiod_test_case {
+       const gchar *path;
+       void (*func)(void);
+};
+
+void _gpiod_test_register(struct _gpiod_test_case *test);
+
+#define _GPIOD_TEST_PATH(_name) \
+               "/gpiod/" GPIOD_TEST_GROUP "/" G_STRINGIFY(_name)
+
+/*
+ * Register a test case function.
+ */
+#define GPIOD_TEST_CASE(_name) \
+       static void _gpiod_test_func_##_name(void); \
+       static struct _gpiod_test_case _##_name##_test_case = { \
+               .path = _GPIOD_TEST_PATH(_name), \
+               .func = _gpiod_test_func_##_name, \
+       }; \
+       static __attribute__((constructor)) void \
+       _##_name##_test_register(void) \
+       { \
+               _gpiod_test_register(&_##_name##_test_case); \
+       } \
+       static void _gpiod_test_func_##_name(void)
+
+#endif /* __GPIOD_TEST_H__ */
diff --git a/tests/helpers.h b/tests/helpers.h
new file mode 100644 (file)
index 0000000..ecb7baf
--- /dev/null
@@ -0,0 +1,173 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <bartosz.golaszewski@linaro.org> */
+
+#ifndef __GPIOD_TEST_HELPERS_H__
+#define __GPIOD_TEST_HELPERS_H__
+
+#include <glib.h>
+#include <gpiod.h>
+#include <gpiod-test-common.h>
+
+/*
+ * These typedefs are needed to make g_autoptr work - it doesn't accept
+ * regular 'struct typename' syntax.
+ */
+
+typedef struct gpiod_chip struct_gpiod_chip;
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(struct_gpiod_chip, gpiod_chip_close);
+
+typedef struct gpiod_chip_info struct_gpiod_chip_info;
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(struct_gpiod_chip_info, gpiod_chip_info_free);
+
+typedef struct gpiod_line_info struct_gpiod_line_info;
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(struct_gpiod_line_info, gpiod_line_info_free);
+
+typedef struct gpiod_info_event struct_gpiod_info_event;
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(struct_gpiod_info_event, gpiod_info_event_free);
+
+typedef struct gpiod_line_config struct_gpiod_line_config;
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(struct_gpiod_line_config, gpiod_line_config_free);
+
+typedef struct gpiod_line_settings struct_gpiod_line_settings;
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(struct_gpiod_line_settings,
+                             gpiod_line_settings_free);
+
+typedef struct gpiod_request_config struct_gpiod_request_config;
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(struct_gpiod_request_config,
+                             gpiod_request_config_free);
+
+typedef struct gpiod_line_request struct_gpiod_line_request;
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(struct_gpiod_line_request,
+                             gpiod_line_request_release);
+
+typedef struct gpiod_edge_event struct_gpiod_edge_event;
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(struct_gpiod_edge_event, gpiod_edge_event_free);
+
+typedef struct gpiod_edge_event_buffer struct_gpiod_edge_event_buffer;
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(struct_gpiod_edge_event_buffer,
+                             gpiod_edge_event_buffer_free);
+
+#define gpiod_test_open_chip_or_fail(_path) \
+       ({ \
+               struct gpiod_chip *_chip = gpiod_chip_open(_path); \
+               g_assert_nonnull(_chip); \
+               gpiod_test_return_if_failed(); \
+               _chip; \
+       })
+
+#define gpiod_test_chip_get_info_or_fail(_chip) \
+       ({ \
+               struct gpiod_chip_info *_info = gpiod_chip_get_info(_chip); \
+               g_assert_nonnull(_info); \
+               gpiod_test_return_if_failed(); \
+               _info; \
+       })
+
+#define gpiod_test_chip_get_line_info_or_fail(_chip, _offset) \
+       ({ \
+               struct gpiod_line_info *_info = \
+                               gpiod_chip_get_line_info(_chip, _offset); \
+               g_assert_nonnull(_info); \
+               gpiod_test_return_if_failed(); \
+               _info; \
+       })
+
+#define gpiod_test_chip_watch_line_info_or_fail(_chip, _offset) \
+       ({ \
+               struct gpiod_line_info *_info = \
+                               gpiod_chip_watch_line_info(_chip, _offset); \
+               g_assert_nonnull(_info); \
+               gpiod_test_return_if_failed(); \
+               _info; \
+       })
+
+#define gpiod_test_create_line_settings_or_fail() \
+       ({ \
+               struct gpiod_line_settings *_settings = \
+                               gpiod_line_settings_new(); \
+               g_assert_nonnull(_settings); \
+               gpiod_test_return_if_failed(); \
+               _settings; \
+       })
+
+#define gpiod_test_create_line_config_or_fail() \
+       ({ \
+               struct gpiod_line_config *_config = \
+                               gpiod_line_config_new(); \
+               g_assert_nonnull(_config); \
+               gpiod_test_return_if_failed(); \
+               _config; \
+       })
+
+#define gpiod_test_create_edge_event_buffer_or_fail(_capacity) \
+       ({ \
+               struct gpiod_edge_event_buffer *_buffer = \
+                               gpiod_edge_event_buffer_new(_capacity); \
+               g_assert_nonnull(_buffer); \
+               gpiod_test_return_if_failed(); \
+               _buffer; \
+       })
+
+#define gpiod_test_line_config_add_line_settings_or_fail(_line_cfg, _offsets, \
+                                                        _num_offsets, \
+                                                        _settings) \
+       do { \
+               gint _ret = gpiod_line_config_add_line_settings(_line_cfg, \
+                                                               _offsets,  \
+                                                               _num_offsets, \
+                                                               _settings); \
+               g_assert_cmpint(_ret, ==, 0); \
+               gpiod_test_return_if_failed(); \
+       } while (0)
+
+#define gpiod_test_line_config_get_line_settings_or_fail(_line_cfg, _offset) \
+       ({ \
+               struct gpiod_line_settings *_settings = \
+                       gpiod_line_config_get_line_settings(_line_cfg, \
+                                                           _offset); \
+               g_assert_nonnull(_settings); \
+               gpiod_test_return_if_failed(); \
+               _settings; \
+       })
+
+#define gpiod_test_line_config_set_output_values_or_fail(_line_cfg, _values, \
+                                                        _num_values) \
+       do { \
+               gint _ret = gpiod_line_config_set_output_values(_line_cfg, \
+                                                               _values, \
+                                                               _num_values); \
+               g_assert_cmpint(_ret, ==, 0); \
+               gpiod_test_return_if_failed(); \
+       } while (0)
+
+#define gpiod_test_create_request_config_or_fail() \
+       ({ \
+               struct gpiod_request_config *_config = \
+                               gpiod_request_config_new(); \
+               g_assert_nonnull(_config); \
+               gpiod_test_return_if_failed(); \
+               _config; \
+       })
+
+#define gpiod_test_chip_request_lines_or_fail(_chip, _req_cfg, _line_cfg) \
+       ({ \
+               struct gpiod_line_request *_request = \
+                       gpiod_chip_request_lines(_chip, \
+                                                _req_cfg, _line_cfg); \
+               g_assert_nonnull(_request); \
+               gpiod_test_return_if_failed(); \
+               _request; \
+       })
+
+#define gpiod_test_line_request_reconfigure_lines_or_fail(_request, _line_cfg) \
+       do { \
+               gint _ret = gpiod_line_request_reconfigure_lines(_request, \
+                                                                _line_cfg); \
+               g_assert_cmpint(_ret, ==, 0); \
+               gpiod_test_return_if_failed(); \
+       } while (0)
+
+#define gpiod_test_expect_errno(_expected) \
+       g_assert_cmpint((_expected), ==, errno)
+
+#endif /* __GPIOD_TEST_HELPERS_H__ */
index db76385c57f4ca11fc33c12eb1239e858aed43ca..7b2e857de9ab0b942f8230208e7acb75538ad62b 100644 (file)
@@ -4,10 +4,11 @@
 #include <errno.h>
 #include <glib.h>
 #include <gpiod.h>
+#include <gpiod-test.h>
+#include <gpiod-test-common.h>
+#include <gpiosim-glib.h>
 
-#include "gpiod-test.h"
-#include "gpiod-test-helpers.h"
-#include "gpiod-test-sim.h"
+#include "helpers.h"
 
 #define GPIOD_TEST_GROUP "chip-info"
 
index 815b4c70847b4476f7d8898baaa2b7e718102f83..13e3f61bfae865a9143a444a3b47c05893153d9b 100644 (file)
@@ -4,10 +4,11 @@
 #include <errno.h>
 #include <glib.h>
 #include <gpiod.h>
+#include <gpiod-test.h>
+#include <gpiod-test-common.h>
+#include <gpiosim-glib.h>
 
-#include "gpiod-test.h"
-#include "gpiod-test-helpers.h"
-#include "gpiod-test-sim.h"
+#include "helpers.h"
 
 #define GPIOD_TEST_GROUP "chip"
 
@@ -89,7 +90,7 @@ GPIOD_TEST_CASE(find_line_bad)
 
        g_autoptr(GPIOSimChip) sim = NULL;
        g_autoptr(struct_gpiod_chip) chip = NULL;
-       g_autoptr(GVariant) vnames = gpiod_test_package_line_names(names);
+       g_autoptr(GVariant) vnames = g_gpiosim_package_line_names(names);
 
        sim = g_gpiosim_chip_new(
                        "num-lines", 8,
@@ -116,7 +117,7 @@ GPIOD_TEST_CASE(find_line_good)
 
        g_autoptr(GPIOSimChip) sim = NULL;
        g_autoptr(struct_gpiod_chip) chip = NULL;
-       g_autoptr(GVariant) vnames = gpiod_test_package_line_names(names);
+       g_autoptr(GVariant) vnames = g_gpiosim_package_line_names(names);
 
        sim = g_gpiosim_chip_new(
                        "num-lines", 8,
@@ -142,7 +143,7 @@ GPIOD_TEST_CASE(find_line_duplicate)
 
        g_autoptr(GPIOSimChip) sim = NULL;
        g_autoptr(struct_gpiod_chip) chip = NULL;
-       g_autoptr(GVariant) vnames = gpiod_test_package_line_names(names);
+       g_autoptr(GVariant) vnames = g_gpiosim_package_line_names(names);
 
        sim = g_gpiosim_chip_new(
                        "num-lines", 8,
@@ -165,7 +166,7 @@ GPIOD_TEST_CASE(find_line_non_standard_names)
                { }
        };
 
-       g_autoptr(GVariant) vnames = gpiod_test_package_line_names(names);
+       g_autoptr(GVariant) vnames = g_gpiosim_package_line_names(names);
        g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8,
                                                        "line-names", vnames,
                                                        NULL);
index b744ca546a1cd42c6e6bdc35d6cc896fe670d719..6389455763ed4ee2215de6409d1b042169776902 100644 (file)
@@ -3,11 +3,12 @@
 
 #include <glib.h>
 #include <gpiod.h>
+#include <gpiod-test.h>
+#include <gpiod-test-common.h>
+#include <gpiosim-glib.h>
 #include <poll.h>
 
-#include "gpiod-test.h"
-#include "gpiod-test-helpers.h"
-#include "gpiod-test-sim.h"
+#include "helpers.h"
 
 #define GPIOD_TEST_GROUP "edge-event"
 
index cbd9e9e2a86b6ec8cbdd630426e1da40a7d5ce67..e014500268d8908ea058edbcff3cae10a577c1d5 100644 (file)
@@ -3,11 +3,12 @@
 
 #include <glib.h>
 #include <gpiod.h>
+#include <gpiod-test.h>
+#include <gpiod-test-common.h>
+#include <gpiosim-glib.h>
 #include <poll.h>
 
-#include "gpiod-test.h"
-#include "gpiod-test-helpers.h"
-#include "gpiod-test-sim.h"
+#include "helpers.h"
 
 #define GPIOD_TEST_GROUP "info-event"
 
index e54cfccfd8ab196a8002912ade67fea4dfd13fca..ff220fc63b6321a8e4fa2cf208745d871a0c116f 100644 (file)
@@ -4,10 +4,11 @@
 #include <glib.h>
 #include <gpiod.h>
 #include <poll.h>
+#include <gpiod-test.h>
+#include <gpiod-test-common.h>
+#include <gpiosim-glib.h>
 
-#include "gpiod-test.h"
-#include "gpiod-test-helpers.h"
-#include "gpiod-test-sim.h"
+#include "helpers.h"
 
 #define GPIOD_TEST_GROUP "kernel-uapi"
 
index 469500b833b6a78f71f6bd28fc9b4c8994bba77e..b61a4451012fe27a79c55697a49f22cf3a2a9d7b 100644 (file)
@@ -4,10 +4,11 @@
 #include <errno.h>
 #include <glib.h>
 #include <gpiod.h>
+#include <gpiod-test.h>
+#include <gpiod-test-common.h>
+#include <gpiosim-glib.h>
 
-#include "gpiod-test.h"
-#include "gpiod-test-helpers.h"
-#include "gpiod-test-sim.h"
+#include "helpers.h"
 
 #define GPIOD_TEST_GROUP "line-config"
 
index cf2c650d0b188c639ecb58b33ed96a715f3b5a10..92cd7e08c248e765abf0ea8b2dab42d1892a9dc2 100644 (file)
@@ -4,10 +4,11 @@
 #include <errno.h>
 #include <glib.h>
 #include <gpiod.h>
+#include <gpiod-test.h>
+#include <gpiod-test-common.h>
+#include <gpiosim-glib.h>
 
-#include "gpiod-test.h"
-#include "gpiod-test-helpers.h"
-#include "gpiod-test-sim.h"
+#include "helpers.h"
 
 #define GPIOD_TEST_GROUP "line-info"
 
@@ -64,8 +65,8 @@ GPIOD_TEST_CASE(line_info_basic_properties)
        g_autoptr(struct_gpiod_chip) chip = NULL;
        g_autoptr(struct_gpiod_line_info) info4 = NULL;
        g_autoptr(struct_gpiod_line_info) info6 = NULL;
-       g_autoptr(GVariant) vnames = gpiod_test_package_line_names(names);
-       g_autoptr(GVariant) vhogs = gpiod_test_package_hogs(hogs);
+       g_autoptr(GVariant) vnames = g_gpiosim_package_line_names(names);
+       g_autoptr(GVariant) vhogs = g_gpiosim_package_hogs(hogs);
 
        sim = g_gpiosim_chip_new(
                        "num-lines", 8,
index 7bba07832995d1a82f04a1dfbb6aaf2bba252c70..dd4e9a8fdf0b48d61c2d1443a9007a45c26b43a1 100644 (file)
@@ -3,10 +3,11 @@
 
 #include <glib.h>
 #include <gpiod.h>
+#include <gpiod-test.h>
+#include <gpiod-test-common.h>
+#include <gpiosim-glib.h>
 
-#include "gpiod-test.h"
-#include "gpiod-test-helpers.h"
-#include "gpiod-test-sim.h"
+#include "helpers.h"
 
 #define GPIOD_TEST_GROUP "line-request"
 
index b86fd26bd9a65555ca0aee7b80ca4a94883b5851..18fde50d8a61037c20ba9f13816fce79a461ea2f 100644 (file)
@@ -4,9 +4,10 @@
 #include <errno.h>
 #include <glib.h>
 #include <gpiod.h>
+#include <gpiod-test.h>
+#include <gpiod-test-common.h>
 
-#include "gpiod-test.h"
-#include "gpiod-test-helpers.h"
+#include "helpers.h"
 
 #define GPIOD_TEST_GROUP "line-settings"
 
index 240dd02487bfc4bb652f1cedf138fff1da7ac642..9d4f3dedbae6f0e415d85be52d7b47dad01cce43 100644 (file)
@@ -4,11 +4,12 @@
 #include <errno.h>
 #include <glib.h>
 #include <gpiod.h>
+#include <gpiod-test.h>
+#include <gpiod-test-common.h>
+#include <gpiosim-glib.h>
 #include <unistd.h>
 
-#include "gpiod-test.h"
-#include "gpiod-test-helpers.h"
-#include "gpiod-test-sim.h"
+#include "helpers.h"
 
 #define GPIOD_TEST_GROUP "misc"
 
index d3c679a8dd637167dc68030a6f7f11e8ffcac642..a38befd2db1df3b82173d0839a3943359da86f3d 100644 (file)
@@ -3,9 +3,10 @@
 
 #include <glib.h>
 #include <gpiod.h>
+#include <gpiod-test.h>
+#include <gpiod-test-common.h>
 
-#include "gpiod-test.h"
-#include "gpiod-test-helpers.h"
+#include "helpers.h"
 
 #define GPIOD_TEST_GROUP "request-config"