diff options
author | Bill Hoffman <bill.hoffman@kitware.com> | 2008-07-03 13:31:33 (GMT) |
---|---|---|
committer | Bill Hoffman <bill.hoffman@kitware.com> | 2008-07-03 13:31:33 (GMT) |
commit | bb7b27e417b72d0387af5393b81ed35deab52c4b (patch) | |
tree | 42d2efd6a2131935eefbe540cd15e8bcb0ed2ac7 /Source/CTest/cmCTestMultiProcessHandler.cxx | |
parent | ea71721b805708f82898c32e007aa3e29e6f458f (diff) | |
download | CMake-bb7b27e417b72d0387af5393b81ed35deab52c4b.zip CMake-bb7b27e417b72d0387af5393b81ed35deab52c4b.tar.gz CMake-bb7b27e417b72d0387af5393b81ed35deab52c4b.tar.bz2 |
ENH: add initial ctest -j feature
Diffstat (limited to 'Source/CTest/cmCTestMultiProcessHandler.cxx')
-rw-r--r-- | Source/CTest/cmCTestMultiProcessHandler.cxx | 323 |
1 files changed, 323 insertions, 0 deletions
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 |