universe@766: /* universe@766: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. universe@766: * universe@766: * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. universe@766: * universe@766: * Redistribution and use in source and binary forms, with or without universe@766: * modification, are permitted provided that the following conditions are met: universe@766: * universe@766: * 1. Redistributions of source code must retain the above copyright universe@766: * notice, this list of conditions and the following disclaimer. universe@766: * universe@766: * 2. Redistributions in binary form must reproduce the above copyright universe@766: * notice, this list of conditions and the following disclaimer in the universe@766: * documentation and/or other materials provided with the distribution. universe@766: * universe@766: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" universe@766: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE universe@766: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE universe@766: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE universe@766: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR universe@766: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF universe@766: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS universe@766: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN universe@766: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) universe@766: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE universe@766: * POSSIBILITY OF SUCH DAMAGE. universe@766: */ universe@766: universe@766: /** universe@766: * @file: test.h universe@766: * universe@766: * UCX Test Framework. universe@766: * universe@766: * Usage of this test framework: universe@766: * universe@766: * **** IN HEADER FILE: **** universe@766: * universe@766: *
universe@766:  * CX_TEST(function_name);
universe@766:  * CX_TEST_SUBROUTINE(subroutine_name, paramlist); // optional
universe@766:  * 
universe@766: * universe@766: * **** IN SOURCE FILE: **** universe@766: *
universe@766:  * CX_TEST_SUBROUTINE(subroutine_name, paramlist) {
universe@766:  *   // tests with CX_TEST_ASSERT()
universe@766:  * }
universe@766:  * 
universe@766:  * CX_TEST(function_name) {
universe@766:  *   // memory allocation and other stuff here
universe@766:  *   #CX_TEST_BEGIN
universe@766:  *   // tests with CX_TEST_ASSERT() and/or
universe@766:  *   // calls with CX_TEST_CALL_SUBROUTINE() here
universe@766:  *   #CX_TEST_END
universe@766:  *   // cleanup of memory here
universe@766:  * }
universe@766:  * 
universe@766: * universe@766: * @remark if a test fails, execution continues at the universe@766: * #CX_TEST_END macro! So make sure every necessary cleanup happens afterwards. universe@766: * universe@766: * @attention Do not call own functions within a test, that use universe@766: * CX_TEST_ASSERT() macros and are not defined by using CX_TEST_SUBROUTINE(). universe@766: * universe@766: * @author Mike Becker universe@766: * @author Olaf Wintermann universe@766: * universe@766: */ universe@766: universe@766: #ifndef UCX_TEST_H universe@766: #define UCX_TEST_H universe@766: universe@766: #include universe@766: #include universe@766: #include universe@766: #include universe@766: universe@766: #ifdef __cplusplus universe@766: extern "C" { universe@766: #endif universe@766: universe@766: #ifndef __FUNCTION__ universe@766: /** universe@766: * Alias for the __func__ preprocessor macro. universe@766: * Some compilers use __func__ and others use __FUNCTION__. universe@766: * We use __FUNCTION__ so we define it for those compilers which use universe@766: * __func__. universe@766: */ universe@766: #define __FUNCTION__ __func__ universe@766: #endif universe@766: universe@766: #ifndef UCX_COMMON_H universe@766: /** universe@766: * Function pointer compatible with fwrite-like functions. universe@766: */ universe@766: typedef size_t (*cx_write_func)( universe@766: void const *, universe@766: size_t, universe@766: size_t, universe@766: void * universe@766: ); universe@766: #endif // UCX_COMMON_H universe@766: universe@766: /** Type for the CxTestSuite. */ universe@766: typedef struct CxTestSuite CxTestSuite; universe@766: universe@766: /** Pointer to a test function. */ universe@766: typedef void(*CxTest)(CxTestSuite *, void *, cx_write_func); universe@766: universe@766: /** Type for the internal list of test cases. */ universe@766: typedef struct CxTestSet CxTestSet; universe@766: universe@766: /** Structure for the internal list of test cases. */ universe@766: struct CxTestSet { universe@766: universe@766: /** Test case. */ universe@766: CxTest test; universe@766: universe@766: /** Pointer to the next list element. */ universe@766: CxTestSet *next; universe@766: }; universe@766: universe@766: /** universe@766: * A test suite containing multiple test cases. universe@766: */ universe@766: struct CxTestSuite { universe@766: universe@766: /** The number of successful tests after the suite has been run. */ universe@766: unsigned int success; universe@766: universe@766: /** The number of failed tests after the suite has been run. */ universe@766: unsigned int failure; universe@766: universe@766: /** The optional name of this test suite. */ universe@766: char const *name; universe@766: universe@766: /** universe@766: * Internal list of test cases. universe@766: * Use cx_test_register() to add tests to this list. universe@766: */ universe@766: CxTestSet *tests; universe@766: }; universe@766: universe@766: /** universe@766: * Creates a new test suite. universe@766: * @param name optional name of the suite universe@766: * @return a new test suite universe@766: */ universe@766: static inline CxTestSuite* cx_test_suite_new(char const *name) { universe@766: CxTestSuite* suite = (CxTestSuite*) malloc(sizeof(CxTestSuite)); universe@766: if (suite != NULL) { universe@766: suite->name = name; universe@766: suite->success = 0; universe@766: suite->failure = 0; universe@766: suite->tests = NULL; universe@766: } universe@766: universe@766: return suite; universe@766: } universe@766: universe@766: /** universe@766: * Destroys a test suite. universe@766: * @param suite the test suite to destroy universe@766: */ universe@766: static inline void cx_test_suite_free(CxTestSuite* suite) { universe@766: CxTestSet *l = suite->tests; universe@766: while (l != NULL) { universe@766: CxTestSet *e = l; universe@766: l = l->next; universe@766: free(e); universe@766: } universe@766: free(suite); universe@766: } universe@766: universe@766: /** universe@766: * Registers a test function with the specified test suite. universe@766: * universe@766: * @param suite the suite, the test function shall be added to universe@766: * @param test the test function to register universe@766: * @return zero on success or non-zero on failure universe@766: */ universe@766: static inline int cx_test_register(CxTestSuite* suite, CxTest test) { universe@766: CxTestSet *t = (CxTestSet*) malloc(sizeof(CxTestSet)); universe@766: if (t) { universe@766: t->test = test; universe@766: t->next = NULL; universe@766: if (suite->tests == NULL) { universe@766: suite->tests = t; universe@766: } else { universe@766: CxTestSet *last = suite->tests; universe@766: while (last->next) { universe@766: last = last->next; universe@766: } universe@766: last->next = t; universe@766: } universe@766: return 0; universe@766: } else { universe@766: return 1; universe@766: } universe@766: } universe@766: universe@766: /** universe@766: * Runs a test suite and writes the test log to the specified stream. universe@766: * @param suite the test suite to run universe@766: * @param out_target the target buffer or file to write the output to universe@766: * @param out_writer the write function writing to \p out_target universe@766: */ universe@766: static inline void cx_test_run(CxTestSuite *suite, universe@766: void *out_target, cx_write_func out_writer) { universe@766: if (suite->name == NULL) { universe@766: out_writer("*** Test Suite ***\n", 1, 19, out_target); universe@766: } else { universe@766: out_writer("*** Test Suite : ", 1, 17, out_target); universe@766: out_writer(suite->name, 1, strlen(suite->name), out_target); universe@766: out_writer(" ***\n", 1, 5, out_target); universe@766: } universe@766: suite->success = 0; universe@766: suite->failure = 0; universe@766: for (CxTestSet *elem = suite->tests; elem; elem = elem->next) { universe@766: elem->test(suite, out_target, out_writer); universe@766: } universe@766: out_writer("\nAll test completed.\n", 1, 21, out_target); universe@766: char total[80]; universe@766: int len = snprintf( universe@766: total, 80, universe@766: " Total: %u\n Success: %u\n Failure: %u\n", universe@766: suite->success + suite->failure, suite->success, suite->failure universe@766: ); universe@766: out_writer(total, 1, len, out_target); universe@766: } universe@766: universe@766: /** universe@766: * Runs a test suite and writes the test log to the specified FILE stream. universe@766: * @param suite the test suite to run universe@766: * @param file the target file to write the output to universe@766: */ universe@766: #define cx_test_run_f(suite, file) cx_test_run(suite, (void*)file, (cx_write_func)fwrite) universe@766: universe@766: /** universe@766: * Runs a test suite and writes the test log to stdout. universe@766: * @param suite the test suite to run universe@766: */ universe@766: #define cx_test_run_stdout(suite) cx_test_run_f(suite, stdout) universe@766: universe@766: /** universe@766: * Macro for a #CxTest function header. universe@766: * universe@766: * Use this macro to declare and/or define a #CxTest function. universe@766: * universe@766: * @param name the name of the test function universe@766: */ universe@766: #define CX_TEST(name) void name(CxTestSuite* _suite_,void *_output_, cx_write_func _writefnc_) universe@766: universe@766: /** universe@766: * Marks the begin of a test. universe@766: * Note: Any CX_TEST_ASSERT() calls must be performed after universe@766: * #CX_TEST_BEGIN. universe@766: * universe@766: * @see #CX_TEST_END universe@766: */ universe@766: #define CX_TEST_BEGIN _writefnc_("Running ", 1, 8, _output_);\ universe@766: _writefnc_(__FUNCTION__, 1, strlen(__FUNCTION__), _output_);\ universe@766: _writefnc_("... ", 1, 4, _output_);\ universe@766: jmp_buf _env_; \ universe@766: if (!setjmp(_env_)) { universe@766: universe@766: /** universe@766: * Checks a test assertion. universe@766: * If the assertion is correct, the test carries on. If the assertion is not universe@766: * correct, the specified message (terminated by a dot and a line break) is universe@766: * written to the test suites output stream. universe@766: * @param condition the condition to check universe@766: * @param message the message that shall be printed out on failure universe@766: */ universe@766: #define CX_TEST_ASSERT(condition,message) if (!(condition)) { \ universe@766: _writefnc_(message".\n", 1, 2+strlen(message), _output_); \ universe@766: _suite_->failure++; \ universe@766: longjmp(_env_, 1);\ universe@766: } universe@766: universe@766: /** universe@766: * Macro for a test subroutine function header. universe@766: * universe@766: * Use this to declare and/or define a subroutine that can be called by using universe@766: * CX_TEST_CALL_SUBROUTINE(). universe@766: * universe@766: * @param name the name of the subroutine universe@766: * @param ... the parameter list universe@766: * universe@766: * @see CX_TEST_CALL_SUBROUTINE() universe@766: */ universe@766: #define CX_TEST_SUBROUTINE(name,...) void name(CxTestSuite* _suite_,\ universe@766: void *_output_, jmp_buf _env_, __VA_ARGS__) universe@766: universe@766: /** universe@766: * Macro for calling a test subroutine. universe@766: * universe@766: * Subroutines declared with CX_TEST_SUBROUTINE() can be called by using this universe@766: * macro. universe@766: * universe@766: * Note: You may only call subroutines within a #CX_TEST_BEGIN- universe@766: * #CX_TEST_END-block. universe@766: * universe@766: * @param name the name of the subroutine universe@766: * @param ... the argument list universe@766: * universe@766: * @see CX_TEST_SUBROUTINE() universe@766: */ universe@766: #define CX_TEST_CALL_SUBROUTINE(name,...) \ universe@766: name(_suite_,_output_,_writefnc_,_env_,__VA_ARGS__); universe@766: universe@766: /** universe@766: * Marks the end of a test. universe@766: * Note: Any CX_TEST_ASSERT() calls must be performed before universe@766: * #CX_TEST_END. universe@766: * universe@766: * @see #CX_TEST_BEGIN universe@766: */ universe@766: #define CX_TEST_END _writefnc_("success.\n", 1, 9, _output_); _suite_->success++;} universe@766: universe@766: #ifdef __cplusplus universe@766: } universe@766: #endif universe@766: universe@766: #endif /* UCX_TEST_H */ universe@766: