ENH: refactored ctest. All testing is now parallel. If no -j option is specified, defaults to a MP level of 1 (non parallel)

This commit is contained in:
Zach Mullen 2009-08-26 12:09:06 -04:00
parent 1171bcfc69
commit 8ffd8d0a03
8 changed files with 347 additions and 524 deletions

View File

@ -23,12 +23,11 @@
cmCTestMultiProcessHandler::cmCTestMultiProcessHandler() cmCTestMultiProcessHandler::cmCTestMultiProcessHandler()
{ {
this->ParallelLevel = 1; this->ParallelLevel = 1;
this->ProcessId = 0;
} }
// Set the tests // Set the tests
void void
cmCTestMultiProcessHandler::SetTests(TestMap& tests, cmCTestMultiProcessHandler::SetTests(TestMap& tests,
std::map<int,cmStdString>& testNames) PropertiesMap& properties)
{ {
// set test run map to false for all // set test run map to false for all
for(TestMap::iterator i = this->Tests.begin(); for(TestMap::iterator i = this->Tests.begin();
@ -38,15 +37,14 @@ cmCTestMultiProcessHandler::SetTests(TestMap& tests,
this->TestFinishMap[i->first] = false; this->TestFinishMap[i->first] = false;
} }
this->Tests = tests; this->Tests = tests;
this->TestNames = testNames; 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 l) void cmCTestMultiProcessHandler::SetParallelLevel(size_t level)
{ {
this->ParallelLevel = l; this->ParallelLevel = level < 1 ? 1 : level;
} }
void cmCTestMultiProcessHandler::RunTests() void cmCTestMultiProcessHandler::RunTests()
{ {
this->StartNextTests(); this->StartNextTests();
@ -59,65 +57,28 @@ void cmCTestMultiProcessHandler::RunTests()
while(this->CheckOutput()) while(this->CheckOutput())
{ {
} }
for(std::map<int, cmStdString>::iterator i =
this->TestOutput.begin();
i != this->TestOutput.end(); ++i)
{
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
i->second << std::endl);
}
} }
void cmCTestMultiProcessHandler::StartTestProcess(int test) void cmCTestMultiProcessHandler::StartTestProcess(int test)
{ {
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, test << ": "
" test " << test << "\n"); << " test " << test << "\n");
this->TestRunningMap[test] = true; // mark the test as running this->TestRunningMap[test] = true; // mark the test as running
// now remove the test itself // now remove the test itself
this->Tests.erase(test); this->Tests.erase(test);
// now run the test cmCTestRunTest* testRun = new cmCTestRunTest;
cmProcess* newp = new cmProcess; testRun->SetCTest(this->CTest);
newp->SetId(this->ProcessId); testRun->SetTestHandler(this->TestHandler);
newp->SetId(test); testRun->SetIndex(test);
newp->SetCommand(this->CTestCommand.c_str()); testRun->SetTestProperties(this->Properties[test]);
std::vector<std::string> args; if(testRun->StartTest())
cmOStringStream width;
if(this->CTest->GetMaxTestNameWidth())
{ {
args.push_back("-W"); this->RunningTests.insert(testRun);
width << this->CTest->GetMaxTestNameWidth();
args.push_back(width.str().c_str());
}
args.push_back("-I");
cmOStringStream strm;
strm << test << "," << test;
args.push_back(strm.str());
args.push_back("--parallel-cache");
args.push_back(this->CTestCacheFile.c_str());
args.push_back("--internal-ctest-parallel");
cmOStringStream strm2;
strm2 << test;
args.push_back(strm2.str());
if(this->CTest->GetExtraVerbose())
{
args.push_back("-VV");
}
newp->SetCommandArguments(args);
if(!newp->StartProcess())
{
cmCTestLog(this->CTest, ERROR_MESSAGE,
"Error starting " << newp->GetCommand() << "\n");
this->EndTest(newp);
} }
else else
{ {
this->RunningTests.insert(newp); testRun->EndTest();
} }
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"ctest -I " << test << "\n");
this->ProcessId++;
} }
bool cmCTestMultiProcessHandler::StartTest(int test) bool cmCTestMultiProcessHandler::StartTest(int test)
@ -195,7 +156,6 @@ void cmCTestMultiProcessHandler::StartNextTests()
} }
} }
bool cmCTestMultiProcessHandler::CheckOutput() bool cmCTestMultiProcessHandler::CheckOutput()
{ {
// no more output we are done // no more output we are done
@ -203,82 +163,48 @@ bool cmCTestMultiProcessHandler::CheckOutput()
{ {
return false; return false;
} }
std::vector<cmProcess*> finished; std::vector<cmCTestRunTest*> finished;
std::string out, err; std::string out, err;
for(std::set<cmProcess*>::const_iterator i = this->RunningTests.begin(); for(std::set<cmCTestRunTest*>::const_iterator i = this->RunningTests.begin();
i != this->RunningTests.end(); ++i) i != this->RunningTests.end(); ++i)
{ {
cmProcess* p = *i; cmCTestRunTest* p = *i;
int pipe = p->CheckOutput(.1, out, err); p->CheckOutput(); //reads and stores the process output
if(pipe == cmsysProcess_Pipe_STDOUT)
{
cmCTestLog(this->CTest, HANDLER_OUTPUT,
p->GetId() << ": " << out << std::endl);
this->TestOutput[ p->GetId() ] += out;
this->TestOutput[ p->GetId() ] += "\n";
}
else if(pipe == cmsysProcess_Pipe_STDERR)
{
cmCTestLog(this->CTest, HANDLER_OUTPUT,
p->GetId() << ": " << err << std::endl);
this->TestOutput[ p->GetId() ] += err;
this->TestOutput[ p->GetId() ] += "\n";
}
if(!p->IsRunning()) if(!p->IsRunning())
{ {
finished.push_back(p); finished.push_back(p);
} }
} }
for( std::vector<cmProcess*>::iterator i = finished.begin();
for( std::vector<cmCTestRunTest*>::iterator i = finished.begin();
i != finished.end(); ++i) i != finished.end(); ++i)
{ {
cmProcess* p = *i; cmCTestRunTest* p = *i;
this->EndTest(p); int test = p->GetIndex();
if(p->EndTest())
{
this->Passed->push_back(p->GetTestProperties()->Name);
}
else
{
this->Failed->push_back(p->GetTestProperties()->Name);
}
for(TestMap::iterator j = this->Tests.begin();
j!= this->Tests.end(); ++j)
{
j->second.erase(test);
}
this->TestFinishMap[test] = true;
this->TestRunningMap[test] = false;
this->RunningTests.erase(p);
delete p;
} }
return true; return true;
} }
void cmCTestMultiProcessHandler::EndTest(cmProcess* p)
{
// Should have a way of getting this stuff from the
// launched ctest, maybe a temp file or some extra xml
// stuff in the stdout
// Need things like Reason and ExecutionTime, Path, etc.
int test = p->GetId();
int exitVal = p->GetExitValue();
cmCTestTestHandler::cmCTestTestResult cres;
cres.Properties = 0;
cres.ExecutionTime = p->GetTotalTime();
cres.ReturnValue = exitVal;
cres.Status = cmCTestTestHandler::COMPLETED;
cres.TestCount = test;
cres.Name = this->TestNames[test];
cres.Path = "";
if(exitVal)
{
cres.Status = cmCTestTestHandler::FAILED;
this->Failed->push_back(this->TestNames[test]);
}
else
{
this->Passed->push_back(this->TestNames[test]);
}
this->TestResults->push_back(cres);
// remove test from depend of all other tests
for(TestMap::iterator i = this->Tests.begin();
i!= this->Tests.end(); ++i)
{
i->second.erase(test);
}
this->TestFinishMap[test] = true;
this->TestRunningMap[test] = false;
this->RunningTests.erase(p);
delete p;
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"finish test " << test << "\n");
}
void cmCTestMultiProcessHandler::PrintTests() void cmCTestMultiProcessHandler::PrintTests()
{ {
#undef cout #undef cout

View File

@ -17,9 +17,9 @@
#ifndef cmCTestMultiProcessHandler_h #ifndef cmCTestMultiProcessHandler_h
#define cmCTestMultiProcessHandler_h #define cmCTestMultiProcessHandler_h
class cmProcess;
#include <cmStandardIncludes.h> #include <cmStandardIncludes.h>
#include <cmCTestTestHandler.h> #include <cmCTestTestHandler.h>
#include <cmCTestRunTest.h>
/** \class cmCTestMultiProcessHandler /** \class cmCTestMultiProcessHandler
* \brief run parallel ctest * \brief run parallel ctest
@ -31,16 +31,18 @@ class cmCTestMultiProcessHandler
public: public:
struct TestSet : public std::set<int> {}; struct TestSet : public std::set<int> {};
struct TestMap : public std::map<int, TestSet> {}; struct TestMap : public std::map<int, TestSet> {};
struct PropertiesMap : public
std::map<int, cmCTestTestHandler::cmCTestTestProperties*> {};
cmCTestMultiProcessHandler(); cmCTestMultiProcessHandler();
// Set the tests // Set the tests
void SetTests(TestMap& tests, void SetTests(TestMap& tests, PropertiesMap& properties);
std::map<int, cmStdString>& testNames);
// 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 SetParallelLevel(size_t); void SetParallelLevel(size_t);
void RunTests(); void RunTests();
void PrintTests(); void PrintTests();
void SetCTestCommand(const char* c) { this->CTestCommand = c;} //void SetCTestCommand(const char* c) { this->CTestCommand = c;}
void SetTestCacheFile(const char* c) { this->CTestCacheFile = c;} //void SetTestCacheFile(const char* c) { this->CTestCacheFile = c;}
void SetPassFailVectors(std::vector<cmStdString>* passed, void SetPassFailVectors(std::vector<cmStdString>* passed,
std::vector<cmStdString>* failed) std::vector<cmStdString>* failed)
{ {
@ -51,7 +53,14 @@ public:
{ {
this->TestResults = r; this->TestResults = r;
} }
void SetCTest(cmCTest* ctest) { this->CTest = ctest;} void SetCTest(cmCTest* ctest) { this->CTest = ctest;}
void SetTestHandler(cmCTestTestHandler * handler)
{ this->TestHandler = handler; }
cmCTestTestHandler * GetTestHandler()
{ return this->TestHandler; }
protected: protected:
cmCTest* CTest; cmCTest* CTest;
// Start the next test or tests as many as are allowed by // Start the next test or tests as many as are allowed by
@ -59,24 +68,25 @@ protected:
void StartNextTests(); void StartNextTests();
void StartTestProcess(int test); void StartTestProcess(int test);
bool StartTest(int test); bool StartTest(int test);
void EndTest(cmProcess*); //void EndTest(cmProcess*);
// 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();
// map from test number to set of depend tests // map from test number to set of depend tests
TestMap Tests; TestMap Tests;
std::map<int, cmStdString> TestNames; //list of test properties (indices concurrent to the test map)
PropertiesMap Properties;
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 CTestCommand;
std::string CTestCacheFile; //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;
int ProcessId;
size_t ParallelLevel; // max number of process that can be run at once size_t ParallelLevel; // max number of process that can be run at once
std::set<cmProcess*> RunningTests; // current running tests std::set<cmCTestRunTest*> RunningTests; // current running tests
cmCTestTestHandler * TestHandler;
}; };
#endif #endif

View File

@ -21,150 +21,52 @@
cmCTestRunTest::cmCTestRunTest() cmCTestRunTest::cmCTestRunTest()
{ {
this->OptimizeForCTest = true;
cmCTestTestHandler::cmCTestTestResult result;
this->TestResult = result;
} }
cmCTestRunTest::~cmCTestRunTest() cmCTestRunTest::~cmCTestRunTest()
{ {
} }
void cmCTestRunTest::SetTestHandler(cmCTestTestHandler * handler) bool cmCTestRunTest::IsRunning()
{ {
this->TestHandler = handler; return this->TestProcess->IsRunning();
this->CTest = handler->CTest;
} }
//---------------------------------------------------------------------- //---------------------------------------------------------
// Executes a test. Returns whether it passed or failed //waits .1 sec for output from this process.
bool cmCTestRunTest::Execute() void cmCTestRunTest::CheckOutput()
{ {
const std::string& testname = this->TestProperties->Name; std::string out, err;
std::vector<std::string>& args = this->TestProperties->Args; int pipe = this->TestProcess->CheckOutput(.1, out, err);
this->TestResult.Properties = this->TestProperties; if(pipe == cmsysProcess_Pipe_STDOUT)
this->TestResult.ExecutionTime = 0;
this->TestResult.ReturnValue = -1;
this->TestResult.Status = cmCTestTestHandler::NOT_RUN;
this->TestResult.TestCount = this->TestProperties->Index;
this->TestResult.Name = testname;
this->TestResult.Path = this->TestProperties->Directory.c_str();
cmCTestLog(this->CTest, HANDLER_OUTPUT, std::setw(3)
<< this->TestProperties->Index << "/");
cmCTestLog(this->CTest, HANDLER_OUTPUT, std::setw(3)
<< this->TestHandler->TotalNumberOfTests << " ");
if ( this->TestHandler->MemCheck )
{ {
cmCTestLog(this->CTest, HANDLER_OUTPUT, "Memory Check"); cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
this->GetIndex() << ": " << out << std::endl);
this->ProcessOutput += out;
this->ProcessOutput += "\n";
} }
else else if(pipe == cmsysProcess_Pipe_STDERR)
{ {
cmCTestLog(this->CTest, HANDLER_OUTPUT, "Testing"); cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
this->GetIndex() << ": " << err << std::endl);
this->ProcessOutput += err;
this->ProcessOutput += "\n";
} }
cmCTestLog(this->CTest, HANDLER_OUTPUT, " "); }
const int maxTestNameWidth = this->CTest->GetMaxTestNameWidth();
std::string outname = testname + " ";
outname.resize(maxTestNameWidth, '.');
*this->TestHandler->LogFile << this->TestProperties->Index << "/"
<< this->TestHandler->TotalNumberOfTests << " Testing: "
<< testname << std::endl;
if ( this->CTest->GetShowOnly() ) //---------------------------------------------------------
bool cmCTestRunTest::EndTest()
{
//restore the old environment
if (this->ModifyEnv)
{ {
cmCTestLog(this->CTest, HANDLER_OUTPUT, outname.c_str() << std::endl); cmSystemTools::RestoreEnv(this->OrigEnv);
} }
else this->WriteLogOutputTop();
{
cmCTestLog(this->CTest, HANDLER_OUTPUT, outname.c_str());
}
cmCTestLog(this->CTest, DEBUG, "Testing " << args[0].c_str() << " ... ");
// find the test executable
std::string actualCommand
= this->TestHandler->FindTheExecutable(args[1].c_str());
std::string testCommand
= cmSystemTools::ConvertToOutputPath(actualCommand.c_str());
// continue if we did not find the executable
if (testCommand == "")
{
*this->TestHandler->LogFile << "Unable to find executable: "
<< args[1].c_str() << std::endl;
cmCTestLog(this->CTest, ERROR_MESSAGE, "Unable to find executable: "
<< args[1].c_str() << std::endl);
this->TestResult.Output = "Unable to find executable: " + args[1];
if ( !this->CTest->GetShowOnly() )
{
this->TestResult.FullCommandLine = actualCommand;
this->TestHandler->TestResults.push_back( this->TestResult );
return false;
}
}
// add the arguments
std::vector<std::string>::const_iterator j = args.begin();
++j; // skip test name
++j; // skip command as it is in actualCommand
std::vector<const char*> arguments;
this->TestHandler->GenerateTestCommand(arguments);
arguments.push_back(actualCommand.c_str());
for(;j != args.end(); ++j)
{
testCommand += " ";
testCommand += cmSystemTools::EscapeSpaces(j->c_str());
arguments.push_back(j->c_str());
}
arguments.push_back(0);
/**
* Run an executable command and put the stdout in output.
*/
std::string output;
int retVal = 0;
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, std::endl
<< (this->TestHandler->MemCheck?"MemCheck":"Test")
<< " command: " << testCommand
<< std::endl);
*this->TestHandler->LogFile << this->TestProperties->Index << "/"
<< this->TestHandler->TotalNumberOfTests
<< " Test: " << testname.c_str() << std::endl;
*this->TestHandler->LogFile << "Command: ";
std::vector<cmStdString>::size_type ll;
for ( ll = 0; ll < arguments.size()-1; ll ++ )
{
*this->TestHandler->LogFile << "\"" << arguments[ll] << "\" ";
}
*this->TestHandler->LogFile
<< std::endl
<< "Directory: " << this->TestProperties->Directory << std::endl
<< "\"" << testname.c_str() << "\" start time: "
<< this->CTest->CurrentTime() << std::endl
<< "Output:" << std::endl
<< "----------------------------------------------------------"
<< std::endl;
int res = 0;
double clock_start, clock_finish;
clock_start = cmSystemTools::GetTime();
if ( !this->CTest->GetShowOnly() )
{
res = this->RunTestProcess(arguments, &output, &retVal,
this->TestHandler->LogFile,
this->TestProperties->Timeout,
&this->TestProperties->Environment);
this->ProcessOutput = output; //save process output in the object
}
clock_finish = cmSystemTools::GetTime();
this->TestResult.ExecutionTime = (double)(clock_finish - clock_start);
this->TestResult.FullCommandLine = testCommand;
std::string reason; std::string reason;
bool passed = true; bool passed = true;
int res = this->TestProcess->GetProcessStatus();
int retVal = this->TestProcess->GetExitValue();
if ( !this->CTest->GetShowOnly() ) if ( !this->CTest->GetShowOnly() )
{ {
std::vector<std::pair<cmsys::RegularExpression, std::vector<std::pair<cmsys::RegularExpression,
@ -177,7 +79,7 @@ bool cmCTestRunTest::Execute()
passIt != this->TestProperties->RequiredRegularExpressions.end(); passIt != this->TestProperties->RequiredRegularExpressions.end();
++ passIt ) ++ passIt )
{ {
if ( passIt->first.find(output.c_str()) ) if ( passIt->first.find(this->ProcessOutput.c_str()) )
{ {
found = true; found = true;
reason = "Required regular expression found."; reason = "Required regular expression found.";
@ -204,7 +106,7 @@ bool cmCTestRunTest::Execute()
passIt != this->TestProperties->ErrorRegularExpressions.end(); passIt != this->TestProperties->ErrorRegularExpressions.end();
++ passIt ) ++ passIt )
{ {
if ( passIt->first.find(output.c_str()) ) if ( passIt->first.find(this->ProcessOutput.c_str()) )
{ {
reason = "Error regular expression found in output."; reason = "Error regular expression found in output.";
reason += " Regex=["; reason += " Regex=[";
@ -214,7 +116,6 @@ bool cmCTestRunTest::Execute()
} }
} }
} }
if (res == cmsysProcess_State_Exited) if (res == cmsysProcess_State_Exited)
{ {
bool success = bool success =
@ -224,13 +125,12 @@ bool cmCTestRunTest::Execute()
|| (!success && this->TestProperties->WillFail)) || (!success && this->TestProperties->WillFail))
{ {
this->TestResult.Status = cmCTestTestHandler::COMPLETED; this->TestResult.Status = cmCTestTestHandler::COMPLETED;
cmCTestLog(this->CTest, HANDLER_OUTPUT, " Passed " ); cmCTestLog(this->CTest, HANDLER_OUTPUT, " Passed " );
} }
else else
{ {
this->TestResult.Status = cmCTestTestHandler::FAILED; this->TestResult.Status = cmCTestTestHandler::FAILED;
cmCTestLog(this->CTest, HANDLER_OUTPUT, cmCTestLog(this->CTest, HANDLER_OUTPUT, "***Failed " << reason );
"***Failed " << reason );
} }
} }
else if ( res == cmsysProcess_State_Expired ) else if ( res == cmsysProcess_State_Expired )
@ -273,28 +173,29 @@ bool cmCTestRunTest::Execute()
passed = this->TestResult.Status == cmCTestTestHandler::COMPLETED; passed = this->TestResult.Status == cmCTestTestHandler::COMPLETED;
char buf[1024]; char buf[1024];
sprintf(buf, "%6.2f sec", this->TestResult.ExecutionTime); 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 << "\nTest time = " << buf << std::endl; *this->TestHandler->LogFile << "Test time = " << buf << std::endl;
} }
this->DartProcessing(output); 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)
{ {
if ( this->TestResult.Status == cmCTestTestHandler::COMPLETED ) if ( this->TestResult.Status == cmCTestTestHandler::COMPLETED )
{ {
this->TestHandler->CleanTestOutput(output, static_cast<size_t> this->TestHandler->CleanTestOutput(this->ProcessOutput,
(this->TestHandler->CustomMaximumPassedTestOutputSize)); static_cast<size_t>
(this->TestHandler->CustomMaximumPassedTestOutputSize));
} }
else else
{ {
this->TestHandler->CleanTestOutput(output, static_cast<size_t> this->TestHandler->CleanTestOutput(this->ProcessOutput,
(this->TestHandler->CustomMaximumFailedTestOutputSize)); static_cast<size_t>
(this->TestHandler->CustomMaximumFailedTestOutputSize));
} }
} }
this->TestResult.Reason = reason; this->TestResult.Reason = reason;
@ -308,7 +209,7 @@ bool cmCTestRunTest::Execute()
reasonType = "Test Fail Reason"; reasonType = "Test Fail Reason";
pass = false; pass = false;
} }
double ttime = clock_finish - clock_start; double ttime = this->TestProcess->GetTotalTime();
int hours = static_cast<int>(ttime / (60 * 60)); int hours = static_cast<int>(ttime / (60 * 60));
int minutes = static_cast<int>(ttime / 60) % 60; int minutes = static_cast<int>(ttime / 60) % 60;
int seconds = static_cast<int>(ttime) % 60; int seconds = static_cast<int>(ttime) % 60;
@ -333,34 +234,99 @@ bool cmCTestRunTest::Execute()
*this->TestHandler->LogFile << "Test Failed.\n"; *this->TestHandler->LogFile << "Test Failed.\n";
} }
} }
*this->TestHandler->LogFile << "\"" << testname.c_str() << "\" end time: " *this->TestHandler->LogFile << "\"" << this->TestProperties->Name.c_str()
<< this->CTest->CurrentTime() << std::endl << "\" end time: " << this->CTest->CurrentTime() << std::endl
<< "\"" << testname.c_str() << "\" time elapsed: " << "\"" << this->TestProperties->Name.c_str() << "\" time elapsed: "
<< buffer << std::endl << buffer << std::endl
<< "----------------------------------------------------------" << "----------------------------------------------------------"
<< std::endl << std::endl; << std::endl << std::endl;
} }
this->TestResult.Output = output; this->TestResult.Output = this->ProcessOutput;
this->TestResult.ReturnValue = retVal; this->TestResult.ReturnValue = this->TestProcess->GetExitValue();
this->TestResult.CompletionStatus = "Completed"; this->TestResult.CompletionStatus = "Completed";
this->TestResult.ExecutionTime = this->TestProcess->GetTotalTime();
this->TestHandler->TestResults.push_back( this->TestResult ); this->TestHandler->TestResults.push_back( this->TestResult );
delete this->TestProcess;
return passed; return passed;
} }
//---------------------------------------------------------------------- void cmCTestRunTest::SetTestHandler(cmCTestTestHandler * handler)
void cmCTestRunTest::DartProcessing(std::string& output)
{ {
if (!output.empty() && output.find("<DartMeasurement") != output.npos) this->TestHandler = handler;
this->CTest = handler->CTest;
}
//----------------------------------------------------------------------
// Starts the execution of a test. Returns once it has started
bool cmCTestRunTest::StartTest()
{
std::vector<std::string>& args = this->TestProperties->Args;
this->TestResult.Properties = this->TestProperties;
this->TestResult.ExecutionTime = 0;
this->TestResult.ReturnValue = -1;
this->TestResult.Status = cmCTestTestHandler::NOT_RUN;
this->TestResult.TestCount = this->TestProperties->Index;
this->TestResult.Name = this->TestProperties->Name;
this->TestResult.Path = this->TestProperties->Directory.c_str();
// find the test executable
this->ActualCommand
= this->TestHandler->FindTheExecutable(args[1].c_str());
this->TestCommand
= cmSystemTools::ConvertToOutputPath(this->ActualCommand.c_str());
// continue if we did not find the executable
if (this->TestCommand == "")
{ {
if (this->TestHandler->DartStuff.find(output.c_str())) *this->TestHandler->LogFile << "Unable to find executable: "
<< args[1].c_str() << std::endl;
cmCTestLog(this->CTest, ERROR_MESSAGE, "Unable to find executable: "
<< args[1].c_str() << std::endl);
this->TestResult.Output = "Unable to find executable: " + args[1];
if ( !this->CTest->GetShowOnly() )
{
this->TestResult.FullCommandLine = this->ActualCommand;
this->TestHandler->TestResults.push_back( this->TestResult );
return false;
}
}
/**
* Run an executable command and put the stdout in output.
*/
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, std::endl
<< this->Index << ": "
<< (this->TestHandler->MemCheck?"MemCheck":"Test")
<< " command: " << this->TestCommand
<< std::endl);
this->StartTime = this->CTest->CurrentTime();
if ( !this->CTest->GetShowOnly() )
{
return this->CreateProcess(this->ActualCommand,
this->TestProperties->Args,
this->TestProperties->Timeout,
&this->TestProperties->Environment);
}
return true;
}
//----------------------------------------------------------------------
void cmCTestRunTest::DartProcessing()
{
if (!this->ProcessOutput.empty() &&
this->ProcessOutput.find("<DartMeasurement") != this->ProcessOutput.npos)
{
if (this->TestHandler->DartStuff.find(this->ProcessOutput.c_str()))
{ {
std::string dartString = this->TestHandler->DartStuff.match(1); std::string dartString = this->TestHandler->DartStuff.match(1);
// keep searching and replacing until none are left // keep searching and replacing until none are left
while (this->TestHandler->DartStuff1.find(output.c_str())) while (this->TestHandler->DartStuff1.find(this->ProcessOutput.c_str()))
{ {
// replace the exact match for the string // replace the exact match for the string
cmSystemTools::ReplaceString(output, cmSystemTools::ReplaceString(this->ProcessOutput,
this->TestHandler->DartStuff1.match(1).c_str(), ""); this->TestHandler->DartStuff1.match(1).c_str(), "");
} }
this->TestResult.RegressionImages this->TestResult.RegressionImages
@ -370,13 +336,27 @@ void cmCTestRunTest::DartProcessing(std::string& output)
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
int cmCTestRunTest::RunTestProcess(std::vector<const char*> argv, bool cmCTestRunTest::CreateProcess(std::string command,
std::string* output, int *retVal, std::vector<std::string> args,
std::ostream* log, double testTimeOut, double testTimeOut,
std::vector<std::string>* environment) std::vector<std::string>* environment)
{ {
std::vector<std::string> commandArgs;
std::vector<std::string>::iterator i = args.begin();
++i; //skip test name
++i; //skip executable name
for(; i != args.end(); ++i)
{
commandArgs.push_back(*i);
}
this->TestProcess = new cmProcess;
this->TestProcess->SetId(this->Index);
this->TestProcess->SetCommand(command.c_str());
this->TestProcess->SetCommandArguments(commandArgs);
std::vector<std::string> origEnv; std::vector<std::string> origEnv;
bool modifyEnv = (environment && environment->size()>0); this->ModifyEnv = (environment && environment->size()>0);
// determine how much time we have // determine how much time we have
double timeout = this->CTest->GetRemainingTimeAllowed() - 120; double timeout = this->CTest->GetRemainingTimeAllowed() - 120;
@ -395,154 +375,89 @@ int cmCTestRunTest::RunTestProcess(std::vector<const char*> argv,
{ {
timeout = 1; timeout = 1;
} }
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, this->Index << ": "
"Test timeout computed to be: " << timeout << "\n"); << "Test timeout computed to be: " << timeout << "\n");
if(cmSystemTools::SameFile(argv[0], this->CTest->CTestSelf.c_str()) && if (this->ModifyEnv)
!this->CTest->ForceNewCTestProcess &&
this->OptimizeForCTest)
{ {
cmCTest inst; this->OrigEnv = cmSystemTools::AppendEnv(environment);
inst.ConfigType = this->CTest->ConfigType;
inst.TimeOut = timeout;
// Capture output of the child ctest.
cmOStringStream oss;
inst.SetStreams(&oss, &oss);
std::vector<std::string> args;
for(unsigned int i =0; i < argv.size(); ++i)
{
if(argv[i])
{
// make sure we pass the timeout in for any build and test
// invocations. Since --build-generator is required this is a
// good place to check for it, and to add the arguments in
if (strcmp(argv[i],"--build-generator") == 0 && timeout)
{
args.push_back("--test-timeout");
cmOStringStream msg;
msg << timeout;
args.push_back(msg.str());
}
args.push_back(argv[i]);
}
}
if ( log )
{
*log << "* Run internal CTest" << std::endl;
}
std::string oldpath = cmSystemTools::GetCurrentWorkingDirectory();
if (modifyEnv)
{
origEnv = cmSystemTools::AppendEnv(environment);
}
*retVal = inst.Run(args, output);
*output += oss.str();
if ( log )
{
*log << output->c_str();
}
cmSystemTools::ChangeDirectory(oldpath.c_str());
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Internal cmCTest object used to run test." << std::endl
<< *output << std::endl);
if (modifyEnv)
{
cmSystemTools::RestoreEnv(origEnv);
}
return cmsysProcess_State_Exited;
}
std::vector<char> tempOutput;
if ( output )
{
*output = "";
} }
if (modifyEnv) return this->TestProcess->StartProcess();
{
origEnv = cmSystemTools::AppendEnv(environment);
}
cmsysProcess* cp = cmsysProcess_New();
cmsysProcess_SetCommand(cp, &*argv.begin());
cmCTestLog(this->CTest, DEBUG, "Command is: " << argv[0] << std::endl);
if(cmSystemTools::GetRunCommandHideConsole())
{
cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
}
cmsysProcess_SetTimeout(cp, timeout);
cmsysProcess_Execute(cp);
char* data;
int length;
while(cmsysProcess_WaitForData(cp, &data, &length, 0))
{
if ( output )
{
tempOutput.insert(tempOutput.end(), data, data+length);
}
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
cmCTestLogWrite(data, length));
if ( log )
{
log->write(data, length);
}
}
cmsysProcess_WaitForExit(cp, 0);
if(output && tempOutput.begin() != tempOutput.end())
{
//We are waiting for exit before finally appending to the output
output->append(&*tempOutput.begin(), tempOutput.size());
}
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "-- Process completed"
<< std::endl);
int result = cmsysProcess_GetState(cp);
if(result == cmsysProcess_State_Exited)
{
*retVal = cmsysProcess_GetExitValue(cp);
if(*retVal != 0 && this->CTest->OutputTestOutputOnTestFailure)
{
this->CTest->OutputTestErrors(tempOutput);
}
}
else if(result == cmsysProcess_State_Exception)
{
if(this->CTest->OutputTestOutputOnTestFailure)
{
this->CTest->OutputTestErrors(tempOutput);
}
*retVal = cmsysProcess_GetExitException(cp);
std::string outerr = "\n*** Exception executing: ";
outerr += cmsysProcess_GetExceptionString(cp);
*output += outerr;
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, outerr.c_str()
<< std::endl << std::flush);
}
else if(result == cmsysProcess_State_Error)
{
std::string outerr = "\n*** ERROR executing: ";
outerr += cmsysProcess_GetErrorString(cp);
*output += outerr;
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, outerr.c_str()
<< std::endl << std::flush);
}
cmsysProcess_Delete(cp);
if (modifyEnv)
{
cmSystemTools::RestoreEnv(origEnv);
}
return result;
} }
void cmCTestRunTest::WriteLogOutputTop()
{
/* Not sure whether we want to prepend the test index anymore
cmCTestLog(this->CTest, HANDLER_OUTPUT, std::setw(3)
<< this->Index << ": ");*/
cmCTestLog(this->CTest, HANDLER_OUTPUT, std::setw(3)
<< this->TestProperties->Index << "/");
cmCTestLog(this->CTest, HANDLER_OUTPUT, std::setw(3)
<< this->TestHandler->TotalNumberOfTests << " ");
if ( this->TestHandler->MemCheck )
{
cmCTestLog(this->CTest, HANDLER_OUTPUT, "Memory Check");
}
else
{
cmCTestLog(this->CTest, HANDLER_OUTPUT, "Testing");
}
cmCTestLog(this->CTest, HANDLER_OUTPUT, " ");
const int maxTestNameWidth = this->CTest->GetMaxTestNameWidth();
std::string outname = this->TestProperties->Name + " ";
outname.resize(maxTestNameWidth, '.');
// add the arguments
std::vector<std::string>::const_iterator j =
this->TestProperties->Args.begin();
++j; // skip test name
++j; // skip command as it is in actualCommand
std::vector<const char*> arguments;
this->TestHandler->GenerateTestCommand(arguments);
arguments.push_back(this->ActualCommand.c_str());
for(;j != this->TestProperties->Args.end(); ++j)
{
this->TestCommand += " ";
this->TestCommand += cmSystemTools::EscapeSpaces(j->c_str());
arguments.push_back(j->c_str());
}
arguments.push_back(0);
this->TestResult.FullCommandLine = this->TestCommand;
*this->TestHandler->LogFile << this->TestProperties->Index << "/"
<< this->TestHandler->TotalNumberOfTests << " Testing: "
<< this->TestProperties->Name << std::endl;
*this->TestHandler->LogFile << this->TestProperties->Index << "/"
<< this->TestHandler->TotalNumberOfTests
<< " Test: " << this->TestProperties->Name.c_str() << std::endl;
*this->TestHandler->LogFile << "Command: ";
std::vector<cmStdString>::size_type ll;
for ( ll = 0; ll < arguments.size()-1; ll ++ )
{
*this->TestHandler->LogFile << "\"" << arguments[ll] << "\" ";
}
*this->TestHandler->LogFile << std::endl
<< "Directory: " << this->TestProperties->Directory << std::endl
<< "\"" << this->TestProperties->Name.c_str() << "\" start time: "
<< this->StartTime << std::endl;
*this->TestHandler->LogFile
<< "Output:" << std::endl
<< "----------------------------------------------------------"
<< std::endl;
*this->TestHandler->LogFile
<< this->ProcessOutput.c_str() << "<end of output>" << std::endl;
if ( this->CTest->GetShowOnly() )
{
cmCTestLog(this->CTest, HANDLER_OUTPUT, outname.c_str() << std::endl);
}
else
{
cmCTestLog(this->CTest, HANDLER_OUTPUT, outname.c_str());
}
cmCTestLog(this->CTest, DEBUG, "Testing "
<< this->TestProperties->Name.c_str() << " ... ");
}

View File

@ -19,6 +19,7 @@
#include <cmStandardIncludes.h> #include <cmStandardIncludes.h>
#include <cmCTestTestHandler.h> #include <cmCTestTestHandler.h>
#include <cmProcess.h>
/** \class cmRunTest /** \class cmRunTest
* \brief represents a single test to be run * \brief represents a single test to be run
@ -39,43 +40,52 @@ public:
void SetTestHandler(cmCTestTestHandler * handler); void SetTestHandler(cmCTestTestHandler * handler);
void SetOptimizeForCTest(bool optimize) void SetIndex(int i) { this->Index = i; }
{ this->OptimizeForCTest = optimize; }
bool GetOptimizeForCTest() void SetCTest(cmCTest * ct) { this->CTest = ct; }
{ return this->OptimizeForCTest; }
std::string GetProcessOutput() int GetIndex() { return this->Index; }
{ return this->ProcessOutput; }
//Provides a handle to the log stream in case someone wants std::string GetProcessOutput() { return this->ProcessOutput; }
// to asynchronously process the log
std::ostream * GetLogStream()
{ return this->TestHandler->LogFile; }
cmCTestTestHandler::cmCTestTestResult GetTestResults() cmCTestTestHandler::cmCTestTestResult GetTestResults()
{ return this->TestResult; } { return this->TestResult; }
//Runs the test bool IsRunning();
bool Execute(); void CheckOutput();
//launch the test process, return whether it started correctly
bool StartTest();
//capture the test results and send them back to the test handler
bool EndTest();
protected: protected:
void DartProcessing(std::string& output); void DartProcessing();
int RunTestProcess(std::vector<const char*> argv, bool CreateProcess(std::string executable,
std::string* output, int *retVal, std::vector<std::string> args,
std::ostream* log, double testTimeOut, double testTimeOut,
std::vector<std::string>* environment); std::vector<std::string>* environment);
private: private:
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;
cmCTest * CTest; cmCTest * CTest;
cmProcess * TestProcess;
//If the executable to run is ctest, don't create a new process; //If the executable to run is ctest, don't create a new process;
//just instantiate a new cmTest. (Can be disabled for a single test //just instantiate a new cmTest. (Can be disabled for a single test
//if this option is set to false.) //if this option is set to false.)
bool OptimizeForCTest; //bool OptimizeForCTest;
//flag for whether the env was modified for this run
bool ModifyEnv;
//stores the original environment if we are modifying it
std::vector<std::string> OrigEnv;
std::string ProcessOutput; std::string ProcessOutput;
//The test results //The test results
cmCTestTestHandler::cmCTestTestResult TestResult; cmCTestTestHandler::cmCTestTestResult TestResult;
int Index;
std::string StartTime;
std::string TestCommand;
std::string ActualCommand;
void WriteLogOutputTop();
}; };
#endif #endif

View File

@ -543,8 +543,15 @@ int cmCTestTestHandler::ProcessHandler()
std::vector<cmStdString> passed; std::vector<cmStdString> passed;
std::vector<cmStdString> failed; std::vector<cmStdString> failed;
int total; int total;
//start the real time clock
double clock_start, clock_finish;
clock_start = cmSystemTools::GetTime();
this->ProcessDirectory(passed, failed); this->ProcessDirectory(passed, failed);
clock_finish = cmSystemTools::GetTime();
total = int(passed.size()) + int(failed.size()); total = int(passed.size()) + int(failed.size());
if (total == 0) if (total == 0)
@ -591,10 +598,15 @@ int cmCTestTestHandler::ProcessHandler()
totalTestTime += result->ExecutionTime; totalTestTime += result->ExecutionTime;
} }
char buf[1024]; char realBuf[1024];
sprintf(buf, "%6.2f sec", totalTestTime); sprintf(realBuf, "%6.2f sec", (double)(clock_finish - clock_start));
cmCTestLog(this->CTest, HANDLER_OUTPUT, "\nTotal Test time = " cmCTestLog(this->CTest, HANDLER_OUTPUT, "\nTotal Test time (real) = "
<< buf << "\n" ); << realBuf << "\n" );
char totalBuf[1024];
sprintf(totalBuf, "%6.2f sec", totalTestTime);
cmCTestLog(this->CTest, HANDLER_OUTPUT, "\nTotal Test time (parallel) = "
<< totalBuf << "\n" );
} }
@ -1528,21 +1540,28 @@ std::string cmCTestTestHandler::SaveTestList()
return fname; return fname;
} }
void cmCTestTestHandler::ProcessParallel(std::vector<cmStdString> &passed, void cmCTestTestHandler::ProcessDirectory(std::vector<cmStdString> &passed,
std::vector<cmStdString> &failed) std::vector<cmStdString> &failed)
{ {
this->ComputeTestList(); this->ComputeTestList();
cmCTestMultiProcessHandler parallel; cmCTestMultiProcessHandler parallel;
parallel.SetCTest(this->CTest); parallel.SetCTest(this->CTest);
parallel.SetParallelLevel(this->CTest->GetParallelLevel()); parallel.SetParallelLevel(this->CTest->GetParallelLevel());
parallel.SetTestHandler(this);
*this->LogFile << "Start testing: "
<< this->CTest->CurrentTime() << std::endl
<< "----------------------------------------------------------"
<< std::endl;
cmCTestMultiProcessHandler::TestSet depends; cmCTestMultiProcessHandler::TestSet depends;
cmCTestMultiProcessHandler::TestMap tests; cmCTestMultiProcessHandler::TestMap tests;
std::map<int, cmStdString> testnames; cmCTestMultiProcessHandler::PropertiesMap properties;
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;
testnames[p.Index] = p.Name;
if(p.Depends.size()) if(p.Depends.size())
{ {
for(std::vector<std::string>::iterator i = p.Depends.begin(); for(std::vector<std::string>::iterator i = p.Depends.begin();
@ -1560,76 +1579,16 @@ void cmCTestTestHandler::ProcessParallel(std::vector<cmStdString> &passed,
} }
} }
tests[it->Index] = depends; tests[it->Index] = depends;
properties[it->Index] = &*it;
} }
parallel.SetCTestCommand(this->CTest->GetCTestExecutable()); parallel.SetTests(tests, properties);
parallel.SetTests(tests, testnames);
std::string fname = this->SaveTestList();
parallel.SetTestCacheFile(fname.c_str());
parallel.SetPassFailVectors(&passed, &failed); parallel.SetPassFailVectors(&passed, &failed);
this->TestResults.clear(); this->TestResults.clear();
parallel.SetTestResults(&this->TestResults); parallel.SetTestResults(&this->TestResults);
parallel.RunTests(); parallel.RunTests();
cmSystemTools::RemoveFile(fname.c_str());
}
*this->LogFile << "End testing: "
//---------------------------------------------------------------------- << this->CTest->CurrentTime() << std::endl;
void cmCTestTestHandler::ProcessDirectory(std::vector<cmStdString> &passed,
std::vector<cmStdString> &failed)
{
if(this->CTest->GetParallelLevel() > 0)
{
this->ProcessParallel(passed, failed);
return;
}
// save the current working directory
std::string current_dir = cmSystemTools::GetCurrentWorkingDirectory();
// compute the list of tests to run
this->ComputeTestList();
this->StartTest = this->CTest->CurrentTime();
this->StartTestTime = static_cast<unsigned int>(cmSystemTools::GetTime());
double elapsed_time_start = cmSystemTools::GetTime();
*this->LogFile << "Start testing: " << this->StartTest << std::endl
<< "----------------------------------------------------------"
<< std::endl;
std::string last_directory = "";
// run each test
for (ListOfTests::iterator it = this->TestList.begin();
it != this->TestList.end(); it ++ )
{
if (!(last_directory == it->Directory))
{
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Changing directory into " << it->Directory.c_str() << "\n");
*this->LogFile << "Changing directory into: " << it->Directory.c_str()
<< std::endl;
last_directory = it->Directory;
cmSystemTools::ChangeDirectory(it->Directory.c_str());
}
// process this one test
cmCTestRunTest testRun;
testRun.SetTestProperties(&(*it));
testRun.SetTestHandler(this);
bool testPassed = testRun.Execute(); //run the test
if(testPassed && !this->CTest->GetShowOnly())
{
passed.push_back(it->Name);
}
else if(!testPassed)
{
failed.push_back(it->Name);
}
}
this->EndTest = this->CTest->CurrentTime();
this->EndTestTime = static_cast<unsigned int>(cmSystemTools::GetTime());
this->ElapsedTestingTime = cmSystemTools::GetTime() - elapsed_time_start;
if ( this->LogFile )
{
*this->LogFile << "End testing: " << this->EndTest << std::endl;
}
cmSystemTools::ChangeDirectory(current_dir.c_str());
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------

View File

@ -31,6 +31,7 @@ class cmMakefile;
class cmCTestTestHandler : public cmCTestGenericHandler class cmCTestTestHandler : public cmCTestGenericHandler
{ {
friend class cmCTestRunTest; friend class cmCTestRunTest;
friend class cmCTestMultiProcessHandler;
public: public:
cmTypeMacro(cmCTestTestHandler, cmCTestGenericHandler); cmTypeMacro(cmCTestTestHandler, cmCTestGenericHandler);
@ -129,6 +130,7 @@ public:
std::vector<std::string> &extraPaths, std::vector<std::string> &extraPaths,
std::vector<std::string> &failed); std::vector<std::string> &failed);
typedef std::vector<cmCTestTestProperties> ListOfTests;
protected: protected:
// comput a final test list // comput a final test list
virtual int PreProcessHandler(); virtual int PreProcessHandler();
@ -193,7 +195,6 @@ private:
void ProcessDirectory(std::vector<cmStdString> &passed, void ProcessDirectory(std::vector<cmStdString> &passed,
std::vector<cmStdString> &failed); std::vector<cmStdString> &failed);
typedef std::vector<cmCTestTestProperties> ListOfTests;
/** /**
* Get the list of tests in directory and subdirectories. * Get the list of tests in directory and subdirectories.
*/ */
@ -221,9 +222,6 @@ private:
bool GetValue(const char* tag, bool GetValue(const char* tag,
double& value, double& value,
std::ifstream& fin); std::ifstream& fin);
// run in -j N mode
void ProcessParallel(std::vector<cmStdString> &passed,
std::vector<cmStdString> &failed);
/** /**
* Find the executable for a test * Find the executable for a test
*/ */

View File

@ -209,7 +209,7 @@ std::string cmCTest::DecodeURL(const std::string& in)
cmCTest::cmCTest() cmCTest::cmCTest()
{ {
this->ParallelSubprocess = false; this->ParallelSubprocess = false;
this->ParallelLevel = 0; this->ParallelLevel = 1;
this->SubmitIndex = 0; this->SubmitIndex = 0;
this->ForceNewCTestProcess = false; this->ForceNewCTestProcess = false;
this->TomorrowTag = false; this->TomorrowTag = false;
@ -292,6 +292,11 @@ cmCTest::~cmCTest()
this->SetOutputLogFileName(0); this->SetOutputLogFileName(0);
} }
void cmCTest::SetParallelLevel(int level)
{
this->ParallelLevel = level < 1 ? 1 : level;
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
cmCTest::Part cmCTest::GetPartFromName(const char* name) cmCTest::Part cmCTest::GetPartFromName(const char* name)
{ {

View File

@ -136,7 +136,7 @@ public:
void SetTimeOut(double t) { this->TimeOut = t; } void SetTimeOut(double t) { this->TimeOut = t; }
// how many test to run at the same time // how many test to run at the same time
int GetParallelLevel() { return this->ParallelLevel; } int GetParallelLevel() { return this->ParallelLevel; }
void SetParallelLevel(int t) { this->ParallelLevel = t; } void SetParallelLevel(int);
bool GetParallelSubprocess() { return this->ParallelSubprocess; } bool GetParallelSubprocess() { return this->ParallelSubprocess; }
void SetParallelSubprocess() { this->ParallelSubprocess = true; } void SetParallelSubprocess() { this->ParallelSubprocess = true; }