circuitpython/atmel-samd/asf/common/utils/unit_test/suite.h

457 lines
14 KiB
C
Raw Normal View History

/**
* \file
*
* \brief Test suite core declarations
*
* Copyright (c) 2011-2015 Atmel Corporation. All rights reserved.
*
* \asf_license_start
*
* \page License
*
* 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
*
*/
/*
* Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
*/
#ifndef TEST_SUITE_H_INCLUDED
#define TEST_SUITE_H_INCLUDED
#include <compiler.h>
#include <parts.h>
#include <stdio.h>
#if 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 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 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);
//@}
#if defined(__GNUC__)
/* GCC should perform additional printf() argument sanity checks on the
* function call site to try to ensure arguments are printf() compatible */
__attribute__((format(__printf__, 5, 6)))
#endif
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:%d\r\n", \
#expr, __FILE__, __LINE__); \
while (true); \
} \
} \
}
#endif
#endif /* TEST_SUITE_H_INCLUDED */