/** * \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 Atmel Support */ #ifndef TEST_SUITE_H_INCLUDED #define TEST_SUITE_H_INCLUDED #include #include #include #if XMEGA # include #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 */