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.
This commit is contained in:
parent
59f6f383e9
commit
d47ada823a
|
@ -15,6 +15,7 @@
|
||||||
#include "cmCTest.h"
|
#include "cmCTest.h"
|
||||||
#include "cmSystemTools.h"
|
#include "cmSystemTools.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stack>
|
||||||
|
|
||||||
cmCTestMultiProcessHandler::cmCTestMultiProcessHandler()
|
cmCTestMultiProcessHandler::cmCTestMultiProcessHandler()
|
||||||
{
|
{
|
||||||
|
@ -56,6 +57,10 @@ void cmCTestMultiProcessHandler::SetParallelLevel(size_t level)
|
||||||
void cmCTestMultiProcessHandler::RunTests()
|
void cmCTestMultiProcessHandler::RunTests()
|
||||||
{
|
{
|
||||||
this->CheckResume();
|
this->CheckResume();
|
||||||
|
if(!this->CheckCycles())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
this->TestHandler->SetMaxIndex(this->FindMaxIndex());
|
this->TestHandler->SetMaxIndex(this->FindMaxIndex());
|
||||||
this->StartNextTests();
|
this->StartNextTests();
|
||||||
while(this->Tests.size() != 0)
|
while(this->Tests.size() != 0)
|
||||||
|
@ -340,7 +345,7 @@ void cmCTestMultiProcessHandler::PrintTestList()
|
||||||
this->TestHandler->SetMaxIndex(this->FindMaxIndex());
|
this->TestHandler->SetMaxIndex(this->FindMaxIndex());
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (PropertiesMap::iterator it = this->Properties.begin();
|
for (PropertiesMap::iterator it = this->Properties.begin();
|
||||||
it != this->Properties.end(); it ++ )
|
it != this->Properties.end(); ++it)
|
||||||
{
|
{
|
||||||
count++;
|
count++;
|
||||||
cmCTestTestHandler::cmCTestTestProperties& p = *it->second;
|
cmCTestTestHandler::cmCTestTestProperties& p = *it->second;
|
||||||
|
@ -433,3 +438,47 @@ int cmCTestMultiProcessHandler::FindMaxIndex()
|
||||||
}
|
}
|
||||||
return max;
|
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<int> s;
|
||||||
|
std::vector<int> 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<int>::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;
|
||||||
|
}
|
||||||
|
|
|
@ -76,6 +76,8 @@ protected:
|
||||||
void RemoveTest(int index);
|
void RemoveTest(int index);
|
||||||
//Check if we need to resume an interrupted test set
|
//Check if we need to resume an interrupted test set
|
||||||
void CheckResume();
|
void CheckResume();
|
||||||
|
//Check if there are any circular dependencies
|
||||||
|
bool CheckCycles();
|
||||||
int FindMaxIndex();
|
int FindMaxIndex();
|
||||||
inline size_t GetProcessorsUsed(int index);
|
inline size_t GetProcessorsUsed(int index);
|
||||||
// map from test number to set of depend tests
|
// map from test number to set of depend tests
|
||||||
|
|
Loading…
Reference in New Issue