Commit 8ffd8d0a authored by Zach's avatar Zach
Browse files

ENH: refactored ctest. All testing is now parallel. If no -j option is...

ENH: refactored ctest.  All testing is now parallel.  If no -j option is specified, defaults to a MP level of 1 (non parallel)
parent 1171bcfc
......@@ -23,12 +23,11 @@
cmCTestMultiProcessHandler::cmCTestMultiProcessHandler()
{
this->ParallelLevel = 1;
this->ProcessId = 0;
}
// Set the tests
void
cmCTestMultiProcessHandler::SetTests(TestMap& tests,
std::map<int,cmStdString>& testNames)
PropertiesMap& properties)
{
// set test run map to false for all
for(TestMap::iterator i = this->Tests.begin();
......@@ -38,15 +37,14 @@ cmCTestMultiProcessHandler::SetTests(TestMap& tests,
this->TestFinishMap[i->first] = false;
}
this->Tests = tests;
this->TestNames = testNames;
this->Properties = properties;
}
// 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()
{
this->StartNextTests();
......@@ -59,65 +57,28 @@ void cmCTestMultiProcessHandler::RunTests()
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)
{
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
" test " << test << "\n");
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, test << ": "
<< " test " << test << "\n");
this->TestRunningMap[test] = true; // mark the test as running
// now remove the test itself
this->Tests.erase(test);
// now run the test
cmProcess* newp = new cmProcess;
newp->SetId(this->ProcessId);
newp->SetId(test);
newp->SetCommand(this->CTestCommand.c_str());
std::vector<std::string> args;
cmOStringStream width;
if(this->CTest->GetMaxTestNameWidth())
{
args.push_back("-W");
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())
cmCTestRunTest* testRun = new cmCTestRunTest;
testRun->SetCTest(this->CTest);
testRun->SetTestHandler(this->TestHandler);
testRun->SetIndex(test);
testRun->SetTestProperties(this->Properties[test]);
if(testRun->StartTest())
{
cmCTestLog(this->CTest, ERROR_MESSAGE,
"Error starting " << newp->GetCommand() << "\n");
this->EndTest(newp);
this->RunningTests.insert(testRun);
}
else
{
this->RunningTests.insert(newp);
testRun->EndTest();
}
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"ctest -I " << test << "\n");
this->ProcessId++;
}
bool cmCTestMultiProcessHandler::StartTest(int test)
......@@ -160,7 +121,7 @@ bool cmCTestMultiProcessHandler::StartTest(int test)
}
// This test was not able to start because it is waiting
// on depends to run
return false;
return false;
}
void cmCTestMultiProcessHandler::StartNextTests()
......@@ -195,7 +156,6 @@ void cmCTestMultiProcessHandler::StartNextTests()
}
}
bool cmCTestMultiProcessHandler::CheckOutput()
{
// no more output we are done
......@@ -203,82 +163,48 @@ bool cmCTestMultiProcessHandler::CheckOutput()
{
return false;
}
std::vector<cmProcess*> finished;
std::vector<cmCTestRunTest*> finished;
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)
{
cmProcess* p = *i;
int pipe = p->CheckOutput(.1, out, err);
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";
}
cmCTestRunTest* p = *i;
p->CheckOutput(); //reads and stores the process output
if(!p->IsRunning())
{
finished.push_back(p);
}
}
for( std::vector<cmProcess*>::iterator i = finished.begin();
i != finished.end(); ++i)
{
cmProcess* p = *i;
this->EndTest(p);
}
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)
for( std::vector<cmCTestRunTest*>::iterator i = finished.begin();
i != finished.end(); ++i)
{
cres.Status = cmCTestTestHandler::FAILED;
this->Failed->push_back(this->TestNames[test]);
}
else
cmCTestRunTest* p = *i;
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)
{
this->Passed->push_back(this->TestNames[test]);
j->second.erase(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;
}
this->TestFinishMap[test] = true;
this->TestRunningMap[test] = false;
this->RunningTests.erase(p);
delete p;
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"finish test " << test << "\n");
return true;
}
void cmCTestMultiProcessHandler::PrintTests()
{
#undef cout
......
......@@ -17,9 +17,9 @@
#ifndef cmCTestMultiProcessHandler_h
#define cmCTestMultiProcessHandler_h
class cmProcess;
#include <cmStandardIncludes.h>
#include <cmCTestTestHandler.h>
#include <cmCTestRunTest.h>
/** \class cmCTestMultiProcessHandler
* \brief run parallel ctest
......@@ -31,16 +31,18 @@ class cmCTestMultiProcessHandler
public:
struct TestSet : public std::set<int> {};
struct TestMap : public std::map<int, TestSet> {};
struct PropertiesMap : public
std::map<int, cmCTestTestHandler::cmCTestTestProperties*> {};
cmCTestMultiProcessHandler();
// Set the tests
void SetTests(TestMap& tests,
std::map<int, cmStdString>& testNames);
void SetTests(TestMap& tests, PropertiesMap& properties);
// Set the max number of tests that can be run at the same time.
void SetParallelLevel(size_t);
void RunTests();
void PrintTests();
void SetCTestCommand(const char* c) { this->CTestCommand = c;}
void SetTestCacheFile(const char* c) { this->CTestCacheFile = c;}
//void SetCTestCommand(const char* c) { this->CTestCommand = c;}
//void SetTestCacheFile(const char* c) { this->CTestCacheFile = c;}
void SetPassFailVectors(std::vector<cmStdString>* passed,
std::vector<cmStdString>* failed)
{
......@@ -51,7 +53,14 @@ public:
{
this->TestResults = r;
}
void SetCTest(cmCTest* ctest) { this->CTest = ctest;}
void SetTestHandler(cmCTestTestHandler * handler)
{ this->TestHandler = handler; }
cmCTestTestHandler * GetTestHandler()
{ return this->TestHandler; }
protected:
cmCTest* CTest;
// Start the next test or tests as many as are allowed by
......@@ -59,24 +68,25 @@ protected:
void StartNextTests();
void StartTestProcess(int test);
bool StartTest(int test);
void EndTest(cmProcess*);
//void EndTest(cmProcess*);
// Return true if there are still tests running
// check all running processes for output and exit case
bool CheckOutput();
// map from test number to set of depend 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> TestFinishMap;
std::map<int, cmStdString> TestOutput;
std::string CTestCommand;
std::string CTestCacheFile;
//std::string CTestCommand;
//std::string CTestCacheFile;
std::vector<cmStdString>* Passed;
std::vector<cmStdString>* Failed;
std::vector<cmCTestTestHandler::cmCTestTestResult>* TestResults;
int ProcessId;
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
This diff is collapsed.
......@@ -19,6 +19,7 @@
#include <cmStandardIncludes.h>
#include <cmCTestTestHandler.h>
#include <cmProcess.h>
/** \class cmRunTest
* \brief represents a single test to be run
......@@ -38,44 +39,53 @@ public:
{ return this->TestProperties; }
void SetTestHandler(cmCTestTestHandler * handler);
void SetOptimizeForCTest(bool optimize)
{ this->OptimizeForCTest = optimize; }
bool GetOptimizeForCTest()
{ return this->OptimizeForCTest; }
void SetIndex(int i) { this->Index = i; }
void SetCTest(cmCTest * ct) { this->CTest = ct; }
std::string GetProcessOutput()
{ return this->ProcessOutput; }
int GetIndex() { return this->Index; }
//Provides a handle to the log stream in case someone wants
// to asynchronously process the log
std::ostream * GetLogStream()
{ return this->TestHandler->LogFile; }
std::string GetProcessOutput() { return this->ProcessOutput; }
cmCTestTestHandler::cmCTestTestResult GetTestResults()
{ return this->TestResult; }
//Runs the test
bool Execute();
bool IsRunning();
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:
void DartProcessing(std::string& output);
int RunTestProcess(std::vector<const char*> argv,
std::string* output, int *retVal,
std::ostream* log, double testTimeOut,
void DartProcessing();
bool CreateProcess(std::string executable,
std::vector<std::string> args,
double testTimeOut,
std::vector<std::string>* environment);
private:
cmCTestTestHandler::cmCTestTestProperties * TestProperties;
//Pointer back to the "parent"; the handler that invoked this test run
cmCTestTestHandler * TestHandler;
cmCTest * CTest;
cmProcess * TestProcess;
//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
//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;
//The test results
cmCTestTestHandler::cmCTestTestResult TestResult;
int Index;
std::string StartTime;
std::string TestCommand;
std::string ActualCommand;
void WriteLogOutputTop();
};
#endif
......
......@@ -543,8 +543,15 @@ int cmCTestTestHandler::ProcessHandler()
std::vector<cmStdString> passed;
std::vector<cmStdString> failed;
int total;
//start the real time clock
double clock_start, clock_finish;
clock_start = cmSystemTools::GetTime();
this->ProcessDirectory(passed, failed);
clock_finish = cmSystemTools::GetTime();
total = int(passed.size()) + int(failed.size());
if (total == 0)
......@@ -591,10 +598,15 @@ int cmCTestTestHandler::ProcessHandler()
totalTestTime += result->ExecutionTime;
}
char buf[1024];
sprintf(buf, "%6.2f sec", totalTestTime);
cmCTestLog(this->CTest, HANDLER_OUTPUT, "\nTotal Test time = "
<< buf << "\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" );
}
......@@ -1528,21 +1540,28 @@ std::string cmCTestTestHandler::SaveTestList()
return fname;
}
void cmCTestTestHandler::ProcessParallel(std::vector<cmStdString> &passed,
void cmCTestTestHandler::ProcessDirectory(std::vector<cmStdString> &passed,
std::vector<cmStdString> &failed)
{
this->ComputeTestList();
cmCTestMultiProcessHandler parallel;
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::TestMap tests;
std::map<int, cmStdString> testnames;
cmCTestMultiProcessHandler::PropertiesMap properties;
for (ListOfTests::iterator it = this->TestList.begin();
it != this->TestList.end(); it ++ )
{
cmCTestTestProperties& p = *it;
testnames[p.Index] = p.Name;
if(p.Depends.size())
{
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;
properties[it->Index] = &*it;
}
parallel.SetCTestCommand(this->CTest->GetCTestExecutable());
parallel.SetTests(tests, testnames);
std::string fname = this->SaveTestList();
parallel.SetTestCacheFile(fname.c_str());
parallel.SetTests(tests, properties);
parallel.SetPassFailVectors(&passed, &failed);
this->TestResults.clear();
parallel.SetTestResults(&this->TestResults);
parallel.RunTests();
cmSystemTools::RemoveFile(fname.c_str());
}
//----------------------------------------------------------------------
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());
*this->LogFile << "End testing: "
<< this->CTest->CurrentTime() << std::endl;
}
//----------------------------------------------------------------------
......
......@@ -31,6 +31,7 @@ class cmMakefile;
class cmCTestTestHandler : public cmCTestGenericHandler
{
friend class cmCTestRunTest;
friend class cmCTestMultiProcessHandler;
public:
cmTypeMacro(cmCTestTestHandler, cmCTestGenericHandler);
......@@ -129,6 +130,7 @@ public:
std::vector<std::string> &extraPaths,
std::vector<std::string> &failed);
typedef std::vector<cmCTestTestProperties> ListOfTests;
protected:
// comput a final test list
virtual int PreProcessHandler();
......@@ -193,7 +195,6 @@ private:
void ProcessDirectory(std::vector<cmStdString> &passed,
std::vector<cmStdString> &failed);
typedef std::vector<cmCTestTestProperties> ListOfTests;
/**
* Get the list of tests in directory and subdirectories.
*/
......@@ -221,9 +222,6 @@ private:
bool GetValue(const char* tag,
double& value,
std::ifstream& fin);
// run in -j N mode
void ProcessParallel(std::vector<cmStdString> &passed,
std::vector<cmStdString> &failed);
/**
* Find the executable for a test
*/
......
......@@ -209,7 +209,7 @@ std::string cmCTest::DecodeURL(const std::string& in)
cmCTest::cmCTest()
{
this->ParallelSubprocess = false;
this->ParallelLevel = 0;
this->ParallelLevel = 1;
this->SubmitIndex = 0;
this->ForceNewCTestProcess = false;
this->TomorrowTag = false;
......@@ -292,6 +292,11 @@ cmCTest::~cmCTest()
this->SetOutputLogFileName(0);
}
void cmCTest::SetParallelLevel(int level)
{
this->ParallelLevel = level < 1 ? 1 : level;
}
//----------------------------------------------------------------------------
cmCTest::Part cmCTest::GetPartFromName(const char* name)