diff options
Diffstat (limited to 'Source/CTest/cmCTestTestHandler.cxx')
-rw-r--r-- | Source/CTest/cmCTestTestHandler.cxx | 170 |
1 files changed, 97 insertions, 73 deletions
diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx index 0d0237face..6175e50263 100644 --- a/Source/CTest/cmCTestTestHandler.cxx +++ b/Source/CTest/cmCTestTestHandler.cxx @@ -2,20 +2,6 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCTestTestHandler.h" -#include "cmCTest.h" -#include "cmCTestBatchTestHandler.h" -#include "cmCTestMultiProcessHandler.h" -#include "cmCommand.h" -#include "cmGeneratedFileStream.h" -#include "cmGlobalGenerator.h" -#include "cmMakefile.h" -#include "cmState.h" -#include "cmSystemTools.h" -#include "cmXMLWriter.h" -#include "cm_auto_ptr.hxx" -#include "cm_utf8.h" -#include "cmake.h" - #include <algorithm> #include <cmsys/Base64.h> #include <cmsys/Directory.hxx> @@ -31,6 +17,21 @@ #include <string.h> #include <time.h> +#include "cmCTest.h" +#include "cmCTestBatchTestHandler.h" +#include "cmCTestMultiProcessHandler.h" +#include "cmCommand.h" +#include "cmGeneratedFileStream.h" +#include "cmGlobalGenerator.h" +#include "cmMakefile.h" +#include "cmState.h" +#include "cmStateSnapshot.h" +#include "cmSystemTools.h" +#include "cmXMLWriter.h" +#include "cm_auto_ptr.hxx" +#include "cm_utf8.h" +#include "cmake.h" + class cmExecutionStatus; class cmCTestSubdirCommand : public cmCommand @@ -58,8 +59,6 @@ public: */ std::string GetName() const CM_OVERRIDE { return "subdirs"; } - cmTypeMacro(cmCTestSubdirCommand, cmCommand); - cmCTestTestHandler* TestHandler; }; @@ -139,8 +138,6 @@ public: */ std::string GetName() const CM_OVERRIDE { return "add_subdirectory"; } - cmTypeMacro(cmCTestAddSubdirectoryCommand, cmCommand); - cmCTestTestHandler* TestHandler; }; @@ -213,8 +210,6 @@ public: */ std::string GetName() const CM_OVERRIDE { return "add_test"; } - cmTypeMacro(cmCTestAddTestCommand, cmCommand); - cmCTestTestHandler* TestHandler; }; @@ -253,8 +248,6 @@ public: */ std::string GetName() const CM_OVERRIDE { return "set_tests_properties"; } - cmTypeMacro(cmCTestSetTestsPropertiesCommand, cmCommand); - cmCTestTestHandler* TestHandler; }; @@ -725,7 +718,7 @@ void cmCTestTestHandler::ComputeTestList() // Now create a final list of tests to run int cnt = 0; inREcnt = 0; - std::string last_directory = ""; + std::string last_directory; ListOfTests finalList; for (it = this->TestList.begin(); it != this->TestList.end(); it++) { cnt++; @@ -804,8 +797,9 @@ void cmCTestTestHandler::UpdateForFixtures(ListOfTests& tests) const // Prepare some maps to help us find setup and cleanup tests for // any given fixture - typedef std::set<ListOfTests::const_iterator> TestIteratorSet; - typedef std::map<std::string, TestIteratorSet> FixtureDependencies; + typedef ListOfTests::const_iterator TestIterator; + typedef std::multimap<std::string, TestIterator> FixtureDependencies; + typedef FixtureDependencies::const_iterator FixtureDepsIterator; FixtureDependencies fixtureSetups; FixtureDependencies fixtureDeps; @@ -816,14 +810,14 @@ void cmCTestTestHandler::UpdateForFixtures(ListOfTests& tests) const const std::set<std::string>& setups = p.FixturesSetup; for (std::set<std::string>::const_iterator depsIt = setups.begin(); depsIt != setups.end(); ++depsIt) { - fixtureSetups[*depsIt].insert(it); - fixtureDeps[*depsIt].insert(it); + fixtureSetups.insert(std::make_pair(*depsIt, it)); + fixtureDeps.insert(std::make_pair(*depsIt, it)); } const std::set<std::string>& cleanups = p.FixturesCleanup; for (std::set<std::string>::const_iterator depsIt = cleanups.begin(); depsIt != cleanups.end(); ++depsIt) { - fixtureDeps[*depsIt].insert(it); + fixtureDeps.insert(std::make_pair(*depsIt, it)); } } @@ -835,23 +829,31 @@ void cmCTestTestHandler::UpdateForFixtures(ListOfTests& tests) const addedTests.insert(p.Name); } - // This is a lookup of fixture name to a list of indices into the - // final tests array for tests which require that fixture. It is - // needed at the end to populate dependencies of the cleanup tests - // in our final list of tests. + // These are lookups of fixture name to a list of indices into the final + // tests array for tests which require that fixture and tests which are + // setups for that fixture. They are needed at the end to populate + // dependencies of the cleanup tests in our final list of tests. std::map<std::string, std::vector<size_t> > fixtureRequirements; + std::map<std::string, std::vector<size_t> > setupFixturesAdded; // Use integer index for iteration because we append to // the tests vector as we go size_t fixtureTestsAdded = 0; std::set<std::string> addedFixtures; for (size_t i = 0; i < tests.size(); ++i) { - if (tests[i].FixturesRequired.empty()) { - continue; - } - // Must copy the set of fixtures because we may invalidate + // There are two things to do for each test: + // 1. For every fixture required by this test, record that fixture as + // being required and create dependencies on that fixture's setup + // tests. + // 2. Record all setup tests in the final test list so we can later make + // cleanup tests in the test list depend on their associated setup + // tests to enforce correct ordering. + + // 1. Handle fixture requirements + // + // Must copy the set of fixtures required because we may invalidate // the tests array by appending to it - const std::set<std::string> fixtures = tests[i].FixturesRequired; + std::set<std::string> fixtures = tests[i].FixturesRequired; for (std::set<std::string>::const_iterator fixturesIt = fixtures.begin(); fixturesIt != fixtures.end(); ++fixturesIt) { @@ -866,17 +868,15 @@ void cmCTestTestHandler::UpdateForFixtures(ListOfTests& tests) const // associated with the required fixture. If any of those setup // tests fail, this test should not run. We make the fixture's // cleanup tests depend on this test case later. - FixtureDependencies::const_iterator setupIt = - fixtureSetups.find(requiredFixtureName); - if (setupIt != fixtureSetups.end()) { - for (TestIteratorSet::const_iterator sIt = setupIt->second.begin(); - sIt != setupIt->second.end(); ++sIt) { - const std::string& setupTestName = (**sIt).Name; - tests[i].RequireSuccessDepends.insert(setupTestName); - if (std::find(tests[i].Depends.begin(), tests[i].Depends.end(), - setupTestName) == tests[i].Depends.end()) { - tests[i].Depends.push_back(setupTestName); - } + std::pair<FixtureDepsIterator, FixtureDepsIterator> setupRange = + fixtureSetups.equal_range(requiredFixtureName); + for (FixtureDepsIterator sIt = setupRange.first; + sIt != setupRange.second; ++sIt) { + const std::string& setupTestName = sIt->second->Name; + tests[i].RequireSuccessDepends.insert(setupTestName); + if (std::find(tests[i].Depends.begin(), tests[i].Depends.end(), + setupTestName) == tests[i].Depends.end()) { + tests[i].Depends.push_back(setupTestName); } } @@ -889,17 +889,11 @@ void cmCTestTestHandler::UpdateForFixtures(ListOfTests& tests) const // Already added this fixture continue; } - FixtureDependencies::const_iterator fixtureIt = - fixtureDeps.find(requiredFixtureName); - if (fixtureIt == fixtureDeps.end()) { - // No setup or cleanup tests for this fixture - continue; - } - - const TestIteratorSet& testIters = fixtureIt->second; - for (TestIteratorSet::const_iterator depsIt = testIters.begin(); - depsIt != testIters.end(); ++depsIt) { - ListOfTests::const_iterator lotIt = *depsIt; + std::pair<FixtureDepsIterator, FixtureDepsIterator> fixtureRange = + fixtureDeps.equal_range(requiredFixtureName); + for (FixtureDepsIterator it = fixtureRange.first; + it != fixtureRange.second; ++it) { + ListOfTests::const_iterator lotIt = it->second; const cmCTestTestProperties& p = *lotIt; if (!addedTests.insert(p.Name).second) { @@ -922,32 +916,62 @@ void cmCTestTestHandler::UpdateForFixtures(ListOfTests& tests) const this->Quiet); } } + + // 2. Record all setup fixtures included in the final list of tests + for (std::set<std::string>::const_iterator fixturesIt = + tests[i].FixturesSetup.begin(); + fixturesIt != tests[i].FixturesSetup.end(); ++fixturesIt) { + + const std::string& setupFixtureName = *fixturesIt; + if (setupFixtureName.empty()) { + continue; + } + + setupFixturesAdded[setupFixtureName].push_back(i); + } } // Now that we have the final list of tests, we can update all cleanup - // tests to depend on those tests which require that fixture + // tests to depend on those tests which require that fixture and on any + // setup tests for that fixture. The latter is required to handle the + // pathological case where setup and cleanup tests are in the test set + // but no other test has that fixture as a requirement. for (ListOfTests::iterator tIt = tests.begin(); tIt != tests.end(); ++tIt) { cmCTestTestProperties& p = *tIt; const std::set<std::string>& cleanups = p.FixturesCleanup; for (std::set<std::string>::const_iterator fIt = cleanups.begin(); fIt != cleanups.end(); ++fIt) { const std::string& fixture = *fIt; + + // This cleanup test could be part of the original test list that was + // passed in. It is then possible that no other test requires the + // fIt fixture, so we have to check for this. std::map<std::string, std::vector<size_t> >::const_iterator cIt = fixtureRequirements.find(fixture); - if (cIt == fixtureRequirements.end()) { - // No test cases require the fixture this cleanup test is for. - // This cleanup test must have been part of the original test - // list passed in (which is not an error) - continue; + if (cIt != fixtureRequirements.end()) { + const std::vector<size_t>& indices = cIt->second; + for (std::vector<size_t>::const_iterator indexIt = indices.begin(); + indexIt != indices.end(); ++indexIt) { + const std::string& reqTestName = tests[*indexIt].Name; + if (std::find(p.Depends.begin(), p.Depends.end(), reqTestName) == + p.Depends.end()) { + p.Depends.push_back(reqTestName); + } + } } - const std::vector<size_t>& indices = cIt->second; - for (std::vector<size_t>::const_iterator indexIt = indices.begin(); - indexIt != indices.end(); ++indexIt) { - const std::string& reqTestName = tests[*indexIt].Name; - if (std::find(p.Depends.begin(), p.Depends.end(), reqTestName) == - p.Depends.end()) { - p.Depends.push_back(reqTestName); + // Ensure fixture cleanup tests always run after their setup tests, even + // if no other test cases require the fixture + cIt = setupFixturesAdded.find(fixture); + if (cIt != setupFixturesAdded.end()) { + const std::vector<size_t>& indices = cIt->second; + for (std::vector<size_t>::const_iterator indexIt = indices.begin(); + indexIt != indices.end(); ++indexIt) { + const std::string& setupTestName = tests[*indexIt].Name; + if (std::find(p.Depends.begin(), p.Depends.end(), setupTestName) == + p.Depends.end()) { + p.Depends.push_back(setupTestName); + } } } } @@ -1663,7 +1687,7 @@ void cmCTestTestHandler::ExpandTestsToRunInformationForRerunFailed() int numFiles = static_cast<int>(cmsys::Directory::GetNumberOfFilesInDirectory(dirName)); std::string pattern = "LastTestsFailed"; - std::string logName = ""; + std::string logName; for (int i = 0; i < numFiles; ++i) { std::string fileName = directory.GetFile(i); |