Fixed ctest -N segfault issue. Further refactored ctest. Enabled failover for ctest

This commit is contained in:
Zach Mullen 2009-08-27 10:37:30 -04:00
parent fdc0d9777c
commit 177edc5ed1
10 changed files with 255 additions and 436 deletions

View File

@ -166,12 +166,6 @@ bool cmCTestGenericHandler::StartLogFile(const char* name,
ostr << "_" << this->CTest->GetCurrentTag(); ostr << "_" << this->CTest->GetCurrentTag();
} }
ostr << ".log"; ostr << ".log";
// if this is a parallel subprocess then add the id to the
// file so they don't clobber each other
if(this->CTest->GetParallelSubprocess())
{
ostr << "." << this->CTest->GetParallelSubprocessId();
}
if( !this->CTest->OpenOutputFile("Temporary", ostr.str().c_str(), xofs) ) if( !this->CTest->OpenOutputFile("Temporary", ostr.str().c_str(), xofs) )
{ {
cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot create log file: " cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot create log file: "

View File

@ -18,7 +18,8 @@
#include "cmProcess.h" #include "cmProcess.h"
#include "cmStandardIncludes.h" #include "cmStandardIncludes.h"
#include "cmCTest.h" #include "cmCTest.h"
#include "cmSystemTools.h"
#include <stdlib.h>
cmCTestMultiProcessHandler::cmCTestMultiProcessHandler() cmCTestMultiProcessHandler::cmCTestMultiProcessHandler()
{ {
@ -39,6 +40,7 @@ cmCTestMultiProcessHandler::SetTests(TestMap& tests,
this->Tests = tests; this->Tests = tests;
this->Properties = properties; this->Properties = properties;
} }
// Set the max number of tests that can be run at the same time. // Set the max number of tests that can be run at the same time.
void cmCTestMultiProcessHandler::SetParallelLevel(size_t level) void cmCTestMultiProcessHandler::SetParallelLevel(size_t level)
{ {
@ -47,6 +49,7 @@ void cmCTestMultiProcessHandler::SetParallelLevel(size_t level)
void cmCTestMultiProcessHandler::RunTests() void cmCTestMultiProcessHandler::RunTests()
{ {
this->CheckResume();
this->StartNextTests(); this->StartNextTests();
while(this->Tests.size() != 0) while(this->Tests.size() != 0)
{ {
@ -57,6 +60,7 @@ void cmCTestMultiProcessHandler::RunTests()
while(this->CheckOutput()) while(this->CheckOutput())
{ {
} }
this->MarkFinished();
} }
void cmCTestMultiProcessHandler::StartTestProcess(int test) void cmCTestMultiProcessHandler::StartTestProcess(int test)
@ -176,7 +180,6 @@ bool cmCTestMultiProcessHandler::CheckOutput()
finished.push_back(p); finished.push_back(p);
} }
} }
for( std::vector<cmCTestRunTest*>::iterator i = finished.begin(); for( std::vector<cmCTestRunTest*>::iterator i = finished.begin();
i != finished.end(); ++i) i != finished.end(); ++i)
{ {
@ -199,12 +202,30 @@ bool cmCTestMultiProcessHandler::CheckOutput()
this->TestFinishMap[test] = true; this->TestFinishMap[test] = true;
this->TestRunningMap[test] = false; this->TestRunningMap[test] = false;
this->RunningTests.erase(p); this->RunningTests.erase(p);
this->WriteCheckpoint(test);
delete p; delete p;
} }
return true; return true;
} }
void cmCTestMultiProcessHandler::WriteCheckpoint(int index)
{
std::string fname = this->CTest->GetBinaryDir()
+ "/Testing/Temporary/CTestCheckpoint.txt";
std::fstream fout;
fout.open(fname.c_str(), std::ios::app);
fout << index << "\n";
fout.close();
}
void cmCTestMultiProcessHandler::MarkFinished()
{
std::string fname = this->CTest->GetBinaryDir()
+ "/Testing/Temporary/CTestCheckpoint.txt";
cmSystemTools::RemoveFile(fname.c_str());
}
void cmCTestMultiProcessHandler::PrintTests() void cmCTestMultiProcessHandler::PrintTests()
{ {
#undef cout #undef cout
@ -221,6 +242,48 @@ void cmCTestMultiProcessHandler::PrintTests()
} }
} }
//----------------------------------------------------------------
void cmCTestMultiProcessHandler::CheckResume()
{
std::string fname = this->CTest->GetBinaryDir()
+ "/Testing/Temporary/CTestCheckpoint.txt";
if(this->CTest->GetFailover())
{
if(cmSystemTools::FileExists(fname.c_str(), true))
{
*this->TestHandler->LogFile << "Resuming previously interrupted test set"
<< std::endl
<< "----------------------------------------------------------"
<< std::endl;
std::ifstream fin;
fin.open(fname.c_str());
std::string line;
while(std::getline(fin, line))
{
int index = atoi(line.c_str());
this->RemoveTest(index);
}
fin.close();
}
}
else
{
if(cmSystemTools::FileExists(fname.c_str(), true))
{
cmSystemTools::RemoveFile(fname.c_str());
}
}
}
void cmCTestMultiProcessHandler::RemoveTest(int index)
{
this->Tests.erase(index);
this->Properties.erase(index);
this->TestRunningMap[index] = false;
this->TestFinishMap[index] = true;
}
#if 0 #if 0
int main() int main()
{ {

View File

@ -68,10 +68,16 @@ protected:
void StartNextTests(); void StartNextTests();
void StartTestProcess(int test); void StartTestProcess(int test);
bool StartTest(int test); bool StartTest(int test);
//void EndTest(cmProcess*); // Mark the checkpoint for the given test
void WriteCheckpoint(int index);
// Removes the checkpoint file
void MarkFinished();
// Return true if there are still tests running // Return true if there are still tests running
// check all running processes for output and exit case // check all running processes for output and exit case
bool CheckOutput(); bool CheckOutput();
void RemoveTest(int index);
//Check if we need to resume an interrupted test set
void CheckResume();
// map from test number to set of depend tests // map from test number to set of depend tests
TestMap Tests; TestMap Tests;
//list of test properties (indices concurrent to the test map) //list of test properties (indices concurrent to the test map)
@ -79,8 +85,6 @@ protected:
std::map<int, bool> TestRunningMap; std::map<int, bool> TestRunningMap;
std::map<int, bool> TestFinishMap; std::map<int, bool> TestFinishMap;
std::map<int, cmStdString> TestOutput; std::map<int, cmStdString> TestOutput;
//std::string CTestCommand;
//std::string CTestCacheFile;
std::vector<cmStdString>* Passed; std::vector<cmStdString>* Passed;
std::vector<cmStdString>* Failed; std::vector<cmStdString>* Failed;
std::vector<cmCTestTestHandler::cmCTestTestResult>* TestResults; std::vector<cmCTestTestHandler::cmCTestTestResult>* TestResults;

View File

@ -67,120 +67,118 @@ bool cmCTestRunTest::EndTest()
bool passed = true; bool passed = true;
int res = this->TestProcess->GetProcessStatus(); int res = this->TestProcess->GetProcessStatus();
int retVal = this->TestProcess->GetExitValue(); int retVal = this->TestProcess->GetExitValue();
if ( !this->CTest->GetShowOnly() )
std::vector<std::pair<cmsys::RegularExpression,
std::string> >::iterator passIt;
bool forceFail = false;
if ( this->TestProperties->RequiredRegularExpressions.size() > 0 )
{ {
std::vector<std::pair<cmsys::RegularExpression, bool found = false;
std::string> >::iterator passIt; for ( passIt = this->TestProperties->RequiredRegularExpressions.begin();
bool forceFail = false; passIt != this->TestProperties->RequiredRegularExpressions.end();
if ( this->TestProperties->RequiredRegularExpressions.size() > 0 ) ++ passIt )
{ {
bool found = false; if ( passIt->first.find(this->ProcessOutput.c_str()) )
for ( passIt = this->TestProperties->RequiredRegularExpressions.begin();
passIt != this->TestProperties->RequiredRegularExpressions.end();
++ passIt )
{ {
if ( passIt->first.find(this->ProcessOutput.c_str()) ) found = true;
{ reason = "Required regular expression found.";
found = true;
reason = "Required regular expression found.";
}
} }
if ( !found ) }
{ if ( !found )
reason = "Required regular expression not found."; {
reason = "Required regular expression not found.";
forceFail = true;
}
reason += "Regex=[";
for ( passIt = this->TestProperties->RequiredRegularExpressions.begin();
passIt != this->TestProperties->RequiredRegularExpressions.end();
++ passIt )
{
reason += passIt->second;
reason += "\n";
}
reason += "]";
}
if ( this->TestProperties->ErrorRegularExpressions.size() > 0 )
{
for ( passIt = this->TestProperties->ErrorRegularExpressions.begin();
passIt != this->TestProperties->ErrorRegularExpressions.end();
++ passIt )
{
if ( passIt->first.find(this->ProcessOutput.c_str()) )
{
reason = "Error regular expression found in output.";
reason += " Regex=[";
reason += passIt->second;
reason += "]";
forceFail = true; forceFail = true;
} }
reason += "Regex=[";
for ( passIt = this->TestProperties->RequiredRegularExpressions.begin();
passIt != this->TestProperties->RequiredRegularExpressions.end();
++ passIt )
{
reason += passIt->second;
reason += "\n";
}
reason += "]";
} }
if ( this->TestProperties->ErrorRegularExpressions.size() > 0 ) }
if (res == cmsysProcess_State_Exited)
{
bool success =
!forceFail && (retVal == 0 ||
this->TestProperties->RequiredRegularExpressions.size());
if((success && !this->TestProperties->WillFail)
|| (!success && this->TestProperties->WillFail))
{ {
for ( passIt = this->TestProperties->ErrorRegularExpressions.begin(); this->TestResult.Status = cmCTestTestHandler::COMPLETED;
passIt != this->TestProperties->ErrorRegularExpressions.end(); cmCTestLog(this->CTest, HANDLER_OUTPUT, " Passed " );
++ passIt )
{
if ( passIt->first.find(this->ProcessOutput.c_str()) )
{
reason = "Error regular expression found in output.";
reason += " Regex=[";
reason += passIt->second;
reason += "]";
forceFail = true;
}
}
} }
if (res == cmsysProcess_State_Exited) else
{ {
bool success = this->TestResult.Status = cmCTestTestHandler::FAILED;
!forceFail && (retVal == 0 || cmCTestLog(this->CTest, HANDLER_OUTPUT, "***Failed " << reason );
this->TestProperties->RequiredRegularExpressions.size());
if((success && !this->TestProperties->WillFail)
|| (!success && this->TestProperties->WillFail))
{
this->TestResult.Status = cmCTestTestHandler::COMPLETED;
cmCTestLog(this->CTest, HANDLER_OUTPUT, " Passed " );
}
else
{
this->TestResult.Status = cmCTestTestHandler::FAILED;
cmCTestLog(this->CTest, HANDLER_OUTPUT, "***Failed " << reason );
}
} }
else if ( res == cmsysProcess_State_Expired ) }
else if ( res == cmsysProcess_State_Expired )
{
cmCTestLog(this->CTest, HANDLER_OUTPUT, "***Timeout");
this->TestResult.Status = cmCTestTestHandler::TIMEOUT;
}
else if ( res == cmsysProcess_State_Exception )
{
cmCTestLog(this->CTest, HANDLER_OUTPUT, "***Exception: ");
switch ( retVal )
{ {
cmCTestLog(this->CTest, HANDLER_OUTPUT, "***Timeout"); case cmsysProcess_Exception_Fault:
this->TestResult.Status = cmCTestTestHandler::TIMEOUT; cmCTestLog(this->CTest, HANDLER_OUTPUT, "SegFault");
} this->TestResult.Status = cmCTestTestHandler::SEGFAULT;
else if ( res == cmsysProcess_State_Exception ) break;
{ case cmsysProcess_Exception_Illegal:
cmCTestLog(this->CTest, HANDLER_OUTPUT, "***Exception: "); cmCTestLog(this->CTest, HANDLER_OUTPUT, "Illegal");
switch ( retVal ) this->TestResult.Status = cmCTestTestHandler::ILLEGAL;
{ break;
case cmsysProcess_Exception_Fault: case cmsysProcess_Exception_Interrupt:
cmCTestLog(this->CTest, HANDLER_OUTPUT, "SegFault"); cmCTestLog(this->CTest, HANDLER_OUTPUT, "Interrupt");
this->TestResult.Status = cmCTestTestHandler::SEGFAULT; this->TestResult.Status = cmCTestTestHandler::INTERRUPT;
break; break;
case cmsysProcess_Exception_Illegal: case cmsysProcess_Exception_Numerical:
cmCTestLog(this->CTest, HANDLER_OUTPUT, "Illegal"); cmCTestLog(this->CTest, HANDLER_OUTPUT, "Numerical");
this->TestResult.Status = cmCTestTestHandler::ILLEGAL; this->TestResult.Status = cmCTestTestHandler::NUMERICAL;
break; break;
case cmsysProcess_Exception_Interrupt: default:
cmCTestLog(this->CTest, HANDLER_OUTPUT, "Interrupt"); cmCTestLog(this->CTest, HANDLER_OUTPUT, "Other");
this->TestResult.Status = cmCTestTestHandler::INTERRUPT; this->TestResult.Status = cmCTestTestHandler::OTHER_FAULT;
break;
case cmsysProcess_Exception_Numerical:
cmCTestLog(this->CTest, HANDLER_OUTPUT, "Numerical");
this->TestResult.Status = cmCTestTestHandler::NUMERICAL;
break;
default:
cmCTestLog(this->CTest, HANDLER_OUTPUT, "Other");
this->TestResult.Status = cmCTestTestHandler::OTHER_FAULT;
}
}
else // if ( res == cmsysProcess_State_Error )
{
cmCTestLog(this->CTest, HANDLER_OUTPUT, "***Bad command " << res );
this->TestResult.Status = cmCTestTestHandler::BAD_COMMAND;
} }
}
else // if ( res == cmsysProcess_State_Error )
{
cmCTestLog(this->CTest, HANDLER_OUTPUT, "***Bad command " << res );
this->TestResult.Status = cmCTestTestHandler::BAD_COMMAND;
}
passed = this->TestResult.Status == cmCTestTestHandler::COMPLETED; passed = this->TestResult.Status == cmCTestTestHandler::COMPLETED;
char buf[1024]; char buf[1024];
sprintf(buf, "%6.2f sec", this->TestProcess->GetTotalTime()); sprintf(buf, "%6.2f sec", this->TestProcess->GetTotalTime());
cmCTestLog(this->CTest, HANDLER_OUTPUT, buf << "\n" ); cmCTestLog(this->CTest, HANDLER_OUTPUT, buf << "\n" );
if ( this->TestHandler->LogFile ) if ( this->TestHandler->LogFile )
{ {
*this->TestHandler->LogFile << "Test time = " << buf << std::endl; *this->TestHandler->LogFile << "Test time = " << buf << std::endl;
} }
this->DartProcessing(); this->DartProcessing();
}
// if this is doing MemCheck then all the output needs to be put into // if this is doing MemCheck then all the output needs to be put into
// Output since that is what is parsed by cmCTestMemCheckHandler // Output since that is what is parsed by cmCTestMemCheckHandler
if(!this->TestHandler->MemCheck) if(!this->TestHandler->MemCheck)
@ -303,14 +301,10 @@ bool cmCTestRunTest::StartTest()
this->StartTime = this->CTest->CurrentTime(); this->StartTime = this->CTest->CurrentTime();
if ( !this->CTest->GetShowOnly() ) return this->CreateProcess(this->ActualCommand,
{ this->TestProperties->Args,
return this->CreateProcess(this->ActualCommand, this->TestProperties->Timeout,
this->TestProperties->Args, &this->TestProperties->Environment);
this->TestProperties->Timeout,
&this->TestProperties->Environment);
}
return true;
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
@ -449,15 +443,7 @@ void cmCTestRunTest::WriteLogOutputTop()
*this->TestHandler->LogFile *this->TestHandler->LogFile
<< this->ProcessOutput.c_str() << "<end of output>" << std::endl; << this->ProcessOutput.c_str() << "<end of output>" << std::endl;
if ( this->CTest->GetShowOnly() ) cmCTestLog(this->CTest, HANDLER_OUTPUT, outname.c_str());
{
cmCTestLog(this->CTest, HANDLER_OUTPUT, outname.c_str() << std::endl);
}
else
{
cmCTestLog(this->CTest, HANDLER_OUTPUT, outname.c_str());
}
cmCTestLog(this->CTest, DEBUG, "Testing " cmCTestLog(this->CTest, DEBUG, "Testing "
<< this->TestProperties->Name.c_str() << " ... "); << this->TestProperties->Name.c_str() << " ... ");
} }

View File

@ -63,7 +63,8 @@ protected:
std::vector<std::string> args, std::vector<std::string> args,
double testTimeOut, double testTimeOut,
std::vector<std::string>* environment); std::vector<std::string>* environment);
private: void WriteLogOutputTop();
cmCTestTestHandler::cmCTestTestProperties * TestProperties; cmCTestTestHandler::cmCTestTestProperties * TestProperties;
//Pointer back to the "parent"; the handler that invoked this test run //Pointer back to the "parent"; the handler that invoked this test run
cmCTestTestHandler * TestHandler; cmCTestTestHandler * TestHandler;
@ -85,7 +86,6 @@ private:
std::string StartTime; std::string StartTime;
std::string TestCommand; std::string TestCommand;
std::string ActualCommand; std::string ActualCommand;
void WriteLogOutputTop();
}; };
#endif #endif

View File

@ -523,14 +523,11 @@ int cmCTestTestHandler::ProcessHandler()
} }
this->TestResults.clear(); this->TestResults.clear();
// do not output startup if this is a sub-process for parallel tests
if(!this->CTest->GetParallelSubprocess()) cmCTestLog(this->CTest, HANDLER_OUTPUT,
{ (this->MemCheck ? "Memory check" : "Test")
cmCTestLog(this->CTest, HANDLER_OUTPUT, << " project " << cmSystemTools::GetCurrentWorkingDirectory()
(this->MemCheck ? "Memory check" : "Test") << std::endl);
<< " project " << cmSystemTools::GetCurrentWorkingDirectory()
<< std::endl);
}
if ( ! this->PreProcessHandler() ) if ( ! this->PreProcessHandler() )
{ {
return -1; return -1;
@ -583,57 +580,49 @@ int cmCTestTestHandler::ProcessHandler()
percent = 99; percent = 99;
} }
if(!this->CTest->GetParallelSubprocess()) cmCTestLog(this->CTest, HANDLER_OUTPUT, std::endl
<< static_cast<int>(percent + .5) << "% tests passed, "
<< failed.size() << " tests failed out of "
<< total << std::endl);
double totalTestTime = 0;
for(cmCTestTestHandler::TestResultsVector::size_type cc = 0;
cc < this->TestResults.size(); cc ++ )
{ {
cmCTestLog(this->CTest, HANDLER_OUTPUT, std::endl cmCTestTestResult *result = &this->TestResults[cc];
<< static_cast<int>(percent + .5) << "% tests passed, " totalTestTime += result->ExecutionTime;
<< failed.size() << " tests failed out of "
<< total << std::endl);
double totalTestTime = 0;
for(cmCTestTestHandler::TestResultsVector::size_type cc = 0;
cc < this->TestResults.size(); cc ++ )
{
cmCTestTestResult *result = &this->TestResults[cc];
totalTestTime += result->ExecutionTime;
}
char realBuf[1024];
sprintf(realBuf, "%6.2f sec", (double)(clock_finish - clock_start));
cmCTestLog(this->CTest, HANDLER_OUTPUT, "\nTotal Test time (real) = "
<< realBuf << "\n" );
char totalBuf[1024];
sprintf(totalBuf, "%6.2f sec", totalTestTime);
cmCTestLog(this->CTest, HANDLER_OUTPUT, "\nTotal Test time (parallel) = "
<< totalBuf << "\n" );
} }
char realBuf[1024];
sprintf(realBuf, "%6.2f sec", (double)(clock_finish - clock_start));
cmCTestLog(this->CTest, HANDLER_OUTPUT, "\nTotal Test time (real) = "
<< realBuf << "\n" );
char totalBuf[1024];
sprintf(totalBuf, "%6.2f sec", totalTestTime);
cmCTestLog(this->CTest, HANDLER_OUTPUT, "\nTotal Test time (parallel) = "
<< totalBuf << "\n" );
if (failed.size()) if (failed.size())
{ {
cmGeneratedFileStream ofs; cmGeneratedFileStream ofs;
if(!this->CTest->GetParallelSubprocess()) cmCTestLog(this->CTest, ERROR_MESSAGE, std::endl
<< "The following tests FAILED:" << std::endl);
this->StartLogFile("TestsFailed", ofs);
std::vector<cmCTestTestHandler::cmCTestTestResult>::iterator ftit;
for(ftit = this->TestResults.begin();
ftit != this->TestResults.end(); ++ftit)
{ {
cmCTestLog(this->CTest, ERROR_MESSAGE, std::endl if ( ftit->Status != cmCTestTestHandler::COMPLETED )
<< "The following tests FAILED:" << std::endl);
this->StartLogFile("TestsFailed", ofs);
std::vector<cmCTestTestHandler::cmCTestTestResult>::iterator ftit;
for(ftit = this->TestResults.begin();
ftit != this->TestResults.end(); ++ftit)
{ {
if ( ftit->Status != cmCTestTestHandler::COMPLETED ) ofs << ftit->TestCount << ":" << ftit->Name << std::endl;
{ cmCTestLog(this->CTest, HANDLER_OUTPUT, "\t" << std::setw(3)
ofs << ftit->TestCount << ":" << ftit->Name << std::endl; << ftit->TestCount << " - "
cmCTestLog(this->CTest, HANDLER_OUTPUT, "\t" << std::setw(3) << ftit->Name.c_str() << " ("
<< ftit->TestCount << " - " << this->GetTestStatus(ftit->Status) << ")"
<< ftit->Name.c_str() << " (" << std::endl);
<< this->GetTestStatus(ftit->Status) << ")"
<< std::endl);
}
} }
} }
} }
} }
@ -808,7 +797,6 @@ void cmCTestTestHandler::ProcessOneTest(cmCTestTestProperties *it,
std::string output; std::string output;
int retVal = 0; int retVal = 0;
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, std::endl cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, std::endl
<< (this->MemCheck?"MemCheck":"Test") << (this->MemCheck?"MemCheck":"Test")
<< " command: " << testCommand << " command: " << testCommand
@ -839,9 +827,7 @@ void cmCTestTestHandler::ProcessOneTest(cmCTestTestProperties *it,
it->Timeout, &it->Environment); it->Timeout, &it->Environment);
} }
clock_finish = cmSystemTools::GetTime(); clock_finish = cmSystemTools::GetTime();
cres.ExecutionTime = (double)(clock_finish - clock_start); cres.ExecutionTime = (double)(clock_finish - clock_start);
cres.FullCommandLine = testCommand; cres.FullCommandLine = testCommand;
@ -1118,15 +1104,7 @@ void cmCTestTestHandler::CheckLabelFilter(cmCTestTestProperties& it)
void cmCTestTestHandler::ComputeTestList() void cmCTestTestHandler::ComputeTestList()
{ {
this->TestList.clear(); // clear list of test this->TestList.clear(); // clear list of test
if(this->CTest->GetParallelSubprocess()) this->GetListOfTests();
{
this->LoadTestList();
return;
}
else
{
this->GetListOfTests();
}
cmCTestTestHandler::ListOfTests::size_type tmsize = this->TestList.size(); cmCTestTestHandler::ListOfTests::size_type tmsize = this->TestList.size();
// how many tests are in based on RegExp? // how many tests are in based on RegExp?
int inREcnt = 0; int inREcnt = 0;
@ -1325,225 +1303,42 @@ bool cmCTestTestHandler::GetValue(const char* tag,
return ret; return ret;
} }
//---------------------------------------------------------------------
// This should load only one test and is used in -j N mode. void cmCTestTestHandler::PrintTestList()
// it is used by the sub-process ctest runs which should have
// only one -I N test to run.
void cmCTestTestHandler::LoadTestList()
{ {
this->TestList.clear(); int total = this->TotalNumberOfTests;
std::string fname = this->CTest->GetBinaryDir()
+ "/Testing/Temporary/PCache.txt";
std::ifstream fin(fname.c_str());
std::string line;
if(!fin)
{
cmCTestLog(this->CTest, ERROR_MESSAGE,
"Could not load PCache.txt file: "
<< fname.c_str() << std::endl);
return;
}
bool ok = true;
int numTestsToRun = 0;
ok = ok && this->GetValue("TotalNumberOfTests:",
this->TotalNumberOfTests, fin);
ok = ok && this->GetValue("NumberOfTestsToRun:", numTestsToRun, fin);
this->ExpandTestsToRunInformation(this->TotalNumberOfTests);
if(this->TestsToRun.size() != 1)
{
cmCTestLog(this->CTest, ERROR_MESSAGE,
"Error when in parallel mode only one test should be run: "
<< this->TestsToRun.size() << std::endl);
}
int testIndexToRun = this->TestsToRun[0];
this->CTest->SetParallelSubprocessId(testIndexToRun);
if(!ok)
{
return;
}
for(int i =0; i < numTestsToRun; i++)
{
cmCTestTestProperties p;
int numArgs = 0;
ok = this->GetValue("Name:", p.Name, fin);
ok = ok && this->GetValue("Directory:", p.Directory, fin);
ok = ok && this->GetValue("Args:", numArgs, fin);
for(int j =0; j < numArgs; ++j)
{
cmSystemTools::GetLineFromStream(fin, line);
p.Args.push_back(line);
}
int numDep = 0;
ok = ok && this->GetValue("Depends:", numDep, fin);
for(int j =0; j < numDep; ++j)
{
cmSystemTools::GetLineFromStream(fin, line);
p.Depends.push_back(line);
}
int numErrRegex = 0;
ok = ok && this->GetValue("ErrorRegularExpressions:",
numErrRegex, fin);
for(int j =0; j < numErrRegex; j++)
{
cmSystemTools::GetLineFromStream(fin, line);
std::pair<cmsys::RegularExpression, std::string> rpair;
rpair.first.compile(line.c_str());
rpair.second = line;
p.ErrorRegularExpressions.push_back(rpair);
}
int numReqRegex = 0;
ok = ok && this->GetValue("RequiredRegularExpressions:",
numReqRegex, fin);
for(int j =0; j < numReqRegex; j++)
{
cmSystemTools::GetLineFromStream(fin, line);
std::pair<cmsys::RegularExpression, std::string> rpair;
rpair.first.compile(line.c_str());
rpair.second = line;
p.RequiredRegularExpressions.push_back(rpair);
}
int numMeasure = 0;
ok = ok && this->GetValue("Measurements:",
numMeasure, fin);
for(int j =0; j < numMeasure; j++)
{
cmStdString m;
cmStdString v;
cmSystemTools::GetLineFromStream(fin, line);
m = line;
cmSystemTools::GetLineFromStream(fin, line);
v = line;
p.Measurements[m] = v;
}
int isinre;
ok = ok && this->GetValue("IsInBasedOnREOptions:", isinre, fin);
ok = ok && this->GetValue("WillFail:", p.WillFail, fin);
ok = ok && this->GetValue("TimeOut:", p.Timeout, fin);
ok = ok && this->GetValue("Index:", p.Index, fin);
int numEnv = 0;
ok = ok && this->GetValue("Environment:",
numEnv, fin);
for(int j =0; j < numEnv; j++)
{
cmSystemTools::GetLineFromStream(fin, line);
p.Environment.push_back(line);
}
int numLabels = 0;
ok = ok && this->GetValue("Labels:",
numLabels, fin);
for(int j =0; j < numLabels; j++)
{
cmSystemTools::GetLineFromStream(fin, line);
p.Labels.push_back(line);
}
if(!ok)
{
cmCTestLog(this->CTest, ERROR_MESSAGE,
"Internal Error reading cached test information."
<< std::endl);
return;
}
if(p.Index == testIndexToRun)
{
// add the one test and stop reading
this->TestList.push_back(p);
return;
}
}
}
std::string cmCTestTestHandler::SaveTestList()
{
std::string fname = this->CTest->GetBinaryDir()
+ "/Testing/Temporary/PCache.txt";
cmGeneratedFileStream fout(fname.c_str());
if(!fout)
{
cmCTestLog(this->CTest, ERROR_MESSAGE, std::endl
<< "Could not open PCache.txt for write:"
<< fname.c_str()
<< std::endl);
}
fout << "TotalNumberOfTests:\n";
fout << this->TotalNumberOfTests << "\n";
fout << "NumberOfTestsToRun:\n";
fout << this->TestList.size() << "\n";
for (ListOfTests::iterator it = this->TestList.begin(); for (ListOfTests::iterator it = this->TestList.begin();
it != this->TestList.end(); it ++ ) it != this->TestList.end(); it ++ )
{ {
cmCTestTestProperties& p = *it; cmCTestTestProperties& p = *it;
fout << "Name:\n" cmCTestLog(this->CTest, HANDLER_OUTPUT, std::setw(3)
<< p.Name.c_str() << p.Index << "/");
<< "\nDirectory:\n" cmCTestLog(this->CTest, HANDLER_OUTPUT, std::setw(3)
<< p.Directory.c_str() << total << " ");
<< "\nArgs:\n" if (this->MemCheck)
<< p.Args.size() << "\n";
for(std::vector<std::string>::iterator i = p.Args.begin();
i != p.Args.end(); ++i)
{ {
fout << i->c_str() << "\n"; cmCTestLog(this->CTest, HANDLER_OUTPUT, "Memory Check");
} }
fout << "Depends:\n" << p.Depends.size() << "\n"; else
for(std::vector<std::string>::iterator i = p.Depends.begin();
i != p.Depends.end(); ++i)
{ {
fout << i->c_str() << "\n"; cmCTestLog(this->CTest, HANDLER_OUTPUT, "Testing");
}
std::vector<std::pair<cmsys::RegularExpression,
std::string> >::iterator regxi;
fout << "ErrorRegularExpressions:\n" <<
p.ErrorRegularExpressions.size() << "\n";
for(regxi = p.ErrorRegularExpressions.begin();
regxi != p.ErrorRegularExpressions.end(); regxi++)
{
fout << regxi->second << "\n";
}
fout << "RequiredRegularExpressions:\n" <<
p.RequiredRegularExpressions.size() << "\n";
for(regxi = p.RequiredRegularExpressions.begin();
regxi != p.RequiredRegularExpressions.end(); regxi++)
{
fout << regxi->second << "\n";
}
fout << "Measurements:\n" <<
p.Measurements.size() << "\n";
for(std::map<cmStdString, cmStdString>::const_iterator m =
p.Measurements.begin(); m != p.Measurements.end(); ++m)
{
fout << m->first << "\n";
fout << m->second << "\n";
}
fout << "IsInBasedOnREOptions:\n"
<< p.IsInBasedOnREOptions
<< "\nWillFail:\n"
<< p.WillFail
<< "\nTimeOut:\n"
<< p.Timeout
<< "\nIndex:\n"
<< p.Index << "\n";
fout << "Environment:\n" <<
p.Environment.size() << "\n";
for(std::vector<std::string>::const_iterator e =
p.Environment.begin(); e != p.Environment.end(); ++e)
{
fout << *e << "\n";
}
fout << "Labels:\n" <<
p.Labels.size() << "\n";
for(std::vector<std::string>::const_iterator e =
p.Labels.begin(); e != p.Labels.end(); ++e)
{
fout << *e << "\n";
} }
cmCTestLog(this->CTest, HANDLER_OUTPUT, " ");
cmCTestLog(this->CTest, HANDLER_OUTPUT, p.Name.c_str() << std::endl);
} }
fout.close();
return fname;
} }
//---------------------------------------------------------------------
void cmCTestTestHandler::ProcessDirectory(std::vector<cmStdString> &passed, void cmCTestTestHandler::ProcessDirectory(std::vector<cmStdString> &passed,
std::vector<cmStdString> &failed) std::vector<cmStdString> &failed)
{ {
this->ComputeTestList(); this->ComputeTestList();
if(this->CTest->GetShowOnly())
{
this->PrintTestList();
return;
}
cmCTestMultiProcessHandler parallel; cmCTestMultiProcessHandler parallel;
parallel.SetCTest(this->CTest); parallel.SetCTest(this->CTest);
parallel.SetParallelLevel(this->CTest->GetParallelLevel()); parallel.SetParallelLevel(this->CTest->GetParallelLevel());

View File

@ -141,6 +141,7 @@ protected:
void WriteTestResultHeader(std::ostream& os, cmCTestTestResult* result); void WriteTestResultHeader(std::ostream& os, cmCTestTestResult* result);
void WriteTestResultFooter(std::ostream& os, cmCTestTestResult* result); void WriteTestResultFooter(std::ostream& os, cmCTestTestResult* result);
void PrintTestList();
//! Clean test output to specified length //! Clean test output to specified length
bool CleanTestOutput(std::string& output, size_t length); bool CleanTestOutput(std::string& output, size_t length);
@ -203,10 +204,6 @@ private:
// based on union regex and -I stuff // based on union regex and -I stuff
void ComputeTestList(); void ComputeTestList();
// Save the state of the test list and return the file
// name it was saved to
std::string SaveTestList();
void LoadTestList();
bool GetValue(const char* tag, bool GetValue(const char* tag,
std::string& value, std::string& value,
std::ifstream& fin); std::ifstream& fin);

View File

@ -208,9 +208,9 @@ std::string cmCTest::DecodeURL(const std::string& in)
//---------------------------------------------------------------------- //----------------------------------------------------------------------
cmCTest::cmCTest() cmCTest::cmCTest()
{ {
this->ParallelSubprocess = false;
this->ParallelLevel = 1; this->ParallelLevel = 1;
this->SubmitIndex = 0; this->SubmitIndex = 0;
this->Failover = false;
this->ForceNewCTestProcess = false; this->ForceNewCTestProcess = false;
this->TomorrowTag = false; this->TomorrowTag = false;
this->Verbose = false; this->Verbose = false;
@ -795,11 +795,7 @@ int cmCTest::ProcessTests()
bool notest = true; bool notest = true;
int update_count = 0; int update_count = 0;
// do not output startup if this is a sub-process for parallel tests cmCTestLog(this, OUTPUT, "Start processing tests" << std::endl);
if(!this->GetParallelSubprocess())
{
cmCTestLog(this, OUTPUT, "Start processing tests" << std::endl);
}
for(Part p = PartStart; notest && p != PartCount; p = Part(p+1)) for(Part p = PartStart; notest && p != PartCount; p = Part(p+1))
{ {
@ -908,11 +904,8 @@ int cmCTest::ProcessTests()
} }
if ( res != 0 ) if ( res != 0 )
{ {
if(!this->GetParallelSubprocess()) cmCTestLog(this, ERROR_MESSAGE, "Errors while running CTest"
{
cmCTestLog(this, ERROR_MESSAGE, "Errors while running CTest"
<< std::endl); << std::endl);
}
} }
return res; return res;
} }
@ -1707,6 +1700,10 @@ void cmCTest::HandleCommandLineArguments(size_t &i,
{ {
std::string arg = args[i]; std::string arg = args[i];
if(this->CheckArgument(arg, "-F"))
{
this->Failover = true;
}
if(this->CheckArgument(arg, "-j", "--parallel") && i < args.size() - 1) if(this->CheckArgument(arg, "-j", "--parallel") && i < args.size() - 1)
{ {
i++; i++;
@ -1718,20 +1715,6 @@ void cmCTest::HandleCommandLineArguments(size_t &i,
int plevel = atoi(arg.substr(2).c_str()); int plevel = atoi(arg.substr(2).c_str());
this->SetParallelLevel(plevel); this->SetParallelLevel(plevel);
} }
if(this->CheckArgument(arg, "--internal-ctest-parallel")
&& i < args.size() - 1)
{
i++;
int pid = atoi(args[i].c_str());
this->SetParallelSubprocessId(pid);
this->SetParallelSubprocess();
}
if(this->CheckArgument(arg, "--parallel-cache") && i < args.size() - 1)
{
i++;
this->SetParallelCacheFile(args[i].c_str());
}
if(this->CheckArgument(arg, "-C", "--build-config") && if(this->CheckArgument(arg, "-C", "--build-config") &&
i < args.size() - 1) i < args.size() - 1)

View File

@ -138,15 +138,6 @@ public:
int GetParallelLevel() { return this->ParallelLevel; } int GetParallelLevel() { return this->ParallelLevel; }
void SetParallelLevel(int); void SetParallelLevel(int);
bool GetParallelSubprocess() { return this->ParallelSubprocess; }
void SetParallelSubprocess() { this->ParallelSubprocess = true; }
void SetParallelSubprocessId(int id) { this->ParallelSubprocessId = id;}
int GetParallelSubprocessId() { return this->ParallelSubprocessId;}
const char* GetParallelCacheFile()
{ return this->ParallelCacheFile.c_str();}
void SetParallelCacheFile(const char* c) { this->ParallelCacheFile = c; }
/** /**
* Check if CTest file exists * Check if CTest file exists
*/ */
@ -371,6 +362,9 @@ public:
void SetSpecificTrack(const char* track); void SetSpecificTrack(const char* track);
const char* GetSpecificTrack(); const char* GetSpecificTrack();
void SetFailover(bool failover) { this->Failover = failover; }
bool GetFailover() { return this->Failover; }
bool GetVerbose() { return this->Verbose;} bool GetVerbose() { return this->Verbose;}
bool GetExtraVerbose() { return this->ExtraVerbose;} bool GetExtraVerbose() { return this->ExtraVerbose;}
@ -384,6 +378,8 @@ private:
bool ExtraVerbose; bool ExtraVerbose;
bool ProduceXML; bool ProduceXML;
bool Failover;
bool ForceNewCTestProcess; bool ForceNewCTestProcess;
bool RunConfigurationScript; bool RunConfigurationScript;
@ -420,10 +416,8 @@ private:
int MaxTestNameWidth; int MaxTestNameWidth;
std::string ParallelCacheFile;
int ParallelLevel; int ParallelLevel;
int ParallelSubprocessId;
bool ParallelSubprocess;
int CompatibilityMode; int CompatibilityMode;
// information for the --build-and-test options // information for the --build-and-test options

View File

@ -70,6 +70,9 @@ static const char * cmDocumentationOptions[][3] =
{"--output-on-failure", "Output anything outputted by the test program " {"--output-on-failure", "Output anything outputted by the test program "
"if the test should fail. This option can also be enabled by setting " "if the test should fail. This option can also be enabled by setting "
"the environment variable CTEST_OUTPUT_ON_FAILURE"}, "the environment variable CTEST_OUTPUT_ON_FAILURE"},
{"-F", "Enable failover.", "This option allows ctest to resume a test "
"set execution that was previously interrupted. If no interruption "
"occurred, the -F option will have no effect."},
{"-Q,--quiet", "Make ctest quiet.", {"-Q,--quiet", "Make ctest quiet.",
"This option will suppress all the output. The output log file will " "This option will suppress all the output. The output log file will "
"still be generated if the --output-log is specified. Options such " "still be generated if the --output-log is specified. Options such "