MOAB: Mesh Oriented datABase  (version 5.4.0)
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,
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
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines