/*************************************************************************** * Copyright (C) 2004 by Patrick Audley * * paudley@blackcat.ca * *************************************************************************** # _______________ # VERSION HISTORY # # v1.0 (2004/12/28) # - prelimary version ## */ / /** @mainpage C++ Unit Testing Framework * See implementation in * @ref unit_test.h * * @par * @section writing Writing Unit Tests * @par * Ideally, unit tests are written with the code they test. The easiest way * to do this is to include all the unit tests at the bottom of each source * that they relate too. It's also possible to create source files of nothing * but tests to expand coverage across multiple translation modules. * @par * Let's take a simple example: we have a new function that adds two numbers. * @code * int addTwoNumbers( int a, int b ) { * return a + b; * } * @endcode * To write a unit test that checks that @f$addTwoNumbers(x_1,x_2)=x_1+x_2@f$ we * would write the unit test like so: * @code * #ifdef UNITTEST * #include "unit_test.h" * * UNIT_TEST_DEFINES * * DEFINE_TEST( check_two_plus_two ) { * unit_assert( "2+2=4", addTwoNumbers(2,2)==4 ); * } * * UNIT_TEST_RUN( "addTwoNumbers Tests" ) * ADD_TEST( check_two_plus_two ) * UNIT_TEST_END * * #endif // UNITTEST * @endcode * @par * Now we have a test suite defined that will only be compiled when we define UNITTEST. * UNIT_TEST_RUN actually creates a main() function that runs the tests so if we put * this code into a file (say add.cpp) then we can compile and run it like so: @verbatim # gcc -DUNITTEST -o unit_test_add add.cpp # ./unit_test_add ---[ addTwoNumbers Tests ]--- 2+2=4: PASSED @endverbatim * @par * So far so good, let's add a new test that we think will fail. * @code * #ifdef UNITTEST * #include "unit_test.h" * * UNIT_TEST_DEFINES * * DEFINE_TEST( check_two_plus_two ) { * unit_assert( "2+2=4", addTwoNumbers(2,2)==4 ); * unit_pass(); * } * * DEFINE_TEST( check_bogus ) { * unit_assert( "1+5=9", addTwoNumbers(1,5)==9 ); * unit_pass(); * } * * UNIT_TEST_RUN( "addTwoNumbers Tests" ) * ADD_TEST( check_negatives ) * UNIT_TEST_END * * #endif // UNITTEST * @endcode * Running the unit_test now we get: @verbatim # gcc -DUNITTEST -o unit_test_add add.cpp # ./unit_test_add ---[ addTwoNumbers Tests ]--- 2+2=4: PASSED 1+5=9: FAILED @endverbatim * @par * @section adding Integrating with Automake * @par * Automake has the ability to define testing targets that get run when * issue "make check" command. Adding these tests are pretty straight * forward. For the above we would add this to our Makefile.am: @verbatim TESTS = unit_test_add noinst_PROGRAMS = unit_test_add CLEANFILES = add_unit.cpp unit_test_add_SOURCES = add_unit.cpp %_unit.cpp: %.cpp $(CXX) -E -o $*_unit.cpp $*.C @CFLAGS@ -DUNITTEST=1 @endverbatim * To add addtional unit tests you just modify the first four lines. For * example: to add a new unit test suite in the file sub.C we might do this. @verbatim TESTS = unit_test_add unit_test_sub noinst_PROGRAMS = unit_test_add unit_test_sub CLEANFILES = add_unit.cpp sub_unit.cpp unit_test_add_SOURCES = add_unit.cpp unit_test_sub_SOURCES = sub_unit.cpp @endverbatim * @section Notes Implementation Notes * @par */ /** * @file unit_test.h Unit Testing framework for C++ * @author Patrick Audley * @date December 2004 */ #ifndef _UNIT_TEST_H #define _UNIT_TEST_H #ifdef UNITTEST #include #include #include #include #include #include struct rusage ruse; extern int getrusage(); /** @brief Gets the current CPU time with microsecond accuracy. * @returns microseconds since UNIX epoch */ inline double cputime( void ) { getrusage( RUSAGE_SELF, &ruse ); return ( ruse.ru_utime.tv_sec + ruse.ru_stime.tv_sec + 1e-6 * (ruse.ru_utime.tv_usec + ruse.ru_stime.tv_usec ) ); } /** @brief Calculates the transactions rate. * @param run_time microsecond resolution run time * @param transactions number of transactions handled in run_time seconds * This is useful if you want to guarantee minimun transactional throughputs in unit tests. * @warning This code is obviously very test platform dependent. */ inline double transactions_per_second( double run_time, unsigned long transactions ) { return (double)transactions / run_time; } /** @brief Prints to stdout the results of timing an event. * @param msg to print with the numbers * @param run_time microsecond resolution run time * @param transactions number of transactions handled in run_time seconds, if 0 then transactional output is suppressed * @warning This code is obviously very test platform dependent. */ inline void print_cputime( const char* msg, double run_time, unsigned long transactions = 0 ) { printf(" -> %s: %7.3f seconds CPU time", msg, run_time ); if( transactions != 0 ) printf( " (%7.3f transactions/second)", transactions_per_second( run_time, transactions ) ); printf( "\n" ); } /// typedef for unittest functions typedef bool(*test_func)(void); /// typedef for vectors of unittest functions typedef std::vector< test_func > test_vector; /** @brief Start of inline Unit Test definitions * Use this to start the list of unit tests. This should be followed * by one or more DEFINE_TEST entries. */ #define UNIT_TEST_DEFINES \ test_vector * add_test( test_func x ) { \ static test_vector unit_tests; \ if( x != NULL ) unit_tests.push_back( x ); \ return &unit_tests; \ } /** @brief Start a new test definition * @param test_name Name of the test - must be unique in this unit test suite. */ #define DEFINE_TEST(test_name) bool unit_test_##test_name (void) /** @brief Adds a defined test to test run. * @param test_name Test name of a previously defined test to add the the current suite. * @sa DEFINE_TEST UNIT_TEST_RUN * This should be called after UNIT_TEST_RUN for each defined test. */ #define ADD_TEST(test_name) add_test( &unit_test_##test_name ); /** @brief Start a Unit test run section. * @param suite Name for this test suite. * @note Must be terminated with an UNIT_TEST_END statement. */ #define UNIT_TEST_RUN( suite ) \ int main(void) { \ bool result = true; \ std::cout << "---[ " << suite << " ]--- " << std::endl; /** @brief Use within a Unit Test to verify a condition. * @warning Terminates test on failure. */ #define unit_assert( msg, cond ) \ std::cout << " " << msg << ": " << std::flush; \ if( !cond ) { std::cout << "FAILED" << std::endl; return false; } \ std::cout << "PASSED" << std::endl; /** @brief Use to end a unit test in success. * @note Either unit_pass or unit_fail should end every test. */ #define unit_pass() return true; /** @brief Use to end a unit test in failure. * @note Either unit_pass or unit_fail should end every test. */ #define unit_fail() return false; /** @brief Finish a Unit Test run section. */ #define UNIT_TEST_END \ test_vector *T = add_test( NULL ); \ for( unsigned short i = 0; i < T->size(); i++ ) { \ bool testresult = (*(*T)[i])(); \ if( result == true && testresult == false ) { result == false; } \ } \ return !result; \ } #endif // UNITTEST #endif // _UNIT_TEST_H