HelenOS sources

root/uspace/lib/pcut/include/pcut/tests.h

/* [<][>][^][v][top][bottom][index][help] */

INCLUDED FROM


/*
 * 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

/* [<][>][^][v][top][bottom][index][help] */
HelenOS homepage, sources at GitHub