Teuchos_UnitTestRepository.cpp

00001 // @HEADER
00002 // ***********************************************************************
00003 // 
00004 //                    Teuchos: Common Tools Package
00005 //                 Copyright (2004) Sandia Corporation
00006 // 
00007 // Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive
00008 // license for use of this work by or on behalf of the U.S. Government.
00009 // 
00010 // This library is free software; you can redistribute it and/or modify
00011 // it under the terms of the GNU Lesser General Public License as
00012 // published by the Free Software Foundation; either version 2.1 of the
00013 // License, or (at your option) any later version.
00014 //  
00015 // This library is distributed in the hope that it will be useful, but
00016 // WITHOUT ANY WARRANTY; without even the implied warranty of
00017 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018 // Lesser General Public License for more details.
00019 //  
00020 // You should have received a copy of the GNU Lesser General Public
00021 // License along with this library; if not, write to the Free Software
00022 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
00023 // USA
00024 // Questions? Contact Michael A. Heroux (maherou@sandia.gov) 
00025 // 
00026 // ***********************************************************************
00027 // @HEADER
00028 
00029 
00030 #include "Teuchos_UnitTestRepository.hpp"
00031 #include "Teuchos_UnitTestBase.hpp"
00032 #include "Teuchos_TestingHelpers.hpp"
00033 #include "Teuchos_Array.hpp"
00034 #include "Teuchos_TestForException.hpp"
00035 #include "Teuchos_VerboseObject.hpp"
00036 #include "Teuchos_CommandLineProcessor.hpp"
00037 #include "Teuchos_Assert.hpp"
00038 #include "Teuchos_Time.hpp"
00039 #include "Teuchos_StandardCatchMacros.hpp"
00040 
00041 
00042 namespace Teuchos {
00043 
00044 
00045 struct UnitTestData {
00046 
00047   const Teuchos::UnitTestBase * unitTest;
00048   std::string groupName;
00049   std::string testName;
00050   int insertionIndex;
00051 
00052   UnitTestData(
00053     Teuchos::UnitTestBase *unitTest_in,
00054     const std::string groupName_in,
00055     const std::string testName_in
00056     )
00057     : unitTest(unitTest_in), groupName(groupName_in), testName(testName_in),
00058       insertionIndex(insersionIndexCounter_++)
00059     {
00060 #ifdef TEUCHOS_DEBUG
00061       TEUCHOS_ASSERT(unitTest_in);
00062 #endif
00063     }
00064 
00065 private:
00066   UnitTestData(); // Not defined!
00067   static int insersionIndexCounter_;
00068 };
00069 
00070 
00071 int UnitTestData::insersionIndexCounter_ = 0;
00072 
00073 
00074 bool operator<(const UnitTestData &a, const UnitTestData &b)
00075 {
00076   if (a.groupName < b.groupName) {
00077     return true;
00078   }
00079   else if (a.groupName > b.groupName) {
00080     return false;
00081   }
00082   return a.insertionIndex < b.insertionIndex;
00083 }
00084 
00085 
00086 
00087 std::string getUnitTestName(const std::string groupName,
00088   const std::string testName)
00089 {
00090   std::ostringstream oss;
00091   oss << groupName<<"_"<<testName<<"_UnitTest";
00092   return oss.str();
00093 }
00094 
00095 
00096 enum EShowTestDetails {
00097   SHOW_TEST_DETAILS_ALL,
00098   SHOW_TEST_DETAILS_TEST_NAMES,
00099   SHOW_TEST_DETAILS_FINAL_RESULTS
00100 };
00101 
00102 
00103 bool strMatch( const std::string &fullMatchStr, const std::string &str )
00104 {
00105 
00106   const std::string::size_type npos = std::string::npos;
00107 
00108   const int strLen = str.length();
00109   const int fullMatchStrLen = fullMatchStr.length();
00110 
00111   if (fullMatchStrLen == 0) {
00112     return true;
00113   }
00114 
00115   const bool beginGlob = fullMatchStr[0] == '*';
00116   const bool endGlob = fullMatchStr[fullMatchStrLen-1] == '*';
00117 
00118   const int matchStrLen =
00119   fullMatchStrLen + (beginGlob ? -1 : 0) + (endGlob ? -1 : 0);
00120 
00121   if (matchStrLen == 0) {
00122     return true;
00123   }
00124 
00125   if (matchStrLen > strLen) {
00126     return false;
00127   }
00128 
00129   if (beginGlob && endGlob) {
00130     return str.find(fullMatchStr.substr(1, matchStrLen)) != npos;
00131   }
00132 
00133   if (endGlob) {
00134     return fullMatchStr.substr(0, matchStrLen) == str.substr(0, matchStrLen);
00135   }
00136 
00137   if (beginGlob) {
00138     return fullMatchStr.substr(1, matchStrLen) ==
00139       str.substr(strLen-matchStrLen, matchStrLen);
00140   }
00141 
00142   return fullMatchStr == str;
00143 
00144 }
00145 
00146 
00147 } // namespace Teuchos
00148 
00149 
00150 
00151 
00152 namespace Teuchos {
00153 
00154 
00155 // Implementation class
00156 
00157 
00158 class UnitTestRepository::InstanceData {
00159 public:
00160 
00161   typedef Teuchos::Array<UnitTestData> unitTests_t;
00162 
00163   unitTests_t unitTests;
00164   CommandLineProcessor clp;
00165   EShowTestDetails showTestDetails;
00166   bool showSrcLocation;
00167   bool showFailSrcLocation;
00168   bool noOp;
00169   std::string groupName;
00170   std::string testName;
00171   std::string notUnitTestName;
00172   int testCounter;
00173 
00174   InstanceData()
00175     :clp(false),
00176      showTestDetails(SHOW_TEST_DETAILS_TEST_NAMES),
00177      showSrcLocation(false),
00178      showFailSrcLocation(true),
00179      noOp(false),
00180      testCounter(0)
00181     {}
00182 
00183 };
00184 
00185 
00186 // public
00187 
00188 
00189 CommandLineProcessor& UnitTestRepository::getCLP()
00190 {
00191   return getData().clp;
00192 }
00193 
00194 
00195 bool UnitTestRepository::runUnitTests(FancyOStream &out)
00196 {
00197 
00198   typedef InstanceData::unitTests_t unitTests_t;
00199 
00200   using std::setprecision;
00201 
00202   Time overallTimer("overallTimer", true);
00203   Time timer("timer");
00204 
00205   const int timerPrec = 3;
00206 
00207   out << "\n***\n*** Unit test suite ...\n***\n\n";
00208 
00209   InstanceData &data = getData();
00210 
00211   const bool showAll = data.showTestDetails == SHOW_TEST_DETAILS_ALL;
00212   const bool showTestNames = data.showTestDetails == SHOW_TEST_DETAILS_TEST_NAMES || showAll;
00213 
00214   showTestFailureLocation(data.showFailSrcLocation);
00215 
00216   bool success = true;
00217   int testCounter = 0;
00218   int numTestsRun = 0;
00219   int numTestsFailed = 0;
00220 
00221   Array<std::string> failedTests;
00222 
00223   try {
00224     
00225     out << "\nSorting tests by group name then by the order they where added ...";
00226     timer.start(true);
00227     std::sort( data.unitTests.begin(), data.unitTests.end() );
00228     timer.stop();
00229     out << " (time = "<<setprecision(timerPrec)<<timer.totalElapsedTime()<<")\n";
00230 
00231     out << "\nRunning unit tests ...\n\n";
00232     unitTests_t::iterator iter = data.unitTests.begin();
00233     for ( ; iter != data.unitTests.end(); ++iter, ++testCounter ) {
00234 
00235       const UnitTestData &utd = (*iter);
00236 
00237       const std::string unitTestName = getUnitTestName(utd.groupName, utd.testName);
00238 
00239       if (
00240         (
00241           strMatch(data.groupName, utd.groupName)
00242           &&
00243           strMatch(data.testName, utd.testName)
00244           )
00245         &&
00246         (
00247           data.notUnitTestName.length() == 0
00248           ||
00249           !strMatch(data.notUnitTestName, unitTestName)
00250           )
00251         )
00252       {
00253 
00254         ++numTestsRun;
00255 
00256         std::ostringstream testHeaderOSS;
00257         testHeaderOSS <<testCounter<<". "<<unitTestName<<" ... ";
00258         const std::string testHeader = testHeaderOSS.str();
00259 
00260         if (showAll)
00261           out <<"\n";
00262 
00263         if (showTestNames)
00264           out <<testHeader<<std::flush;
00265 
00266         {
00267 
00268           RCP<std::ostringstream> oss;
00269           RCP<FancyOStream> localOut;
00270           if (showAll) {
00271             out << "\n";
00272             localOut = rcpFromRef(out);
00273           }
00274           else {
00275             oss = rcp(new std::ostringstream);
00276             localOut = fancyOStream(rcp_implicit_cast<std::ostream>(oss));
00277           }
00278 
00279           OSTab tab(out);
00280 
00281           if (!data.noOp) {
00282 
00283             timer.start(true);
00284             const bool result = utd.unitTest->runUnitTest(*localOut);
00285             timer.stop();
00286 
00287             if (!result) {
00288 
00289               failedTests.push_back(testHeader);
00290               
00291               if (!showTestNames)
00292                 out <<testHeader<<"\n"<<std::flush;
00293               else if (!showAll)
00294                 out <<"\n";
00295               
00296               if (!is_null(oss))
00297                 out << oss->str();
00298               
00299               out
00300                 <<"[FAILED] "
00301                 <<" "<<setprecision(timerPrec)<<"("<<timer.totalElapsedTime()<< " sec)"
00302                 <<" "<<unitTestName<<"\n"
00303                 <<"Location: "<<utd.unitTest->unitTestFile()<<":"
00304                 <<utd.unitTest->unitTestFileLineNumber()<<"\n";
00305               
00306               if (!is_null(oss))
00307                 out << "\n";
00308               
00309               success = false;
00310               
00311               ++numTestsFailed;
00312               
00313             }
00314             else {
00315               
00316               if (showTestNames)
00317                 out << "[Passed] "
00318                     << setprecision(timerPrec)<<"("<<timer.totalElapsedTime()<<" sec)\n";
00319               
00320               if (showAll && data.showSrcLocation)
00321                 out
00322                   << "Location: "<<utd.unitTest->unitTestFile()<<":"
00323                   <<utd.unitTest->unitTestFileLineNumber()<<"\n";
00324               
00325             }
00326 
00327           }
00328           else {
00329 
00330             if (showTestNames)
00331               out << "[Not Run]\n";
00332             
00333           }
00334 
00335         }
00336    
00337       }
00338 
00339     }
00340 
00341     TEUCHOS_ASSERT_EQUALITY(testCounter, as<int>(data.unitTests.size()));
00342 
00343   }
00344   TEUCHOS_STANDARD_CATCH_STATEMENTS(true, out, success);
00345 
00346   if (failedTests.size()) {
00347     out << "\nThe following tests FAILED:\n";
00348     for (Teuchos_Ordinal i = 0; i < failedTests.size(); ++i)
00349       out << "    " << failedTests[i] << "\n";
00350   }
00351 
00352   overallTimer.stop();
00353   out << "\nTotal Time: " << setprecision(timerPrec)
00354       << overallTimer.totalElapsedTime() << " sec\n";
00355 
00356   out
00357     << "\nSummary: total = " << testCounter
00358     << ", run = " << numTestsRun;
00359 
00360   if (!data.noOp) {
00361     out
00362       << ", passed = " << (numTestsRun-numTestsFailed)
00363       << ", failed = " << numTestsFailed << "\n";
00364   }
00365   else {
00366     out
00367       << ", passed = ???"
00368       << ", failed = ???\n";
00369   }
00370     
00371   return success;
00372 
00373 }
00374 
00375 
00376 int UnitTestRepository::runUnitTestsFromMain( int argc, char* argv[] )
00377 {
00378 
00379   const RCP<FancyOStream> out = VerboseObjectBase::getDefaultOStream();
00380 
00381   CommandLineProcessor &clp = getData().clp;
00382   setUpCLP(outArg(clp));
00383   CommandLineProcessor::EParseCommandLineReturn parse_return =
00384     clp.parse(argc,argv);
00385   if ( parse_return != CommandLineProcessor::PARSE_SUCCESSFUL ) {
00386     *out << "\nEnd Result: TEST FAILED" << std::endl;
00387     return parse_return;
00388   }
00389 
00390   const bool success = runUnitTests(*out);
00391 
00392   if (success)
00393     *out << "\nEnd Result: TEST PASSED" << std::endl;
00394   else
00395     *out << "\nEnd Result: TEST FAILED" << std::endl;
00396 
00397   return (success ? 0 : 1);
00398 
00399 }
00400 
00401 
00402 void UnitTestRepository::addUnitTest( UnitTestBase *unitTest,
00403   const std::string groupName, const std::string testName_in )
00404 {
00405   InstanceData &data = getData();
00406   std::string testName = testName_in;
00407   data.unitTests.push_back(UnitTestData(unitTest, groupName, testName));
00408 }
00409 
00410 
00411 // private:
00412 
00413 
00414 UnitTestRepository::UnitTestRepository()
00415 {}
00416 
00417 
00418 void UnitTestRepository::setUpCLP(const Ptr<CommandLineProcessor>& clp)
00419 {
00420 
00421   clp->addOutputSetupOptions(true);
00422 
00423   const int numShowTestDetails = 3;
00424   const EShowTestDetails showTestDetailsValues[numShowTestDetails] =
00425     { SHOW_TEST_DETAILS_ALL,
00426       SHOW_TEST_DETAILS_TEST_NAMES,
00427       SHOW_TEST_DETAILS_FINAL_RESULTS
00428     };
00429   const char* showTestDetailsNames[numShowTestDetails] =
00430     { "ALL",
00431       "TEST_NAMES",
00432       "FINAL_RESULTS"
00433     };
00434   clp->setOption(
00435     "show-test-details", &getData().showTestDetails,
00436     numShowTestDetails, showTestDetailsValues, showTestDetailsNames,
00437     "Level of detail to show in the tests"
00438     );
00439   clp->setOption(
00440     "details", &getData().showTestDetails,
00441     numShowTestDetails, showTestDetailsValues, showTestDetailsNames,
00442     "Short for --details"
00443     );
00444 
00445   clp->setOption(
00446     "show-src-location", "no-show-src-location", &getData().showSrcLocation,
00447     "If true, then the location of the unit test source code is shown."
00448     "  Only meaningfull if --show-test-details=ALL."
00449     );
00450 
00451   clp->setOption(
00452     "show-fail-src-location", "no-show-fail-src-location", &getData().showFailSrcLocation,
00453     "If true, then the location of every failed unit test check is printed."
00454     );
00455 
00456   clp->setOption(
00457     "group-name", &getData().groupName,
00458     "If specified, selects only tests that match the group name glob." );
00459   clp->setOption(
00460     "group", &getData().groupName,
00461     "Short for --group-name." );
00462 
00463   clp->setOption(
00464     "test-name", &getData().testName,
00465     "If specified, selects only tests that match the test name glob." );
00466   clp->setOption(
00467     "test", &getData().testName,
00468     "Short for --test-name." );
00469 
00470   clp->setOption(
00471     "not-unit-test", &getData().notUnitTestName,
00472     "If specified, full unit tests with glob matches will *not* be run." );
00473 
00474   clp->setOption(
00475     "no-op", "do-op", &getData().noOp,
00476     "If --no-op, then only the names of the tests that would be run are run."
00477     );
00478   
00479 }
00480 
00481 
00482 UnitTestRepository::InstanceData& UnitTestRepository::getData()
00483 {
00484   static UnitTestRepository::InstanceData data;
00485   return data;
00486 }
00487 
00488 
00489 } // namespace Teuchos

Generated on Tue Jul 13 09:23:00 2010 for Teuchos - Trilinos Tools Package by  doxygen 1.4.7