diff options
author | Brad King <brad.king@kitware.com> | 2013-10-26 14:29:34 (GMT) |
---|---|---|
committer | CMake Topic Stage <kwrobot@kitware.com> | 2013-10-26 14:29:34 (GMT) |
commit | 38fc334fd0619bf7b440003a1598f5467ed9a728 (patch) | |
tree | 4e648073956cba8e77b09296682d00573471b5f2 | |
parent | ef13fc4dfcbb2b7cf79aade2d7612c3748fb24b2 (diff) | |
parent | ff59365f8bdcb302f55d4fc882cae057a70acd4b (diff) | |
download | CMake-38fc334fd0619bf7b440003a1598f5467ed9a728.zip CMake-38fc334fd0619bf7b440003a1598f5467ed9a728.tar.gz CMake-38fc334fd0619bf7b440003a1598f5467ed9a728.tar.bz2 |
Merge topic 'ctest-fix-run-serial'
ff59365 CTest: fix dashboard issues associated with the ctest-fix-run-serial topic
7a665ae CTest: added test for RUN_SERIAL issue #14484
384beff CTest: added comments that describe the basic test sorting approach
adbe00d CTest: removed redundant copy of test dependency set
1b750cb CTest: perform cycle test early
6d4d7ca CTest: consider previously failed tests before all others
e809d8c CTest: prioritize tests by their depth in the dependency graph
44017a4 CTest: handle dependent and non dependent test requirements equally
-rw-r--r-- | Source/CTest/cmCTestMultiProcessHandler.cxx | 143 | ||||
-rw-r--r-- | Source/CTest/cmCTestMultiProcessHandler.h | 1 | ||||
-rw-r--r-- | Tests/CMakeLists.txt | 3 | ||||
-rw-r--r-- | Tests/CTestTestSerialInDepends/CMakeLists.txt | 23 | ||||
-rw-r--r-- | Tests/CTestTestSerialInDepends/test.ctest | 16 |
5 files changed, 127 insertions, 59 deletions
diff --git a/Source/CTest/cmCTestMultiProcessHandler.cxx b/Source/CTest/cmCTestMultiProcessHandler.cxx index 76ddeea..2cae179 100644 --- a/Source/CTest/cmCTestMultiProcessHandler.cxx +++ b/Source/CTest/cmCTestMultiProcessHandler.cxx @@ -41,6 +41,7 @@ cmCTestMultiProcessHandler::cmCTestMultiProcessHandler() this->Completed = 0; this->RunningCount = 0; this->StopTimePassed = false; + this->HasCycles = false; } cmCTestMultiProcessHandler::~cmCTestMultiProcessHandler() @@ -65,6 +66,11 @@ cmCTestMultiProcessHandler::SetTests(TestMap& tests, if(!this->CTest->GetShowOnly()) { this->ReadCostData(); + this->HasCycles = !this->CheckCycles(); + if(this->HasCycles) + { + return; + } this->CreateTestCostList(); } } @@ -79,7 +85,7 @@ void cmCTestMultiProcessHandler::SetParallelLevel(size_t level) void cmCTestMultiProcessHandler::RunTests() { this->CheckResume(); - if(!this->CheckCycles()) + if(this->HasCycles) { return; } @@ -205,37 +211,8 @@ bool cmCTestMultiProcessHandler::StartTest(int test) } } - // copy the depend tests locally because when - // a test is finished it will be removed from the depend list - // and we don't want to be iterating a list while removing from it - TestSet depends = this->Tests[test]; - size_t totalDepends = depends.size(); - if(totalDepends) - { - for(TestSet::const_iterator i = depends.begin(); - i != depends.end(); ++i) - { - // if the test is not already running then start it - if(!this->TestRunningMap[*i]) - { - // this test might be finished, but since - // this is a copy of the depend map we might - // still have it - if(!this->TestFinishMap[*i]) - { - // only start one test in this function - return this->StartTest(*i); - } - else - { - // the depend has been and finished - totalDepends--; - } - } - } - } // if there are no depends left then run this test - if(totalDepends == 0) + if(this->Tests[test].empty()) { this->StartTestProcess(test); return true; @@ -262,25 +239,17 @@ void cmCTestMultiProcessHandler::StartNextTests() TestList copy = this->SortedTests; for(TestList::iterator test = copy.begin(); test != copy.end(); ++test) { - //in case this test has already been started due to dependency - if(this->TestRunningMap[*test] || this->TestFinishMap[*test]) - { - continue; - } size_t processors = GetProcessorsUsed(*test); - if(processors > numToStart) - { - return; - } - if(this->StartTest(*test)) + + if(processors <= numToStart && this->StartTest(*test)) { - if(this->StopTimePassed) - { - return; - } - numToStart -= processors; + if(this->StopTimePassed) + { + return; + } + numToStart -= processors; } - if(numToStart == 0) + else if(numToStart == 0) { return; } @@ -472,24 +441,80 @@ int cmCTestMultiProcessHandler::SearchByName(std::string name) //--------------------------------------------------------- void cmCTestMultiProcessHandler::CreateTestCostList() { - for(TestMap::iterator i = this->Tests.begin(); - i != this->Tests.end(); ++i) - { - SortedTests.push_back(i->first); + std::list<TestSet> priorityStack; + priorityStack.push_back(TestSet()); + TestSet &topLevel = priorityStack.back(); - //If the test failed last time, it should be run first, so max the cost. - //Only do this for parallel runs; in non-parallel runs, avoid clobbering - //the test's explicitly set cost. - if(this->ParallelLevel > 1 && - std::find(this->LastTestsFailed.begin(), this->LastTestsFailed.end(), + // Add previously failed tests to the front of the cost list + // and queue other tests for further sorting + for(TestMap::const_iterator i = this->Tests.begin(); + i != this->Tests.end(); ++i) + { + if(std::find(this->LastTestsFailed.begin(), this->LastTestsFailed.end(), this->Properties[i->first]->Name) != this->LastTestsFailed.end()) { - this->Properties[i->first]->Cost = FLT_MAX; + //If the test failed last time, it should be run first. + this->SortedTests.push_back(i->first); + } + else + { + topLevel.insert(i->first); + } + } + + // Repeatedly move dependencies of the tests on the current dependency level + // to the next level until no further dependencies exist. + while(priorityStack.back().size()) + { + TestSet &previousSet = priorityStack.back(); + priorityStack.push_back(TestSet()); + TestSet ¤tSet = priorityStack.back(); + + for(TestSet::const_iterator i = previousSet.begin(); + i != previousSet.end(); ++i) + { + TestSet const& dependencies = this->Tests[*i]; + for(TestSet::const_iterator j = dependencies.begin(); + j != dependencies.end(); ++j) + { + currentSet.insert(*j); + } + } + + for(TestSet::const_iterator i = currentSet.begin(); + i != currentSet.end(); ++i) + { + previousSet.erase(*i); } } - TestComparator comp(this); - std::stable_sort(SortedTests.begin(), SortedTests.end(), comp); + // Remove the empty dependency level + priorityStack.pop_back(); + + // Reverse iterate over the different dependency levels (deepest first). + // Sort tests within each level by COST and append them to the cost list. + for(std::list<TestSet>::reverse_iterator i = priorityStack.rbegin(); + i != priorityStack.rend(); ++i) + { + TestSet const& currentSet = *i; + TestComparator comp(this); + + TestList sortedCopy; + + for(TestSet::const_iterator j = currentSet.begin(); + j != currentSet.end(); ++j) + { + sortedCopy.push_back(*j); + } + + std::stable_sort(sortedCopy.begin(), sortedCopy.end(), comp); + + for(TestList::const_iterator j = sortedCopy.begin(); + j != sortedCopy.end(); ++j) + { + this->SortedTests.push_back(*j); + } + } } //--------------------------------------------------------- diff --git a/Source/CTest/cmCTestMultiProcessHandler.h b/Source/CTest/cmCTestMultiProcessHandler.h index cd21d91..439a8f3 100644 --- a/Source/CTest/cmCTestMultiProcessHandler.h +++ b/Source/CTest/cmCTestMultiProcessHandler.h @@ -111,6 +111,7 @@ protected: std::set<cmCTestRunTest*> RunningTests; // current running tests cmCTestTestHandler * TestHandler; cmCTest* CTest; + bool HasCycles; }; #endif diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 825504b..5ffd9d7 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -2111,6 +2111,9 @@ ${CMake_BINARY_DIR}/bin/cmake -DVERSION=master -P ${CMake_SOURCE_DIR}/Utilities/ --output-log "${CMake_BINARY_DIR}/Tests/CTestTestParallel/testOutput.log" ) + ADD_TEST_MACRO(CTestTestSerialInDepends ${CMAKE_CTEST_COMMAND} -j 4 + --output-on-failure -C "\${CTestTest_CONFIG}") + if(NOT BORLAND) set(CTestLimitDashJ_EXTRA_OPTIONS --force-new-ctest-process) add_test_macro(CTestLimitDashJ ${CMAKE_CTEST_COMMAND} -j 4 diff --git a/Tests/CTestTestSerialInDepends/CMakeLists.txt b/Tests/CTestTestSerialInDepends/CMakeLists.txt new file mode 100644 index 0000000..90e50f9 --- /dev/null +++ b/Tests/CTestTestSerialInDepends/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 2.8.12) + +project(CTestTestSerialInDepends) + +enable_testing() + +function(my_add_test NAME COST) + add_test(NAME ${NAME} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_CTEST_COMMAND} -DTEST_NAME=${NAME} + -S ${CMAKE_CURRENT_SOURCE_DIR}/test.ctest) + set_tests_properties(${NAME} PROPERTIES COST ${COST}) +endfunction() + +my_add_test(i_like_company 1000) +my_add_test(i_like_company_too 0) + +my_add_test(i_have_dependencies 1000) +set_tests_properties(i_have_dependencies PROPERTIES + DEPENDS "i_want_to_be_alone") + +my_add_test(i_want_to_be_alone 100) +set_tests_properties(i_want_to_be_alone PROPERTIES RUN_SERIAL 1) diff --git a/Tests/CTestTestSerialInDepends/test.ctest b/Tests/CTestTestSerialInDepends/test.ctest new file mode 100644 index 0000000..28ee094 --- /dev/null +++ b/Tests/CTestTestSerialInDepends/test.ctest @@ -0,0 +1,16 @@ +set(CTEST_RUN_CURRENT_SCRIPT 0) + +set(LOCK_FILE "${TEST_NAME}.lock") + +if("${TEST_NAME}" STREQUAL "i_want_to_be_alone") + file(GLOB LOCK_FILES *.lock) + if(LOCK_FILES) + message(FATAL_ERROR "found lock files of other tests even though this test should be running by itself: ${LOCK_FILES}") + endif() +endif() + +file(WRITE "${LOCK_FILE}") +ctest_sleep(3) +file(REMOVE "${LOCK_FILE}") + +return() |