/*
 * Testing framework for libgpiod.
 *
 * Copyright (C) 2017 Bartosz Golaszewski <bartekgola@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of version 2.1 of the GNU Lesser General Public License
 * as published by the Free Software Foundation.
 */

#ifndef __GPIOD_TEST_H__
#define __GPIOD_TEST_H__

#include <gpiod.h>
#include <string.h>

#define TEST_CONSUMER		"gpiod-test"

#define TEST_INIT		__attribute__((constructor))
#define TEST_UNUSED		__attribute__((unused))
#define TEST_PRINTF(fmt, arg)	__attribute__((format(printf, fmt, arg)))
#define TEST_CLEANUP(func)	__attribute__((cleanup(func)))
#define TEST_BIT(nr)		(1UL << (nr))

#define TEST_ARRAY_SIZE(x)	(sizeof(x) / sizeof(*(x)))

typedef void (*_test_func)(void);

struct _test_chip_descr {
	unsigned int num_chips;
	unsigned int *num_lines;
	int flags;
};

struct _test_case {
	struct _test_case *_next;

	const char *name;
	_test_func func;

	struct _test_chip_descr chip_descr;
};

void _test_register(struct _test_case *test);
void _test_print_failed(const char *fmt, ...) TEST_PRINTF(1, 2);

enum {
	TEST_FLAG_NAMED_LINES = TEST_BIT(0),
};

/*
 * This macro should be used for code brevity instead of manually declaring
 * the _test_case structure.
 *
 * The macro accepts the following arguments:
 *   _a_func: name of the test function
 *   _a_name: name of the test case (will be shown to user)
 *   _a_flags: various switches for the test case
 *
 * The last argument must be an array of unsigned integers specifying the
 * number of GPIO lines in each subsequent mockup chip. The size of this
 * array will at the same time specify the number of gpiochips to create.
 */
#define TEST_DEFINE(_a_func, _a_name, _a_flags, ...)			\
	static unsigned int _##_a_func##_lines[] = __VA_ARGS__;		\
	static struct _test_case _##_a_func##_descr = {			\
		.name = _a_name,					\
		.func = _a_func,					\
		.chip_descr = {						\
			.num_chips = TEST_ARRAY_SIZE(			\
						_##_a_func##_lines),	\
			.num_lines = _##_a_func##_lines,		\
			.flags = _a_flags,				\
		},							\
	};								\
	static TEST_INIT void _test_register_##_a_func##_test(void)	\
	{								\
		_test_register(&_##_a_func##_descr);			\
	}								\
	static int _test_##_a_func##_sentinel TEST_UNUSED

/*
 * We want libgpiod tests to co-exist with gpiochips created by other GPIO
 * drivers. For that reason we can't just use hardcoded device file paths or
 * gpiochip names.
 *
 * The test suite detects the chips that were exported by the gpio-mockup
 * module and stores them in the internal test context structure. Test cases
 * should use the routines declared below to access the gpiochip path, name
 * or number by index corresponding with the order in which the mockup chips
 * were requested in the TEST_DEFINE() macro.
 */
const char * test_chip_path(unsigned int index);
const char * test_chip_name(unsigned int index);
unsigned int test_chip_num(unsigned int index);

enum {
	TEST_EVENT_FALLING,
	TEST_EVENT_RISING,
	TEST_EVENT_ALTERNATING,
};

void test_set_event(unsigned int chip_index, unsigned int line_offset,
		    int event_type, unsigned int freq);

void test_tool_run(char *tool, ...);
void test_tool_wait(void);
const char * test_tool_stdout(void);
const char * test_tool_stderr(void);
bool test_tool_exited(void);
int test_tool_exit_status(void);
void test_tool_signal(int signum);
void test_tool_stdin_write(const char *fmt, ...) TEST_PRINTF(1, 2);

/*
 * Every TEST_ASSERT_*() macro expansion can make a test function return, so
 * it would be quite difficult to keep track of every resource allocation. At
 * the same time we don't want any deliberate memory leaks as we also use this
 * test suite to find actual memory leaks in the library with valgrind.
 *
 * For this reason, the tests should generally always use the TEST_CLEANUP()
 * macro for dynamically allocated variables and objects that need closing.
 *
 * The functions below can be reused by different tests for common use cases.
 */
void test_close_chip(struct gpiod_chip **chip);
void test_free_str(char **str);
void test_free_chip_iter(struct gpiod_chip_iter **iter);
void test_free_chip_iter_noclose(struct gpiod_chip_iter **iter);

bool test_regex_match(const char *str, const char *pattern);

#define TEST_ASSERT(statement)						\
	do {								\
		if (!(statement)) {					\
			_test_print_failed(				\
				"assertion failed (%s:%d): '%s'",	\
				__FILE__, __LINE__, #statement);	\
			return;						\
		}							\
	} while (0)

#define TEST_ASSERT_FALSE(statement)	TEST_ASSERT(!(statement))
#define TEST_ASSERT_NOT_NULL(ptr)	TEST_ASSERT(ptr != NULL)
#define TEST_ASSERT_RET_OK(status)	TEST_ASSERT(status == 0)
#define TEST_ASSERT_NULL(ptr)		TEST_ASSERT(ptr == NULL)
#define TEST_ASSERT_EQ(a1, a2)		TEST_ASSERT(a1 == a2)
#define TEST_ASSERT_NOTEQ(a1, a2)	TEST_ASSERT(a1 != a2)
#define TEST_ASSERT_STR_EQ(s1, s2)	TEST_ASSERT(strcmp(s1, s2) == 0)
#define TEST_ASSERT_STR_CONTAINS(s, p)	TEST_ASSERT(strstr(s, p) != NULL)
#define TEST_ASSERT_STR_NOTCONT(s, p)	TEST_ASSERT(strstr(s, p) == NULL)
#define TEST_ASSERT_REGEX_MATCH(s, p)	TEST_ASSERT(test_regex_match(s, p))
#define TEST_ASSERT_REGEX_NOMATCH(s, p)	TEST_ASSERT(!test_regex_match(s, p))

#endif /* __GPIOD_TEST_H__ */
