MOAB: Mesh Oriented datABase  (version 5.5.0)
TestRunner.hpp
Go to the documentation of this file.
1 #ifndef TEST_RUNNER_HPP
2 #define TEST_RUNNER_HPP
3 
4 #include "TestUtil.hpp"
5 
6 /***************************************************************************************
7  * Begin test runner implementation.
8  * This is a higher-level API that can be used to register tests,
9  * test dependencies, and to run-time select a subset of tests to
10  * run.
11  ***************************************************************************************/
12 
13 /* Register a test to be run */
14 #define REGISTER_TEST( TEST_FUNC ) runner_register_test( __FILE__, __LINE__, #TEST_FUNC, ( TEST_FUNC ), NULL )
15 
16 /* Mark a dependency between tests. The second argument must be
17  * an already-registered test. The first argument will be registered
18  * as a test if it has not already been registered. The test specified
19  * by the first argument will be run only if the test specified by
20  * the second argument is run and succeeds.
21  */
22 #define REGISTER_DEP_TEST( TEST_FUNC, REQUIRED_FUNC ) \
23  runner_register_test( __FILE__, __LINE__, #TEST_FUNC, ( TEST_FUNC ), ( REQUIRED_FUNC ) )
24 
25 /* Run registered tests.
26  * Arguments should be argc and argv passed to main.
27  * If ARGC is less than or equal to 1 then all tests are run.
28  * Otherwise only tests specified in the argument list are run.
29  * Returns number of failed tests.
30  */
31 #define RUN_TESTS( ARGC, ARGV ) runner_run_tests( ( ARGC ), ( ARGV ) )
32 
33 /***************************************************************************************
34  * NOTE: The remainder of this file contains the implementation of the above macros.
35  * The above macros constitute the entire intended API.
36  ***************************************************************************************/
37 
38 static void runner_register_test( const char* filename,
39  int line_number,
40  const char* name,
41  test_func function,
42  test_func requisite = 0 );
43 static int runner_run_tests( int argc, char* argv[] );
44 
45 static void runner_usage( FILE* str, int argc, char* argv[] );
46 static void runner_list_tests( int long_format );
47 static void runner_help( int argc, char* argv[] );
48 
50 {
54  SELECTED
55 };
56 struct RunnerTest
57 {
59  char* testName;
62  size_t numRequisites;
63 };
64 
66 size_t RunnerTestCount = 0;
67 const size_t RUNNER_NOT_FOUND = ~(size_t)0;
68 static size_t runner_find_test_func( test_func f );
69 static size_t runner_find_test_name( const char* name );
70 static size_t runner_add_test( test_func f, const char* name );
71 static void runner_add_requisite( size_t idx, size_t req );
72 static void free_test_list();
73 
74 static size_t runner_find_test_func( test_func f )
75 {
76  for( size_t i = 0; i < RunnerTestCount; ++i )
77  if( RunnerTestList[i].testFunc == f ) return i;
78  return RUNNER_NOT_FOUND;
79 }
80 static size_t runner_find_test_name( const char* name )
81 {
82  for( size_t i = 0; i < RunnerTestCount; ++i )
83  if( !strcmp( RunnerTestList[i].testName, name ) ) return i;
84  return RUNNER_NOT_FOUND;
85 }
86 static size_t runner_add_test( test_func f, const char* name )
87 {
88  size_t idx = runner_find_test_func( f );
89  if( idx == RUNNER_NOT_FOUND )
90  {
91  if( !RunnerTestCount ) atexit( &free_test_list );
92  idx = RunnerTestCount++;
93  RunnerTest* new_RunnerTestList = (RunnerTest*)realloc( RunnerTestList, RunnerTestCount * sizeof( RunnerTest ) );
94  if( !new_RunnerTestList )
95  {
96  fprintf( stderr, "TestRunner::runner_add_test(): reallocation of RunnerTestList failed\n" );
97  atexit( &free_test_list );
98  }
99  else
100  RunnerTestList = new_RunnerTestList;
101  RunnerTestList[idx].testFunc = f;
102  RunnerTestList[idx].testName = strdup( name );
105  RunnerTestList[idx].numRequisites = 0;
106  }
107  return idx;
108 }
109 static void runner_add_requisite( size_t idx, size_t req )
110 {
111  size_t i;
112  for( i = 0; i < RunnerTestList[idx].numRequisites; ++i )
113  if( RunnerTestList[idx].testRequisites[i] == (int)req ) return;
116  (int*)realloc( RunnerTestList[idx].testRequisites,
119 }
120 static void free_test_list()
121 {
122  for( size_t i = 0; i < RunnerTestCount; ++i )
123  {
124  free( RunnerTestList[i].testName );
125  free( RunnerTestList[i].testRequisites );
126  }
127  free( RunnerTestList );
128 }
129 
130 void runner_register_test( const char* filename, int line_number, const char* name, test_func test, test_func req )
131 {
132  size_t i = runner_add_test( test, name );
133  size_t req_idx;
134  if( req )
135  {
136  req_idx = runner_find_test_func( req );
137  if( RUNNER_NOT_FOUND == req_idx )
138  {
139  fprintf( stderr,
140  "Error registering requisite for test: \"%s\"\n"
141  "\tat %s:%d\n"
142  "\tRequisite test function not registered.\n",
143  name, filename, line_number );
144  abort();
145  }
146  runner_add_requisite( i, req_idx );
147  }
148 }
149 
150 void runner_usage( FILE* str, int /*argc*/, char* argv[] )
151 {
152  fprintf( str, "%s [-l|-L] [-h] [-r] [<test_name> [<test_name> ...]]\n", argv[0] );
153 }
154 
155 void runner_help( int argc, char* argv[] )
156 {
157  runner_usage( stdout, argc, argv );
158  fprintf( stdout, "-l : List test names and exit\n"
159  "-L : List test names and requisites and exit\n"
160  "-h : This help text\n"
161  "-r : Recursively run requisite tests for any specified test\n"
162  "\n" );
163 }
164 
165 void runner_list_tests( int long_format )
166 {
167  size_t i, j;
168  printf( "Test List:\n" );
169  for( i = 0; i < RunnerTestCount; ++i )
170  {
171  if( RunnerTestList[i].testStatus == DESELECTED ) continue;
172  printf( " o %s\n", RunnerTestList[i].testName );
173  if( !long_format || !RunnerTestList[i].numRequisites ) continue;
174  if( RunnerTestList[i].numRequisites == 1 )
175  printf( " Requires : %s\n", RunnerTestList[RunnerTestList[i].testRequisites[0]].testName );
176  else
177  {
178  printf( " Requires : \n" );
179  for( j = 0; j < RunnerTestList[i].numRequisites; ++j )
180  printf( " - %s\n", RunnerTestList[RunnerTestList[i].testRequisites[j]].testName );
181  }
182  }
183 }
184 
185 int runner_run_tests( int argc, char* argv[] )
186 {
187  /* Counters */
188  int error_count = 0;
189  int fail_count = 0;
190  int num_selected = 0;
191  int num_run = 0;
192 
193  /* Flags from parsed arguments */
194  int run_requisites = 0;
195  int list_tests = 0;
196  int first_selected = 1;
197 
198  /* Misc iterator vars and such */
199  int changed_some, ran_some, can_run, fail;
200  int k;
201  const char* c;
202  size_t i, j;
203 
204  /* Process command line arguments */
205  for( k = 1; k < argc; ++k )
206  {
207  if( argv[k][0] == '-' )
208  {
209  for( c = argv[k] + 1; *c; ++c )
210  {
211  switch( *c )
212  {
213  case 'l':
214  list_tests = 1;
215  break;
216  case 'L':
217  list_tests = 2;
218  break;
219  case 'r':
220  run_requisites = true;
221  break;
222  case 'h':
223  runner_help( argc, argv );
224  return 0;
225  default:
226  runner_usage( stderr, argc, argv );
227  fprintf( stderr, "Unknown flag: '%c'\n", *c );
228  return 1;
229  }
230  }
231  }
232  else
233  {
234  // If user has specified some tests to run, begin
235  // by marking all tests as de-selected.
236  if( first_selected )
237  {
238  for( i = 0; i < RunnerTestCount; ++i )
240  first_selected = 0;
241  }
242  // Mark specified test as selected.
243  i = runner_find_test_name( argv[k] );
244  if( RUNNER_NOT_FOUND == i )
245  {
246  fprintf( stderr, "Unknown test name: \"%s\"\n", argv[k] );
247  ++error_count;
248  }
249  else
250  {
252  }
253  }
254  }
255 
256  /* If recursively running requisite tests, select those also. */
257  if( run_requisites )
258  {
259  do
260  {
261  changed_some = 0;
262  for( i = 0; i < RunnerTestCount; ++i )
263  {
264  if( RunnerTestList[i].testStatus == DESELECTED ) continue;
265 
266  for( j = 0; j < RunnerTestList[i].numRequisites; ++j )
267  {
269  {
271  changed_some = 1;
272  }
273  }
274  }
275  } while( changed_some );
276  }
277 
278  // Count number of selected tests
279  num_selected = 0;
280  for( i = 0; i < RunnerTestCount; ++i )
281  if( RunnerTestList[i].testStatus == SELECTED ) ++num_selected;
282 
283  if( list_tests )
284  {
286  return error_count;
287  }
288 
289  // Now run the tests
290  num_run = 0;
291  do
292  {
293  ran_some = 0;
294  for( i = 0; i < RunnerTestCount; ++i )
295  {
296  if( RunnerTestList[i].testStatus != SELECTED ) continue;
297  can_run = 1;
298  for( j = 0; j < RunnerTestList[i].numRequisites; ++j )
299  {
300  k = RunnerTestList[i].testRequisites[j];
302  {
303  can_run = 0;
304  break;
305  }
306  }
307  if( !can_run ) continue;
308 
309  ran_some = 1;
310  ++num_run;
312  if( fail )
313  {
314  error_count++;
315  fail_count++;
317  }
318  else
319  {
321  }
322  }
323  } while( ran_some );
324 
325  // check if we are running parallel MPI tests
326  int rank = 0;
327 #ifdef MOAB_HAVE_MPI
328  int isInit;
329  MPI_Initialized( &isInit );
330  if( isInit )
331  {
332  MPI_Comm_rank( MPI_COMM_WORLD, &rank );
333  }
334 #endif
335 
336  // Print brief summary
337  if( rank == 0 )
338  {
339  if( num_run == (int)RunnerTestCount && !fail_count )
340  {
341  printf( "All %d tests passed.\n", num_run );
342  }
343  else if( num_run == num_selected && !fail_count )
344  {
345  printf( "All %d selected tests passed.\n", num_run );
346  printf( "Skipped %d non-selected tests\n", (int)( RunnerTestCount - num_selected ) );
347  }
348  else
349  {
350  printf( "%2d tests registered\n", (int)RunnerTestCount );
351  if( num_selected == num_run )
352  printf( "%2d tests selected\n", num_selected );
353  else
354  printf( "%2d of %2d tests ran\n", num_run, num_selected );
355  if( num_run < (int)RunnerTestCount )
356  printf( "%2d of %2d tests skipped\n", (int)RunnerTestCount - num_run, (int)RunnerTestCount );
357  printf( "%2d of %2d tests passed\n", num_run - fail_count, num_run );
358  if( fail_count ) printf( "%2d of %2d tests FAILED\n", fail_count, num_run );
359  }
360  }
361 
362  return error_count;
363 }
364 
365 #endif