new file mode 100644
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2024 Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+
+#include "helpers.h"
+
+GArray *gpiodglib_test_array_from_const(gconstpointer data, gsize len,
+ gsize elem_size)
+{
+ GArray *arr = g_array_new(FALSE, TRUE, elem_size);
+
+ return g_array_append_vals(arr, data, len);
+}
new file mode 100644
@@ -0,0 +1,140 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* SPDX-FileCopyrightText: 2022-2024 Bartosz Golaszewski <bartosz.golaszewski@linaro.org> */
+
+#ifndef __GPIODGLIB_TEST_HELPERS_H__
+#define __GPIODGLIB_TEST_HELPERS_H__
+
+#include <glib.h>
+#include <gpiod-test-common.h>
+
+#define gpiodglib_test_new_chip_or_fail(_path) \
+ ({ \
+ g_autoptr(GError) _err = NULL; \
+ GpiodglibChip *_chip = gpiodglib_chip_new(_path, &_err); \
+ g_assert_nonnull(_chip); \
+ g_assert_no_error(_err); \
+ gpiod_test_return_if_failed(); \
+ _chip; \
+ })
+
+#define gpiodglib_test_chip_get_info_or_fail(_chip) \
+ ({ \
+ g_autoptr(GError) _err = NULL; \
+ GpiodglibChipInfo *_info = gpiodglib_chip_get_info(_chip, \
+ &_err); \
+ g_assert_nonnull(_info); \
+ g_assert_no_error(_err); \
+ gpiod_test_return_if_failed(); \
+ _info; \
+ })
+
+#define gpiodglib_test_chip_get_line_info_or_fail(_chip, _offset) \
+ ({ \
+ g_autoptr(GError) _err = NULL; \
+ GpiodglibLineInfo *_info = \
+ gpiodglib_chip_get_line_info(_chip, _offset, &_err); \
+ g_assert_nonnull(_info); \
+ g_assert_no_error(_err); \
+ gpiod_test_return_if_failed(); \
+ _info; \
+ })
+
+#define gpiodglib_test_chip_watch_line_info_or_fail(_chip, _offset) \
+ ({ \
+ g_autoptr(GError) _err = NULL; \
+ GpiodglibLineInfo *_info = \
+ gpiodglib_chip_watch_line_info(_chip, _offset, \
+ &_err); \
+ g_assert_nonnull(_info); \
+ g_assert_no_error(_err); \
+ gpiod_test_return_if_failed(); \
+ _info; \
+ })
+
+#define gpiodglib_test_chip_unwatch_line_info_or_fail(_chip, _offset) \
+ do { \
+ g_autoptr(GError) _err = NULL; \
+ gboolean ret = gpiodglib_chip_unwatch_line_info(_chip, \
+ _offset, \
+ &_err); \
+ g_assert_true(ret); \
+ g_assert_no_error(_err); \
+ gpiod_test_return_if_failed(); \
+ } while (0)
+
+#define gpiodglib_test_line_config_add_line_settings_or_fail(_config, \
+ _offsets, \
+ _settings) \
+ do { \
+ g_autoptr(GError) _err = NULL; \
+ gboolean _ret = \
+ gpiodglib_line_config_add_line_settings(_config, \
+ _offsets,\
+ _settings, \
+ &_err); \
+ g_assert_true(_ret); \
+ g_assert_no_error(_err); \
+ gpiod_test_return_if_failed(); \
+ } while (0)
+
+#define gpiodglib_test_line_config_get_line_settings_or_fail(_config, \
+ _offset) \
+ ({ \
+ GpiodglibLineSettings *_settings = \
+ gpiodglib_line_config_get_line_settings(_config, \
+ _offset); \
+ g_assert_nonnull(_settings); \
+ gpiod_test_return_if_failed(); \
+ _settings; \
+ })
+
+#define gpiodglib_test_line_config_set_output_values_or_fail(_config, \
+ _values) \
+ do { \
+ g_autoptr(GError) _err = NULL; \
+ gboolean _ret = \
+ gpiodglib_line_config_set_output_values(_config, \
+ _values, \
+ &_err); \
+ g_assert_true(_ret); \
+ g_assert_no_error(_err); \
+ gpiod_test_return_if_failed(); \
+ } while (0)
+
+#define gpiodglib_test_chip_request_lines_or_fail(_chip, _req_cfg, _line_cfg) \
+ ({ \
+ g_autoptr(GError) _err = NULL; \
+ GpiodglibLineRequest *_req = \
+ gpiodglib_chip_request_lines(_chip, _req_cfg, \
+ _line_cfg, &_err); \
+ g_assert_nonnull(_req); \
+ g_assert_no_error(_err); \
+ gpiod_test_return_if_failed(); \
+ _req; \
+ })
+
+#define gpiodglib_test_request_lines_or_fail(_path, _req_cfg, _line_cfg) \
+ ({ \
+ g_autoptr(GpiodglibChip) _chip = \
+ gpiodglib_test_new_chip_or_fail(_path); \
+ GpiodglibLineRequest *_req = \
+ gpiodglib_test_chip_request_lines_or_fail(_chip, \
+ _req_cfg, \
+ _line_cfg); \
+ _req; \
+ })
+
+#define gpiodglib_test_check_error_or_fail(_err, _domain, _code) \
+ do { \
+ g_assert_nonnull(_err); \
+ gpiod_test_return_if_failed(); \
+ g_assert_cmpint(_domain, ==, (_err)->domain); \
+ g_assert_cmpint(_code, ==, (_err)->code); \
+ gpiod_test_return_if_failed(); \
+ } while (0)
+
+GArray *gpiodglib_test_array_from_const(const gconstpointer data, gsize len,
+ gsize elem_size);
+
+#endif /* __GPIODGLIB_TEST_HELPERS_H__ */
+
new file mode 100644
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2022-2024 Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+
+#include <gpiod-glib.h>
+#include <gpiod-test.h>
+#include <gpiod-test-common.h>
+#include <gpiosim-glib.h>
+
+#include "helpers.h"
+
+#define GPIOD_TEST_GROUP "glib/chip-info"
+
+GPIOD_TEST_CASE(get_name)
+{
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new(NULL);
+ g_autoptr(GpiodglibChip) chip = NULL;
+ g_autoptr(GpiodglibChipInfo) info = NULL;
+ g_autofree gchar *name = NULL;
+
+ chip = gpiodglib_test_new_chip_or_fail(
+ g_gpiosim_chip_get_dev_path(sim));
+
+ info = gpiodglib_test_chip_get_info_or_fail(chip);
+ name = gpiodglib_chip_info_dup_name(info);
+
+ g_assert_cmpstr(name, ==, g_gpiosim_chip_get_name(sim));
+}
+
+GPIOD_TEST_CASE(get_label)
+{
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("label", "foobar",
+ NULL);
+ g_autoptr(GpiodglibChip) chip = NULL;
+ g_autoptr(GpiodglibChipInfo) info = NULL;
+ g_autofree gchar *label = NULL;
+
+ chip = gpiodglib_test_new_chip_or_fail(
+ g_gpiosim_chip_get_dev_path(sim));
+
+ info = gpiodglib_test_chip_get_info_or_fail(chip);
+ label = gpiodglib_chip_info_dup_label(info);
+
+ g_assert_cmpstr(label, ==, "foobar");
+}
+
+GPIOD_TEST_CASE(get_num_lines)
+{
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 16, NULL);
+ g_autoptr(GpiodglibChip) chip = NULL;
+ g_autoptr(GpiodglibChipInfo) info = NULL;
+
+ chip = gpiodglib_test_new_chip_or_fail(
+ g_gpiosim_chip_get_dev_path(sim));
+
+ info = gpiodglib_test_chip_get_info_or_fail(chip);
+
+ g_assert_cmpuint(gpiodglib_chip_info_get_num_lines(info), ==, 16);
+}
new file mode 100644
@@ -0,0 +1,187 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2022-2024 Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+
+#include <glib.h>
+#include <gpiod-glib.h>
+#include <gpiod-test.h>
+#include <gpiod-test-common.h>
+#include <gpiosim-glib.h>
+
+#include "helpers.h"
+
+#define GPIOD_TEST_GROUP "glib/chip"
+
+GPIOD_TEST_CASE(open_chip_good)
+{
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new(NULL);
+ g_autoptr(GpiodglibChip) chip = NULL;
+ g_autoptr(GError) err = NULL;
+
+ chip = gpiodglib_chip_new(g_gpiosim_chip_get_dev_path(sim), &err);
+ g_assert_nonnull(chip);
+ g_assert_null(err);
+}
+
+GPIOD_TEST_CASE(open_chip_nonexistent)
+{
+ g_autoptr(GpiodglibChip) chip = NULL;
+ g_autoptr(GError) err = NULL;
+
+ chip = gpiodglib_chip_new("/dev/nonexistent", &err);
+ g_assert_null(chip);
+ gpiodglib_test_check_error_or_fail(err, GPIODGLIB_ERROR,
+ GPIODGLIB_ERR_NOENT);
+}
+
+GPIOD_TEST_CASE(open_chip_not_a_character_device)
+{
+ g_autoptr(GpiodglibChip) chip = NULL;
+ g_autoptr(GError) err = NULL;
+
+ chip = gpiodglib_chip_new("/tmp", &err);
+ g_assert_null(chip);
+ gpiodglib_test_check_error_or_fail(err, GPIODGLIB_ERROR,
+ GPIODGLIB_ERR_NOTTY);
+}
+
+GPIOD_TEST_CASE(open_chip_not_a_gpio_device)
+{
+ g_autoptr(GpiodglibChip) chip = NULL;
+ g_autoptr(GError) err = NULL;
+
+ chip = gpiodglib_chip_new("/dev/null", &err);
+ g_assert_null(chip);
+ gpiodglib_test_check_error_or_fail(err, GPIODGLIB_ERROR,
+ GPIODGLIB_ERR_NODEV);
+}
+
+GPIOD_TEST_CASE(get_chip_path)
+{
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new(NULL);
+ g_autoptr(GpiodglibChip) chip = NULL;
+ const gchar *path = g_gpiosim_chip_get_dev_path(sim);
+ g_autofree gchar *chip_path = NULL;
+
+ chip = gpiodglib_test_new_chip_or_fail(path);
+
+ chip_path = gpiodglib_chip_dup_path(chip);
+ g_assert_cmpstr(chip_path, ==, path);
+}
+
+GPIOD_TEST_CASE(closed_chip)
+{
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new(NULL);
+ g_autoptr(GpiodglibChip) chip = NULL;
+ g_autoptr(GError) err = NULL;
+ g_autoptr(GpiodglibChipInfo) info = NULL;
+ const gchar *path = g_gpiosim_chip_get_dev_path(sim);
+ g_autofree gchar *chip_path = NULL;
+
+ chip = gpiodglib_test_new_chip_or_fail(path);
+
+ gpiodglib_chip_close(chip);
+
+ info = gpiodglib_chip_get_info(chip, &err);
+ g_assert_error(err, GPIODGLIB_ERROR, GPIODGLIB_ERR_CHIP_CLOSED);
+
+ /* Properties still work. */
+ chip_path = gpiodglib_chip_dup_path(chip);
+ g_assert_cmpstr(chip_path, ==, path);
+}
+
+GPIOD_TEST_CASE(find_line_bad)
+{
+ static const GPIOSimLineName names[] = {
+ { .offset = 1, .name = "foo", },
+ { .offset = 2, .name = "bar", },
+ { .offset = 4, .name = "baz", },
+ { .offset = 5, .name = "xyz", },
+ { }
+ };
+
+ 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);
+ g_autoptr(GpiodglibChip) chip = NULL;
+ g_autoptr(GError) err = NULL;
+ guint offset;
+
+ chip = gpiodglib_test_new_chip_or_fail(
+ g_gpiosim_chip_get_dev_path(sim));
+
+ g_assert_false(gpiodglib_chip_get_line_offset_from_name(chip,
+ "nonexistent",
+ &offset, &err));
+ g_assert_no_error(err);
+}
+
+GPIOD_TEST_CASE(find_line_good)
+{
+ static const GPIOSimLineName names[] = {
+ { .offset = 1, .name = "foo", },
+ { .offset = 2, .name = "bar", },
+ { .offset = 4, .name = "baz", },
+ { .offset = 5, .name = "xyz", },
+ { }
+ };
+
+ 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);
+ g_autoptr(GpiodglibChip) chip = NULL;
+ g_autoptr(GError) err = NULL;
+ guint offset;
+
+ chip = gpiodglib_test_new_chip_or_fail(
+ g_gpiosim_chip_get_dev_path(sim));
+
+ g_assert_true(gpiodglib_chip_get_line_offset_from_name(chip, "baz",
+ &offset, &err));
+ g_assert_no_error(err);
+ g_assert_cmpuint(offset, ==, 4);
+}
+
+/* Verify that for duplicated line names, the first one is returned. */
+GPIOD_TEST_CASE(find_line_duplicate)
+{
+ static const GPIOSimLineName names[] = {
+ { .offset = 1, .name = "foo", },
+ { .offset = 2, .name = "baz", },
+ { .offset = 4, .name = "baz", },
+ { .offset = 5, .name = "xyz", },
+ { }
+ };
+
+ 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);
+ g_autoptr(GpiodglibChip) chip = NULL;
+ g_autoptr(GError) err = NULL;
+ guint offset;
+
+ chip = gpiodglib_test_new_chip_or_fail(
+ g_gpiosim_chip_get_dev_path(sim));
+
+ g_assert_true(gpiodglib_chip_get_line_offset_from_name(chip, "baz",
+ &offset, &err));
+ g_assert_no_error(err);
+ g_assert_cmpuint(offset, ==, 2);
+}
+
+GPIOD_TEST_CASE(find_line_null_name)
+{
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new(NULL);
+ g_autoptr(GpiodglibChip) chip = NULL;
+ g_autoptr(GError) err = NULL;
+ guint offset;
+
+ chip = gpiodglib_test_new_chip_or_fail(
+ g_gpiosim_chip_get_dev_path(sim));
+
+ g_assert_false(gpiodglib_chip_get_line_offset_from_name(chip, NULL,
+ &offset, &err));
+ g_assert_error(err, GPIODGLIB_ERROR, GPIODGLIB_ERR_INVAL);
+}
new file mode 100644
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2023-2024 Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+
+#include <gpiod-glib.h>
+#include <gpiod-test.h>
+#include <gpiod-test-common.h>
+#include <gpiosim-glib.h>
+
+#include "helpers.h"
+
+#define GPIOD_TEST_GROUP "glib/edge-event"
+
+static gpointer falling_and_rising_edge_events(gpointer data)
+{
+ GPIOSimChip *sim = data;
+
+ g_usleep(1000);
+
+ g_gpiosim_chip_set_pull(sim, 2, G_GPIOSIM_PULL_UP);
+
+ g_usleep(1000);
+
+ g_gpiosim_chip_set_pull(sim, 2, G_GPIOSIM_PULL_DOWN);
+
+ return NULL;
+}
+
+typedef struct {
+ gboolean rising;
+ gboolean falling;
+ gboolean failed;
+ guint64 falling_ts;
+ guint64 rising_ts;
+ guint falling_offset;
+ guint rising_offset;
+} EdgeEventCallbackData;
+
+static void on_edge_event(GpiodglibLineRequest *request G_GNUC_UNUSED,
+ GpiodglibEdgeEvent *event, gpointer data)
+{
+ EdgeEventCallbackData *cb_data = data;
+
+ if (gpiodglib_edge_event_get_event_type(event) ==
+ GPIODGLIB_EDGE_EVENT_FALLING_EDGE) {
+ cb_data->falling = TRUE;
+ cb_data->falling_ts =
+ gpiodglib_edge_event_get_timestamp_ns(event);
+ cb_data->falling_offset =
+ gpiodglib_edge_event_get_line_offset(event);
+ } else {
+ cb_data->rising = TRUE;
+ cb_data->rising_ts =
+ gpiodglib_edge_event_get_timestamp_ns(event);
+ cb_data->rising_offset =
+ gpiodglib_edge_event_get_line_offset(event);
+ }
+}
+
+static gboolean on_timeout(gpointer data)
+{
+ EdgeEventCallbackData *cb_data = data;
+
+ g_test_fail_printf("timeout while waiting for edge events");
+ cb_data->failed = TRUE;
+
+ return G_SOURCE_CONTINUE;
+}
+
+GPIOD_TEST_CASE(read_both_events)
+{
+ static const guint offset = 2;
+
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL);
+ g_autoptr(GpiodglibChip) chip = NULL;
+ g_autoptr(GpiodglibLineSettings) settings = NULL;
+ g_autoptr(GpiodglibLineConfig) config = NULL;
+ g_autoptr(GpiodglibLineRequest) request = NULL;
+ g_autoptr(GArray) offsets = NULL;
+ g_autoptr(GThread) thread = NULL;
+ EdgeEventCallbackData cb_data = { };
+ guint timeout_id;
+
+ chip = gpiodglib_test_new_chip_or_fail(
+ g_gpiosim_chip_get_dev_path(sim));
+ settings = gpiodglib_line_settings_new(
+ "direction", GPIODGLIB_LINE_DIRECTION_INPUT,
+ "edge-detection", GPIODGLIB_LINE_EDGE_BOTH, NULL);
+ config = gpiodglib_line_config_new();
+ offsets = gpiodglib_test_array_from_const(&offset, 1, sizeof(guint));
+
+ gpiodglib_test_line_config_add_line_settings_or_fail(config, offsets,
+ settings);
+
+ request = gpiodglib_test_chip_request_lines_or_fail(chip, NULL, config);
+
+ g_signal_connect(request, "edge-event",
+ G_CALLBACK(on_edge_event), &cb_data);
+ timeout_id = g_timeout_add_seconds(5, on_timeout, &cb_data);
+
+ thread = g_thread_new("rising-falling-edge-events",
+ falling_and_rising_edge_events, sim);
+ g_thread_ref(thread);
+
+ while (!cb_data.failed && (!cb_data.falling || !cb_data.rising))
+ g_main_context_iteration(NULL, TRUE);
+
+ g_source_remove(timeout_id);
+ g_thread_join(thread);
+
+ g_assert_cmpuint(cb_data.falling_ts, >, cb_data.rising_ts);
+ g_assert_cmpuint(cb_data.falling_offset, ==, offset);
+ g_assert_cmpuint(cb_data.rising_offset, ==, offset);
+}
+
+typedef struct {
+ gboolean failed;
+ gboolean first;
+ gboolean second;
+ guint first_offset;
+ guint second_offset;
+ gulong first_line_seqno;
+ gulong second_line_seqno;
+ gulong first_global_seqno;
+ gulong second_global_seqno;
+} SeqnoCallbackData;
+
+static void on_seqno_edge_event(GpiodglibLineRequest *request G_GNUC_UNUSED,
+ GpiodglibEdgeEvent *event, gpointer data)
+{
+ SeqnoCallbackData *cb_data = data;
+
+ if (!cb_data->first) {
+ cb_data->first_offset =
+ gpiodglib_edge_event_get_line_offset(event);
+ cb_data->first_line_seqno =
+ gpiodglib_edge_event_get_line_seqno(event);
+ cb_data->first_global_seqno =
+ gpiodglib_edge_event_get_global_seqno(event);
+ cb_data->first = TRUE;
+ } else {
+ cb_data->second_offset =
+ gpiodglib_edge_event_get_line_offset(event);
+ cb_data->second_line_seqno =
+ gpiodglib_edge_event_get_line_seqno(event);
+ cb_data->second_global_seqno =
+ gpiodglib_edge_event_get_global_seqno(event);
+ cb_data->second = TRUE;
+ }
+}
+
+static gpointer rising_edge_events_on_two_offsets(gpointer data)
+{
+ GPIOSimChip *sim = data;
+
+ g_usleep(1000);
+
+ g_gpiosim_chip_set_pull(sim, 2, G_GPIOSIM_PULL_UP);
+
+ g_usleep(1000);
+
+ g_gpiosim_chip_set_pull(sim, 3, G_GPIOSIM_PULL_UP);
+
+ return NULL;
+}
+
+static gboolean on_seqno_timeout(gpointer data)
+{
+ SeqnoCallbackData *cb_data = data;
+
+ g_test_fail_printf("timeout while waiting for edge events");
+ cb_data->failed = TRUE;
+
+ return G_SOURCE_CONTINUE;
+}
+
+GPIOD_TEST_CASE(seqno)
+{
+ static const guint offset_vals[] = { 2, 3 };
+
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL);
+ g_autoptr(GpiodglibChip) chip = NULL;
+ g_autoptr(GpiodglibLineSettings) settings = NULL;
+ g_autoptr(GpiodglibLineConfig) config = NULL;
+ g_autoptr(GpiodglibLineRequest) request = NULL;
+ g_autoptr(GArray) offsets = NULL;
+ g_autoptr(GThread) thread = NULL;
+ SeqnoCallbackData cb_data = { };
+ guint timeout_id;
+
+ chip = gpiodglib_test_new_chip_or_fail(
+ g_gpiosim_chip_get_dev_path(sim));
+ settings = gpiodglib_line_settings_new(
+ "direction", GPIODGLIB_LINE_DIRECTION_INPUT,
+ "edge-detection", GPIODGLIB_LINE_EDGE_BOTH, NULL);
+ config = gpiodglib_line_config_new();
+ offsets = gpiodglib_test_array_from_const(offset_vals, 2,
+ sizeof(guint));
+
+ gpiodglib_test_line_config_add_line_settings_or_fail(config, offsets,
+ settings);
+
+ request = gpiodglib_test_chip_request_lines_or_fail(chip, NULL,
+ config);
+ g_signal_connect(request, "edge-event",
+ G_CALLBACK(on_seqno_edge_event), &cb_data);
+
+ timeout_id = g_timeout_add_seconds(5, on_seqno_timeout, &cb_data);
+
+ thread = g_thread_new("two-rising-edge-events",
+ rising_edge_events_on_two_offsets, sim);
+ g_thread_ref(thread);
+
+ while (!cb_data.failed && (!cb_data.first || !cb_data.second))
+ g_main_context_iteration(NULL, TRUE);
+
+ g_source_remove(timeout_id);
+ g_thread_join(thread);
+
+ g_assert_cmpuint(cb_data.first_offset, ==, 2);
+ g_assert_cmpuint(cb_data.second_offset, ==, 3);
+ g_assert_cmpuint(cb_data.first_line_seqno, ==, 1);
+ g_assert_cmpuint(cb_data.second_line_seqno, ==, 1);
+ g_assert_cmpuint(cb_data.first_global_seqno, ==, 1);
+ g_assert_cmpuint(cb_data.second_global_seqno, ==, 2);
+}
new file mode 100644
@@ -0,0 +1,322 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2023-2024 Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+
+#include <gpiod-glib.h>
+#include <gpiod-test.h>
+#include <gpiod-test-common.h>
+#include <gpiosim-glib.h>
+
+#include "helpers.h"
+
+#define GPIOD_TEST_GROUP "glib/info-event"
+
+GPIOD_TEST_CASE(watching_info_events_returns_line_info)
+{
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL);
+ g_autoptr(GpiodglibChip) chip = NULL;
+ g_autoptr(GpiodglibLineInfo) info = NULL;
+
+ chip = gpiodglib_test_new_chip_or_fail(
+ g_gpiosim_chip_get_dev_path(sim));
+ info = gpiodglib_test_chip_watch_line_info_or_fail(chip, 3);
+ g_assert_cmpuint(gpiodglib_line_info_get_offset(info), ==, 3);
+}
+
+GPIOD_TEST_CASE(try_offset_of_out_range)
+{
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL);
+ g_autoptr(GpiodglibChip) chip = NULL;
+ g_autoptr(GpiodglibLineInfo) info = NULL;
+ g_autoptr(GError) err = NULL;
+
+ chip = gpiodglib_test_new_chip_or_fail(
+ g_gpiosim_chip_get_dev_path(sim));
+ info = gpiodglib_chip_watch_line_info(chip, 11, &err);
+ g_assert_null(info);
+ g_assert_error(err, GPIODGLIB_ERROR, GPIODGLIB_ERR_INVAL);
+}
+
+static void on_bad_info_event(GpiodglibChip *chip G_GNUC_UNUSED,
+ GpiodglibInfoEvent *event G_GNUC_UNUSED,
+ gpointer data G_GNUC_UNUSED)
+{
+ g_test_fail_printf("unexpected info event received");
+}
+
+static gboolean on_expected_timeout(gpointer data)
+{
+ gboolean *done = data;
+
+ *done = TRUE;
+
+ return G_SOURCE_REMOVE;
+}
+
+GPIOD_TEST_CASE(event_timeout)
+{
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL);
+ g_autoptr(GpiodglibChip) chip = NULL;
+ g_autoptr(GpiodglibLineInfo) info = NULL;
+ gboolean done = FALSE;
+
+ chip = gpiodglib_test_new_chip_or_fail(
+ g_gpiosim_chip_get_dev_path(sim));
+
+ g_signal_connect(chip, "info-event",
+ G_CALLBACK(on_bad_info_event), NULL);
+ g_timeout_add(100, on_expected_timeout, &done);
+
+ info = gpiodglib_test_chip_watch_line_info_or_fail(chip, 3);
+
+ while (!done && !g_test_failed())
+ g_main_context_iteration(NULL, TRUE);
+}
+
+typedef struct {
+ const gchar *chip_path;
+ guint offset;
+} RequestContext;
+
+typedef struct {
+ GPtrArray *events;
+ guint done;
+ gboolean failed;
+} EventContext;
+
+static gpointer request_reconfigure_release_line(gpointer data)
+{
+ g_autoptr(GpiodglibLineRequest) request = NULL;
+ g_autoptr(GpiodglibLineSettings) settings = NULL;
+ g_autoptr(GpiodglibLineConfig) config = NULL;
+ g_autoptr(GArray) offsets = NULL;
+ g_autoptr(GpiodglibChip) chip = NULL;
+ g_autoptr(GError) err = NULL;
+ RequestContext *ctx = data;
+ gboolean ret;
+
+ chip = gpiodglib_chip_new(ctx->chip_path, &err);
+ g_assert_no_error(err);
+ if (g_test_failed())
+ return NULL;
+
+ offsets = gpiodglib_test_array_from_const(&ctx->offset, 1,
+ sizeof(guint));
+ config = gpiodglib_line_config_new();
+ settings = gpiodglib_line_settings_new(NULL);
+
+ ret = gpiodglib_line_config_add_line_settings(config, offsets,
+ settings, &err);
+ g_assert_true(ret);
+ g_assert_no_error(err);
+ if (g_test_failed())
+ return NULL;
+
+ g_usleep(1000);
+
+ request = gpiodglib_chip_request_lines(chip, NULL, config, &err);
+ g_assert_nonnull(request);
+ g_assert_no_error(err);
+
+ g_usleep(1000);
+
+ gpiodglib_line_config_reset(config);
+ gpiodglib_line_settings_set_direction(settings,
+ GPIODGLIB_LINE_DIRECTION_OUTPUT);
+ ret = gpiodglib_line_config_add_line_settings(config, offsets,
+ settings, &err);
+ g_assert_true(ret);
+ g_assert_no_error(err);
+ if (g_test_failed())
+ return NULL;
+
+ ret = gpiodglib_line_request_reconfigure_lines(request, config, &err);
+ g_assert_true(ret);
+ g_assert_no_error(err);
+ if (g_test_failed())
+ return NULL;
+
+ g_usleep(1000);
+
+ gpiodglib_line_request_release(request);
+
+ return NULL;
+}
+
+static void basic_on_info_event(GpiodglibChip *chip G_GNUC_UNUSED,
+ GpiodglibInfoEvent *event, gpointer data)
+{
+ EventContext *ctx = data;
+
+ g_ptr_array_add(ctx->events, g_object_ref(event));
+ ctx->done++;
+}
+
+static gboolean on_timeout(gpointer data)
+{
+ gboolean *failed = data;
+
+ g_test_fail_printf("wait for info event timed out");
+ *failed = TRUE;
+
+ return G_SOURCE_CONTINUE;
+}
+
+GPIOD_TEST_CASE(request_reconfigure_release_events)
+{
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL);
+ g_autoptr(GpiodglibChip) chip = NULL;
+ g_autoptr(GpiodglibLineInfo) info = NULL;
+ g_autoptr(GPtrArray) events = NULL;
+ g_autoptr(GThread) thread = NULL;
+ const gchar *chip_path = g_gpiosim_chip_get_dev_path(sim);
+ GpiodglibInfoEvent *req_ev, *reconf_ev, *rel_ev;
+ guint64 req_ts, reconf_ts, rel_ts;
+ EventContext ev_ctx;
+ RequestContext req_ctx;
+ guint timeout_id;
+
+ events = g_ptr_array_new_full(3, g_object_unref);
+
+ chip = gpiodglib_test_new_chip_or_fail(chip_path);
+ g_signal_connect(chip, "info-event", G_CALLBACK(basic_on_info_event),
+ &ev_ctx);
+ timeout_id = g_timeout_add_seconds(5, on_timeout, &ev_ctx.failed);
+
+ info = gpiodglib_test_chip_watch_line_info_or_fail(chip, 3);
+
+ g_assert_false(gpiodglib_line_info_is_used(info));
+
+ req_ctx.chip_path = chip_path;
+ req_ctx.offset = 3;
+
+ thread = g_thread_new("request-reconfigure-release",
+ request_reconfigure_release_line, &req_ctx);
+ g_thread_ref(thread);
+
+ ev_ctx.done = 0;
+ ev_ctx.failed = FALSE;
+ ev_ctx.events = events;
+
+ while (ev_ctx.done != 3 && !ev_ctx.failed)
+ g_main_context_iteration(NULL, TRUE);
+
+ g_source_remove(timeout_id);
+ g_thread_join(thread);
+
+ req_ev = g_ptr_array_index(events, 0);
+ reconf_ev = g_ptr_array_index(events, 1);
+ rel_ev = g_ptr_array_index(events, 2);
+
+ g_assert_cmpint(gpiodglib_info_event_get_event_type(req_ev), ==,
+ GPIODGLIB_INFO_EVENT_LINE_REQUESTED);
+ g_assert_cmpint(gpiodglib_info_event_get_event_type(reconf_ev), ==,
+ GPIODGLIB_INFO_EVENT_LINE_CONFIG_CHANGED);
+ g_assert_cmpint(gpiodglib_info_event_get_event_type(rel_ev), ==,
+ GPIODGLIB_INFO_EVENT_LINE_RELEASED);
+
+ req_ts = gpiodglib_info_event_get_timestamp_ns(req_ev);
+ reconf_ts = gpiodglib_info_event_get_timestamp_ns(reconf_ev);
+ rel_ts = gpiodglib_info_event_get_timestamp_ns(rel_ev);
+
+ g_assert_cmpuint(req_ts, <, reconf_ts);
+ g_assert_cmpuint(reconf_ts, <, rel_ts);
+}
+
+static void unwatch_on_info_event(GpiodglibChip *chip G_GNUC_UNUSED,
+ GpiodglibInfoEvent *event G_GNUC_UNUSED,
+ gpointer data)
+{
+ gboolean *got_event = data;
+
+ *got_event = TRUE;
+}
+
+GPIOD_TEST_CASE(unwatch_and_check_that_no_events_are_generated)
+{
+ static const guint offset = 3;
+
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL);
+ g_autoptr(GpiodglibChip) chip = NULL;
+ g_autoptr(GpiodglibLineInfo) info = NULL;
+ g_autoptr(GpiodglibLineRequest) request = NULL;
+ g_autoptr(GpiodglibLineSettings) settings = NULL;
+ g_autoptr(GpiodglibLineConfig) config = NULL;
+ g_autoptr(GArray) offsets = NULL;
+ gboolean got_event = FALSE;
+
+ chip = gpiodglib_test_new_chip_or_fail(
+ g_gpiosim_chip_get_dev_path(sim));
+ g_signal_connect(chip, "info-event", G_CALLBACK(unwatch_on_info_event),
+ &got_event);
+
+ offsets = gpiodglib_test_array_from_const(&offset, 1, sizeof(guint));
+ config = gpiodglib_line_config_new();
+ settings = gpiodglib_line_settings_new(NULL);
+
+ gpiodglib_test_line_config_add_line_settings_or_fail(config, offsets,
+ settings);
+
+ info = gpiodglib_test_chip_watch_line_info_or_fail(chip, offset);
+
+ request = gpiodglib_test_chip_request_lines_or_fail(chip, NULL,
+ config);
+
+ g_main_context_iteration(NULL, TRUE);
+
+ g_assert_true(got_event);
+
+ gpiodglib_test_chip_unwatch_line_info_or_fail(chip, offset);
+
+ got_event = FALSE;
+ gpiodglib_line_request_release(request);
+
+ g_main_context_iteration(NULL, TRUE);
+
+ g_assert_false(got_event);
+}
+
+static void check_line_info_on_info_event(GpiodglibChip *chip G_GNUC_UNUSED,
+ GpiodglibInfoEvent *event,
+ gpointer data)
+{
+ GpiodglibLineInfo **info = data;
+
+ *info = gpiodglib_info_event_get_line_info(event);
+}
+
+GPIOD_TEST_CASE(info_event_contains_new_line_info)
+{
+ static const guint offset = 3;
+
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL);
+ g_autoptr(GpiodglibChip) chip = NULL;
+ g_autoptr(GpiodglibLineInfo) initial_info = NULL;
+ g_autoptr(GpiodglibLineInfo) event_info = NULL;
+ g_autoptr(GpiodglibLineRequest) request = NULL;
+ g_autoptr(GpiodglibLineSettings) settings = NULL;
+ g_autoptr(GpiodglibLineConfig) config = NULL;
+ g_autoptr(GArray) offsets = NULL;
+
+ chip = gpiodglib_test_new_chip_or_fail(
+ g_gpiosim_chip_get_dev_path(sim));
+ g_signal_connect(chip, "info-event",
+ G_CALLBACK(check_line_info_on_info_event),
+ &event_info);
+
+ offsets = gpiodglib_test_array_from_const(&offset, 1, sizeof(guint));
+ config = gpiodglib_line_config_new();
+ settings = gpiodglib_line_settings_new(NULL);
+
+ gpiodglib_test_line_config_add_line_settings_or_fail(config, offsets,
+ settings);
+
+ initial_info = gpiodglib_test_chip_watch_line_info_or_fail(chip,
+ offset);
+
+ request = gpiodglib_test_chip_request_lines_or_fail(chip, NULL,
+ config);
+
+ g_main_context_iteration(NULL, TRUE);
+
+ g_assert_nonnull(event_info);
+}
new file mode 100644
@@ -0,0 +1,187 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2023-2024 Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+
+#include <gpiod-glib.h>
+#include <gpiod-test.h>
+#include <gpiod-test-common.h>
+#include <gpiosim-glib.h>
+
+#include "helpers.h"
+
+#define GPIOD_TEST_GROUP "glib/line-config"
+
+GPIOD_TEST_CASE(too_many_lines)
+{
+ g_autoptr(GpiodglibLineSettings) settings = NULL;
+ g_autoptr(GpiodglibLineConfig) config = NULL;
+ g_autoptr(GArray) offsets = NULL;
+ g_autoptr(GError) err = NULL;
+ gboolean ret;
+ guint i;
+
+ settings = gpiodglib_line_settings_new(NULL);
+ config = gpiodglib_line_config_new();
+ offsets = g_array_new(FALSE, TRUE, sizeof(guint));
+
+ for (i = 0; i < 65; i++)
+ g_array_append_val(offsets, i);
+
+ ret = gpiodglib_line_config_add_line_settings(config, offsets,
+ settings, &err);
+ g_assert_false(ret);
+ g_assert_error(err, GPIODGLIB_ERROR, GPIODGLIB_ERR_E2BIG);
+}
+
+GPIOD_TEST_CASE(get_line_settings)
+{
+ static const guint offset_vals[] = { 0, 1, 2, 3 };
+
+ g_autoptr(GpiodglibLineSettings) settings = NULL;
+ g_autoptr(GpiodglibLineSettings) retrieved = NULL;
+ g_autoptr(GpiodglibLineConfig) config = NULL;
+ g_autoptr(GArray) offsets = NULL;
+
+ settings = gpiodglib_line_settings_new(
+ "direction", GPIODGLIB_LINE_DIRECTION_INPUT,
+ "bias", GPIODGLIB_LINE_BIAS_PULL_DOWN,
+ NULL);
+ config = gpiodglib_line_config_new();
+ offsets = gpiodglib_test_array_from_const(offset_vals, 4,
+ sizeof(guint));
+
+ gpiodglib_test_line_config_add_line_settings_or_fail(config, offsets,
+ settings);
+
+ retrieved = gpiodglib_test_line_config_get_line_settings_or_fail(
+ config, 2);
+ g_assert_cmpint(gpiodglib_line_settings_get_direction(retrieved), ==,
+ GPIODGLIB_LINE_DIRECTION_INPUT);
+ g_assert_cmpint(gpiodglib_line_settings_get_bias(retrieved), ==,
+ GPIODGLIB_LINE_BIAS_PULL_DOWN);
+}
+
+GPIOD_TEST_CASE(null_settings)
+{
+ static const guint offset_vals[] = { 0, 1, 2, 3 };
+
+ g_autoptr(GpiodglibLineConfig) config = NULL;
+ g_autoptr(GpiodglibLineSettings) settings = NULL;
+ g_autoptr(GArray) offsets = NULL;
+
+ config = gpiodglib_line_config_new();
+ offsets = gpiodglib_test_array_from_const(offset_vals, 4,
+ sizeof(guint));
+
+ gpiodglib_test_line_config_add_line_settings_or_fail(config, offsets,
+ NULL);
+
+ settings = gpiodglib_test_line_config_get_line_settings_or_fail(config,
+ 2);
+
+ g_assert_cmpint(gpiodglib_line_settings_get_drive(settings), ==,
+ GPIODGLIB_LINE_DIRECTION_AS_IS);
+}
+
+GPIOD_TEST_CASE(null_offsets)
+{
+ g_autoptr(GpiodglibLineConfig) config = NULL;
+ g_autoptr(GpiodglibLineSettings) settings = NULL;
+ g_autoptr(GArray) offsets = NULL;
+ g_autoptr(GError) err = NULL;
+ gboolean ret;
+
+ settings = gpiodglib_line_settings_new(NULL);
+ config = gpiodglib_line_config_new();
+ offsets = g_array_new(FALSE, TRUE, sizeof(guint));
+
+ ret = gpiodglib_line_config_add_line_settings(config, NULL, settings,
+ &err);
+ g_assert_false(ret);
+ g_assert_error(err, GPIODGLIB_ERROR, GPIODGLIB_ERR_INVAL);
+}
+
+GPIOD_TEST_CASE(zero_offsets)
+{
+ g_autoptr(GpiodglibLineConfig) config = NULL;
+ g_autoptr(GpiodglibLineSettings) settings = NULL;
+ g_autoptr(GArray) offsets = NULL;
+ g_autoptr(GError) err = NULL;
+ gboolean ret;
+
+ settings = gpiodglib_line_settings_new(NULL);
+ config = gpiodglib_line_config_new();
+ offsets = g_array_new(FALSE, TRUE, sizeof(guint));
+
+ ret = gpiodglib_line_config_add_line_settings(config, offsets, settings,
+ &err);
+ g_assert_false(ret);
+ g_assert_error(err, GPIODGLIB_ERROR, GPIODGLIB_ERR_INVAL);
+}
+
+GPIOD_TEST_CASE(set_global_output_values)
+{
+ static const guint offset_vals[] = { 0, 1, 2, 3 };
+ static const GpiodglibLineValue output_values[] = {
+ GPIODGLIB_LINE_VALUE_ACTIVE,
+ GPIODGLIB_LINE_VALUE_INACTIVE,
+ GPIODGLIB_LINE_VALUE_ACTIVE,
+ GPIODGLIB_LINE_VALUE_INACTIVE,
+ };
+
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 4, NULL);
+ g_autoptr(GpiodglibChip) chip = NULL;
+ g_autoptr(GpiodglibLineConfig) config = NULL;
+ g_autoptr(GpiodglibLineSettings) settings = NULL;
+ g_autoptr(GArray) offsets = NULL;
+ g_autoptr(GArray) values = NULL;
+ g_autoptr(GpiodglibLineRequest) request = NULL;
+
+ chip = gpiodglib_test_new_chip_or_fail(
+ g_gpiosim_chip_get_dev_path(sim));
+ settings = gpiodglib_line_settings_new("direction",
+ GPIODGLIB_LINE_DIRECTION_OUTPUT,
+ NULL);
+ config = gpiodglib_line_config_new();
+ offsets = gpiodglib_test_array_from_const(offset_vals, 4,
+ sizeof(guint));
+ values = gpiodglib_test_array_from_const(output_values, 4,
+ sizeof(GpiodglibLineValue));
+
+ gpiodglib_test_line_config_add_line_settings_or_fail(config, offsets,
+ settings);
+ gpiodglib_test_line_config_set_output_values_or_fail(config, values);
+
+ request = gpiodglib_test_chip_request_lines_or_fail(chip, NULL,
+ config);
+
+ g_assert_cmpint(g_gpiosim_chip_get_value(sim, 0), ==,
+ G_GPIOSIM_VALUE_ACTIVE);
+ g_assert_cmpint(g_gpiosim_chip_get_value(sim, 1), ==,
+ G_GPIOSIM_VALUE_INACTIVE);
+ g_assert_cmpint(g_gpiosim_chip_get_value(sim, 2), ==,
+ G_GPIOSIM_VALUE_ACTIVE);
+ g_assert_cmpint(g_gpiosim_chip_get_value(sim, 3), ==,
+ G_GPIOSIM_VALUE_INACTIVE);
+}
+
+GPIOD_TEST_CASE(handle_duplicate_offsets)
+{
+ static const guint offset_vals[] = { 0, 2, 2, 3 };
+
+ g_autoptr(GpiodglibLineConfig) config = NULL;
+ g_autoptr(GArray) offsets = NULL;
+ g_autoptr(GArray) retrieved = NULL;
+
+ config = gpiodglib_line_config_new();
+ offsets = gpiodglib_test_array_from_const(offset_vals, 4,
+ sizeof(guint));
+
+ gpiodglib_test_line_config_add_line_settings_or_fail(config, offsets,
+ NULL);
+
+ retrieved = gpiodglib_line_config_get_configured_offsets(config);
+ g_assert_cmpuint(retrieved->len, ==, 3);
+ g_assert_cmpuint(g_array_index(retrieved, guint, 0), ==, 0);
+ g_assert_cmpuint(g_array_index(retrieved, guint, 1), ==, 2);
+ g_assert_cmpuint(g_array_index(retrieved, guint, 2), ==, 3);
+}
new file mode 100644
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2023-2024 Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+
+#include <gpiod-glib.h>
+#include <gpiod-test.h>
+#include <gpiod-test-common.h>
+#include <gpiosim-glib.h>
+
+#include "helpers.h"
+
+#define GPIOD_TEST_GROUP "glib/line-info"
+
+GPIOD_TEST_CASE(get_line_info_good)
+{
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL);
+ g_autoptr(GpiodglibChip) chip = NULL;
+ g_autoptr(GpiodglibLineInfo) info = NULL;
+
+ chip = gpiodglib_test_new_chip_or_fail(
+ g_gpiosim_chip_get_dev_path(sim));
+
+ info = gpiodglib_test_chip_get_line_info_or_fail(chip, 3);
+
+ g_assert_cmpuint(gpiodglib_line_info_get_offset(info), ==, 3);
+}
+
+GPIOD_TEST_CASE(get_line_info_offset_out_of_range)
+{
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL);
+ g_autoptr(GpiodglibChip) chip = NULL;
+ g_autoptr(GpiodglibLineInfo) info = NULL;
+ g_autoptr(GError) err = NULL;
+
+ chip = gpiodglib_test_new_chip_or_fail(
+ g_gpiosim_chip_get_dev_path(sim));
+
+ info = gpiodglib_chip_get_line_info(chip, 8, &err);
+ g_assert_error(err, GPIODGLIB_ERROR, GPIODGLIB_ERR_INVAL);
+}
+
+GPIOD_TEST_CASE(line_info_basic_properties)
+{
+ static const GPIOSimLineName names[] = {
+ { .offset = 1, .name = "foo", },
+ { .offset = 2, .name = "bar", },
+ { .offset = 4, .name = "baz", },
+ { .offset = 5, .name = "xyz", },
+ { }
+ };
+
+ static const GPIOSimHog hogs[] = {
+ {
+ .offset = 3,
+ .name = "hog3",
+ .direction = G_GPIOSIM_DIRECTION_OUTPUT_HIGH,
+ },
+ {
+ .offset = 4,
+ .name = "hog4",
+ .direction = G_GPIOSIM_DIRECTION_OUTPUT_LOW,
+ },
+ { }
+ };
+
+ g_autoptr(GVariant) vnames = g_gpiosim_package_line_names(names);
+ g_autoptr(GVariant) vhogs = g_gpiosim_package_hogs(hogs);
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8,
+ "line-names", vnames,
+ "hogs", vhogs,
+ NULL);
+ g_autoptr(GpiodglibChip) chip = NULL;
+ g_autoptr(GpiodglibLineInfo) info4 = NULL;
+ g_autoptr(GpiodglibLineInfo) info6 = NULL;
+ g_autofree gchar *consumer = NULL;
+ g_autofree gchar *name = NULL;
+
+ chip = gpiodglib_test_new_chip_or_fail(
+ g_gpiosim_chip_get_dev_path(sim));
+ info4 = gpiodglib_test_chip_get_line_info_or_fail(chip, 4);
+ info6 = gpiodglib_test_chip_get_line_info_or_fail(chip, 6);
+
+ g_assert_cmpuint(gpiodglib_line_info_get_offset(info4), ==, 4);
+ name = gpiodglib_line_info_dup_name(info4);
+ g_assert_cmpstr(name, ==, "baz");
+ consumer = gpiodglib_line_info_dup_consumer(info4);
+ g_assert_cmpstr(consumer, ==, "hog4");
+ g_assert_true(gpiodglib_line_info_is_used(info4));
+ g_assert_cmpint(gpiodglib_line_info_get_direction(info4), ==,
+ GPIODGLIB_LINE_DIRECTION_OUTPUT);
+ g_assert_cmpint(gpiodglib_line_info_get_edge_detection(info4), ==,
+ GPIODGLIB_LINE_EDGE_NONE);
+ g_assert_false(gpiodglib_line_info_is_active_low(info4));
+ g_assert_cmpint(gpiodglib_line_info_get_bias(info4), ==,
+ GPIODGLIB_LINE_BIAS_UNKNOWN);
+ g_assert_cmpint(gpiodglib_line_info_get_drive(info4), ==,
+ GPIODGLIB_LINE_DRIVE_PUSH_PULL);
+ g_assert_cmpint(gpiodglib_line_info_get_event_clock(info4), ==,
+ GPIODGLIB_LINE_CLOCK_MONOTONIC);
+ g_assert_false(gpiodglib_line_info_is_debounced(info4));
+ g_assert_cmpuint(gpiodglib_line_info_get_debounce_period_us(info4), ==,
+ 0);
+}
new file mode 100644
@@ -0,0 +1,710 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2023-2024 Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+
+#include <glib.h>
+#include <gpiod-glib.h>
+#include <gpiod-test.h>
+#include <gpiosim-glib.h>
+
+#include "helpers.h"
+
+#define GPIOD_TEST_GROUP "glib/line-request"
+
+GPIOD_TEST_CASE(request_fails_with_no_offsets)
+{
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL);
+ g_autoptr(GpiodglibChip) chip = NULL;
+ g_autoptr(GpiodglibLineConfig) line_cfg = NULL;
+ g_autoptr(GpiodglibLineRequest) request = NULL;
+ g_autoptr(GError) err = NULL;
+
+ line_cfg = gpiodglib_line_config_new();
+
+ chip = gpiodglib_test_new_chip_or_fail(
+ g_gpiosim_chip_get_dev_path(sim));
+
+ request = gpiodglib_chip_request_lines(chip, NULL, line_cfg, &err);
+ g_assert_null(request);
+ g_assert_error(err, GPIODGLIB_ERROR, GPIODGLIB_ERR_INVAL);
+}
+
+GPIOD_TEST_CASE(request_fails_with_no_line_config)
+{
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL);
+ g_autoptr(GpiodglibChip) chip = NULL;
+ g_autoptr(GpiodglibLineRequest) request = NULL;
+ g_autoptr(GError) err = NULL;
+
+ chip = gpiodglib_test_new_chip_or_fail(
+ g_gpiosim_chip_get_dev_path(sim));
+
+ request = gpiodglib_chip_request_lines(chip, NULL, NULL, &err);
+ g_assert_null(request);
+ g_assert_error(err, GPIODGLIB_ERROR, GPIODGLIB_ERR_INVAL);
+}
+
+GPIOD_TEST_CASE(set_consumer)
+{
+ static const gchar *const consumer = "foobar";
+ static const guint offset = 2;
+
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL);
+ g_autoptr(GpiodglibChip) chip = NULL;
+ g_autoptr(GpiodglibRequestConfig) req_cfg = NULL;
+ g_autoptr(GpiodglibLineConfig) line_cfg = NULL;
+ g_autoptr(GpiodglibLineRequest) request = NULL;
+ g_autoptr(GArray) offsets = NULL;
+ g_autoptr(GpiodglibLineInfo) info = NULL;
+ g_autofree gchar *cpy = NULL;
+
+ chip = gpiodglib_test_new_chip_or_fail(
+ g_gpiosim_chip_get_dev_path(sim));
+
+ req_cfg = gpiodglib_request_config_new("consumer", consumer, NULL);
+ line_cfg = gpiodglib_line_config_new();
+ offsets = gpiodglib_test_array_from_const(&offset, 1, sizeof(guint));
+
+ gpiodglib_test_line_config_add_line_settings_or_fail(line_cfg,
+ offsets, NULL);
+
+ request = gpiodglib_test_chip_request_lines_or_fail(chip, req_cfg,
+ line_cfg);
+
+ info = gpiodglib_test_chip_get_line_info_or_fail(chip, offset);
+ cpy = gpiodglib_line_info_dup_consumer(info);
+ g_assert_cmpstr(cpy, ==, consumer);
+}
+
+GPIOD_TEST_CASE(empty_consumer)
+{
+ static const guint offset = 2;
+
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL);
+ g_autoptr(GpiodglibChip) chip = NULL;
+ g_autoptr(GpiodglibLineConfig) line_cfg = NULL;
+ g_autoptr(GpiodglibLineRequest) request = NULL;
+ g_autoptr(GArray) offsets = NULL;
+ g_autoptr(GpiodglibLineInfo) info = NULL;
+ g_autofree gchar *consumer = NULL;
+
+ chip = gpiodglib_test_new_chip_or_fail(
+ g_gpiosim_chip_get_dev_path(sim));
+
+ line_cfg = gpiodglib_line_config_new();
+ offsets = gpiodglib_test_array_from_const(&offset, 1, sizeof(guint));
+
+ gpiodglib_test_line_config_add_line_settings_or_fail(line_cfg,
+ offsets, NULL);
+
+ request = gpiodglib_test_chip_request_lines_or_fail(chip, NULL,
+ line_cfg);
+
+ info = gpiodglib_test_chip_get_line_info_or_fail(chip, offset);
+ consumer = gpiodglib_line_info_dup_consumer(info);
+ g_assert_cmpstr(consumer, ==, "?");
+}
+
+GPIOD_TEST_CASE(get_requested_offsets)
+{
+ static const guint offset_vals[] = { 2, 1, 6, 4 };
+
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL);
+ g_autoptr(GpiodglibLineConfig) line_cfg = NULL;
+ g_autoptr(GpiodglibLineRequest) request = NULL;
+ g_autoptr(GArray) offsets = NULL;
+ g_autoptr(GArray) retrieved = NULL;
+
+ line_cfg = gpiodglib_line_config_new();
+ offsets = gpiodglib_test_array_from_const(offset_vals, 4,
+ sizeof(guint));
+
+ gpiodglib_test_line_config_add_line_settings_or_fail(line_cfg,
+ offsets, NULL);
+
+ request = gpiodglib_test_request_lines_or_fail(
+ g_gpiosim_chip_get_dev_path(sim), NULL, line_cfg);
+
+ retrieved = gpiodglib_line_request_get_requested_offsets(request);
+ g_assert_cmpuint(retrieved->len, ==, 4);
+ g_assert_cmpuint(g_array_index(retrieved, guint, 0), ==, 2);
+ g_assert_cmpuint(g_array_index(retrieved, guint, 1), ==, 1);
+ g_assert_cmpuint(g_array_index(retrieved, guint, 2), ==, 6);
+ g_assert_cmpuint(g_array_index(retrieved, guint, 3), ==, 4);
+}
+
+GPIOD_TEST_CASE(released_request_cannot_be_used_reconfigure)
+{
+ static const guint offset = 3;
+
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL);
+ g_autoptr(GpiodglibLineConfig) line_cfg = NULL;
+ g_autoptr(GpiodglibLineRequest) request = NULL;
+ g_autoptr(GArray) offsets = NULL;
+ g_autoptr(GError) err = NULL;
+ gboolean ret;
+
+ line_cfg = gpiodglib_line_config_new();
+ offsets = gpiodglib_test_array_from_const(&offset, 1, sizeof(guint));
+
+ gpiodglib_test_line_config_add_line_settings_or_fail(line_cfg,
+ offsets, NULL);
+
+ request = gpiodglib_test_request_lines_or_fail(
+ g_gpiosim_chip_get_dev_path(sim), NULL, line_cfg);
+
+ gpiodglib_line_request_release(request);
+
+ ret = gpiodglib_line_request_reconfigure_lines(request, line_cfg, &err);
+ g_assert_false(ret);
+ g_assert_error(err, GPIODGLIB_ERROR, GPIODGLIB_ERR_REQUEST_RELEASED);
+}
+
+GPIOD_TEST_CASE(released_request_cannot_be_used_get_value)
+{
+ static const guint offset = 3;
+
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL);
+ g_autoptr(GpiodglibLineSettings) settings = NULL;
+ g_autoptr(GpiodglibLineConfig) line_cfg = NULL;
+ g_autoptr(GpiodglibLineRequest) request = NULL;
+ g_autoptr(GArray) offsets = NULL;
+ g_autoptr(GArray) values = NULL;
+ g_autoptr(GError) err = NULL;
+ GpiodglibLineValue value;
+ gboolean ret;
+
+ line_cfg = gpiodglib_line_config_new();
+ settings = gpiodglib_line_settings_new(
+ "direction", GPIODGLIB_LINE_DIRECTION_INPUT, NULL);
+ offsets = gpiodglib_test_array_from_const(&offset, 1, sizeof(guint));
+ gpiodglib_test_line_config_add_line_settings_or_fail(line_cfg,
+ offsets, NULL);
+
+ request = gpiodglib_test_request_lines_or_fail(
+ g_gpiosim_chip_get_dev_path(sim), NULL, line_cfg);
+
+ gpiodglib_line_request_release(request);
+
+ ret = gpiodglib_line_request_get_value(request, offset, &value, &err);
+ g_assert_false(ret);
+ g_assert_error(err, GPIODGLIB_ERROR, GPIODGLIB_ERR_REQUEST_RELEASED);
+
+ g_clear_pointer(&err, g_error_free);
+
+ ret = gpiodglib_line_request_get_values(request, &values, &err);
+ g_assert_false(ret);
+ g_assert_error(err, GPIODGLIB_ERROR, GPIODGLIB_ERR_REQUEST_RELEASED);
+}
+
+GPIOD_TEST_CASE(released_request_cannot_be_used_set_value)
+{
+ static const guint offset = 3;
+ static const GpiodglibLineValue value = GPIODGLIB_LINE_VALUE_ACTIVE;
+
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL);
+ g_autoptr(GpiodglibLineSettings) settings = NULL;
+ g_autoptr(GpiodglibLineConfig) line_cfg = NULL;
+ g_autoptr(GpiodglibLineRequest) request = NULL;
+ g_autoptr(GArray) offsets = NULL;
+ g_autoptr(GArray) values = NULL;
+ g_autoptr(GError) err = NULL;
+ gboolean ret;
+
+ line_cfg = gpiodglib_line_config_new();
+ settings = gpiodglib_line_settings_new(
+ "direction", GPIODGLIB_LINE_DIRECTION_OUTPUT, NULL);
+ offsets = gpiodglib_test_array_from_const(&offset, 1, sizeof(guint));
+ gpiodglib_test_line_config_add_line_settings_or_fail(line_cfg,
+ offsets, NULL);
+
+ request = gpiodglib_test_request_lines_or_fail(
+ g_gpiosim_chip_get_dev_path(sim), NULL, line_cfg);
+
+ gpiodglib_line_request_release(request);
+
+ ret = gpiodglib_line_request_set_value(request, offset, value, &err);
+ g_assert_false(ret);
+ g_assert_error(err, GPIODGLIB_ERROR, GPIODGLIB_ERR_REQUEST_RELEASED);
+
+ g_clear_pointer(&err, g_error_free);
+
+ values = gpiodglib_test_array_from_const(&value, 1, sizeof(value));
+ ret = gpiodglib_line_request_set_values(request, values, &err);
+ g_assert_false(ret);
+ g_assert_error(err, GPIODGLIB_ERROR, GPIODGLIB_ERR_REQUEST_RELEASED);
+}
+
+GPIOD_TEST_CASE(reconfigure_lines)
+{
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL);
+ g_autoptr(GpiodglibLineConfig) line_cfg = NULL;
+ g_autoptr(GpiodglibLineSettings) settings = NULL;
+ g_autoptr(GpiodglibLineRequest) request = NULL;
+ g_autoptr(GArray) offsets = NULL;
+ g_autoptr(GError) err = NULL;
+ guint offset_vals[2];
+ gboolean ret;
+
+ line_cfg = gpiodglib_line_config_new();
+ settings = gpiodglib_line_settings_new(
+ "direction", GPIODGLIB_LINE_DIRECTION_OUTPUT,
+ "output-value", GPIODGLIB_LINE_VALUE_ACTIVE,
+ NULL);
+
+ offsets = g_array_new(FALSE, TRUE, sizeof(guint));
+ offset_vals[0] = 0;
+ offset_vals[1] = 2;
+ g_array_append_vals(offsets, offset_vals, 2);
+ gpiodglib_test_line_config_add_line_settings_or_fail(line_cfg,
+ offsets,
+ settings);
+ g_free(g_array_steal(offsets, NULL));
+
+ gpiodglib_line_settings_set_output_value(settings,
+ GPIODGLIB_LINE_VALUE_INACTIVE);
+ offset_vals[0] = 1;
+ offset_vals[1] = 3;
+ g_array_append_vals(offsets, offset_vals, 2);
+ gpiodglib_test_line_config_add_line_settings_or_fail(line_cfg,
+ offsets,
+ settings);
+ g_free(g_array_steal(offsets, NULL));
+
+ request = gpiodglib_test_request_lines_or_fail(
+ g_gpiosim_chip_get_dev_path(sim), NULL, line_cfg);
+
+ g_assert_cmpint(g_gpiosim_chip_get_value(sim, 0), ==,
+ G_GPIOSIM_VALUE_ACTIVE);
+ g_assert_cmpint(g_gpiosim_chip_get_value(sim, 1), ==,
+ G_GPIOSIM_VALUE_INACTIVE);
+ g_assert_cmpint(g_gpiosim_chip_get_value(sim, 2), ==,
+ G_GPIOSIM_VALUE_ACTIVE);
+ g_assert_cmpint(g_gpiosim_chip_get_value(sim, 3), ==,
+ G_GPIOSIM_VALUE_INACTIVE);
+
+ gpiodglib_line_config_reset(line_cfg);
+
+ gpiodglib_line_settings_set_output_value(settings,
+ GPIODGLIB_LINE_VALUE_INACTIVE);
+ offset_vals[0] = 0;
+ offset_vals[1] = 2;
+ g_array_append_vals(offsets, offset_vals, 2);
+ gpiodglib_test_line_config_add_line_settings_or_fail(line_cfg,
+ offsets,
+ settings);
+ g_free(g_array_steal(offsets, NULL));
+
+ gpiodglib_line_settings_set_output_value(settings,
+ GPIODGLIB_LINE_VALUE_ACTIVE);
+ offset_vals[0] = 1;
+ offset_vals[1] = 3;
+ g_array_append_vals(offsets, offset_vals, 2);
+ gpiodglib_test_line_config_add_line_settings_or_fail(line_cfg,
+ offsets,
+ settings);
+
+ ret = gpiodglib_line_request_reconfigure_lines(request, line_cfg, &err);
+ g_assert_true(ret);
+ g_assert_no_error(err);
+ gpiod_test_return_if_failed();
+
+ g_assert_cmpint(g_gpiosim_chip_get_value(sim, 0), ==,
+ G_GPIOSIM_VALUE_INACTIVE);
+ g_assert_cmpint(g_gpiosim_chip_get_value(sim, 1), ==,
+ G_GPIOSIM_VALUE_ACTIVE);
+ g_assert_cmpint(g_gpiosim_chip_get_value(sim, 2), ==,
+ G_GPIOSIM_VALUE_INACTIVE);
+ g_assert_cmpint(g_gpiosim_chip_get_value(sim, 3), ==,
+ G_GPIOSIM_VALUE_ACTIVE);
+}
+
+GPIOD_TEST_CASE(reconfigure_fails_without_config)
+{
+ static const guint offset = 3;
+
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL);
+ g_autoptr(GpiodglibLineConfig) line_cfg = NULL;
+ g_autoptr(GpiodglibLineRequest) request = NULL;
+ g_autoptr(GArray) offsets = NULL;
+ g_autoptr(GError) err = NULL;
+ gboolean ret;
+
+ line_cfg = gpiodglib_line_config_new();
+ offsets = gpiodglib_test_array_from_const(&offset, 1, sizeof(guint));
+
+ gpiodglib_test_line_config_add_line_settings_or_fail(line_cfg,
+ offsets, NULL);
+
+ request = gpiodglib_test_request_lines_or_fail(
+ g_gpiosim_chip_get_dev_path(sim), NULL, line_cfg);
+
+ ret = gpiodglib_line_request_reconfigure_lines(request, NULL, &err);
+ g_assert_false(ret);
+ g_assert_error(err, GPIODGLIB_ERROR, GPIODGLIB_ERR_INVAL);
+}
+
+GPIOD_TEST_CASE(reconfigure_with_different_offsets)
+{
+ static const guint offsets0[] = { 0, 1, 2, 3 };
+ static const guint offsets1[] = { 2, 4, 5 };
+
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL);
+ g_autoptr(GpiodglibLineConfig) line_cfg = NULL;
+ g_autoptr(GpiodglibLineRequest) request = NULL;
+ g_autoptr(GArray) offsets = NULL;
+ g_autoptr(GError) err = NULL;
+ gboolean ret;
+
+ line_cfg = gpiodglib_line_config_new();
+ offsets = gpiodglib_test_array_from_const(offsets0, 4, sizeof(guint));
+ gpiodglib_test_line_config_add_line_settings_or_fail(line_cfg,
+ offsets, NULL);
+ g_free(g_array_steal(offsets, NULL));
+
+ request = gpiodglib_test_request_lines_or_fail(
+ g_gpiosim_chip_get_dev_path(sim), NULL, line_cfg);
+
+ gpiodglib_line_config_reset(line_cfg);
+
+ g_array_append_vals(offsets, offsets1, 3);
+ gpiodglib_test_line_config_add_line_settings_or_fail(line_cfg,
+ offsets, NULL);
+
+ ret = gpiodglib_line_request_reconfigure_lines(request, line_cfg, &err);
+ g_assert_false(ret);
+ g_assert_error(err, GPIODGLIB_ERROR, GPIODGLIB_ERR_INVAL);
+}
+
+GPIOD_TEST_CASE(read_one_value)
+{
+ static const guint offset_vals[] = { 0, 2, 4 };
+ static const gint pulls[] = { 0, 1, 0 };
+
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL);
+ g_autoptr(GpiodglibLineConfig) line_cfg = NULL;
+ g_autoptr(GpiodglibLineSettings) settings = NULL;
+ g_autoptr(GpiodglibLineRequest) request = NULL;
+ g_autoptr(GArray) offsets = NULL;
+ g_autoptr(GError) err = NULL;
+ GpiodglibLineValue value;
+ gboolean ret;
+ guint i;
+
+ line_cfg = gpiodglib_line_config_new();
+ settings = gpiodglib_line_settings_new(
+ "direction", GPIODGLIB_LINE_DIRECTION_INPUT, NULL);
+ offsets = gpiodglib_test_array_from_const(offset_vals, 3,
+ sizeof(guint));
+ gpiodglib_test_line_config_add_line_settings_or_fail(line_cfg,
+ offsets,
+ settings);
+
+ request = gpiodglib_test_request_lines_or_fail(
+ g_gpiosim_chip_get_dev_path(sim), NULL, line_cfg);
+
+ for (i = 0; i < 3; i++)
+ g_gpiosim_chip_set_pull(sim, offset_vals[i],
+ pulls[i] ? G_GPIOSIM_PULL_UP :
+ G_GPIOSIM_PULL_DOWN);
+
+ ret = gpiodglib_line_request_get_value(request, 2, &value, &err);
+ g_assert_true(ret);
+ g_assert_no_error(err);
+ gpiod_test_return_if_failed();
+ g_assert_cmpint(value, ==, GPIODGLIB_LINE_VALUE_ACTIVE);
+}
+
+GPIOD_TEST_CASE(read_all_values_null_array)
+{
+ static const guint offset_vals[] = { 0, 2, 4, 5, 7 };
+ static const gint pulls[] = { 0, 1, 0, 1, 1 };
+
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL);
+ g_autoptr(GpiodglibLineConfig) line_cfg = NULL;
+ g_autoptr(GpiodglibLineSettings) settings = NULL;
+ g_autoptr(GpiodglibLineRequest) request = NULL;
+ g_autoptr(GArray) offsets = NULL;
+ g_autoptr(GArray) values = NULL;
+ g_autoptr(GError) err = NULL;
+ gboolean ret;
+ guint i;
+
+ line_cfg = gpiodglib_line_config_new();
+ settings = gpiodglib_line_settings_new(
+ "direction", GPIODGLIB_LINE_DIRECTION_INPUT, NULL);
+ offsets = gpiodglib_test_array_from_const(offset_vals, 5,
+ sizeof(guint));
+ gpiodglib_test_line_config_add_line_settings_or_fail(line_cfg,
+ offsets,
+ settings);
+
+ request = gpiodglib_test_request_lines_or_fail(
+ g_gpiosim_chip_get_dev_path(sim), NULL, line_cfg);
+
+ for (i = 0; i < 5; i++)
+ g_gpiosim_chip_set_pull(sim, offset_vals[i],
+ pulls[i] ? G_GPIOSIM_PULL_UP :
+ G_GPIOSIM_PULL_DOWN);
+
+ ret = gpiodglib_line_request_get_values(request, &values, &err);
+ g_assert_true(ret);
+ g_assert_no_error(err);
+ gpiod_test_return_if_failed();
+
+ g_assert_cmpuint(values->len, ==, 5);
+
+ for (i = 0; i < 5; i++)
+ g_assert_cmpint(g_array_index(values, GpiodglibLineValue, i), ==,
+ pulls[i]);
+}
+
+GPIOD_TEST_CASE(read_all_values_preallocated_array)
+{
+ static const guint offset_vals[] = { 0, 2, 4, 5, 7 };
+ static const gint pulls[] = { 0, 1, 0, 1, 1 };
+
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL);
+ g_autoptr(GpiodglibLineConfig) line_cfg = NULL;
+ g_autoptr(GpiodglibLineSettings) settings = NULL;
+ g_autoptr(GpiodglibLineRequest) request = NULL;
+ g_autoptr(GArray) offsets = NULL;
+ g_autoptr(GArray) values = NULL;
+ g_autoptr(GError) err = NULL;
+ gboolean ret;
+ guint i;
+
+ line_cfg = gpiodglib_line_config_new();
+ settings = gpiodglib_line_settings_new(
+ "direction", GPIODGLIB_LINE_DIRECTION_INPUT, NULL);
+ offsets = gpiodglib_test_array_from_const(offset_vals, 5,
+ sizeof(guint));
+ gpiodglib_test_line_config_add_line_settings_or_fail(line_cfg,
+ offsets,
+ settings);
+
+ request = gpiodglib_test_request_lines_or_fail(
+ g_gpiosim_chip_get_dev_path(sim), NULL, line_cfg);
+
+ for (i = 0; i < 5; i++)
+ g_gpiosim_chip_set_pull(sim, offset_vals[i],
+ pulls[i] ? G_GPIOSIM_PULL_UP :
+ G_GPIOSIM_PULL_DOWN);
+
+ values = g_array_new(FALSE, TRUE, sizeof(GpiodglibLineValue));
+ g_array_set_size(values, 5);
+
+ ret = gpiodglib_line_request_get_values(request, &values, &err);
+ g_assert_true(ret);
+ g_assert_no_error(err);
+ gpiod_test_return_if_failed();
+
+ g_assert_cmpuint(values->len, ==, 5);
+
+ for (i = 0; i < 5; i++)
+ g_assert_cmpint(g_array_index(values, GpiodglibLineValue, i),
+ ==, pulls[i]);
+}
+
+GPIOD_TEST_CASE(set_one_value)
+{
+ static const guint offset = 4;
+
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL);
+ g_autoptr(GpiodglibLineConfig) line_cfg = NULL;
+ g_autoptr(GpiodglibLineSettings) settings = NULL;
+ g_autoptr(GpiodglibLineRequest) request = NULL;
+ g_autoptr(GArray) offsets = NULL;
+ g_autoptr(GError) err = NULL;
+ gboolean ret;
+
+ line_cfg = gpiodglib_line_config_new();
+ settings = gpiodglib_line_settings_new(
+ "direction", GPIODGLIB_LINE_DIRECTION_OUTPUT,
+ "output-value", GPIODGLIB_LINE_VALUE_INACTIVE,
+ NULL);
+ offsets = gpiodglib_test_array_from_const(&offset, 1, sizeof(guint));
+ gpiodglib_test_line_config_add_line_settings_or_fail(line_cfg,
+ offsets,
+ settings);
+
+ request = gpiodglib_test_request_lines_or_fail(
+ g_gpiosim_chip_get_dev_path(sim), NULL, line_cfg);
+
+ g_assert_cmpuint(g_gpiosim_chip_get_value(sim, offset), ==,
+ G_GPIOSIM_VALUE_INACTIVE);
+
+ ret = gpiodglib_line_request_set_value(request, 4,
+ GPIODGLIB_LINE_VALUE_ACTIVE,
+ &err);
+ g_assert_true(ret);
+ g_assert_no_error(err);
+
+ g_assert_cmpuint(g_gpiosim_chip_get_value(sim, offset), ==,
+ G_GPIOSIM_VALUE_ACTIVE);
+}
+
+GPIOD_TEST_CASE(set_all_values)
+{
+ static const guint offset_vals[] = { 0, 2, 4, 5, 6 };
+ static const GpiodglibLineValue value_vals[] = {
+ GPIODGLIB_LINE_VALUE_ACTIVE,
+ GPIODGLIB_LINE_VALUE_INACTIVE,
+ GPIODGLIB_LINE_VALUE_ACTIVE,
+ GPIODGLIB_LINE_VALUE_ACTIVE,
+ GPIODGLIB_LINE_VALUE_ACTIVE
+ };
+ static const GPIOSimValue sim_values[] = {
+ G_GPIOSIM_VALUE_ACTIVE,
+ G_GPIOSIM_VALUE_INACTIVE,
+ G_GPIOSIM_VALUE_ACTIVE,
+ G_GPIOSIM_VALUE_ACTIVE,
+ G_GPIOSIM_VALUE_ACTIVE
+ };
+
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL);
+ g_autoptr(GpiodglibLineConfig) line_cfg = NULL;
+ g_autoptr(GpiodglibLineSettings) settings = NULL;
+ g_autoptr(GpiodglibLineRequest) request = NULL;
+ g_autoptr(GArray) offsets = NULL;
+ g_autoptr(GArray) values = NULL;
+ g_autoptr(GError) err = NULL;
+ gboolean ret;
+ guint i;
+
+ line_cfg = gpiodglib_line_config_new();
+ settings = gpiodglib_line_settings_new(
+ "direction", GPIODGLIB_LINE_DIRECTION_OUTPUT, NULL);
+ offsets = gpiodglib_test_array_from_const(offset_vals, 5, sizeof(guint));
+ gpiodglib_test_line_config_add_line_settings_or_fail(line_cfg,
+ offsets,
+ settings);
+
+ request = gpiodglib_test_request_lines_or_fail(
+ g_gpiosim_chip_get_dev_path(sim), NULL, line_cfg);
+
+ values = gpiodglib_test_array_from_const(value_vals, 5,
+ sizeof(GpiodglibLineValue));
+
+ ret = gpiodglib_line_request_set_values(request, values, &err);
+ g_assert_true(ret);
+ g_assert_no_error(err);
+ gpiod_test_return_if_failed();
+
+ for (i = 0; i < 5; i++)
+ g_assert_cmpint(g_gpiosim_chip_get_value(sim, offset_vals[i]),
+ ==, sim_values[i]);
+}
+
+GPIOD_TEST_CASE(get_values_invalid_arguments)
+{
+ static const guint offset = 3;
+
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL);
+ g_autoptr(GpiodglibLineSettings) settings = NULL;
+ g_autoptr(GpiodglibLineConfig) line_cfg = NULL;
+ g_autoptr(GpiodglibLineRequest) request = NULL;
+ g_autoptr(GArray) offsets = NULL;
+ g_autoptr(GArray) values = NULL;
+ g_autoptr(GError) err = NULL;
+ gboolean ret;
+
+ line_cfg = gpiodglib_line_config_new();
+ settings = gpiodglib_line_settings_new(
+ "direction", GPIODGLIB_LINE_DIRECTION_INPUT, NULL);
+ offsets = gpiodglib_test_array_from_const(&offset, 1, sizeof(offset));
+ gpiodglib_test_line_config_add_line_settings_or_fail(line_cfg,
+ offsets,
+ settings);
+
+ request = gpiodglib_test_request_lines_or_fail(
+ g_gpiosim_chip_get_dev_path(sim), NULL, line_cfg);
+
+ ret = gpiodglib_line_request_get_values_subset(request, offsets, NULL,
+ &err);
+ g_assert_false(ret);
+ g_assert_error(err, GPIODGLIB_ERROR, GPIODGLIB_ERR_INVAL);
+
+ g_clear_pointer(&err, g_error_free);
+
+ ret = gpiodglib_line_request_get_values_subset(request, NULL, &values,
+ &err);
+ g_assert_false(ret);
+ g_assert_error(err, GPIODGLIB_ERROR, GPIODGLIB_ERR_INVAL);
+}
+
+GPIOD_TEST_CASE(set_values_invalid_arguments)
+{
+ static const guint offset = 3;
+ static const GpiodglibLineValue value_vals[] = {
+ GPIODGLIB_LINE_VALUE_ACTIVE,
+ GPIODGLIB_LINE_VALUE_INACTIVE,
+ };
+
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL);
+ g_autoptr(GpiodglibLineSettings) settings = NULL;
+ g_autoptr(GpiodglibLineConfig) line_cfg = NULL;
+ g_autoptr(GpiodglibLineRequest) request = NULL;
+ g_autoptr(GArray) offsets = NULL;
+ g_autoptr(GArray) values = NULL;
+ g_autoptr(GArray) vals_inval = NULL;
+ g_autoptr(GError) err = NULL;
+ gboolean ret;
+
+ line_cfg = gpiodglib_line_config_new();
+ settings = gpiodglib_line_settings_new(
+ "direction", GPIODGLIB_LINE_DIRECTION_OUTPUT, NULL);
+ offsets = gpiodglib_test_array_from_const(&offset, 1, sizeof(offset));
+ values = gpiodglib_test_array_from_const(value_vals, 1,
+ sizeof(GpiodglibLineValue));
+ gpiodglib_test_line_config_add_line_settings_or_fail(line_cfg,
+ offsets,
+ settings);
+
+ request = gpiodglib_test_request_lines_or_fail(
+ g_gpiosim_chip_get_dev_path(sim), NULL, line_cfg);
+
+ ret = gpiodglib_line_request_set_values_subset(request, offsets, NULL,
+ &err);
+ g_assert_false(ret);
+ g_assert_error(err, GPIODGLIB_ERROR, GPIODGLIB_ERR_INVAL);
+
+ g_clear_pointer(&err, g_error_free);
+
+ ret = gpiodglib_line_request_set_values_subset(request, NULL, values,
+ &err);
+ g_assert_false(ret);
+ g_assert_error(err, GPIODGLIB_ERROR, GPIODGLIB_ERR_INVAL);
+
+ g_clear_pointer(&err, g_error_free);
+
+ vals_inval = gpiodglib_test_array_from_const(value_vals, 2,
+ sizeof(GpiodglibLineValue));
+
+ ret = gpiodglib_line_request_set_values_subset(request, offsets,
+ vals_inval, &err);
+ g_assert_false(ret);
+ g_assert_error(err, GPIODGLIB_ERROR, GPIODGLIB_ERR_INVAL);
+}
+
+GPIOD_TEST_CASE(get_chip_name)
+{
+ static const guint offset = 4;
+
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL);
+ g_autoptr(GpiodglibLineConfig) line_cfg = NULL;
+ g_autoptr(GpiodglibLineRequest) request = NULL;
+ g_autoptr(GArray) offsets = NULL;
+ g_autofree gchar *name = NULL;
+
+ line_cfg = gpiodglib_line_config_new();
+ offsets = gpiodglib_test_array_from_const(&offset, 1, sizeof(guint));
+
+ gpiodglib_test_line_config_add_line_settings_or_fail(line_cfg,
+ offsets, NULL);
+
+ request = gpiodglib_test_request_lines_or_fail(
+ g_gpiosim_chip_get_dev_path(sim), NULL, line_cfg);
+
+ name = gpiodglib_line_request_dup_chip_name(request);
+ g_assert_cmpstr(g_gpiosim_chip_get_name(sim), ==, name);
+}
new file mode 100644
@@ -0,0 +1,256 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2023-2024 Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+
+#include <gpiod-glib.h>
+#include <gpiod-test.h>
+
+#include "helpers.h"
+
+#define GPIOD_TEST_GROUP "glib/line-settings"
+
+GPIOD_TEST_CASE(default_config)
+{
+ g_autoptr(GpiodglibLineSettings) settings = NULL;
+
+ settings = gpiodglib_line_settings_new(NULL);
+
+ g_assert_cmpint(gpiodglib_line_settings_get_direction(settings), ==,
+ GPIODGLIB_LINE_DIRECTION_AS_IS);
+ g_assert_cmpint(gpiodglib_line_settings_get_edge_detection(settings),
+ ==, GPIODGLIB_LINE_EDGE_NONE);
+ g_assert_cmpint(gpiodglib_line_settings_get_bias(settings), ==,
+ GPIODGLIB_LINE_BIAS_AS_IS);
+ g_assert_cmpint(gpiodglib_line_settings_get_drive(settings), ==,
+ GPIODGLIB_LINE_DRIVE_PUSH_PULL);
+ g_assert_false(gpiodglib_line_settings_get_active_low(settings));
+ g_assert_cmpint(
+ gpiodglib_line_settings_get_debounce_period_us(settings),
+ ==, 0);
+ g_assert_cmpint(gpiodglib_line_settings_get_event_clock(settings), ==,
+ GPIODGLIB_LINE_CLOCK_MONOTONIC);
+ g_assert_cmpint(gpiodglib_line_settings_get_output_value(settings), ==,
+ GPIODGLIB_LINE_VALUE_INACTIVE);
+}
+
+GPIOD_TEST_CASE(set_direction)
+{
+ g_autoptr(GpiodglibLineSettings) settings = NULL;
+
+ settings = gpiodglib_line_settings_new(NULL);
+
+ gpiodglib_line_settings_set_direction(settings,
+ GPIODGLIB_LINE_DIRECTION_INPUT);
+ g_assert_cmpint(gpiodglib_line_settings_get_direction(settings), ==,
+ GPIODGLIB_LINE_DIRECTION_INPUT);
+
+ gpiodglib_line_settings_set_direction(settings,
+ GPIODGLIB_LINE_DIRECTION_AS_IS);
+ g_assert_cmpint(gpiodglib_line_settings_get_direction(settings), ==,
+ GPIODGLIB_LINE_DIRECTION_AS_IS);
+
+ gpiodglib_line_settings_set_direction(settings,
+ GPIODGLIB_LINE_DIRECTION_OUTPUT);
+ g_assert_cmpint(gpiodglib_line_settings_get_direction(settings), ==,
+ GPIODGLIB_LINE_DIRECTION_OUTPUT);
+}
+
+GPIOD_TEST_CASE(set_edge_detection)
+{
+ g_autoptr(GpiodglibLineSettings) settings = NULL;
+
+ settings = gpiodglib_line_settings_new(NULL);
+
+ gpiodglib_line_settings_set_edge_detection(settings,
+ GPIODGLIB_LINE_EDGE_BOTH);
+ g_assert_cmpint(gpiodglib_line_settings_get_edge_detection(settings),
+ ==, GPIODGLIB_LINE_EDGE_BOTH);
+
+ gpiodglib_line_settings_set_edge_detection(settings,
+ GPIODGLIB_LINE_EDGE_NONE);
+ g_assert_cmpint(gpiodglib_line_settings_get_edge_detection(settings),
+ ==, GPIODGLIB_LINE_EDGE_NONE);
+
+ gpiodglib_line_settings_set_edge_detection(settings,
+ GPIODGLIB_LINE_EDGE_FALLING);
+ g_assert_cmpint(gpiodglib_line_settings_get_edge_detection(settings),
+ ==, GPIODGLIB_LINE_EDGE_FALLING);
+
+ gpiodglib_line_settings_set_edge_detection(settings,
+ GPIODGLIB_LINE_EDGE_RISING);
+ g_assert_cmpint(gpiodglib_line_settings_get_edge_detection(settings),
+ ==, GPIODGLIB_LINE_EDGE_RISING);
+}
+
+GPIOD_TEST_CASE(set_bias)
+{
+ g_autoptr(GpiodglibLineSettings) settings = NULL;
+
+ settings = gpiodglib_line_settings_new(NULL);
+
+ gpiodglib_line_settings_set_bias(settings,
+ GPIODGLIB_LINE_BIAS_DISABLED);
+ g_assert_cmpint(gpiodglib_line_settings_get_bias(settings), ==,
+ GPIODGLIB_LINE_BIAS_DISABLED);
+
+ gpiodglib_line_settings_set_bias(settings, GPIODGLIB_LINE_BIAS_AS_IS);
+ g_assert_cmpint(gpiodglib_line_settings_get_bias(settings), ==,
+ GPIODGLIB_LINE_BIAS_AS_IS);
+
+ gpiodglib_line_settings_set_bias(settings,
+ GPIODGLIB_LINE_BIAS_PULL_DOWN);
+ g_assert_cmpint(gpiodglib_line_settings_get_bias(settings), ==,
+ GPIODGLIB_LINE_BIAS_PULL_DOWN);
+
+ gpiodglib_line_settings_set_bias(settings, GPIODGLIB_LINE_BIAS_PULL_UP);
+ g_assert_cmpint(gpiodglib_line_settings_get_bias(settings), ==,
+ GPIODGLIB_LINE_BIAS_PULL_UP);
+}
+
+GPIOD_TEST_CASE(set_drive)
+{
+ g_autoptr(GpiodglibLineSettings) settings = NULL;
+
+ settings = gpiodglib_line_settings_new(NULL);
+
+ gpiodglib_line_settings_set_drive(settings,
+ GPIODGLIB_LINE_DRIVE_OPEN_DRAIN);
+ g_assert_cmpint(gpiodglib_line_settings_get_drive(settings), ==,
+ GPIODGLIB_LINE_DRIVE_OPEN_DRAIN);
+
+ gpiodglib_line_settings_set_drive(settings,
+ GPIODGLIB_LINE_DRIVE_PUSH_PULL);
+ g_assert_cmpint(gpiodglib_line_settings_get_drive(settings), ==,
+ GPIODGLIB_LINE_DRIVE_PUSH_PULL);
+
+ gpiodglib_line_settings_set_drive(settings,
+ GPIODGLIB_LINE_DRIVE_OPEN_SOURCE);
+ g_assert_cmpint(gpiodglib_line_settings_get_drive(settings), ==,
+ GPIODGLIB_LINE_DRIVE_OPEN_SOURCE);
+}
+
+GPIOD_TEST_CASE(set_active_low)
+{
+ g_autoptr(GpiodglibLineSettings) settings = NULL;
+
+ settings = gpiodglib_line_settings_new(NULL);
+
+ gpiodglib_line_settings_set_active_low(settings, TRUE);
+ g_assert_true(gpiodglib_line_settings_get_active_low(settings));
+
+ gpiodglib_line_settings_set_active_low(settings, FALSE);
+ g_assert_false(gpiodglib_line_settings_get_active_low(settings));
+}
+
+GPIOD_TEST_CASE(set_debounce_period)
+{
+ g_autoptr(GpiodglibLineSettings) settings = NULL;
+
+ settings = gpiodglib_line_settings_new(NULL);
+
+ gpiodglib_line_settings_set_debounce_period_us(settings, 4000);
+ g_assert_cmpint(gpiodglib_line_settings_get_debounce_period_us(settings),
+ ==, 4000);
+}
+
+GPIOD_TEST_CASE(set_event_clock)
+{
+ g_autoptr(GpiodglibLineSettings) settings = NULL;
+
+ settings = gpiodglib_line_settings_new(NULL);
+
+ gpiodglib_line_settings_set_event_clock(settings,
+ GPIODGLIB_LINE_CLOCK_MONOTONIC);
+ g_assert_cmpint(gpiodglib_line_settings_get_event_clock(settings), ==,
+ GPIODGLIB_LINE_CLOCK_MONOTONIC);
+
+ gpiodglib_line_settings_set_event_clock(settings,
+ GPIODGLIB_LINE_CLOCK_REALTIME);
+ g_assert_cmpint(gpiodglib_line_settings_get_event_clock(settings), ==,
+ GPIODGLIB_LINE_CLOCK_REALTIME);
+
+ gpiodglib_line_settings_set_event_clock(settings,
+ GPIODGLIB_LINE_CLOCK_HTE);
+ g_assert_cmpint(gpiodglib_line_settings_get_event_clock(settings), ==,
+ GPIODGLIB_LINE_CLOCK_HTE);
+}
+
+GPIOD_TEST_CASE(set_output_value)
+{
+ g_autoptr(GpiodglibLineSettings) settings = NULL;
+
+ settings = gpiodglib_line_settings_new(NULL);
+
+ gpiodglib_line_settings_set_output_value(settings,
+ GPIODGLIB_LINE_VALUE_ACTIVE);
+ g_assert_cmpint(gpiodglib_line_settings_get_output_value(settings), ==,
+ GPIODGLIB_LINE_VALUE_ACTIVE);
+
+ gpiodglib_line_settings_set_output_value(settings,
+ GPIODGLIB_LINE_VALUE_INACTIVE);
+ g_assert_cmpint(gpiodglib_line_settings_get_output_value(settings), ==,
+ GPIODGLIB_LINE_VALUE_INACTIVE);
+}
+
+GPIOD_TEST_CASE(reset_settings)
+{
+ g_autoptr(GpiodglibLineSettings) settings = NULL;
+
+ settings = gpiodglib_line_settings_new(NULL);
+
+ gpiodglib_line_settings_set_direction(settings,
+ GPIODGLIB_LINE_DIRECTION_INPUT);
+ gpiodglib_line_settings_set_edge_detection(settings,
+ GPIODGLIB_LINE_EDGE_BOTH);
+ gpiodglib_line_settings_set_debounce_period_us(settings, 2000);
+ gpiodglib_line_settings_set_event_clock(settings,
+ GPIODGLIB_LINE_CLOCK_REALTIME);
+
+ gpiodglib_line_settings_reset(settings);
+
+ g_assert_cmpint(gpiodglib_line_settings_get_direction(settings), ==,
+ GPIODGLIB_LINE_DIRECTION_AS_IS);
+ g_assert_cmpint(gpiodglib_line_settings_get_edge_detection(settings),
+ ==, GPIODGLIB_LINE_EDGE_NONE);
+ g_assert_cmpint(gpiodglib_line_settings_get_bias(settings), ==,
+ GPIODGLIB_LINE_BIAS_AS_IS);
+ g_assert_cmpint(gpiodglib_line_settings_get_drive(settings), ==,
+ GPIODGLIB_LINE_DRIVE_PUSH_PULL);
+ g_assert_false(gpiodglib_line_settings_get_active_low(settings));
+ g_assert_cmpint(
+ gpiodglib_line_settings_get_debounce_period_us(settings),
+ ==, 0);
+ g_assert_cmpint(gpiodglib_line_settings_get_event_clock(settings), ==,
+ GPIODGLIB_LINE_CLOCK_MONOTONIC);
+ g_assert_cmpint(gpiodglib_line_settings_get_output_value(settings), ==,
+ GPIODGLIB_LINE_VALUE_INACTIVE);
+}
+
+GPIOD_TEST_CASE(set_props_in_constructor)
+{
+ g_autoptr(GpiodglibLineSettings) settings = NULL;
+
+ settings = gpiodglib_line_settings_new(
+ "direction", GPIODGLIB_LINE_DIRECTION_INPUT,
+ "edge-detection", GPIODGLIB_LINE_EDGE_BOTH,
+ "active-low", TRUE,
+ "debounce-period-us", (GTimeSpan)3000,
+ "bias", GPIODGLIB_LINE_BIAS_PULL_UP,
+ "event-clock", GPIODGLIB_LINE_CLOCK_REALTIME,
+ NULL);
+
+ g_assert_cmpint(gpiodglib_line_settings_get_direction(settings), ==,
+ GPIODGLIB_LINE_DIRECTION_INPUT);
+ g_assert_cmpint(gpiodglib_line_settings_get_edge_detection(settings), ==,
+ GPIODGLIB_LINE_EDGE_BOTH);
+ g_assert_cmpint(gpiodglib_line_settings_get_bias(settings), ==,
+ GPIODGLIB_LINE_BIAS_PULL_UP);
+ g_assert_cmpint(gpiodglib_line_settings_get_drive(settings), ==,
+ GPIODGLIB_LINE_DRIVE_PUSH_PULL);
+ g_assert_true(gpiodglib_line_settings_get_active_low(settings));
+ g_assert_cmpint(gpiodglib_line_settings_get_debounce_period_us(settings),
+ ==, 3000);
+ g_assert_cmpint(gpiodglib_line_settings_get_event_clock(settings), ==,
+ GPIODGLIB_LINE_CLOCK_REALTIME);
+ g_assert_cmpint(gpiodglib_line_settings_get_output_value(settings), ==,
+ GPIODGLIB_LINE_VALUE_INACTIVE);
+}
new file mode 100644
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2022-2024 Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+
+#include <glib.h>
+#include <gpiod-glib.h>
+#include <gpiod-test.h>
+#include <gpiod-test-common.h>
+#include <gpiosim-glib.h>
+
+#define GPIOD_TEST_GROUP "glib/misc"
+
+GPIOD_TEST_CASE(is_gpiochip_bad)
+{
+ g_assert_false(gpiodglib_is_gpiochip_device("/dev/null"));
+ g_assert_false(gpiodglib_is_gpiochip_device("/dev/nonexistent"));
+}
+
+GPIOD_TEST_CASE(is_gpiochip_good)
+{
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new(NULL);
+
+ g_assert_true(gpiodglib_is_gpiochip_device(
+ g_gpiosim_chip_get_dev_path(sim)));
+}
+
+GPIOD_TEST_CASE(is_gpiochip_link_bad)
+{
+ g_autofree gchar *link = NULL;
+ gint ret;
+
+ link = g_strdup_printf("/tmp/gpiod-test-link.%u", getpid());
+ ret = symlink("/dev/null", link);
+ g_assert_cmpint(ret, ==, 0);
+ gpiod_test_return_if_failed();
+
+ g_assert_false(gpiodglib_is_gpiochip_device(link));
+ ret = unlink(link);
+ g_assert_cmpint(ret, ==, 0);
+}
+
+GPIOD_TEST_CASE(is_gpiochip_link_good)
+{
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new(NULL);
+ g_autofree gchar *link = NULL;
+ gint ret;
+
+ link = g_strdup_printf("/tmp/gpiod-test-link.%u", getpid());
+ ret = symlink(g_gpiosim_chip_get_dev_path(sim), link);
+ g_assert_cmpint(ret, ==, 0);
+ gpiod_test_return_if_failed();
+
+ g_assert_true(gpiodglib_is_gpiochip_device(link));
+ ret = unlink(link);
+ g_assert_cmpint(ret, ==, 0);
+}
+
+GPIOD_TEST_CASE(version_string)
+{
+ static const gchar *const pattern = "^\\d+\\.\\d+(\\.\\d+|\\-devel|\\-rc\\d+)$";
+
+ g_autoptr(GError) err = NULL;
+ g_autoptr(GRegex) regex = NULL;
+ g_autoptr(GMatchInfo) match = NULL;
+ g_autofree gchar *res = NULL;
+ const gchar *ver;
+ gboolean ret;
+
+ ver = gpiodglib_api_version();
+ g_assert_nonnull(ver);
+ gpiod_test_return_if_failed();
+
+ regex = g_regex_new(pattern, 0, 0, &err);
+ g_assert_nonnull(regex);
+ g_assert_no_error(err);
+ gpiod_test_return_if_failed();
+
+ ret = g_regex_match(regex, ver, 0, &match);
+ g_assert_true(ret);
+ gpiod_test_return_if_failed();
+
+ g_assert_true(g_match_info_matches(match));
+ res = g_match_info_fetch(match, 0);
+ g_assert_nonnull(res);
+ g_assert_cmpstr(res, ==, ver);
+ g_match_info_next(match, &err);
+ g_assert_no_error(err);
+ g_assert_false(g_match_info_matches(match));
+}
new file mode 100644
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2023-2024 Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+
+#include <glib.h>
+#include <gpiod-glib.h>
+#include <gpiod-test.h>
+
+#include "helpers.h"
+
+#define GPIOD_TEST_GROUP "glib/request-config"
+
+GPIOD_TEST_CASE(default_config)
+{
+ g_autoptr(GpiodglibRequestConfig) config = NULL;
+ g_autofree gchar *consumer = NULL;
+
+ config = gpiodglib_request_config_new(NULL);
+ consumer = gpiodglib_request_config_dup_consumer(config);
+
+ g_assert_null(consumer);
+ g_assert_cmpuint(gpiodglib_request_config_get_event_buffer_size(config),
+ ==, 0);
+}
+
+GPIOD_TEST_CASE(set_consumer)
+{
+ g_autoptr(GpiodglibRequestConfig) config = NULL;
+ g_autofree gchar *consumer = NULL;
+
+ config = gpiodglib_request_config_new(NULL);
+
+ gpiodglib_request_config_set_consumer(config, "foobar");
+ consumer = gpiodglib_request_config_dup_consumer(config);
+ g_assert_cmpstr(consumer, ==, "foobar");
+
+ gpiodglib_request_config_set_consumer(config, NULL);
+ g_free(consumer);
+ consumer = gpiodglib_request_config_dup_consumer(config);
+ g_assert_null(consumer);
+}
+
+GPIOD_TEST_CASE(set_event_buffer_size)
+{
+ g_autoptr(GpiodglibRequestConfig) config = NULL;
+
+ config = gpiodglib_request_config_new(NULL);
+
+ gpiodglib_request_config_set_event_buffer_size(config, 128);
+ g_assert_cmpuint(gpiodglib_request_config_get_event_buffer_size(config),
+ ==, 128);
+}
+
+GPIOD_TEST_CASE(set_properties_in_constructor)
+{
+ g_autoptr(GpiodglibRequestConfig) config = NULL;
+ g_autofree gchar *consumer = NULL;
+
+ config = gpiodglib_request_config_new("consumer", "foobar",
+ "event-buffer-size", 64, NULL);
+ consumer = gpiodglib_request_config_dup_consumer(config);
+ g_assert_cmpstr(consumer, ==, "foobar");
+ g_assert_cmpuint(gpiodglib_request_config_get_event_buffer_size(config),
+ ==, 64);
+}