summaryrefslogtreecommitdiffstats
path: root/Source/CTest
diff options
context:
space:
mode:
authorBill Hoffman <bill.hoffman@kitware.com>2008-07-03 13:31:33 (GMT)
committerBill Hoffman <bill.hoffman@kitware.com>2008-07-03 13:31:33 (GMT)
commitbb7b27e417b72d0387af5393b81ed35deab52c4b (patch)
tree42d2efd6a2131935eefbe540cd15e8bcb0ed2ac7 /Source/CTest
parentea71721b805708f82898c32e007aa3e29e6f458f (diff)
downloadCMake-bb7b27e417b72d0387af5393b81ed35deab52c4b.zip
CMake-bb7b27e417b72d0387af5393b81ed35deab52c4b.tar.gz
CMake-bb7b27e417b72d0387af5393b81ed35deab52c4b.tar.bz2
ENH: add initial ctest -j feature
Diffstat (limited to 'Source/CTest')
-rw-r--r--Source/CTest/cmCTestGenericHandler.cxx6
-rw-r--r--Source/CTest/cmCTestMultiProcessHandler.cxx323
-rw-r--r--Source/CTest/cmCTestMultiProcessHandler.h84
-rw-r--r--Source/CTest/cmCTestTestHandler.cxx438
-rw-r--r--Source/CTest/cmCTestTestHandler.h35
5 files changed, 819 insertions, 67 deletions
diff --git a/Source/CTest/cmCTestGenericHandler.cxx b/Source/CTest/cmCTestGenericHandler.cxx
index 50c5a20..1695bc1 100644
--- a/Source/CTest/cmCTestGenericHandler.cxx
+++ b/Source/CTest/cmCTestGenericHandler.cxx
@@ -161,6 +161,12 @@ bool cmCTestGenericHandler::StartLogFile(const char* name,
ostr << "_" << this->CTest->GetCurrentTag();
}
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) )
{
cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot create log file: "
diff --git a/Source/CTest/cmCTestMultiProcessHandler.cxx b/Source/CTest/cmCTestMultiProcessHandler.cxx
new file mode 100644
index 0000000..4c9be36
--- /dev/null
+++ b/Source/CTest/cmCTestMultiProcessHandler.cxx
@@ -0,0 +1,323 @@
+/*=========================================================================
+
+ Program: CMake - Cross-Platform Makefile Generator
+ Module: $RCSfile$
+ Language: C++
+ Date: $Date$
+ Version: $Revision$
+
+ Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
+ See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the above copyright notices for more information.
+
+=========================================================================*/
+#include "cmCTestMultiProcessHandler.h"
+#include "cmProcess.h"
+#include "cmStandardIncludes.h"
+#include "cmCTest.h"
+
+
+cmCTestMultiProcessHandler::cmCTestMultiProcessHandler()
+{
+ this->ParallelLevel = 1;
+ this->ProcessId = 0;
+}
+ // Set the tests
+void
+cmCTestMultiProcessHandler::SetTests(std::map<int, std::set<int> >& tests,
+ std::map<int,cmStdString>& testNames)
+{
+ // set test run map to false for all
+ for(std::map<int, std::set<int> >::iterator i = this->Tests.begin();
+ i != this->Tests.end(); ++i)
+ {
+ this->TestRunningMap[i->first] = false;
+ this->TestFinishMap[i->first] = false;
+ }
+ this->Tests = tests;
+ this->TestNames = testNames;
+}
+ // Set the max number of tests that can be run at the same time.
+void cmCTestMultiProcessHandler::SetParallelLevel(size_t l)
+{
+ this->ParallelLevel = l;
+}
+
+
+void cmCTestMultiProcessHandler::RunTests()
+{
+ this->StartNextTests();
+ while(this->Tests.size() != 0)
+ {
+ this->CheckOutput();
+ this->StartNextTests();
+ }
+ // let all running tests finish
+ 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");
+ 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;
+ 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
+ {
+ this->RunningTests.insert(newp);
+ }
+ cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
+ "ctest -I " << test << "\n");
+ this->ProcessId++;
+}
+
+bool cmCTestMultiProcessHandler::StartTest(int test)
+{
+ // copy the depend tests locally because when
+ // a test is finished it will be removed from the depend list
+ // and we don't want to be iterating a list while removing from it
+ std::set<int> depends = this->Tests[test];
+ size_t totalDepends = depends.size();
+ if(totalDepends)
+ {
+ for(std::set<int>::const_iterator i = depends.begin();
+ i != depends.end(); ++i)
+ {
+ // if the test is not already running then start it
+ if(!this->TestRunningMap[*i])
+ {
+ // this test might be finished, but since
+ // this is a copy of the depend map we might
+ // still have it
+ if(!this->TestFinishMap[*i])
+ {
+ // only start one test in this function
+ return this->StartTest(*i);
+ }
+ else
+ {
+ // the depend has been and finished
+ totalDepends--;
+ }
+ }
+ }
+ }
+ // if there are no depends left then run this test
+ if(totalDepends == 0)
+ {
+ // Start this test it has no depends
+ this->StartTestProcess(test);
+ return true;
+ }
+ // This test was not able to start because it is waiting
+ // on depends to run
+ return false;
+}
+
+void cmCTestMultiProcessHandler::StartNextTests()
+{
+ cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, std::endl
+ << "Number of running tests : " << this->RunningTests.size()
+ << "\n");
+ size_t numToStart = this->ParallelLevel - this->RunningTests.size();
+ if(numToStart == 0)
+ {
+ return;
+ }
+ std::map<int, std::set<int> > tests = this->Tests;
+ for(std::map<int, std::set<int> >::iterator i = tests.begin();
+ i != tests.end(); ++i)
+ {
+ // start test should start only one test
+ if(this->StartTest(i->first))
+ {
+ numToStart--;
+ }
+ else
+ {
+ cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, std::endl
+ << "Test did not start waiting on depends to finish: "
+ << i->first << "\n");
+ }
+ if(numToStart == 0 )
+ {
+ return;
+ }
+ }
+}
+
+
+bool cmCTestMultiProcessHandler::CheckOutput()
+{
+ // no more output we are done
+ if(this->RunningTests.size() == 0)
+ {
+ return false;
+ }
+ std::vector<cmProcess*> finished;
+ std::string out, err;
+ for(std::set<cmProcess*>::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";
+ }
+ 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)
+{
+ int test = p->GetId();
+ int exitVal = p->GetExitValue();
+ cmCTestTestHandler::cmCTestTestResult cres;
+ cres.Properties = 0;
+ cres.ExecutionTime = 0;// ???
+ cres.ReturnValue = exitVal;
+ cres.Status = cmCTestTestHandler::COMPLETED;
+ cres.TestCount = test;
+ cres.Name = this->TestNames[test];
+ cres.Path.clear();
+ 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( std::map<int, std::set<int> >::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()
+{
+#undef cout
+ for( std::map<int, std::set<int> >::iterator i = this->Tests.begin();
+ i!= this->Tests.end(); ++i)
+ {
+ std::cout << "Test " << i->first << " (";
+ for(std::set<int>::iterator j = i->second.begin();
+ j != i->second.end(); ++j)
+ {
+ std::cout << *j << " ";
+ }
+ std::cout << ")\n";
+ }
+}
+
+#if 0
+int main()
+{
+ cmCTestMultiProcessHandler h;
+ h.SetParallelLevel(4);
+ std::map<int, std::set<int> > tests;
+ std::set<int> depends;
+ for(int i =1; i < 92; i++)
+ {
+ tests[i] = depends;
+ }
+ depends.clear();
+ depends.insert(45); subprject
+ tests[46] = depends; subproject-stage2
+ depends.clear();
+ depends.insert(55); simpleinstall simpleinstall-s2
+ tests[56] = depends;
+ depends.clear();
+ depends.insert(70); wrapping
+ tests[71] = depends; qtwrapping
+ depends.clear();
+ depends.insert(71); qtwrapping
+ tests[72] = depends; testdriver1
+ depends.clear();
+ depends.insert(72) testdriver1
+ tests[73] = depends; testdriver2
+ depends.clear();
+ depends.insert(73); testdriver2
+ tests[74] = depends; testdriver3
+ depends.clear();
+ depends.insert(79); linkorder1
+ tests[80] = depends; linkorder2
+ h.SetTests(tests);
+ h.PrintTests();
+ h.RunTests();
+}
+#endif
diff --git a/Source/CTest/cmCTestMultiProcessHandler.h b/Source/CTest/cmCTestMultiProcessHandler.h
new file mode 100644
index 0000000..9f7976d
--- /dev/null
+++ b/Source/CTest/cmCTestMultiProcessHandler.h
@@ -0,0 +1,84 @@
+/*=========================================================================
+
+ Program: CMake - Cross-Platform Makefile Generator
+ Module: $RCSfile$
+ Language: C++
+ Date: $Date$
+ Version: $Revision$
+
+ Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
+ See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the above copyright notices for more information.
+
+=========================================================================*/
+#ifndef cmCTestMultiProcessHandler_h
+#define cmCTestMultiProcessHandler_h
+
+#include <set>
+#include <map>
+#include <string>
+#include <vector>
+class cmProcess;
+#include <cmStandardIncludes.h>
+#include <cmCTestTestHandler.h>
+
+/** \class cmCTestMultiProcessHandler
+ * \brief run parallel ctest
+ *
+ * cmCTestMultiProcessHandler
+ */
+class cmCTestMultiProcessHandler
+{
+public:
+ cmCTestMultiProcessHandler();
+ // Set the tests
+ void SetTests(std::map<int, std::set<int> >& tests,
+ std::map<int, cmStdString>& testNames);
+ // 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 SetPassFailVectors(std::vector<cmStdString>* passed,
+ std::vector<cmStdString>* failed)
+ {
+ this->Passed = passed;
+ this->Failed = failed;
+ }
+ void SetTestResults(std::vector<cmCTestTestHandler::cmCTestTestResult>* r)
+ {
+ this->TestResults = r;
+ }
+ void SetCTest(cmCTest* ctest) { this->CTest = ctest;}
+protected:
+ cmCTest* CTest;
+ // Start the next test or tests as many as are allowed by
+ // ParallelLevel
+ void StartNextTests();
+ void StartTestProcess(int test);
+ bool StartTest(int test);
+ 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
+ std::map<int, std::set<int> > Tests;
+ std::map<int, cmStdString> TestNames;
+ std::map<int, bool> TestRunningMap;
+ std::map<int, bool> TestFinishMap;
+ std::map<int, cmStdString> TestOutput;
+ 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
+};
+
+#endif
diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx
index fe3c3ae..2db4d6d 100644
--- a/Source/CTest/cmCTestTestHandler.cxx
+++ b/Source/CTest/cmCTestTestHandler.cxx
@@ -16,7 +16,7 @@
=========================================================================*/
#include "cmCTestTestHandler.h"
-
+#include "cmCTestMultiProcessHandler.h"
#include "cmCTest.h"
#include "cmake.h"
#include "cmGeneratedFileStream.h"
@@ -500,11 +500,14 @@ int cmCTestTestHandler::ProcessHandler()
}
this->TestResults.clear();
-
- cmCTestLog(this->CTest, HANDLER_OUTPUT,
- (this->MemCheck ? "Memory check" : "Test")
- << " project " << cmSystemTools::GetCurrentWorkingDirectory()
- << std::endl);
+ // 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")
+ << " project " << cmSystemTools::GetCurrentWorkingDirectory()
+ << std::endl);
+ }
if ( ! this->PreProcessHandler() )
{
return -1;
@@ -517,7 +520,6 @@ int cmCTestTestHandler::ProcessHandler()
std::vector<cmStdString> passed;
std::vector<cmStdString> failed;
int total;
-
this->ProcessDirectory(passed, failed);
total = int(passed.size()) + int(failed.size());
@@ -550,33 +552,37 @@ int cmCTestTestHandler::ProcessHandler()
{
percent = 99;
}
- cmCTestLog(this->CTest, HANDLER_OUTPUT, std::endl
- << static_cast<int>(percent + .5) << "% tests passed, "
- << failed.size() << " tests failed out of " << total << std::endl);
- //fprintf(stderr,"\n%.0f%% tests passed, %i tests failed out of %i\n",
- // percent, int(failed.size()), total);
+ 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);
+ }
if (failed.size())
{
cmGeneratedFileStream ofs;
-
- 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)
+ if(!this->CTest->GetParallelSubprocess())
{
- if ( ftit->Status != cmCTestTestHandler::COMPLETED )
+ 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)
{
- ofs << ftit->TestCount << ":" << ftit->Name << std::endl;
- cmCTestLog(this->CTest, HANDLER_OUTPUT, "\t" << std::setw(3)
- << ftit->TestCount << " - " << ftit->Name.c_str() << " ("
- << this->GetTestStatus(ftit->Status) << ")" << std::endl);
+ if ( ftit->Status != cmCTestTestHandler::COMPLETED )
+ {
+ ofs << ftit->TestCount << ":" << ftit->Name << std::endl;
+ cmCTestLog(this->CTest, HANDLER_OUTPUT, "\t" << std::setw(3)
+ << ftit->TestCount << " - " << ftit->Name.c_str() << " ("
+ << this->GetTestStatus(ftit->Status) << ")" << std::endl);
+ }
}
+
}
-
}
}
@@ -677,8 +683,8 @@ void cmCTestTestHandler::ProcessOneTest(cmCTestTestProperties *it,
// add the arguments
std::vector<std::string>::const_iterator j = args.begin();
- ++j;
- ++j;
+ ++j; // skip test name
+ ++j; // skip command as it is in actualCommand
std::vector<const char*> arguments;
this->GenerateTestCommand(arguments);
arguments.push_back(actualCommand.c_str());
@@ -917,23 +923,20 @@ void cmCTestTestHandler::ProcessOneTest(cmCTestTestProperties *it,
}
//----------------------------------------------------------------------
-void cmCTestTestHandler::ProcessDirectory(std::vector<cmStdString> &passed,
- std::vector<cmStdString> &failed)
+void cmCTestTestHandler::ComputeTestList()
{
- std::string current_dir = cmSystemTools::GetCurrentWorkingDirectory();
- this->TestList.clear();
-
- this->GetListOfTests();
+ this->TestList.clear(); // clear list of test
+ if(this->CTest->GetParallelSubprocess())
+ {
+ this->LoadTestList();
+ return;
+ }
+ else
+ {
+ this->GetListOfTests();
+ }
cmCTestTestHandler::ListOfTests::size_type tmsize = this->TestList.size();
- 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;
-
// how many tests are in based on RegExp?
int inREcnt = 0;
cmCTestTestHandler::ListOfTests::iterator it;
@@ -953,10 +956,11 @@ void cmCTestTestHandler::ProcessDirectory(std::vector<cmStdString> &passed,
{
this->ExpandTestsToRunInformation(inREcnt);
}
-
+ // Now create a final list of tests to run
int cnt = 0;
inREcnt = 0;
- std::string last_directory = "";
+ std::string last_directory = "";
+ ListOfTests finalList;
for ( it = this->TestList.begin(); it != this->TestList.end(); it ++ )
{
cnt ++;
@@ -965,23 +969,6 @@ void cmCTestTestHandler::ProcessDirectory(std::vector<cmStdString> &passed,
inREcnt++;
}
- // if we are out of time then skip this test, we leave two minutes
- // to submit results
- if (this->CTest->GetRemainingTimeAllowed() - 120 <= 0)
- {
- continue;
- }
-
- 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());
- }
-
if (this->UseUnion)
{
// if it is not in the list and not in the regexp then skip
@@ -1003,10 +990,322 @@ void cmCTestTestHandler::ProcessDirectory(std::vector<cmStdString> &passed,
continue;
}
}
-
+ it->Index = cnt; // save the index into the test list for this test
+ finalList.push_back(*it);
+ }
+ // Save the total number of tests before exclusions
+ this->TotalNumberOfTests = this->TestList.size();
+ // Set the TestList to the final list of all test
+ this->TestList = finalList;
+}
+
+bool cmCTestTestHandler::GetValue(const char* tag,
+ int& value,
+ std::ifstream& fin)
+{
+ std::string line;
+ cmSystemTools::GetLineFromStream(fin, line);
+ if(line == tag)
+ {
+ fin >> value;
+ cmSystemTools::GetLineFromStream(fin, line); // read blank line
+ }
+ else
+ {
+ cmCTestLog(this->CTest, ERROR_MESSAGE,
+ "parse error: missing tag: "
+ << tag << " found [" << line << "]" << std::endl);
+ return false;
+ }
+ return true;
+}
+
+bool cmCTestTestHandler::GetValue(const char* tag,
+ double& value,
+ std::ifstream& fin)
+{
+ std::string line;
+ cmSystemTools::GetLineFromStream(fin, line);
+ if(line == tag)
+ {
+ fin >> value;
+ cmSystemTools::GetLineFromStream(fin, line); // read blank line
+ }
+ else
+ {
+ cmCTestLog(this->CTest, ERROR_MESSAGE,
+ "parse error: missing tag: "
+ << tag << " found [" << line << "]" << std::endl);
+ return false;
+ }
+ return true;
+}
+
+bool cmCTestTestHandler::GetValue(const char* tag,
+ bool& value,
+ std::ifstream& fin)
+{
+ std::string line;
+ cmSystemTools::GetLineFromStream(fin, line);
+ if(line == tag)
+ {
+ fin >> value;
+ cmSystemTools::GetLineFromStream(fin, line); // read blank line
+ }
+ else
+ {
+ cmCTestLog(this->CTest, ERROR_MESSAGE,
+ "parse error: missing tag: "
+ << tag << " found [" << line << "]" << std::endl);
+ return false;
+ }
+ return true;
+}
+
+bool cmCTestTestHandler::GetValue(const char* tag,
+ size_t& value,
+ std::ifstream& fin)
+{
+ std::string line;
+ cmSystemTools::GetLineFromStream(fin, line);
+ if(line == tag)
+ {
+ fin >> value;
+ cmSystemTools::GetLineFromStream(fin, line); // read blank line
+ }
+ else
+ {
+ cmCTestLog(this->CTest, ERROR_MESSAGE,
+ "parse error: missing tag: "
+ << tag << " found [" << line.c_str() << "]" << std::endl);
+ return false;
+ }
+ return true;
+}
+
+bool cmCTestTestHandler::GetValue(const char* tag,
+ std::string& value,
+ std::ifstream& fin)
+{
+ std::string line;
+ cmSystemTools::GetLineFromStream(fin, line);
+ if(line == tag)
+ {
+ return cmSystemTools::GetLineFromStream(fin, value);
+ }
+ else
+ {
+ cmCTestLog(this->CTest, ERROR_MESSAGE,
+ "parse error: missing tag: "
+ << tag << " found [" << line << "]" << std::endl);
+ return false;
+ }
+ return true;
+}
+
+
+// This should load only one test and is used in -j N mode.
+// 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();
+ 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;
+ 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;
+ bool 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 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);
+ if(!ok)
+ {
+ 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();
+ it != this->TestList.end(); it ++ )
+ {
+ cmCTestTestProperties& p = *it;
+ fout << "Name:\n"
+ << p.Name.c_str()
+ << "\nDirectory:\n"
+ << p.Directory.c_str()
+ << "\nArgs:\n"
+ << p.Args.size() << "\n";
+ for(std::vector<std::string>::iterator i = p.Args.begin();
+ i != p.Args.end(); ++i)
+ {
+ fout << i->c_str() << "\n";
+ }
+ fout << "Depends:\n" << p.Depends.size() << "\n";
+ for(std::vector<std::string>::iterator i = p.Depends.begin();
+ i != p.Depends.end(); ++i)
+ {
+ fout << i->c_str() << "\n";
+ }
+ fout << "IsInBasedOnREOptions:\n"
+ << p.IsInBasedOnREOptions
+ << "\nWillFail:\n"
+ << p.WillFail
+ << "\nTimeOut:\n"
+ << p.Timeout
+ << "\nIndex:\n"
+ << p.Index << "\n";
+ }
+ fout.close();
+ return fname;
+}
+
+void cmCTestTestHandler::ProcessParallel(std::vector<cmStdString> &passed,
+ std::vector<cmStdString> &failed)
+{
+ this->ComputeTestList();
+ cmCTestMultiProcessHandler parallel;
+ parallel.SetCTest(this->CTest);
+ parallel.SetParallelLevel(this->CTest->GetParallelLevel());
+ std::set<int> depends;
+ std::map<int, std::set<int> > tests;
+ std::map<int, cmStdString> testnames;
+ 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();
+ i != p.Depends.end(); ++i)
+ {
+ for(ListOfTests::iterator it2 = this->TestList.begin();
+ it2 != this->TestList.end(); it2 ++ )
+ {
+ if(it2->Name == *i)
+ {
+ depends.insert(it2->Index);
+ break; // break out of test loop as name can only match 1
+ }
+ }
+ }
+ }
+ tests[it->Index] = depends;
+ }
+ parallel.SetCTestCommand(this->CTest->GetCTestExecutable());
+ parallel.SetTests(tests, testnames);
+ std::string fname = this->SaveTestList();
+ parallel.SetTestCacheFile(fname.c_str());
+ 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
- this->ProcessOneTest(&(*it), passed, failed, cnt,
- static_cast<int>(tmsize));
+ this->ProcessOneTest(&(*it), passed, failed, it->Index,
+ static_cast<int>(this->TotalNumberOfTests));
}
this->EndTest = this->CTest->CurrentTime();
@@ -1890,6 +2189,17 @@ bool cmCTestTestHandler::SetTestsProperties(
std::string(crit->c_str())));
}
}
+ if ( key == "DEPENDS" )
+ {
+ std::vector<std::string> lval;
+ cmSystemTools::ExpandListArgument(val.c_str(), lval);
+ std::vector<std::string>::iterator crit;
+ for ( crit = lval.begin(); crit != lval.end(); ++ crit )
+ {
+ cmCTestTestProperties* tp = &(*rtit);
+ rtit->Depends.push_back(*crit);
+ }
+ }
if ( key == "MEASUREMENT" )
{
size_t pos = val.find_first_of("=");
diff --git a/Source/CTest/cmCTestTestHandler.h b/Source/CTest/cmCTestTestHandler.h
index 395ad3b..1b92831 100644
--- a/Source/CTest/cmCTestTestHandler.h
+++ b/Source/CTest/cmCTestTestHandler.h
@@ -80,6 +80,7 @@ public:
cmStdString Name;
cmStdString Directory;
std::vector<std::string> Args;
+ std::vector<std::string> Depends;
std::vector<std::pair<cmsys::RegularExpression,
std::string> > ErrorRegularExpressions;
std::vector<std::pair<cmsys::RegularExpression,
@@ -88,6 +89,7 @@ public:
bool IsInBasedOnREOptions;
bool WillFail;
double Timeout;
+ int Index;
};
struct cmCTestTestResult
@@ -120,6 +122,7 @@ public:
std::vector<std::string> &failed);
protected:
+ // comput a final test list
virtual int PreProcessHandler();
virtual int PostProcessHandler();
virtual void GenerateTestCommand(std::vector<const char*>& args);
@@ -152,7 +155,7 @@ protected:
-private:
+public:
enum { // Program statuses
NOT_RUN = 0,
TIMEOUT,
@@ -166,7 +169,7 @@ private:
COMPLETED
};
-
+private:
/**
* Generate the Dart compatible output
*/
@@ -183,7 +186,32 @@ private:
* Get the list of tests in directory and subdirectories.
*/
void GetListOfTests();
-
+ // compute the lists of tests that will actually run
+ // based on union regex and -I stuff
+ 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,
+ std::string& value,
+ std::ifstream& fin);
+ bool GetValue(const char* tag,
+ int& value,
+ std::ifstream& fin);
+ bool GetValue(const char* tag,
+ size_t& value,
+ std::ifstream& fin);
+ bool GetValue(const char* tag,
+ bool& value,
+ std::ifstream& fin);
+ 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
*/
@@ -210,6 +238,7 @@ private:
std::string TestsToRunString;
bool UseUnion;
ListOfTests TestList;
+ size_t TotalNumberOfTests;
cmsys::RegularExpression DartStuff;
std::ostream* LogFile;