/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
 * This file is part of libgpiod.
 *
 * Copyright (C) 2019 Bartosz Golaszewski <bgolaszewski@baylibre.com>
 */

/*
 * 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>
#include <gpiod.h>

/*
 * These typedefs are needed to make g_autoptr work - it doesn't accept
 * regular 'struct typename' syntax.
 */
typedef struct gpiod_chip gpiod_chip_struct;
typedef struct gpiod_chip_iter gpiod_chip_iter_struct;
typedef struct gpiod_line_iter gpiod_line_iter_struct;

G_DEFINE_AUTOPTR_CLEANUP_FUNC(gpiod_chip_struct, gpiod_chip_close);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(gpiod_chip_iter_struct, gpiod_chip_iter_free);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(gpiod_line_iter_struct, gpiod_line_iter_free);

/* These are private definitions and should not be used directly. */
typedef void (*_gpiod_test_func)(void);

typedef struct _gpiod_test_case _GpiodTestCase;
struct _gpiod_test_case {
	const gchar *path;
	_gpiod_test_func func;

	guint num_chips;
	guint *chip_sizes;
	gint flags;
};

void _gpiod_test_register(_GpiodTestCase *test);

#define _GPIOD_TEST_PATH(_name) \
		"/gpiod/" GPIOD_TEST_GROUP "/" G_STRINGIFY(_name)

enum {
	/* Dummy lines for this test case should have names assigned. */
	GPIOD_TEST_FLAG_NAMED_LINES = (1 << 0),
};

/*
 * Register a test case function. The last argument is the array of numbers
 * of lines per mockup chip.
 */
#define GPIOD_TEST_CASE(_name, _flags, ...)				\
	static void _name(void);					\
	static guint _##_name##_chip_sizes[] = __VA_ARGS__;		\
	static _GpiodTestCase _##_name##_test_case = {			\
		.path = _GPIOD_TEST_PATH(_name),			\
		.func = _name,						\
		.num_chips = G_N_ELEMENTS(_##_name##_chip_sizes),	\
		.chip_sizes = _##_name##_chip_sizes,			\
		.flags = _flags,					\
	};								\
	static __attribute__((constructor)) void			\
	_##_name##_test_register(void)					\
	{								\
		_gpiod_test_register(&_##_name##_test_case);		\
	}								\
	static void _name(void)

#define GPIOD_TEST_CONSUMER "gpiod-test"

#define gpiod_test_return_if_failed()					\
	do {								\
		if (g_test_failed())					\
			return;						\
	} while (0)

/* Wrappers around libgpiomockup helpers. */
const gchar *gpiod_test_chip_path(guint idx);
const gchar *gpiod_test_chip_name(guint idx);
gint gpiod_test_chip_num(guint idx);
gint gpiod_test_chip_get_value(guint chip_index, guint line_offset);
void gpiod_test_chip_set_pull(guint chip_index, guint line_offset, gint pull);

/* Helpers for triggering line events in a separate thread. */
struct gpiod_test_event_thread;
typedef struct gpiod_test_event_thread GpiodTestEventThread;

GpiodTestEventThread *
gpiod_test_start_event_thread(guint chip_index,
			      guint line_offset, guint freq);
void gpiod_test_stop_event_thread(GpiodTestEventThread *thread);

G_DEFINE_AUTOPTR_CLEANUP_FUNC(GpiodTestEventThread,
			      gpiod_test_stop_event_thread);

#endif /* __GPIOD_TEST_H__ */
