From d47ada823abbed75f20e086176d5345ca0fb965a Mon Sep 17 00:00:00 2001 From: Zach Mullen Date: Mon, 2 Nov 2009 12:52:50 -0500 Subject: [PATCH] Added logic to check for cycles in the test dependency graph before any tests are run. Previously a cycle resulted in a segfault from stack overflow. --- Source/CTest/cmCTestMultiProcessHandler.cxx | 51 ++++++++++++++++++++- Source/CTest/cmCTestMultiProcessHandler.h | 2 + 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/Source/CTest/cmCTestMultiProcessHandler.cxx b/Source/CTest/cmCTestMultiProcessHandler.cxx index 024599ce9..4f3a6057f 100644 --- a/Source/CTest/cmCTestMultiProcessHandler.cxx +++ b/Source/CTest/cmCTestMultiProcessHandler.cxx @@ -15,6 +15,7 @@ #include "cmCTest.h" #include "cmSystemTools.h" #include +#include cmCTestMultiProcessHandler::cmCTestMultiProcessHandler() { @@ -56,6 +57,10 @@ void cmCTestMultiProcessHandler::SetParallelLevel(size_t level) void cmCTestMultiProcessHandler::RunTests() { this->CheckResume(); + if(!this->CheckCycles()) + { + return; + } this->TestHandler->SetMaxIndex(this->FindMaxIndex()); this->StartNextTests(); while(this->Tests.size() != 0) @@ -340,7 +345,7 @@ void cmCTestMultiProcessHandler::PrintTestList() this->TestHandler->SetMaxIndex(this->FindMaxIndex()); int count = 0; for (PropertiesMap::iterator it = this->Properties.begin(); - it != this->Properties.end(); it ++ ) + it != this->Properties.end(); ++it) { count++; cmCTestTestHandler::cmCTestTestProperties& p = *it->second; @@ -433,3 +438,47 @@ int cmCTestMultiProcessHandler::FindMaxIndex() } return max; } + +//Returns true if no cycles exist in the dependency graph +bool cmCTestMultiProcessHandler::CheckCycles() +{ + cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, + "Checking test dependency graph..." << std::endl); + for(TestMap::iterator it = this->Tests.begin(); + it != this->Tests.end(); ++it) + { + //DFS from each element to itself + std::stack s; + std::vector visited; + s.push(it->first); + visited.push_back(it->first); + + while(!s.empty()) + { + int test = s.top(); + s.pop(); + + for(TestSet::iterator d = this->Tests[test].begin(); + d != this->Tests[test].end(); ++d) + { + s.push(*d); + for(std::vector::iterator v = visited.begin(); + v != visited.end(); ++v) + { + if(*v == *d) + { + //cycle exists + cmCTestLog(this->CTest, ERROR_MESSAGE, "Error: a cycle exists in " + "the test dependency graph for the test \"" + << this->Properties[*d]->Name << "\"." << std::endl + << "Please fix the cycle and run ctest again." << std::endl); + return false; + } + } + visited.push_back(*d); + } + visited.pop_back(); + } + } + return true; +} diff --git a/Source/CTest/cmCTestMultiProcessHandler.h b/Source/CTest/cmCTestMultiProcessHandler.h index c99eb3dac..16591b043 100644 --- a/Source/CTest/cmCTestMultiProcessHandler.h +++ b/Source/CTest/cmCTestMultiProcessHandler.h @@ -76,6 +76,8 @@ protected: void RemoveTest(int index); //Check if we need to resume an interrupted test set void CheckResume(); + //Check if there are any circular dependencies + bool CheckCycles(); int FindMaxIndex(); inline size_t GetProcessorsUsed(int index); // map from test number to set of depend tests