1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmCTestMultiProcessHandler.cxx,v $
6 Date: $Date: 2009-02-24 22:23:51 $
7 Version: $Revision: 1.5 $
9 Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
10 See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
12 This software is distributed WITHOUT ANY WARRANTY; without even
13 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 PURPOSE. See the above copyright notices for more information.
16 =========================================================================*/
17 #include "cmCTestMultiProcessHandler.h"
18 #include "cmProcess.h"
19 #include "cmStandardIncludes.h"
23 cmCTestMultiProcessHandler::cmCTestMultiProcessHandler()
25 this->ParallelLevel
= 1;
30 cmCTestMultiProcessHandler::SetTests(TestMap
& tests
,
31 std::map
<int,cmStdString
>& testNames
)
33 // set test run map to false for all
34 for(TestMap::iterator i
= this->Tests
.begin();
35 i
!= this->Tests
.end(); ++i
)
37 this->TestRunningMap
[i
->first
] = false;
38 this->TestFinishMap
[i
->first
] = false;
41 this->TestNames
= testNames
;
43 // Set the max number of tests that can be run at the same time.
44 void cmCTestMultiProcessHandler::SetParallelLevel(size_t l
)
46 this->ParallelLevel
= l
;
50 void cmCTestMultiProcessHandler::RunTests()
52 this->StartNextTests();
53 while(this->Tests
.size() != 0)
56 this->StartNextTests();
58 // let all running tests finish
59 while(this->CheckOutput())
63 for(std::map
<int, cmStdString
>::iterator i
=
64 this->TestOutput
.begin();
65 i
!= this->TestOutput
.end(); ++i
)
67 cmCTestLog(this->CTest
, HANDLER_VERBOSE_OUTPUT
,
68 i
->second
<< std::endl
);
73 void cmCTestMultiProcessHandler::StartTestProcess(int test
)
75 cmCTestLog(this->CTest
, HANDLER_VERBOSE_OUTPUT
,
76 " test " << test
<< "\n");
77 this->TestRunningMap
[test
] = true; // mark the test as running
78 // now remove the test itself
79 this->Tests
.erase(test
);
81 cmProcess
* newp
= new cmProcess
;
82 newp
->SetId(this->ProcessId
);
84 newp
->SetCommand(this->CTestCommand
.c_str());
85 std::vector
<std::string
> args
;
86 cmOStringStream width
;
87 if(this->CTest
->GetMaxTestNameWidth())
90 width
<< this->CTest
->GetMaxTestNameWidth();
91 args
.push_back(width
.str().c_str());
95 strm
<< test
<< "," << test
;
96 args
.push_back(strm
.str());
97 args
.push_back("--parallel-cache");
98 args
.push_back(this->CTestCacheFile
.c_str());
99 args
.push_back("--internal-ctest-parallel");
100 cmOStringStream strm2
;
102 args
.push_back(strm2
.str());
103 if(this->CTest
->GetExtraVerbose())
105 args
.push_back("-VV");
107 newp
->SetCommandArguments(args
);
108 if(!newp
->StartProcess())
110 cmCTestLog(this->CTest
, ERROR_MESSAGE
,
111 "Error starting " << newp
->GetCommand() << "\n");
116 this->RunningTests
.insert(newp
);
118 cmCTestLog(this->CTest
, HANDLER_VERBOSE_OUTPUT
,
119 "ctest -I " << test
<< "\n");
123 bool cmCTestMultiProcessHandler::StartTest(int test
)
125 // copy the depend tests locally because when
126 // a test is finished it will be removed from the depend list
127 // and we don't want to be iterating a list while removing from it
128 TestSet depends
= this->Tests
[test
];
129 size_t totalDepends
= depends
.size();
132 for(TestSet::const_iterator i
= depends
.begin();
133 i
!= depends
.end(); ++i
)
135 // if the test is not already running then start it
136 if(!this->TestRunningMap
[*i
])
138 // this test might be finished, but since
139 // this is a copy of the depend map we might
141 if(!this->TestFinishMap
[*i
])
143 // only start one test in this function
144 return this->StartTest(*i
);
148 // the depend has been and finished
154 // if there are no depends left then run this test
155 if(totalDepends
== 0)
157 // Start this test it has no depends
158 this->StartTestProcess(test
);
161 // This test was not able to start because it is waiting
166 void cmCTestMultiProcessHandler::StartNextTests()
168 cmCTestLog(this->CTest
, HANDLER_VERBOSE_OUTPUT
, std::endl
169 << "Number of running tests : " << this->RunningTests
.size()
171 size_t numToStart
= this->ParallelLevel
- this->RunningTests
.size();
176 TestMap tests
= this->Tests
;
177 for(TestMap::iterator i
= tests
.begin();
178 i
!= tests
.end(); ++i
)
180 // start test should start only one test
181 if(this->StartTest(i
->first
))
187 cmCTestLog(this->CTest
, HANDLER_VERBOSE_OUTPUT
, std::endl
188 << "Test did not start waiting on depends to finish: "
189 << i
->first
<< "\n");
199 bool cmCTestMultiProcessHandler::CheckOutput()
201 // no more output we are done
202 if(this->RunningTests
.size() == 0)
206 std::vector
<cmProcess
*> finished
;
207 std::string out
, err
;
208 for(std::set
<cmProcess
*>::const_iterator i
= this->RunningTests
.begin();
209 i
!= this->RunningTests
.end(); ++i
)
212 int pipe
= p
->CheckOutput(.1, out
, err
);
213 if(pipe
== cmsysProcess_Pipe_STDOUT
)
215 cmCTestLog(this->CTest
, HANDLER_OUTPUT
,
216 p
->GetId() << ": " << out
<< std::endl
);
217 this->TestOutput
[ p
->GetId() ] += out
;
218 this->TestOutput
[ p
->GetId() ] += "\n";
220 else if(pipe
== cmsysProcess_Pipe_STDERR
)
222 cmCTestLog(this->CTest
, HANDLER_OUTPUT
,
223 p
->GetId() << ": " << err
<< std::endl
);
224 this->TestOutput
[ p
->GetId() ] += err
;
225 this->TestOutput
[ p
->GetId() ] += "\n";
229 finished
.push_back(p
);
232 for( std::vector
<cmProcess
*>::iterator i
= finished
.begin();
233 i
!= finished
.end(); ++i
)
241 void cmCTestMultiProcessHandler::EndTest(cmProcess
* p
)
243 // Should have a way of getting this stuff from the
244 // launched ctest, maybe a temp file or some extra xml
245 // stuff in the stdout
246 // Need things like Reason and ExecutionTime, Path, etc.
247 int test
= p
->GetId();
248 int exitVal
= p
->GetExitValue();
249 cmCTestTestHandler::cmCTestTestResult cres
;
251 cres
.ExecutionTime
= 0;// ???
252 cres
.ReturnValue
= exitVal
;
253 cres
.Status
= cmCTestTestHandler::COMPLETED
;
254 cres
.TestCount
= test
;
255 cres
.Name
= this->TestNames
[test
];
259 cres
.Status
= cmCTestTestHandler::FAILED
;
260 this->Failed
->push_back(this->TestNames
[test
]);
264 this->Passed
->push_back(this->TestNames
[test
]);
266 this->TestResults
->push_back(cres
);
267 // remove test from depend of all other tests
268 for(TestMap::iterator i
= this->Tests
.begin();
269 i
!= this->Tests
.end(); ++i
)
271 i
->second
.erase(test
);
273 this->TestFinishMap
[test
] = true;
274 this->TestRunningMap
[test
] = false;
275 this->RunningTests
.erase(p
);
277 cmCTestLog(this->CTest
, HANDLER_VERBOSE_OUTPUT
,
278 "finish test " << test
<< "\n");
282 void cmCTestMultiProcessHandler::PrintTests()
285 for( TestMap::iterator i
= this->Tests
.begin();
286 i
!= this->Tests
.end(); ++i
)
288 std::cout
<< "Test " << i
->first
<< " (";
289 for(TestSet::iterator j
= i
->second
.begin();
290 j
!= i
->second
.end(); ++j
)
292 std::cout
<< *j
<< " ";
301 cmCTestMultiProcessHandler h
;
302 h
.SetParallelLevel(4);
303 std::map
<int, std::set
<int> > tests
;
304 std::set
<int> depends
;
305 for(int i
=1; i
< 92; i
++)
310 depends
.insert(45); subprject
311 tests
[46] = depends
; subproject
-stage2
313 depends
.insert(55); simpleinstall simpleinstall
-s2
316 depends
.insert(70); wrapping
317 tests
[71] = depends
; qtwrapping
319 depends
.insert(71); qtwrapping
320 tests
[72] = depends
; testdriver1
322 depends
.insert(72) testdriver1
323 tests
[73] = depends
; testdriver2
325 depends
.insert(73); testdriver2
326 tests
[74] = depends
; testdriver3
328 depends
.insert(79); linkorder1
329 tests
[80] = depends
; linkorder2