/*
* Copyright (c) 2014 Vojtech Horky
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - 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.
* - The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR 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.
*/
/** @file
* Tests and test suites.
*
* @defgroup tests Tests
* Create test suites and test cases.
* @{
*/
#ifndef PCUT_TESTS_H_GUARD
#define PCUT_TESTS_H_GUARD
#include <pcut/helper.h>
#include <pcut/datadef.h>
/*
* Trick with [] and & copied from
* http://bytes.com/topic/c/answers/553555-initializer-element-not-constant#post2159846
* because following does not work:
* extern int *a;
* int *b = a;
*/
#ifndef __COUNTER__
#define PCUT_WITHOUT_COUNTER
#endif
#ifndef PCUT_WITHOUT_COUNTER
#define PCUT_ITEM_COUNTER_INCREMENT
#endif
/** Default timeout for a single test (in seconds).
* @showinitializer
*/
#define PCUT_DEFAULT_TEST_TIMEOUT 3
/** Item counter that is expected to be incremented by preprocessor.
*
* Used compiler must support __COUNTER__ for this to work without any
* extra preprocessing.
*/
#ifdef PCUT_WITHOUT_COUNTER
#define PCUT_ITEM_COUNTER PCUT_you_need_to_run_pcut_preprocessor
#else
#define PCUT_ITEM_COUNTER __COUNTER__
#endif
/*
* Helper macros
* -------------
*/
/** @cond devel */
/** Produce identifier name for an item with given number.
*
* @param number Item number.
*/
#ifndef PCUT_WITHOUT_COUNTER
#define PCUT_ITEM_NAME(number) \
PCUT_JOIN(pcut_item_, number)
#else
#define PCUT_ITEM_NAME(number) PCUT_ITEM_NAME
#endif
/** Produce identifier name for a preceding item.
*
* @param number Number of the current item.
*/
#ifndef PCUT_WITHOUT_COUNTER
#define PCUT_ITEM_NAME_PREV(number) \
PCUT_JOIN(pcut_item_, PCUT_JOIN(PCUT_PREV_, number))
#else
#define PCUT_ITEM_NAME_PREV(number) PCUT_ITEM_NAME_PREV
#endif
#ifndef PCUT_WITHOUT_COUNTER
#define PCUT_ITEM_EXTRAS_NAME(number) \
PCUT_JOIN(pcut_extras_, number)
#else
#define PCUT_ITEM_EXTRAS_NAME(number) PCUT_ITEM2_NAME
#endif
#ifndef PCUT_WITHOUT_COUNTER
#define PCUT_ITEM_SETUP_NAME(number) \
PCUT_JOIN(pcut_setup_, number)
#else
#define PCUT_ITEM_SETUP_NAME(number) PCUT_ITEM3_NAME
#endif
/** Create a new item, append it to the list.
*
* @param number Number of this item.
* @param itemkind Kind of this item (PCUT_KIND_*).
* @param ... Other initializers of the pcut_item_t.
*/
#define PCUT_ADD_ITEM(number, itemkind, ...) \
static pcut_item_t PCUT_ITEM_NAME(number) = { \
&PCUT_ITEM_NAME_PREV(number), \
NULL, \
-1, \
itemkind, \
__VA_ARGS__ \
}
/** @endcond */
/*
* Test-case related macros
* ------------------------
*/
/** Define test time-out.
*
* Use as argument to PCUT_TEST().
*
* @param time_out Time-out value in seconds.
*/
#define PCUT_TEST_SET_TIMEOUT(time_out) \
{ PCUT_EXTRA_TIMEOUT, (time_out) }
/** Skip current test.
*
* Use as argument to PCUT_TEST().
*/
#define PCUT_TEST_SKIP \
{ PCUT_EXTRA_SKIP, 0 }
/** @cond devel */
/** Terminate list of extra test options. */
#define PCUT_TEST_EXTRA_LAST { PCUT_EXTRA_LAST, 0 }
/** Define a new test with given name and given item number.
*
* @param testname A valid C identifier name (not quoted).
* @param number Number of the item describing this test.
* @param ... Extra test properties.
*/
#define PCUT_TEST_WITH_NUMBER(number, testname, ...) \
PCUT_ITEM_COUNTER_INCREMENT \
static pcut_extra_t PCUT_ITEM_EXTRAS_NAME(number)[] = { \
__VA_ARGS__ \
}; \
static int PCUT_CC_UNUSED_VARIABLE(PCUT_JOIN(testname, 0_test_name_missing_or_duplicated), 0); \
static void PCUT_JOIN(test_, testname)(void); \
PCUT_ADD_ITEM(number, PCUT_KIND_TEST, \
PCUT_QUOTE(testname), \
PCUT_JOIN(test_, testname), \
NULL, NULL, \
PCUT_ITEM_EXTRAS_NAME(number), \
NULL, NULL \
); \
void PCUT_JOIN(test_, testname)(void)
/** @endcond */
/** Define a new test with given name.
*
* @param ... Test name (C identifier) followed by extra test properties.
*/
#define PCUT_TEST(...) \
PCUT_TEST_WITH_NUMBER(PCUT_ITEM_COUNTER, \
PCUT_VARG_GET_FIRST(__VA_ARGS__, this_arg_is_ignored), \
PCUT_VARG_SKIP_FIRST(__VA_ARGS__, PCUT_TEST_EXTRA_LAST) \
)
/*
* Test suite related macros
* -------------------------
*/
/** @cond devel */
/** Define and start a new test suite.
*
* @see PCUT_TEST_SUITE
*
* @param suitename Suite name (a valid C identifier).
* @param number Item number.
*/
#define PCUT_TEST_SUITE_WITH_NUMBER(suitename, number) \
PCUT_ITEM_COUNTER_INCREMENT \
PCUT_ADD_ITEM(number, PCUT_KIND_TESTSUITE, \
#suitename, \
NULL, \
NULL, NULL, \
NULL, NULL, \
NULL \
)
/** Define a set-up function for a test suite.
*
* @see PCUT_TEST_BEFORE
*
* @param number Item number.
*/
#define PCUT_TEST_BEFORE_WITH_NUMBER(number) \
PCUT_ITEM_COUNTER_INCREMENT \
static void PCUT_ITEM_SETUP_NAME(number)(void); \
PCUT_ADD_ITEM(number, PCUT_KIND_SETUP, \
"setup", NULL, \
PCUT_ITEM_SETUP_NAME(number), \
NULL, NULL, NULL, NULL \
); \
void PCUT_ITEM_SETUP_NAME(number)(void)
/** Define a tear-down function for a test suite.
*
* @see PCUT_TEST_AFTER
*
* @param number Item number.
*/
#define PCUT_TEST_AFTER_WITH_NUMBER(number) \
PCUT_ITEM_COUNTER_INCREMENT \
static void PCUT_ITEM_SETUP_NAME(number)(void); \
PCUT_ADD_ITEM(number, PCUT_KIND_TEARDOWN, \
"teardown", NULL, NULL, \
PCUT_ITEM_SETUP_NAME(number), \
NULL, NULL, NULL \
); \
void PCUT_ITEM_SETUP_NAME(number)(void)
/** @endcond */
/** Define and start a new test suite.
*
* All tests following this macro belong to the new suite
* (up to next occurrence of PCUT_TEST_SUITE).
*
* This command shall be used as is without any extra code.
*
* @param name Suite name (a valid C identifier).
*/
#define PCUT_TEST_SUITE(name) \
PCUT_TEST_SUITE_WITH_NUMBER(name, PCUT_ITEM_COUNTER)
/** Define a set-up function for a test suite.
*
* The code of the function immediately follows this macro.
*
* @code
* PCUT_TEST_SUITE(my_suite);
*
* PCUT_TEST_BEFORE {
* printf("This is executed before each test in this suite.\n");
* }
* @endcode
*
* There could be only a single set-up function for each suite.
*/
#define PCUT_TEST_BEFORE \
PCUT_TEST_BEFORE_WITH_NUMBER(PCUT_ITEM_COUNTER)
/** Define a tear-down function for a test suite.
*
* The code of the function immediately follows this macro.
*
* @code
* PCUT_TEST_SUITE(my_suite);
*
* PCUT_TEST_AFTER {
* printf("This is executed after each test in this suite.\n");
* }
* @endcode
*
* There could be only a single tear-down function for each suite.
*/
#define PCUT_TEST_AFTER \
PCUT_TEST_AFTER_WITH_NUMBER(PCUT_ITEM_COUNTER)
/*
* Import/export related macros
* ----------------------------
*/
/** @cond devel */
/** Export test cases from current file.
*
* @see PCUT_EXPORT
*
* @param identifier Identifier of this block of tests.
* @param number Item number.
*/
#define PCUT_EXPORT_WITH_NUMBER(identifier, number) \
PCUT_ITEM_COUNTER_INCREMENT \
pcut_item_t pcut_exported_##identifier = { \
&PCUT_ITEM_NAME_PREV(number), \
NULL, \
-1, \
PCUT_KIND_SKIP, \
"exported_" #identifier, NULL, NULL, NULL, NULL, NULL, NULL \
}
/** Import test cases from a different file.
*
* @see PCUT_EXPORT
*
* @param identifier Identifier of the tests to import.
* @param number Item number.
*/
#define PCUT_IMPORT_WITH_NUMBER(identifier, number) \
PCUT_ITEM_COUNTER_INCREMENT \
extern pcut_item_t pcut_exported_##identifier; \
PCUT_ADD_ITEM(number, PCUT_KIND_NESTED, \
"import_" #identifier, NULL, NULL, NULL, NULL, NULL, \
&pcut_exported_##identifier \
)
/** @endcond */
/** Export test cases from current file.
*
* @param identifier Identifier of this block of tests.
*/
#define PCUT_EXPORT(identifier) \
PCUT_EXPORT_WITH_NUMBER(identifier, PCUT_ITEM_COUNTER)
/** Import test cases from a different file.
*
* @param identifier Identifier of the tests to import.
* (previously exported with PCUT_EXPORT).
*/
#define PCUT_IMPORT(identifier) \
PCUT_IMPORT_WITH_NUMBER(identifier, PCUT_ITEM_COUNTER)
/*
* PCUT initialization and invocation macros
* -----------------------------------------
*/
/** @cond devel */
/** Initialize the PCUT testing framework with a first item.
*
* @param first_number Number of the first item.
*/
#define PCUT_INIT_WITH_NUMBER(first_number) \
PCUT_ITEM_COUNTER_INCREMENT \
static pcut_item_t PCUT_ITEM_NAME(first_number) = { \
NULL, \
NULL, \
-1, \
PCUT_KIND_SKIP, \
"init", NULL, NULL, NULL, NULL, NULL, NULL \
}; \
PCUT_TEST_SUITE(Default);
int pcut_main(pcut_item_t *last, int argc, char *argv[]);
/** Insert code to run all the tests.
*
* @param number Item number.
*/
#define PCUT_MAIN_WITH_NUMBER(number, ...) \
PCUT_ITEM_COUNTER_INCREMENT \
static pcut_main_extra_t pcut_main_extras[] = { \
__VA_ARGS__ \
}; \
static pcut_item_t pcut_item_last = { \
&PCUT_ITEM_NAME_PREV(number), \
NULL, \
-1, \
PCUT_KIND_SKIP, \
"main", NULL, NULL, NULL, \
NULL, \
pcut_main_extras, \
NULL \
}; \
int main(int argc, char *argv[]) { \
return pcut_main(&pcut_item_last, argc, argv); \
}
/** Terminate list of extra options for main. */
#define PCUT_MAIN_EXTRA_SET_LAST \
{ PCUT_MAIN_EXTRA_LAST, NULL, NULL }
/** @endcond */
/** Initialize the PCUT testing framework. */
#define PCUT_INIT \
PCUT_INIT_WITH_NUMBER(PCUT_ITEM_COUNTER)
/** Insert code to run all the tests. */
#define PCUT_MAIN() \
PCUT_MAIN_WITH_NUMBER(PCUT_ITEM_COUNTER, PCUT_MAIN_EXTRA_SET_LAST)
/** Set callback for PCUT initialization.
*
* Use from within PCUT_CUSTOM_MAIN().
*
* @warning The callback is called for each test and also for the wrapping
* invocation.
*/
#define PCUT_MAIN_SET_INIT_HOOK(callback) \
{ PCUT_MAIN_EXTRA_INIT_HOOK, callback, NULL }
/** Set callback for PCUT pre-initialization.
*
* Use from within PCUT_CUSTOM_MAIN().
* This callback is useful only if you want to manipulate command-line
* arguments.
* You probably will not need this.
*
* @warning The callback is called for each test and also for the wrapping
* invocation.
*/
#define PCUT_MAIN_SET_PREINIT_HOOK(callback) \
{ PCUT_MAIN_EXTRA_PREINIT_HOOK, NULL, callback }
/** Set XML report as default.
*
* Use from within PCUT_CUSTOM_MAIN().
*
*/
#define PCUT_MAIN_SET_XML_REPORT \
{ PCUT_MAIN_EXTRA_REPORT_XML, NULL, NULL }
/** Insert code to run all tests. */
#define PCUT_CUSTOM_MAIN(...) \
PCUT_MAIN_WITH_NUMBER(PCUT_ITEM_COUNTER, \
PCUT_VARG_GET_FIRST(__VA_ARGS__, PCUT_MAIN_EXTRA_SET_LAST), \
PCUT_VARG_SKIP_FIRST(__VA_ARGS__, PCUT_MAIN_EXTRA_SET_LAST) \
)
/**
* @}
*/
#endif