MOAB: Mesh Oriented datABase  (version 5.2.1)
TestRunner.hpp
Go to the documentation of this file.
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
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines