testFixture.h

Go to the documentation of this file.
00001 
00015 #ifndef _DLR_TESTFIXTURE_H_
00016 #define _DLR_TESTFIXTURE_H_
00017 
00018 #include <iostream>
00019 #include <sstream>
00020 #include <string>
00021 #include <vector>
00022 
00023 #include <dlrTest/runnableObject.h>
00024 #include <dlrTest/testException.h>
00025 #include <dlrTest/testMacros.h>
00026 
00027 namespace dlr {
00028 
00029   namespace test {
00030 
00069     template <class FixtureType>
00070     class TestFixture
00071       : public RunnableObject
00072     {
00073     public:
00074 
00075       /* ============== Public Typedefs ============== */
00076 
00081       typedef FixtureType TestFixtureType;
00082     
00088       typedef void (FixtureType::* TestFunctionPtr)();
00089 
00090     
00091       /* ============== Public Member Functions ============== */
00092 
00100       explicit
00101       TestFixture(const std::string& testFixtureName);
00102 
00103 
00108       virtual
00109       ~TestFixture();
00110 
00111 
00126       virtual void
00127       registerTest(const std::string& testName,
00128                    TestFunctionPtr testFunctionPtr);
00129       
00138       virtual bool
00139       run();
00140 
00141     protected:
00142 
00143       /* ============== Protected Member Functions ============== */
00144 
00151       virtual void
00152       announceTestFinish();
00153     
00160       virtual void
00161       announceTestStart();
00162 
00163 
00188       virtual std::string
00189       buildFailureMessage(size_t failureIndex,
00190                           const std::string& testName,
00191                           const std::string& failureType,
00192                           const std::string& whatMessage);
00193                         
00194 
00205       void
00206       printTestStatistics(int errorCount,
00207                           const std::vector<std::string>& testMessages);
00208 
00218       virtual void
00219       setUp(const std::string&) {}
00220     
00230       virtual void
00231       tearDown(const std::string&) {}
00232 
00233     
00234       /* ============== Protected Member Variables ============== */
00235 
00240       int m_textOutputLineLength;
00241 
00246       int m_verbosity;
00247     
00248     private:
00249 
00250       /* ============== Private Member Variables ============== */
00251 
00252 
00253       /* ============== Private Member Variables ============== */
00254 
00255       std::string m_testFixtureName;
00256       std::vector<TestFunctionPtr> m_testFunctionPtrVector;
00257       std::vector<std::string> m_testNameVector;
00258     };
00259 
00260   } // namespace test
00261   
00262 } // namespace dlr
00263 
00264 
00265 /* ======= Declarations to maintain compatibility with legacy code. ======= */
00266 
00267 namespace dlr {
00268 
00269   using test::TestFixture;
00270   
00271 } // namespace dlr
00272 
00273 
00274 
00275 /* =================================================================
00276  * Member function definitions follow.
00277  * This would be a .cpp file if TestFixture weren't a template.
00278  * ================================================================= */
00279 
00280 namespace dlr {
00281 
00282   namespace test {
00283 
00284     // This constructor sets the name of the group of tests run by
00285     // this test fixture.
00286     template <class FixtureType>
00287     TestFixture<FixtureType>::
00288     TestFixture(const std::string& testFixtureName)
00289       : RunnableObject(),
00290         m_textOutputLineLength(75),
00291         m_verbosity(1),
00292         m_testFixtureName(testFixtureName),
00293         m_testFunctionPtrVector(),
00294         m_testNameVector()
00295     {
00296       // Empty
00297     }
00298 
00299   
00300     // The destructor cleans up any resources and destroys the class
00301     // instance.
00302     template <class FixtureType>
00303     TestFixture<FixtureType>::
00304     ~TestFixture()
00305     {
00306       // Empty
00307     }
00308 
00309 
00310     // This member function is used to indicate which member functions
00311     // of the subclass should be run as tests.
00312     template <class FixtureType>
00313     void
00314     TestFixture<FixtureType>::
00315     registerTest(const std::string& testName,
00316                  typename TestFixture<FixtureType>::TestFunctionPtr testFunctionPtr)
00317     {
00318       m_testNameVector.push_back(testName);
00319       m_testFunctionPtrVector.push_back(testFunctionPtr);
00320     }
00321 
00322 
00323     // This member function runs all of the registered tests, keeps
00324     // track of the results, and prints any diagnostic output, finally
00325     // returning a bool indicating success (true) or failure (false).
00326     template <class FixtureType>
00327     bool
00328     TestFixture<FixtureType>::
00329     run()
00330     {
00331       // Print the starting banner.
00332       this->announceTestStart();
00333     
00334       // This vector will hold the a message for each failed test.
00335       std::vector<std::string> testMessages;
00336 
00337       // This counter will reflect how many tests fail by throwing
00338       // non-test exceptions.
00339       int errorCount = 0;
00340 
00341       // Now run each test in turn.
00342       for(size_t index = 0; index < m_testFunctionPtrVector.size(); ++index) {
00343         // Run the setUp() member function to initialize any test data.
00344         try {
00345           this->setUp(m_testNameVector[index]);
00346         } catch(const std::exception&) {
00347           // Is there anything useful to do here other than just re-throw
00348           // the exception?
00349           throw;
00350         }
00351       
00352         // Extract the function pointer.
00353         TestFunctionPtr testFunctionPtr = m_testFunctionPtrVector[index];
00354         try {
00355           // Run the test.
00356           FixtureType* subclassThis = dynamic_cast<FixtureType*>(this);
00357           (subclassThis->*testFunctionPtr)();
00358           // Successful completion.  Print a happy ".".
00359           std::cout << "." << std::flush;
00360 
00361         } catch(const TestException& caughtException) {
00362           // Failure!  The test threw a TestException, the purpose of which
00363           // is to indicate a failed test.
00364           // Write status output.
00365           std::cout << "F" << std::flush;
00366 
00367           // And save information about the failure.
00368           std::string message = this->buildFailureMessage(
00369             testMessages.size() + 1, m_testNameVector[index], "Failed test",
00370             caughtException.what());
00371           testMessages.push_back(message);
00372 
00373         } catch(const std::exception& caughtException) {
00374           // Error!  The test threw an std::exception besides TestException.
00375           // Write status output.
00376           std::cout << "E" << std::flush;
00377 
00378           // Increment the count of errors.
00379           ++errorCount;
00380         
00381           // And save information about the error.
00382           std::string message = this->buildFailureMessage(
00383             testMessages.size() + 1, m_testNameVector[index], "Error in test",
00384             caughtException.what());
00385           testMessages.push_back(message);
00386 
00387         } catch(...) {
00388           // Error!  The test threw something, but who knows what!
00389           // Write status output.
00390           std::cout << "E" << std::flush;
00391 
00392           // Increment the count of errors.
00393           ++errorCount;
00394 
00395           // And save information about the error.
00396           std::string message = this->buildFailureMessage(
00397             testMessages.size() + 1, m_testNameVector[index], "Error in test",
00398             "Unrecognized exception");
00399           testMessages.push_back(message);
00400         }
00401 
00402         // Run the tearDown() member function to clean up.
00403         try {
00404           this->tearDown(m_testNameVector[index]);
00405         } catch(const std::exception&) {
00406           // Is there anything useful to do here other than just re-throw
00407           // the exception?
00408           throw;
00409         }
00410 
00411         // We respect 80 column terminals by printing a carriage return
00412         // every once in a while.
00413         if((index % m_textOutputLineLength == 0)
00414            && (index != 0)) {
00415           std::cout << std::endl;
00416         }
00417       }
00418 
00419       // Print a summary of what happened.
00420       this->printTestStatistics(errorCount, testMessages);
00421 
00422       // Print the ending banner and return.
00423       this->announceTestFinish();
00424       return (testMessages.size() == 0);
00425     }
00426 
00427   
00428     // This protected member function prints a message indicating that
00429     // the test fixture has run all of its tests.
00430     template <class FixtureType>
00431     void
00432     TestFixture<FixtureType>::
00433     announceTestFinish()
00434     {
00435       if(m_verbosity >= 3) {
00436         std::cout << "/////////////////////////////////////////////////////\n"
00437                   << "// Completed test: " << m_testFixtureName << "\n"
00438                   << "/////////////////////////////////////////////////////\n"
00439                   << std::endl;
00440       }
00441     }
00442 
00443   
00444     // This protected member function prints a message indicating that
00445     // the test fixture is about to run its tests.
00446     template <class FixtureType>
00447     void
00448     TestFixture<FixtureType>::
00449     announceTestStart()
00450     {
00451       if(m_verbosity >= 1) {
00452         std::cout << "/////////////////////////////////////////////////////\n"
00453                   << "// Starting test: " << m_testFixtureName << "\n"
00454                   << "/////////////////////////////////////////////////////\n"
00455                   << std::endl;
00456       }
00457     }
00458 
00459 
00460     // This protected member function builds a diagnostic string
00461     // describing a test failure.
00462     template <class FixtureType>
00463     std::string
00464     TestFixture<FixtureType>::
00465     buildFailureMessage(size_t failureIndex,
00466                         const std::string& testName,
00467                         const std::string& failureType,
00468                         const std::string& whatMessage)
00469     {
00470       // We'll build the message using stream IO.
00471       std::ostringstream messageBuffer;
00472     
00473       // First format up the "1) " part.
00474       messageBuffer << failureIndex << ") ";
00475 
00476       // We'll want to know how many spaces the string takes up so far
00477       // so we can format better later.
00478       size_t indentSize = messageBuffer.str().size();
00479     
00480       // Add the failure description and test name.
00481       messageBuffer << failureType << " " << m_testFixtureName << "::"
00482                     << testName;
00483 
00484       // Now add the exception output, blindly breaking the text to fit
00485       // the (usually 80 column) screen.
00486       std::string::size_type currentIndex = 0;
00487       std::string::size_type chunkSize = m_textOutputLineLength - indentSize;
00488 
00489       std::string whitespace = " \t\n";
00490       while(currentIndex < whatMessage.size()) {
00491         // Carriage return and indent as appropriate.
00492         messageBuffer << "\n";
00493         for(size_t index = 0; index < indentSize; ++index) {
00494           messageBuffer << " ";
00495         }
00496 
00497         // If the line is too long to fit without a line break, then
00498         // search for a good whitespace position at which to break the
00499         // line.  When this loop terminates, whitespaceIndex should
00500         // point to the whitespace that comes closest to the ideal line
00501         // break position (chunkSize) without exceeding it.  If there's
00502         // no whitespace within the target line length, then
00503         // whitespaceIndex will be equal to currentIndex.  In either
00504         // case, nextWhitespaceIndex will point to the nearest
00505         // whitespace which exceeds the ideal line break length, or npos
00506         // if there's no whitespace at all.
00507         std::string::size_type whitespaceIndex = currentIndex;
00508         std::string::size_type nextWhitespaceIndex = currentIndex;
00509         if(whatMessage.size() - currentIndex > chunkSize) {
00510           while(nextWhitespaceIndex <= currentIndex + chunkSize) {
00511             whitespaceIndex = nextWhitespaceIndex;
00512             nextWhitespaceIndex =
00513               whatMessage.find_first_not_of(whitespace, whitespaceIndex);
00514             if(nextWhitespaceIndex == std::string::npos) {
00515               break;
00516             }
00517             nextWhitespaceIndex =
00518               whatMessage.find_first_of(whitespace, nextWhitespaceIndex);
00519           }
00520         }
00521 
00522         // Write the next chunk of the string and move to the next chunk.
00523         if(whitespaceIndex != currentIndex) {
00524           std::string::size_type localChunkSize = whitespaceIndex - currentIndex;
00525           messageBuffer << whatMessage.substr(currentIndex, localChunkSize);
00526           currentIndex += localChunkSize + 1;
00527         } else {
00528           messageBuffer << whatMessage.substr(currentIndex, chunkSize);
00529           currentIndex += chunkSize;
00530         }
00531       }
00532 
00533       // Done with formatting.
00534       return messageBuffer.str();
00535     }
00536 
00537 
00538     // This protected member function prints the final output
00539     // indicating how many tests passed/failed, etc.
00540     template <class FixtureType>
00541     void
00542     TestFixture<FixtureType>::
00543     printTestStatistics(int errorCount,
00544                         const std::vector<std::string>& testMessages)
00545     {
00546       // Print statistics from the test run.
00547       std::cout << "\n\n";
00548       std::cout << "Test Results:\n"
00549                 << "Run: " << m_testFunctionPtrVector.size()
00550                 << "  \tFailures: " << testMessages.size() - errorCount
00551                 << "  \tErrors: " << errorCount << "\n" << std::endl;
00552 
00553       // Print accumulated error messages.
00554       for(size_t index = 0; index < testMessages.size(); ++index) {
00555         std::cout << testMessages[index] << std::endl;
00556         std::cout << std::endl;
00557       }
00558     }
00559 
00560   } // namespace test
00561 
00562 } // namespace dlr
00563 
00564 #endif // #ifdef _DLR_TESTFIXTURE_H_

Generated on Wed Nov 25 01:02:07 2009 for dlrTest Utility Library by  doxygen 1.5.8