1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmCTestMultiProcessHandler.cxx,v $
6 Date: $Date: 2009-09-11 16:26:41 $
7 Version: $Revision: 1.23 $
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"
21 #include "cmSystemTools.h"
24 cmCTestMultiProcessHandler::cmCTestMultiProcessHandler()
26 this->ParallelLevel
= 1;
28 this->RunningCount
= 0;
31 cmCTestMultiProcessHandler::~cmCTestMultiProcessHandler()
37 cmCTestMultiProcessHandler::SetTests(TestMap
& tests
,
38 PropertiesMap
& properties
)
41 this->Properties
= properties
;
42 this->Total
= this->Tests
.size();
43 // set test run map to false for all
44 for(TestMap::iterator i
= this->Tests
.begin();
45 i
!= this->Tests
.end(); ++i
)
47 this->TestRunningMap
[i
->first
] = false;
48 this->TestFinishMap
[i
->first
] = false;
51 this->CreateTestCostList();
54 // Set the max number of tests that can be run at the same time.
55 void cmCTestMultiProcessHandler::SetParallelLevel(size_t level
)
57 this->ParallelLevel
= level
< 1 ? 1 : level
;
60 //---------------------------------------------------------
61 void cmCTestMultiProcessHandler::RunTests()
64 this->TestHandler
->SetMaxIndex(this->FindMaxIndex());
65 this->StartNextTests();
66 while(this->Tests
.size() != 0)
69 this->StartNextTests();
71 // let all running tests finish
72 while(this->CheckOutput())
78 //---------------------------------------------------------
79 void cmCTestMultiProcessHandler::StartTestProcess(int test
)
81 cmCTestLog(this->CTest
, HANDLER_VERBOSE_OUTPUT
, "test " << test
<< "\n");
82 this->TestRunningMap
[test
] = true; // mark the test as running
83 // now remove the test itself
84 this->EraseTest(test
);
86 cmCTestRunTest
* testRun
= new cmCTestRunTest(this->TestHandler
);
87 testRun
->SetIndex(test
);
88 testRun
->SetTestProperties(this->Properties
[test
]);
89 if(testRun
->StartTest())
91 this->RunningTests
.insert(testRun
);
96 this->RunningCount
-= GetProcessorsUsed(test
);
97 testRun
->EndTest(this->Completed
, this->Total
, false);
101 //---------------------------------------------------------
102 void cmCTestMultiProcessHandler::EraseTest(int test
)
104 this->Tests
.erase(test
);
105 for(TestCostMap::iterator i
= this->TestCosts
.begin();
106 i
!= this->TestCosts
.end(); ++i
)
108 if(i
->second
.find(test
) != i
->second
.end())
110 i
->second
.erase(test
);
116 //---------------------------------------------------------
117 inline size_t cmCTestMultiProcessHandler::GetProcessorsUsed(int test
)
120 static_cast<int>(this->Properties
[test
]->Processors
);
121 //If this is set to run serially, it must run alone.
122 //Also, if processors setting is set higher than the -j
123 //setting, we default to using all of the process slots.
124 if(this->Properties
[test
]->RunSerial
125 || processors
> this->ParallelLevel
)
127 processors
= this->ParallelLevel
;
132 //---------------------------------------------------------
133 bool cmCTestMultiProcessHandler::StartTest(int test
)
135 // copy the depend tests locally because when
136 // a test is finished it will be removed from the depend list
137 // and we don't want to be iterating a list while removing from it
138 TestSet depends
= this->Tests
[test
];
139 size_t totalDepends
= depends
.size();
142 for(TestSet::const_iterator i
= depends
.begin();
143 i
!= depends
.end(); ++i
)
145 // if the test is not already running then start it
146 if(!this->TestRunningMap
[*i
])
148 // this test might be finished, but since
149 // this is a copy of the depend map we might
151 if(!this->TestFinishMap
[*i
])
153 // only start one test in this function
154 return this->StartTest(*i
);
158 // the depend has been and finished
164 // if there are no depends left then run this test
165 if(totalDepends
== 0)
167 this->StartTestProcess(test
);
170 // This test was not able to start because it is waiting
175 //---------------------------------------------------------
176 void cmCTestMultiProcessHandler::StartNextTests()
178 size_t numToStart
= this->ParallelLevel
- this->RunningCount
;
184 for(TestCostMap::reverse_iterator i
= this->TestCosts
.rbegin();
185 i
!= this->TestCosts
.rend(); ++i
)
187 TestSet tests
= i
->second
; //copy the test set
188 for(TestSet::iterator test
= tests
.begin();
189 test
!= tests
.end(); ++test
)
191 size_t processors
= GetProcessorsUsed(*test
);
192 if(processors
> numToStart
)
196 if(this->StartTest(*test
))
198 numToStart
-= processors
;
199 this->RunningCount
+= processors
;
203 cmCTestLog(this->CTest
, HANDLER_VERBOSE_OUTPUT
, std::endl
204 << "Test did not start waiting on depends to finish: "
215 //---------------------------------------------------------
216 bool cmCTestMultiProcessHandler::CheckOutput()
218 // no more output we are done
219 if(this->RunningTests
.size() == 0)
223 std::vector
<cmCTestRunTest
*> finished
;
224 std::string out
, err
;
225 for(std::set
<cmCTestRunTest
*>::const_iterator i
= this->RunningTests
.begin();
226 i
!= this->RunningTests
.end(); ++i
)
228 cmCTestRunTest
* p
= *i
;
229 if(!p
->CheckOutput())
231 finished
.push_back(p
);
234 for( std::vector
<cmCTestRunTest
*>::iterator i
= finished
.begin();
235 i
!= finished
.end(); ++i
)
238 cmCTestRunTest
* p
= *i
;
239 int test
= p
->GetIndex();
241 if(p
->EndTest(this->Completed
, this->Total
, true))
243 this->Passed
->push_back(p
->GetTestProperties()->Name
);
247 this->Failed
->push_back(p
->GetTestProperties()->Name
);
249 for(TestMap::iterator j
= this->Tests
.begin();
250 j
!= this->Tests
.end(); ++j
)
252 j
->second
.erase(test
);
254 this->TestFinishMap
[test
] = true;
255 this->TestRunningMap
[test
] = false;
256 this->RunningTests
.erase(p
);
257 this->WriteCheckpoint(test
);
258 this->WriteCostData(test
, p
->GetTestResults().ExecutionTime
);
259 this->RunningCount
-= GetProcessorsUsed(test
);
265 //---------------------------------------------------------
266 void cmCTestMultiProcessHandler::ReadCostData()
268 std::string fname
= this->CTest
->GetBinaryDir()
269 + "/Testing/Temporary/CTestCostData.txt";
271 if(cmSystemTools::FileExists(fname
.c_str(), true)
272 && this->ParallelLevel
> 1)
275 fin
.open(fname
.c_str());
277 while(std::getline(fin
, line
))
279 std::vector
<cmsys::String
> parts
=
280 cmSystemTools::SplitString(line
.c_str(), ' ');
282 int index
= atoi(parts
[0].c_str());
283 float cost
= atof(parts
[1].c_str());
284 if(this->Properties
[index
] && this->Properties
[index
]->Cost
== 0)
286 this->Properties
[index
]->Cost
= cost
;
291 cmSystemTools::RemoveFile(fname
.c_str());
294 //---------------------------------------------------------
295 void cmCTestMultiProcessHandler::CreateTestCostList()
297 for(TestMap::iterator i
= this->Tests
.begin();
298 i
!= this->Tests
.end(); ++i
)
300 this->TestCosts
[this->Properties
[i
->first
]->Cost
].insert(i
->first
);
304 //---------------------------------------------------------
305 void cmCTestMultiProcessHandler::WriteCostData(int index
, float cost
)
307 std::string fname
= this->CTest
->GetBinaryDir()
308 + "/Testing/Temporary/CTestCostData.txt";
310 fout
.open(fname
.c_str(), std::ios::app
);
311 fout
<< index
<< " " << cost
<< "\n";
315 //---------------------------------------------------------
316 void cmCTestMultiProcessHandler::WriteCheckpoint(int index
)
318 std::string fname
= this->CTest
->GetBinaryDir()
319 + "/Testing/Temporary/CTestCheckpoint.txt";
321 fout
.open(fname
.c_str(), std::ios::app
);
322 fout
<< index
<< "\n";
326 //---------------------------------------------------------
327 void cmCTestMultiProcessHandler::MarkFinished()
329 std::string fname
= this->CTest
->GetBinaryDir()
330 + "/Testing/Temporary/CTestCheckpoint.txt";
331 cmSystemTools::RemoveFile(fname
.c_str());
334 //---------------------------------------------------------
336 void cmCTestMultiProcessHandler::PrintTestList()
339 for (PropertiesMap::iterator it
= this->Properties
.begin();
340 it
!= this->Properties
.end(); it
++ )
343 cmCTestTestHandler::cmCTestTestProperties
& p
= *it
->second
;
345 cmCTestRunTest
testRun(this->TestHandler
);
346 testRun
.SetIndex(p
.Index
);
347 testRun
.SetTestProperties(&p
);
348 testRun
.ComputeArguments(); //logs the command in verbose mode
350 cmCTestLog(this->CTest
, HANDLER_OUTPUT
, std::setw(3)
352 cmCTestLog(this->CTest
, HANDLER_OUTPUT
, std::setw(3)
353 << this->Total
<< " ");
354 if (this->TestHandler
->MemCheck
)
356 cmCTestLog(this->CTest
, HANDLER_OUTPUT
, "Memory Check");
360 cmCTestLog(this->CTest
, HANDLER_OUTPUT
, "Testing");
362 cmCTestLog(this->CTest
, HANDLER_OUTPUT
, " ");
363 cmCTestLog(this->CTest
, HANDLER_OUTPUT
, p
.Name
.c_str() << std::endl
);
367 //---------------------------------------------------------
368 void cmCTestMultiProcessHandler::CheckResume()
370 std::string fname
= this->CTest
->GetBinaryDir()
371 + "/Testing/Temporary/CTestCheckpoint.txt";
372 if(this->CTest
->GetFailover())
374 if(cmSystemTools::FileExists(fname
.c_str(), true))
376 *this->TestHandler
->LogFile
<< "Resuming previously interrupted test set"
378 << "----------------------------------------------------------"
382 fin
.open(fname
.c_str());
384 while(std::getline(fin
, line
))
386 int index
= atoi(line
.c_str());
387 this->RemoveTest(index
);
394 if(cmSystemTools::FileExists(fname
.c_str(), true))
396 cmSystemTools::RemoveFile(fname
.c_str());
401 //---------------------------------------------------------
402 void cmCTestMultiProcessHandler::RemoveTest(int index
)
404 this->EraseTest(index
);
405 this->Properties
.erase(index
);
406 this->TestRunningMap
[index
] = false;
407 this->TestFinishMap
[index
] = true;
411 //---------------------------------------------------------
412 int cmCTestMultiProcessHandler::FindMaxIndex()
415 cmCTestMultiProcessHandler::TestMap::iterator i
= this->Tests
.begin();
416 for(; i
!= this->Tests
.end(); ++i
)