/**
 * \file
 *
 * \brief Test suite core declarations
 *
 * Copyright (c) 2011 Atmel Corporation. All rights reserved.
 *
 * \asf_license_start
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * 3. The name of Atmel may not be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * 4. This software may only be redistributed and used in connection with an
 *    Atmel microcontroller product.
 *
 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \asf_license_stop
 *
 */
#ifndef TEST_SUITE_H_INCLUDED
#define TEST_SUITE_H_INCLUDED

#include <compiler.h>
#include <parts.h>
#include <stdio.h>

#if defined(XMEGA)
#  include <progmem.h>
#endif

/**
 * \defgroup test_suite_group Test Suite Framework
 *
 * This module is the test suite framework, which provides a set of standard
 * functions and macros for defining and running test suites.
 *
 * A test \e suite consists of test \e cases. Each test case consists of a set
 * of functions that are called in a specific order by the framework when the
 * test suite is run. The runtime environment of a test is referred to as the
 * test \e fixture. A setup function can be defined for the purpose of
 * fulfilling preconditions that are needed for the test to run. To free up or
 * reset resources which were used in the test case, a cleanup function can be
 * defined.
 *
 * The following three functions can be defined for each test case:
 * - fixture \e setup
 * - test \e run
 * - fixture \e cleanup
 *
 * The setup and/or cleanup functions may be skipped if they are not needed.
 *
 * A function may report an error or failure by use of the macros \ref test_fail
 * , \ref test_assert_true and \ref test_assert_false. Note that only the first
 * macro allows for the result value to be specified, while the two latter will
 * automatically return TEST_FAIL if the specified condition is false.
 *
 * If any of the functions return a failure/error result, execution of that
 * specific function is ended. However, depending on which function did not
 * pass, execution of the test case may continue:
 * - if the setup does not pass, the rest of the test case is skipped
 * - if the test itself does not pass, cleanup will still be executed
 *
 * The result of the first function in the test case which does not pass
 * determines the end result.
 *
 * Example code for definition of a test suite:
 * \code
 * void setup_1(const struct test_case *test);
 * void run_1(const struct test_case *test);
 * void cleanup_1(const struct test_case *test);
 *
 * void run_2(const struct test_case *test);
 *
 * ...
 * DEFINE_TEST_CASE(test_1,
 * 		setup_1,
 * 		run_1,
 * 		cleanup_1,
 * 		"First test");
 * DEFINE_TEST_CASE(test_2,
 * 		NULL,
 * 		run_2,
 * 		NULL,
 * 		"Second test");
 *
 * DEFINE_TEST_ARRAY(my_tests) = {
 * 	&test_1,
 * 	&test_2,
 * };
 *
 * DEFINE_TEST_SUITE(my_suite, my_tests, "Two-test suite");
 *
 * test_suite_run(&my_suite);
 * ...
 * \endcode
 *
 * Example code for definition of a test case:
 * \code
 * void setup_1(const struct test_case *test)
 * {
 * 	void *resource = allocate_test_resource();
 *
 * 	if (resource == NULL) {
 * 		test_fail(test, TEST_ERROR,
 * 			"Could not allocate test resource");
 * 	}
 * 	test_set_data(resource);
 * }
 *
 * void run_1(const struct test_case *test)
 * {
 * 	void *resource = test_get_data();
 * 	int8_t test_result;
 *
 *      ...
 *	// TEST CODE
 *      ...
 * 	test_assert_true(test, test_result > 0,
 * 			"Test result is not larger than 0: %i", test_result);
 * 	test_assert_false(test, test_result > 2,
 * 			"Test result is larger than 2: %i", test_result);
 * }
 *
 * void cleanup_1(const struct test_case *test)
 * {
 * 	void *resource = test_get_data();
 *
 * 	free_test_resource(resource);
 * }
 * \endcode
 *
 * @{
 */

/**
 * \brief A test case
 *
 * This structure represents a test case which tests one specific
 * feature or behavior.
 */
struct test_case {
	/**
	 * \brief Set up the environment in which \a test is to be run
	 *
	 * This may involve allocating memory, initializing hardware,
	 * etc. If something goes wrong, this function may call
	 * test_fail(), normally with a negative status code.
	 */
	void (*setup)(const struct test_case *test);
	//! Run the test
	void (*run)(const struct test_case *test);
	/**
	 * \brief Clean up the environment after \a test has been run
	 *
	 * This may free up any memory allocated by setup(), gracefully
	 * shutting down hardware, etc. If something goes wrong, this
	 * function may call test_fail(), normally with a negative
	 * status code.
	 */
	void (*cleanup)(const struct test_case *test);
	//! The name of the test case
	const char *name;
};

/**
 * \brief A test suite
 *
 * A test suite may contain one or more test cases which are to be run
 * in sequence.
 */
struct test_suite {
	//! The number of tests in this suite
	unsigned int nr_tests;
	//! Array of pointers to the test cases
	const struct test_case *const *tests;
	//! The name of the test suite
	const char *name;
};

/**
 * \name Wrappers for printing debug information
 *
 * \note The test suite framework feeds its output to printf. It is left up to
 * the test application to set up the stream.
 */
//@{
#if defined(XMEGA) && !defined(__ICCAVR__)
#  define dbg(__fmt_)                                                          \
	printf_P(PROGMEM_STRING(__fmt_))
#  define dbg_info(__fmt_, ...)                                                \
	printf_P(PROGMEM_STRING(__fmt_), __VA_ARGS__)
#  define dbg_error(_x, ...)                                                   \
	printf_P(PROGMEM_STRING(_x), __VA_ARGS__)
#  define dbg_putchar(c)                                                       \
	putc(c, stdout)
#  define dbg_vprintf_pgm(...)                                                 \
	vfprintf_P(stdout, __VA_ARGS__)
#else
#  define dbg(__fmt_)                                                          \
	printf(__fmt_)
#  define dbg_info(__fmt_, ...)                                                \
	printf(__fmt_, __VA_ARGS__)
#  define dbg_error(_x, ...)                                                   \
	printf(_x, __VA_ARGS__)
#  define dbg_putchar(c)                                                       \
	putc(c, stdout)
#  define dbg_vprintf_pgm(...)                                                 \
	vfprintf(stdout, __VA_ARGS__)
#endif
//@}

//! \name Test suite definition macros
//@{
/**
 * \brief Create a test case struct.
 *
 * \param _sym Variable name of the resulting struct
 * \param _setup Function which sets up a test case environment. Can be NULL.
 * \param _run Test function
 * \param _cleanup Function which cleans up what was set up. Can be NULL.
 * \param _name String describing the test case.
 */
#define DEFINE_TEST_CASE(_sym, _setup, _run, _cleanup, _name)                  \
	static const char _test_str_##_sym[] = _name;                          \
	static const struct test_case _sym = {                                 \
		.setup   = _setup,                                             \
		.run     = _run,                                               \
		.cleanup = _cleanup,                                           \
		.name    = _test_str_##_sym,                                   \
	}

/**
 * \brief Create an array of test case pointers.
 *
 * \param _sym Variable name of the resulting array
 */
#define DEFINE_TEST_ARRAY(_sym)                                                \
	const struct test_case *const _sym[]

/**
 * \brief Create a test suite.
 *
 * \param _sym Variable name of the resulting struct
 * \param _test_array Array of test cases, created with DEFINE_TEST_ARRAY()
 * \param _name String describing the test suite.
 */
#define DEFINE_TEST_SUITE(_sym, _test_array, _name)                            \
	static const char _test_str_##_sym[] = _name;                          \
	const struct test_suite _sym = {                                       \
		.nr_tests = ARRAY_LEN(_test_array),                            \
		.tests    = _test_array,                                       \
		.name     = _test_str_##_sym,                                  \
	}
//@}

#ifndef ARRAY_LEN
/**
 * \internal
 * \brief Convenience macro for counting elements in arrays
 */
# define ARRAY_LEN(a) (sizeof(a) / sizeof((a)[0]))
#endif

//! \name Test data access
//@{
extern void *test_priv_data;

/**
 * \brief Set private data pointer for the current test.
 *
 * \param data Pointer to arbitrary run-time data for the test currently
 * being run.
 */
static inline void test_set_data(void *data)
{
	test_priv_data = data;
}

/**
 * \brief Get the private data pointer for the current test.
 *
 * \return Pointer to arbitrary run-time data for the test currently
 * being run, previously registered using test_set_data().
 */
static inline void *test_get_data(void)
{
	return test_priv_data;
}
//@}

//! \name Test case pointer access
//@{

/**
 * \brief Pointer to current test case
 */
extern struct test_case *test_case_ptr;

/**
 * \internal
 * \brief Set pointer to current test.
 */
static inline void test_set_case(const struct test_case *test)
{
	test_case_ptr = (struct test_case *)test;
}

/**
 * \internal
 * \brief Get pointer to current test.
 */
static inline struct test_case *test_get_case(void)
{
	return test_case_ptr;
}
//@}

//@{
//! \name Test result reporting
//@{
/**
 * \brief Status codes returned by test cases and fixtures
 *
 * Note that test cases and especially fixtures may return any of the
 * status codes defined by \ref status_code as well.
 */
enum test_status {
	TEST_ERROR = -1, //!< Test error
	TEST_PASS  = 0,  //!< Test success
	TEST_FAIL  = 1,  //!< Test failure
};

/**
 * \def test_fail(test, result, ...)
 * \brief Fail the test
 *
 * Calling this function will cause the test to terminate with a
 * failure. It will return directly to the test suite core, not to the
 * caller.
 *
 * \param test Test case which failed
 * \param result The result of the test (may not be 0)
 * \param ... printf()-style format string and arguments
 */
#if defined(XMEGA) && !defined(__ICCAVR__)
#  define test_priv_fail_ps(test, result, format, ...)                         \
	do {                                                                   \
		static PROGMEM_DECLARE(char, _fmtstr[]) = format "%s";         \
		test_case_fail(test, result, __FILE__, __LINE__, _fmtstr,      \
				__VA_ARGS__);                                  \
	} while (0)

#  define test_fail(test, result, ...)                                         \
	test_priv_fail_ps(test, result, __VA_ARGS__, "")
#else
#  define test_fail(test, result, ...)                                         \
	test_case_fail(test, result, __FILE__, __LINE__, __VA_ARGS__)
#endif

/**
 * \brief Verify that \a condition is true
 *
 * If \a condition is false, fail the test with an error message
 * indicating the condition which failed.
 *
 * \param test The test case currently being run
 * \param condition Expression to be validated
 * \param ... Format string and arguments
 */
#define test_assert_true(test, condition, ...)                                 \
	do {                                                                   \
		if (!(condition)) {                                            \
			test_fail(test, TEST_FAIL, __VA_ARGS__);               \
		}                                                              \
	} while (0)

/**
 * \brief Verify that \a condition is false
 *
 * If \a condition is true, fail the test with an error message
 * indicating the condition which failed.
 *
 * \param test The test case currently being run
 * \param condition Expression to be validated
 * \param ... Format string and arguments
 */
#define test_assert_false(test, condition, ...)                                \
	test_assert_true(test, !(condition), __VA_ARGS__)
//@}

//! \name Test suite interaction
//@{
extern int test_suite_run(const struct test_suite *suite);
//@}

extern void test_case_fail(const struct test_case *test, int result,
		const char *file, unsigned int line,
		const char *fmt, ...);

//! @}

/**
 * \brief Assert() macro definition for unit testing
 *
 * The Assert() macro is set up to use test_assert_true(),
 * as this will only halt execution of the current test, allowing
 * the remaining tests a chance to complete.
 */
#if defined(_ASSERT_ENABLE_) && defined(TEST_SUITE_DEFINE_ASSERT_MACRO)
#  define Assert(expr) \
 	{ \
 		if (test_get_case() != NULL) { \
 			test_assert_true(test_get_case(), expr, \
 					"Assertion failed: %s", #expr); \
 		} else { \
 			if (!(expr)) { \
 				dbg_error("Assertion '%s' failed at %s:%u\r\n", \
 						#expr, __FILE__, __LINE__); \
				while (true); \
 			} \
 		} \
 	}
#endif

#endif /* TEST_SUITE_H_INCLUDED */
