MOAB: Mesh Oriented datABase
(version 5.2.1)
|
00001 #ifndef TEST_RUNNER_HPP 00002 #define TEST_RUNNER_HPP 00003 00004 #include "TestUtil.hpp" 00005 00006 /*************************************************************************************** 00007 * Begin test runner implementation. 00008 * This is a higher-level API that can be used to register tests, 00009 * test dependencies, and to run-time select a subset of tests to 00010 * run. 00011 ***************************************************************************************/ 00012 00013 /* Register a test to be run */ 00014 #define REGISTER_TEST( TEST_FUNC ) runner_register_test( __FILE__, __LINE__, #TEST_FUNC, ( TEST_FUNC ), NULL ) 00015 00016 /* Mark a dependency between tests. The second argument must be 00017 * an already-registered test. The first argument will be registered 00018 * as a test if it has not already been registered. The test specified 00019 * by the first argument will be run only if the test specified by 00020 * the second argument is run and succeeds. 00021 */ 00022 #define REGISTER_DEP_TEST( TEST_FUNC, REQUIRED_FUNC ) \ 00023 runner_register_test( __FILE__, __LINE__, #TEST_FUNC, ( TEST_FUNC ), ( REQUIRED_FUNC ) ) 00024 00025 /* Run registered tests. 00026 * Arguments should be argc and argv passed to main. 00027 * If ARGC is less than or equal to 1 then all tests are run. 00028 * Otherwise only tests specified in the argument list are run. 00029 * Returns number of failed tests. 00030 */ 00031 #define RUN_TESTS( ARGC, ARGV ) runner_run_tests( ( ARGC ), ( ARGV ) ) 00032 00033 /*************************************************************************************** 00034 * NOTE: The remainder of this file contains the implementation of the above macros. 00035 * The above macros constitute the entire intended API. 00036 ***************************************************************************************/ 00037 00038 static void runner_register_test( const char* filename, int line_number, const char* name, test_func function, 00039 test_func requisite = 0 ); 00040 static int runner_run_tests( int argc, char* argv[] ); 00041 00042 static void runner_usage( FILE* str, int argc, char* argv[] ); 00043 static void runner_list_tests( int long_format ); 00044 static void runner_help( int argc, char* argv[] ); 00045 00046 enum RunnerStatus 00047 { 00048 PASSED, 00049 FAILED, 00050 DESELECTED, 00051 SELECTED 00052 }; 00053 struct RunnerTest 00054 { 00055 test_func testFunc; 00056 char* testName; 00057 enum RunnerStatus testStatus; 00058 int* testRequisites; 00059 size_t numRequisites; 00060 }; 00061 00062 struct RunnerTest* RunnerTestList = 0; 00063 size_t RunnerTestCount = 0; 00064 const size_t RUNNER_NOT_FOUND = ~(size_t)0; 00065 static size_t runner_find_test_func( test_func f ); 00066 static size_t runner_find_test_name( const char* name ); 00067 static size_t runner_add_test( test_func f, const char* name ); 00068 static void runner_add_requisite( size_t idx, size_t req ); 00069 static void free_test_list(); 00070 00071 static size_t runner_find_test_func( test_func f ) 00072 { 00073 for( size_t i = 0; i < RunnerTestCount; ++i ) 00074 if( RunnerTestList[i].testFunc == f ) return i; 00075 return RUNNER_NOT_FOUND; 00076 } 00077 static size_t runner_find_test_name( const char* name ) 00078 { 00079 for( size_t i = 0; i < RunnerTestCount; ++i ) 00080 if( !strcmp( RunnerTestList[i].testName, name ) ) return i; 00081 return RUNNER_NOT_FOUND; 00082 } 00083 static size_t runner_add_test( test_func f, const char* name ) 00084 { 00085 size_t idx = runner_find_test_func( f ); 00086 if( idx == RUNNER_NOT_FOUND ) 00087 { 00088 if( !RunnerTestCount ) atexit( &free_test_list ); 00089 idx = RunnerTestCount++; 00090 RunnerTest* new_RunnerTestList = (RunnerTest*)realloc( RunnerTestList, RunnerTestCount * sizeof( RunnerTest ) ); 00091 if( !new_RunnerTestList ) 00092 { 00093 fprintf( stderr, "TestRunner::runner_add_test(): reallocation of RunnerTestList failed\n" ); 00094 atexit( &free_test_list ); 00095 } 00096 else 00097 RunnerTestList = new_RunnerTestList; 00098 RunnerTestList[idx].testFunc = f; 00099 RunnerTestList[idx].testName = strdup( name ); 00100 RunnerTestList[idx].testStatus = SELECTED; 00101 RunnerTestList[idx].testRequisites = 0; 00102 RunnerTestList[idx].numRequisites = 0; 00103 } 00104 return idx; 00105 } 00106 static void runner_add_requisite( size_t idx, size_t req ) 00107 { 00108 size_t i; 00109 for( i = 0; i < RunnerTestList[idx].numRequisites; ++i ) 00110 if( RunnerTestList[idx].testRequisites[i] == (int)req ) return; 00111 ++RunnerTestList[idx].numRequisites; 00112 RunnerTestList[idx].testRequisites = 00113 (int*)realloc( RunnerTestList[idx].testRequisites, 00114 RunnerTestList[idx].numRequisites * sizeof( *RunnerTestList[idx].testRequisites ) ); 00115 RunnerTestList[idx].testRequisites[RunnerTestList[idx].numRequisites - 1] = req; 00116 } 00117 static void free_test_list() 00118 { 00119 for( size_t i = 0; i < RunnerTestCount; ++i ) 00120 { 00121 free( RunnerTestList[i].testName ); 00122 free( RunnerTestList[i].testRequisites ); 00123 } 00124 free( RunnerTestList ); 00125 } 00126 00127 void runner_register_test( const char* filename, int line_number, const char* name, test_func test, test_func req ) 00128 { 00129 size_t i = runner_add_test( test, name ); 00130 size_t req_idx; 00131 if( req ) 00132 { 00133 req_idx = runner_find_test_func( req ); 00134 if( RUNNER_NOT_FOUND == req_idx ) 00135 { 00136 fprintf( stderr, 00137 "Error registering requisite for test: \"%s\"\n" 00138 "\tat %s:%d\n" 00139 "\tRequisite test function not registered.\n", 00140 name, filename, line_number ); 00141 abort(); 00142 } 00143 runner_add_requisite( i, req_idx ); 00144 } 00145 } 00146 00147 void runner_usage( FILE* str, int /*argc*/, char* argv[] ) 00148 { 00149 fprintf( str, "%s [-l|-L] [-h] [-r] [<test_name> [<test_name> ...]]\n", argv[0] ); 00150 } 00151 00152 void runner_help( int argc, char* argv[] ) 00153 { 00154 runner_usage( stdout, argc, argv ); 00155 fprintf( stdout, "-l : List test names and exit\n" 00156 "-L : List test names and requisites and exit\n" 00157 "-h : This help text\n" 00158 "-r : Recursively run requisite tests for any specified test\n" 00159 "\n" ); 00160 } 00161 00162 void runner_list_tests( int long_format ) 00163 { 00164 size_t i, j; 00165 printf( "Test List:\n" ); 00166 for( i = 0; i < RunnerTestCount; ++i ) 00167 { 00168 if( RunnerTestList[i].testStatus == DESELECTED ) continue; 00169 printf( " o %s\n", RunnerTestList[i].testName ); 00170 if( !long_format || !RunnerTestList[i].numRequisites ) continue; 00171 if( RunnerTestList[i].numRequisites == 1 ) 00172 printf( " Requires : %s\n", RunnerTestList[RunnerTestList[i].testRequisites[0]].testName ); 00173 else 00174 { 00175 printf( " Requires : \n" ); 00176 for( j = 0; j < RunnerTestList[i].numRequisites; ++j ) 00177 printf( " - %s\n", RunnerTestList[RunnerTestList[i].testRequisites[j]].testName ); 00178 } 00179 } 00180 } 00181 00182 int runner_run_tests( int argc, char* argv[] ) 00183 { 00184 /* Counters */ 00185 int error_count = 0; 00186 int fail_count = 0; 00187 int num_selected = 0; 00188 int num_run = 0; 00189 00190 /* Flags from parsed arguments */ 00191 int run_requisites = 0; 00192 int list_tests = 0; 00193 int first_selected = 1; 00194 00195 /* Misc iterator vars and such */ 00196 int changed_some, ran_some, can_run, fail; 00197 int k; 00198 const char* c; 00199 size_t i, j; 00200 00201 /* Process command line arguments */ 00202 for( k = 1; k < argc; ++k ) 00203 { 00204 if( argv[k][0] == '-' ) 00205 { 00206 for( c = argv[k] + 1; *c; ++c ) 00207 { 00208 switch( *c ) 00209 { 00210 case 'l': 00211 list_tests = 1; 00212 break; 00213 case 'L': 00214 list_tests = 2; 00215 break; 00216 case 'r': 00217 run_requisites = true; 00218 break; 00219 case 'h': 00220 runner_help( argc, argv ); 00221 return 0; 00222 default: 00223 runner_usage( stderr, argc, argv ); 00224 fprintf( stderr, "Unknown flag: '%c'\n", *c ); 00225 return 1; 00226 } 00227 } 00228 } 00229 else 00230 { 00231 // If user has specified some tests to run, begin 00232 // by marking all tests as de-selected. 00233 if( first_selected ) 00234 { 00235 for( i = 0; i < RunnerTestCount; ++i ) 00236 RunnerTestList[i].testStatus = DESELECTED; 00237 first_selected = 0; 00238 } 00239 // Mark specified test as selected. 00240 i = runner_find_test_name( argv[k] ); 00241 if( RUNNER_NOT_FOUND == i ) 00242 { 00243 fprintf( stderr, "Unknown test name: \"%s\"\n", argv[k] ); 00244 ++error_count; 00245 } 00246 else 00247 { 00248 RunnerTestList[i].testStatus = SELECTED; 00249 } 00250 } 00251 } 00252 00253 /* If recursively running requisite tests, select those also. */ 00254 if( run_requisites ) 00255 { 00256 do 00257 { 00258 changed_some = 0; 00259 for( i = 0; i < RunnerTestCount; ++i ) 00260 { 00261 if( RunnerTestList[i].testStatus == DESELECTED ) continue; 00262 00263 for( j = 0; j < RunnerTestList[i].numRequisites; ++j ) 00264 { 00265 if( RunnerTestList[RunnerTestList[i].testRequisites[j]].testStatus == DESELECTED ) 00266 { 00267 RunnerTestList[RunnerTestList[i].testRequisites[j]].testStatus = SELECTED; 00268 changed_some = 1; 00269 } 00270 } 00271 } 00272 } while( changed_some ); 00273 } 00274 00275 // Count number of selected tests 00276 num_selected = 0; 00277 for( i = 0; i < RunnerTestCount; ++i ) 00278 if( RunnerTestList[i].testStatus == SELECTED ) ++num_selected; 00279 00280 if( list_tests ) 00281 { 00282 runner_list_tests( list_tests - 1 ); 00283 return error_count; 00284 } 00285 00286 // Now run the tests 00287 num_run = 0; 00288 do 00289 { 00290 ran_some = 0; 00291 for( i = 0; i < RunnerTestCount; ++i ) 00292 { 00293 if( RunnerTestList[i].testStatus != SELECTED ) continue; 00294 can_run = 1; 00295 for( j = 0; j < RunnerTestList[i].numRequisites; ++j ) 00296 { 00297 k = RunnerTestList[i].testRequisites[j]; 00298 if( RunnerTestList[k].testStatus != PASSED && RunnerTestList[k].testStatus != DESELECTED ) 00299 { 00300 can_run = 0; 00301 break; 00302 } 00303 } 00304 if( !can_run ) continue; 00305 00306 ran_some = 1; 00307 ++num_run; 00308 fail = run_test( RunnerTestList[i].testFunc, RunnerTestList[i].testName ); 00309 if( fail ) 00310 { 00311 error_count++; 00312 fail_count++; 00313 RunnerTestList[i].testStatus = FAILED; 00314 } 00315 else 00316 { 00317 RunnerTestList[i].testStatus = PASSED; 00318 } 00319 } 00320 } while( ran_some ); 00321 00322 // check if we are running parallel MPI tests 00323 int rank = 0; 00324 #ifdef MOAB_HAVE_MPI 00325 int isInit; 00326 MPI_Initialized( &isInit ); 00327 if( isInit ) { MPI_Comm_rank( MPI_COMM_WORLD, &rank ); } 00328 #endif 00329 00330 // Print brief summary 00331 if( rank == 0 ) 00332 { 00333 if( num_run == (int)RunnerTestCount && !fail_count ) { printf( "All %d tests passed.\n", num_run ); } 00334 else if( num_run == num_selected && !fail_count ) 00335 { 00336 printf( "All %d selected tests passed.\n", num_run ); 00337 printf( "Skipped %d non-selected tests\n", (int)( RunnerTestCount - num_selected ) ); 00338 } 00339 else 00340 { 00341 printf( "%2d tests registered\n", (int)RunnerTestCount ); 00342 if( num_selected == num_run ) 00343 printf( "%2d tests selected\n", num_selected ); 00344 else 00345 printf( "%2d of %2d tests ran\n", num_run, num_selected ); 00346 if( num_run < (int)RunnerTestCount ) 00347 printf( "%2d of %2d tests skipped\n", (int)RunnerTestCount - num_run, (int)RunnerTestCount ); 00348 printf( "%2d of %2d tests passed\n", num_run - fail_count, num_run ); 00349 if( fail_count ) printf( "%2d of %2d tests FAILED\n", fail_count, num_run ); 00350 } 00351 } 00352 00353 return error_count; 00354 } 00355 00356 #endif