core/testlib/testlib_main.cxx
Go to the documentation of this file.
00001 #include "testlib_register.h"
00002 
00003 #include <vcl_iostream.h>
00004 #include <vcl_string.h>
00005 #include <vcl_vector.h>
00006 #include <vcl_cstdlib.h>
00007 #if VCL_HAS_EXCEPTIONS
00008 #include <vcl_exception.h>
00009 #endif
00010 
00011 #if defined(VCL_VC) || defined(VCL_BORLAND)
00012 #  include <crtdbg.h>
00013 #  include <windows.h>
00014 #  include <vcl_cstdio.h>
00015 
00016 LONG WINAPI vxl_exception_filter( struct _EXCEPTION_POINTERS *ExceptionInfo )
00017 {
00018   // Retrieve exception information
00019   PVOID ExceptionAddress       = ExceptionInfo->ExceptionRecord->ExceptionAddress;
00020   DWORD ExceptionCode          = ExceptionInfo->ExceptionRecord->ExceptionCode;
00021   DWORD* ExceptionInformation  = (DWORD*)ExceptionInfo->ExceptionRecord->ExceptionInformation;
00022 
00023   vcl_fprintf(stderr, "\nTOP-LEVEL EXCEPTION HANDLER\n");
00024   switch (ExceptionCode)
00025   {
00026    case EXCEPTION_ACCESS_VIOLATION:
00027     vcl_fprintf(stderr, "The instruction at \"0x%.8p\" failed to %s memory at \"0x%.8x\".\n\n",
00028                 ExceptionAddress, ExceptionInformation[0] ? "write to" :"read",
00029                 ExceptionInformation[1]);
00030     break;
00031 
00032    case EXCEPTION_INT_DIVIDE_BY_ZERO:
00033     vcl_fprintf(stderr, "The instruction at \"0x%.8p\" caused an exception of integer devision by zero.\n\n",
00034                 ExceptionAddress);
00035     break;
00036    default:
00037     vcl_fprintf(stderr, "The instruction at \"0x%.8p\" caused an unknown exception (exception code: \"0x%.8x\").\n\n",
00038                 ExceptionAddress,
00039                 ExceptionCode);
00040   }
00041 
00042   // Default action is to abort
00043   vcl_printf("Execution aborted!\n");
00044   return EXCEPTION_EXECUTE_HANDLER;
00045 }
00046 #endif // defined(VCL_WIN32)
00047 
00048 #if defined(VCL_BORLAND)
00049 # include <math.h>
00050 # include <float.h> // for _control87()
00051 #endif // defined(VCL_BORLAND)
00052 
00053 static vcl_vector<TestMainFunction> testlib_test_func_;
00054 static vcl_vector<vcl_string>       testlib_test_name_;
00055 
00056 void
00057 list_test_names( vcl_ostream& ostr )
00058 {
00059   ostr << "The registered test names are:\n";
00060   for ( unsigned int i = 0; i < testlib_test_name_.size(); ++i )
00061     ostr << "   " << testlib_test_name_[i] << '\n';
00062   ostr << "\nOmitting a test name, or specifying the name \"all\" will run all the tests.\n";
00063 }
00064 
00065 
00066 void
00067 testlib_enter_stealth_mode()
00068 {
00069   // check for Dashboard test
00070   char * env_var1 = vcl_getenv("DART_TEST_FROM_DART");
00071   char * env_var2 = vcl_getenv("DASHBOARD_TEST_FROM_CTEST");  // DART Client built in CMake
00072   if ( env_var1 || env_var2 ) {
00073 
00074   // Don't allow DART test to open critical error dialog boxes
00075 #if defined(VCL_VC)
00076     // No abort or ANSI assertion failure dialog box
00077     _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
00078     _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
00079 
00080     // No Windows style ASSERT failure dialog box
00081     _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
00082     _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
00083 
00084     // No unhandled exceptions dialog box,
00085     // such as access violation and integer division by zero
00086     SetUnhandledExceptionFilter( vxl_exception_filter );
00087 #endif //defined(VCL_VC)
00088 
00089     // Disable Borland's floating point exceptions.
00090 #if defined(VCL_BORLAND)
00091     _control87(MCW_EM, MCW_EM);
00092 #endif // defined(VCL_BORLAND)
00093   }
00094 }
00095 
00096 
00097 int testlib_run_test_unit(vcl_vector<vcl_string>::size_type i, int argc, char *argv[])
00098 {
00099 #if VCL_HAS_EXCEPTIONS
00100   char * env_var1 = vcl_getenv("DART_TEST_FROM_DART");
00101   char * env_var2 = vcl_getenv("DASHBOARD_TEST_FROM_CTEST");  // DART Client built in CMake
00102   if ( env_var1 || env_var2 ) {
00103     try {
00104       return testlib_test_func_[i]( argc, argv );
00105     }
00106     catch (const vcl_exception &e)
00107     {
00108       vcl_cerr << "\nTOP-LEVEL EXCEPTION HANDLER                                        **FAILED**\n"
00109                << e.what() << "\n\n";
00110       return 1;
00111     }
00112   }
00113 // Leave MS structured exceptions to the SE handler.
00114   else
00115 #endif
00116   return testlib_test_func_[i]( argc, argv );
00117 }
00118 
00119 
00120 int
00121 testlib_main( int argc, char* argv[] )
00122 {
00123   // The caller should already have called register_tests().
00124 
00125   // NOT to produce any dialog windows
00126   testlib_enter_stealth_mode();
00127 
00128   // Assume the index type for vector<string> and
00129   // vector<TestMainFunction> are the same.
00130   typedef vcl_vector<vcl_string>::size_type vec_size_t;
00131 
00132   // Error check.
00133   if ( testlib_test_func_.size() != testlib_test_name_.size() ) {
00134     vcl_cerr << "Error: " << testlib_test_func_.size() << " test functions are registered, but "
00135              << testlib_test_name_.size() << " test names are registered.\n";
00136     return 1;
00137   }
00138 
00139 
00140   // If a test name is given, try to run it. Otherwise, try to run all
00141   // the tests. The first argument, if available, is assumed to be a
00142   // test name. The special test name "all" can be used to run all the tests
00143   // with the subsequent arguments passed to each test.
00144 
00145   bool test_name_given = argc >= 2;
00146 
00147   if ( test_name_given && vcl_string("all") == argv[1] )
00148   {
00149     --argc; ++argv; test_name_given = false;
00150   }
00151   if ( test_name_given )
00152   {
00153     for ( vec_size_t i = 0; i < testlib_test_name_.size(); ++i )
00154       if ( testlib_test_name_[i] == argv[1] )
00155         return testlib_run_test_unit(i, argc-1, argv+1);
00156 
00157 
00158     vcl_cerr << "Test " << argv[1] << " not registered.\n";
00159     list_test_names( vcl_cerr );
00160   }
00161   else
00162   {
00163     vcl_cout << "No test name provided.  Attempting to run all tests.\n";
00164     list_test_names( vcl_cout );
00165     vcl_cout << "If you want to run a single test, specify one of the above on the command line.\n\n" << vcl_flush;
00166 
00167     bool all_pass = true;
00168     for ( vec_size_t i = 0; i < testlib_test_name_.size(); ++i )
00169     {
00170       vcl_cout << "----------------------------------------\n"
00171                << "Running: " << testlib_test_name_[i] << '\n'
00172                << "----------------------------------------\n" << vcl_flush;
00173 
00174       int result = testlib_run_test_unit(i, argc, argv);
00175 
00176       vcl_cout << "----------------------------------------\n"
00177                << testlib_test_name_[i] << " returned " << result << ' '
00178                << ( result==0 ? "(PASS)" : "(FAIL)" ) << '\n'
00179                << "----------------------------------------\n" << vcl_flush;
00180       all_pass &= (result == 0);
00181     }
00182 
00183     vcl_cout << "\n\nCombined result of " << testlib_test_name_.size() << " tests: "
00184              << ( all_pass ? "PASS" : "FAIL" ) << vcl_endl;
00185     return all_pass ? 0 : 1;
00186   }
00187 
00188   return 1;
00189 }
00190 
00191 void testlib_register_test(const vcl_string & name, TestMainFunction func)
00192 {
00193   testlib_test_func_.push_back(func);
00194   testlib_test_name_.push_back(name);
00195 }
00196 
00197 
00198 void testlib_cleanup()
00199 {
00200   testlib_test_func_.clear();
00201   testlib_test_func_.clear();
00202 }