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