mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-01-26 19:09:06 +00:00
Merge topic 'ctest-cleanup'
5d2e93f9e8 cmCTestMultiProcessHandler: Simplify logic on unavailable resources a4b061a035 cmCTestMultiProcessHandler: Clarify resource availability error member names 1487e540aa cmCTestMultiProcessHandler: Reduce repeat test property map lookups b02b628ad9 cmCTestMultiProcessHandler: Simplify loop termination on serial test 8f1e8af0cc cmCTestMultiProcessHandler: Stop searching for tests when limit is reached bd0b4ca867 cmCTestMultiProcessHandler: Invert spare load condition 9b548139fd cmCTestMultiProcessHandler: Clarify search for tests <= concurrency limit ee321dc85f cmCTestMultiProcessHandler: Clarify search for tests <= spare load ... Acked-by: Kitware Robot <kwrobot@kitware.com> Acked-by: buildbot <buildbot@kitware.com> Merge-request: !8981
This commit is contained in:
commit
6818925b9a
@ -82,17 +82,12 @@ cmCTestMultiProcessHandler::cmCTestMultiProcessHandler()
|
||||
cmCTestMultiProcessHandler::~cmCTestMultiProcessHandler() = default;
|
||||
|
||||
// Set the tests
|
||||
void cmCTestMultiProcessHandler::SetTests(TestMap& tests,
|
||||
PropertiesMap& properties)
|
||||
void cmCTestMultiProcessHandler::SetTests(TestMap tests,
|
||||
PropertiesMap properties)
|
||||
{
|
||||
this->Tests = tests;
|
||||
this->Properties = properties;
|
||||
this->Total = this->Tests.size();
|
||||
// set test run map to false for all
|
||||
for (auto const& t : this->Tests) {
|
||||
this->TestRunningMap[t.first] = false;
|
||||
this->TestFinishMap[t.first] = false;
|
||||
}
|
||||
this->PendingTests = std::move(tests);
|
||||
this->Properties = std::move(properties);
|
||||
this->Total = this->PendingTests.size();
|
||||
if (!this->CTest->GetShowOnly()) {
|
||||
this->ReadCostData();
|
||||
this->HasCycles = !this->CheckCycles();
|
||||
@ -125,6 +120,11 @@ void cmCTestMultiProcessHandler::SetTestLoad(unsigned long load)
|
||||
}
|
||||
}
|
||||
|
||||
bool cmCTestMultiProcessHandler::Complete()
|
||||
{
|
||||
return this->Completed == this->Total;
|
||||
}
|
||||
|
||||
void cmCTestMultiProcessHandler::RunTests()
|
||||
{
|
||||
this->CheckResume();
|
||||
@ -133,14 +133,14 @@ void cmCTestMultiProcessHandler::RunTests()
|
||||
}
|
||||
this->TestHandler->SetMaxIndex(this->FindMaxIndex());
|
||||
|
||||
uv_loop_init(&this->Loop);
|
||||
this->Loop.init();
|
||||
this->StartNextTests();
|
||||
uv_run(&this->Loop, UV_RUN_DEFAULT);
|
||||
uv_loop_close(&this->Loop);
|
||||
uv_run(this->Loop, UV_RUN_DEFAULT);
|
||||
this->Loop.reset();
|
||||
|
||||
if (!this->StopTimePassed && !this->CheckStopOnFailure()) {
|
||||
assert(this->Completed == this->Total);
|
||||
assert(this->Tests.empty());
|
||||
assert(this->Complete());
|
||||
assert(this->PendingTests.empty());
|
||||
}
|
||||
assert(this->AllResourcesAvailable());
|
||||
|
||||
@ -152,9 +152,7 @@ bool cmCTestMultiProcessHandler::StartTestProcess(int test)
|
||||
{
|
||||
if (this->HaveAffinity && this->Properties[test]->WantAffinity) {
|
||||
size_t needProcessors = this->GetProcessorsUsed(test);
|
||||
if (needProcessors > this->ProcessorsAvailable.size()) {
|
||||
return false;
|
||||
}
|
||||
assert(needProcessors <= this->ProcessorsAvailable.size());
|
||||
std::vector<size_t> affinity;
|
||||
affinity.reserve(needProcessors);
|
||||
for (size_t i = 0; i < needProcessors; ++i) {
|
||||
@ -167,19 +165,16 @@ bool cmCTestMultiProcessHandler::StartTestProcess(int test)
|
||||
|
||||
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
|
||||
"test " << test << "\n", this->Quiet);
|
||||
this->TestRunningMap[test] = true; // mark the test as running
|
||||
// now remove the test itself
|
||||
this->EraseTest(test);
|
||||
this->ErasePendingTest(test);
|
||||
this->RunningCount += this->GetProcessorsUsed(test);
|
||||
|
||||
auto testRun = cm::make_unique<cmCTestRunTest>(*this);
|
||||
auto testRun = cm::make_unique<cmCTestRunTest>(*this, test);
|
||||
|
||||
if (this->RepeatMode != cmCTest::Repeat::Never) {
|
||||
testRun->SetRepeatMode(this->RepeatMode);
|
||||
testRun->SetNumberOfRuns(this->RepeatCount);
|
||||
}
|
||||
testRun->SetIndex(test);
|
||||
testRun->SetTestProperties(this->Properties[test]);
|
||||
if (this->UseResourceSpec) {
|
||||
testRun->SetUseAllocatedResources(true);
|
||||
testRun->SetAllocatedResources(this->AllocatedResources[test]);
|
||||
@ -197,18 +192,18 @@ bool cmCTestMultiProcessHandler::StartTestProcess(int test)
|
||||
// working directory because FinishTestProcess() will try to unlock them
|
||||
this->LockResources(test);
|
||||
|
||||
if (!this->ResourceAllocationErrors[test].empty()) {
|
||||
if (!this->ResourceAvailabilityErrors[test].empty()) {
|
||||
std::ostringstream e;
|
||||
e << "Insufficient resources for test " << this->Properties[test]->Name
|
||||
<< ":\n\n";
|
||||
for (auto const& it : this->ResourceAllocationErrors[test]) {
|
||||
for (auto const& it : this->ResourceAvailabilityErrors[test]) {
|
||||
switch (it.second) {
|
||||
case ResourceAllocationError::NoResourceType:
|
||||
case ResourceAvailabilityError::NoResourceType:
|
||||
e << " Test requested resources of type '" << it.first
|
||||
<< "' which does not exist\n";
|
||||
break;
|
||||
|
||||
case ResourceAllocationError::InsufficientResources:
|
||||
case ResourceAvailabilityError::InsufficientResources:
|
||||
e << " Test requested resources of type '" << it.first
|
||||
<< "' in the following amounts:\n";
|
||||
for (auto const& group : this->Properties[test]->ResourceGroups) {
|
||||
@ -257,6 +252,12 @@ bool cmCTestMultiProcessHandler::AllocateResources(int index)
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the test needs unavailable resources then do not allocate anything
|
||||
// because it will never run. We will issue the recorded errors instead.
|
||||
if (!this->ResourceAvailabilityErrors[index].empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::map<std::string, std::vector<cmCTestBinPackerAllocation>> allocations;
|
||||
if (!this->TryAllocateResources(index, allocations)) {
|
||||
return false;
|
||||
@ -281,7 +282,7 @@ bool cmCTestMultiProcessHandler::AllocateResources(int index)
|
||||
bool cmCTestMultiProcessHandler::TryAllocateResources(
|
||||
int index,
|
||||
std::map<std::string, std::vector<cmCTestBinPackerAllocation>>& allocations,
|
||||
std::map<std::string, ResourceAllocationError>* errors)
|
||||
std::map<std::string, ResourceAvailabilityError>* errors)
|
||||
{
|
||||
allocations.clear();
|
||||
|
||||
@ -301,7 +302,7 @@ bool cmCTestMultiProcessHandler::TryAllocateResources(
|
||||
for (auto& it : allocations) {
|
||||
if (!availableResources.count(it.first)) {
|
||||
if (errors) {
|
||||
(*errors)[it.first] = ResourceAllocationError::NoResourceType;
|
||||
(*errors)[it.first] = ResourceAvailabilityError::NoResourceType;
|
||||
result = false;
|
||||
} else {
|
||||
return false;
|
||||
@ -309,7 +310,7 @@ bool cmCTestMultiProcessHandler::TryAllocateResources(
|
||||
} else if (!cmAllocateCTestResourcesRoundRobin(
|
||||
availableResources.at(it.first), it.second)) {
|
||||
if (errors) {
|
||||
(*errors)[it.first] = ResourceAllocationError::InsufficientResources;
|
||||
(*errors)[it.first] = ResourceAvailabilityError::InsufficientResources;
|
||||
result = false;
|
||||
} else {
|
||||
return false;
|
||||
@ -356,14 +357,14 @@ bool cmCTestMultiProcessHandler::AllResourcesAvailable()
|
||||
return true;
|
||||
}
|
||||
|
||||
void cmCTestMultiProcessHandler::CheckResourcesAvailable()
|
||||
void cmCTestMultiProcessHandler::CheckResourceAvailability()
|
||||
{
|
||||
if (this->UseResourceSpec) {
|
||||
for (auto test : this->SortedTests) {
|
||||
for (auto const& t : this->PendingTests) {
|
||||
std::map<std::string, std::vector<cmCTestBinPackerAllocation>>
|
||||
allocations;
|
||||
this->TryAllocateResources(test, allocations,
|
||||
&this->ResourceAllocationErrors[test]);
|
||||
this->TryAllocateResources(t.first, allocations,
|
||||
&this->ResourceAvailabilityErrors[t.first]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -399,30 +400,30 @@ void cmCTestMultiProcessHandler::SetStopTimePassed()
|
||||
|
||||
void cmCTestMultiProcessHandler::LockResources(int index)
|
||||
{
|
||||
this->LockedResources.insert(
|
||||
this->Properties[index]->LockedResources.begin(),
|
||||
this->Properties[index]->LockedResources.end());
|
||||
|
||||
if (this->Properties[index]->RunSerial) {
|
||||
auto* properties = this->Properties[index];
|
||||
this->LockedResources.insert(properties->LockedResources.begin(),
|
||||
properties->LockedResources.end());
|
||||
if (properties->RunSerial) {
|
||||
this->SerialTestRunning = true;
|
||||
}
|
||||
}
|
||||
|
||||
void cmCTestMultiProcessHandler::UnlockResources(int index)
|
||||
{
|
||||
for (std::string const& i : this->Properties[index]->LockedResources) {
|
||||
auto* properties = this->Properties[index];
|
||||
for (std::string const& i : properties->LockedResources) {
|
||||
this->LockedResources.erase(i);
|
||||
}
|
||||
if (this->Properties[index]->RunSerial) {
|
||||
if (properties->RunSerial) {
|
||||
this->SerialTestRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
void cmCTestMultiProcessHandler::EraseTest(int test)
|
||||
void cmCTestMultiProcessHandler::ErasePendingTest(int test)
|
||||
{
|
||||
this->Tests.erase(test);
|
||||
this->SortedTests.erase(
|
||||
std::find(this->SortedTests.begin(), this->SortedTests.end(), test));
|
||||
this->PendingTests.erase(test);
|
||||
this->OrderedTests.erase(
|
||||
std::find(this->OrderedTests.begin(), this->OrderedTests.end(), test));
|
||||
}
|
||||
|
||||
inline size_t cmCTestMultiProcessHandler::GetProcessorsUsed(int test)
|
||||
@ -455,15 +456,12 @@ bool cmCTestMultiProcessHandler::StartTest(int test)
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate resources
|
||||
if (this->ResourceAllocationErrors[test].empty() &&
|
||||
!this->AllocateResources(test)) {
|
||||
this->DeallocateResources(test);
|
||||
if (!this->AllocateResources(test)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if there are no depends left then run this test
|
||||
if (this->Tests[test].empty()) {
|
||||
if (this->PendingTests[test].Depends.empty()) {
|
||||
return this->StartTestProcess(test);
|
||||
}
|
||||
// This test was not able to start because it is waiting
|
||||
@ -480,7 +478,7 @@ void cmCTestMultiProcessHandler::StartNextTests()
|
||||
uv_timer_stop(this->TestLoadRetryTimer);
|
||||
}
|
||||
|
||||
if (this->Tests.empty()) {
|
||||
if (this->PendingTests.empty()) {
|
||||
this->TestLoadRetryTimer.reset();
|
||||
return;
|
||||
}
|
||||
@ -541,12 +539,14 @@ void cmCTestMultiProcessHandler::StartNextTests()
|
||||
}
|
||||
}
|
||||
|
||||
TestList copy = this->SortedTests;
|
||||
for (auto const& test : copy) {
|
||||
// Take a nap if we're currently performing a RUN_SERIAL test.
|
||||
if (this->SerialTestRunning) {
|
||||
break;
|
||||
}
|
||||
// Start tests in the preferred order, each subject to readiness checks.
|
||||
auto ti = this->OrderedTests.begin();
|
||||
while (numToStart > 0 && !this->SerialTestRunning &&
|
||||
ti != this->OrderedTests.end()) {
|
||||
// Increment the test iterator now because the current list
|
||||
// entry may be deleted below.
|
||||
int test = *ti++;
|
||||
|
||||
// We can only start a RUN_SERIAL test if no other tests are also
|
||||
// running.
|
||||
if (this->Properties[test]->RunSerial && this->RunningCount > 0) {
|
||||
@ -554,28 +554,32 @@ void cmCTestMultiProcessHandler::StartNextTests()
|
||||
}
|
||||
|
||||
size_t processors = this->GetProcessorsUsed(test);
|
||||
bool testLoadOk = true;
|
||||
if (this->TestLoad > 0) {
|
||||
if (processors <= spareLoad) {
|
||||
cmCTestLog(this->CTest, DEBUG,
|
||||
"OK to run " << this->GetName(test) << ", it requires "
|
||||
<< processors << " procs & system load is: "
|
||||
<< systemLoad << std::endl);
|
||||
allTestsFailedTestLoadCheck = false;
|
||||
} else {
|
||||
testLoadOk = false;
|
||||
// Exclude tests that are too big to fit in the spare load.
|
||||
if (processors > spareLoad) {
|
||||
// Keep track of the smallest excluded test to report in message below.
|
||||
if (processors <= minProcessorsRequired) {
|
||||
minProcessorsRequired = processors;
|
||||
testWithMinProcessors = this->GetName(test);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// We found a test that fits in the spare load.
|
||||
allTestsFailedTestLoadCheck = false;
|
||||
cmCTestLog(this->CTest, DEBUG,
|
||||
"OK to run "
|
||||
<< this->GetName(test) << ", it requires " << processors
|
||||
<< " procs & system load is: " << systemLoad << std::endl);
|
||||
}
|
||||
|
||||
if (processors <= minProcessorsRequired) {
|
||||
minProcessorsRequired = processors;
|
||||
testWithMinProcessors = this->GetName(test);
|
||||
// Exclude tests that are too big to fit in the concurrency limit.
|
||||
if (processors > numToStart) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (testLoadOk && processors <= numToStart && this->StartTest(test)) {
|
||||
if (this->StartTest(test)) {
|
||||
numToStart -= processors;
|
||||
} else if (numToStart == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -583,8 +587,8 @@ void cmCTestMultiProcessHandler::StartNextTests()
|
||||
// Find out whether there are any non RUN_SERIAL tests left, so that the
|
||||
// correct warning may be displayed.
|
||||
bool onlyRunSerialTestsLeft = true;
|
||||
for (auto const& test : copy) {
|
||||
if (!this->Properties[test]->RunSerial) {
|
||||
for (auto const& t : this->PendingTests) {
|
||||
if (!this->Properties[t.first]->RunSerial) {
|
||||
onlyRunSerialTestsLeft = false;
|
||||
}
|
||||
}
|
||||
@ -596,7 +600,7 @@ void cmCTestMultiProcessHandler::StartNextTests()
|
||||
} else if (onlyRunSerialTestsLeft) {
|
||||
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
|
||||
"Only RUN_SERIAL tests remain, awaiting available slot.");
|
||||
} else {
|
||||
} else if (!testWithMinProcessors.empty()) {
|
||||
/* clang-format off */
|
||||
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
|
||||
"System Load: " << systemLoad << ", "
|
||||
@ -604,6 +608,12 @@ void cmCTestMultiProcessHandler::StartNextTests()
|
||||
"Smallest test " << testWithMinProcessors <<
|
||||
" requires " << minProcessorsRequired);
|
||||
/* clang-format on */
|
||||
} else {
|
||||
/* clang-format off */
|
||||
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
|
||||
"System Load: " << systemLoad << ", "
|
||||
"Max Allowed Load: " << this->TestLoad);
|
||||
/* clang-format on */
|
||||
}
|
||||
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "*****" << std::endl);
|
||||
|
||||
@ -613,7 +623,7 @@ void cmCTestMultiProcessHandler::StartNextTests()
|
||||
milliseconds = 10;
|
||||
}
|
||||
if (this->TestLoadRetryTimer.get() == nullptr) {
|
||||
this->TestLoadRetryTimer.init(this->Loop, this);
|
||||
this->TestLoadRetryTimer.init(*this->Loop, this);
|
||||
}
|
||||
this->TestLoadRetryTimer.start(
|
||||
&cmCTestMultiProcessHandler::OnTestLoadRetryCB, milliseconds, 0);
|
||||
@ -653,12 +663,10 @@ void cmCTestMultiProcessHandler::FinishTestProcess(
|
||||
this->Failed->push_back(properties->Name);
|
||||
}
|
||||
|
||||
for (auto& t : this->Tests) {
|
||||
t.second.erase(test);
|
||||
for (auto& t : this->PendingTests) {
|
||||
t.second.Depends.erase(test);
|
||||
}
|
||||
|
||||
this->TestFinishMap[test] = true;
|
||||
this->TestRunningMap[test] = false;
|
||||
this->WriteCheckpoint(test);
|
||||
this->DeallocateResources(test);
|
||||
this->UnlockResources(test);
|
||||
@ -803,7 +811,7 @@ void cmCTestMultiProcessHandler::CreateTestCostList()
|
||||
|
||||
void cmCTestMultiProcessHandler::CreateParallelTestCostList()
|
||||
{
|
||||
TestSet alreadySortedTests;
|
||||
TestSet alreadyOrderedTests;
|
||||
|
||||
std::list<TestSet> priorityStack;
|
||||
priorityStack.emplace_back();
|
||||
@ -811,11 +819,11 @@ void cmCTestMultiProcessHandler::CreateParallelTestCostList()
|
||||
|
||||
// In parallel test runs add previously failed tests to the front
|
||||
// of the cost list and queue other tests for further sorting
|
||||
for (auto const& t : this->Tests) {
|
||||
for (auto const& t : this->PendingTests) {
|
||||
if (cm::contains(this->LastTestsFailed, this->Properties[t.first]->Name)) {
|
||||
// If the test failed last time, it should be run first.
|
||||
this->SortedTests.push_back(t.first);
|
||||
alreadySortedTests.insert(t.first);
|
||||
this->OrderedTests.push_back(t.first);
|
||||
alreadyOrderedTests.insert(t.first);
|
||||
} else {
|
||||
topLevel.insert(t.first);
|
||||
}
|
||||
@ -830,7 +838,7 @@ void cmCTestMultiProcessHandler::CreateParallelTestCostList()
|
||||
TestSet& currentSet = priorityStack.back();
|
||||
|
||||
for (auto const& i : previousSet) {
|
||||
TestSet const& dependencies = this->Tests[i];
|
||||
TestSet const& dependencies = this->PendingTests[i].Depends;
|
||||
currentSet.insert(dependencies.begin(), dependencies.end());
|
||||
}
|
||||
|
||||
@ -851,9 +859,9 @@ void cmCTestMultiProcessHandler::CreateParallelTestCostList()
|
||||
TestComparator(this));
|
||||
|
||||
for (auto const& j : sortedCopy) {
|
||||
if (!cm::contains(alreadySortedTests, j)) {
|
||||
this->SortedTests.push_back(j);
|
||||
alreadySortedTests.insert(j);
|
||||
if (!cm::contains(alreadyOrderedTests, j)) {
|
||||
this->OrderedTests.push_back(j);
|
||||
alreadyOrderedTests.insert(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -862,7 +870,7 @@ void cmCTestMultiProcessHandler::CreateParallelTestCostList()
|
||||
void cmCTestMultiProcessHandler::GetAllTestDependencies(int test,
|
||||
TestList& dependencies)
|
||||
{
|
||||
TestSet const& dependencySet = this->Tests[test];
|
||||
TestSet const& dependencySet = this->PendingTests[test].Depends;
|
||||
for (int i : dependencySet) {
|
||||
this->GetAllTestDependencies(i, dependencies);
|
||||
dependencies.push_back(i);
|
||||
@ -873,17 +881,17 @@ void cmCTestMultiProcessHandler::CreateSerialTestCostList()
|
||||
{
|
||||
TestList presortedList;
|
||||
|
||||
for (auto const& i : this->Tests) {
|
||||
for (auto const& i : this->PendingTests) {
|
||||
presortedList.push_back(i.first);
|
||||
}
|
||||
|
||||
std::stable_sort(presortedList.begin(), presortedList.end(),
|
||||
TestComparator(this));
|
||||
|
||||
TestSet alreadySortedTests;
|
||||
TestSet alreadyOrderedTests;
|
||||
|
||||
for (int test : presortedList) {
|
||||
if (cm::contains(alreadySortedTests, test)) {
|
||||
if (cm::contains(alreadyOrderedTests, test)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -891,14 +899,14 @@ void cmCTestMultiProcessHandler::CreateSerialTestCostList()
|
||||
this->GetAllTestDependencies(test, dependencies);
|
||||
|
||||
for (int testDependency : dependencies) {
|
||||
if (!cm::contains(alreadySortedTests, testDependency)) {
|
||||
alreadySortedTests.insert(testDependency);
|
||||
this->SortedTests.push_back(testDependency);
|
||||
if (!cm::contains(alreadyOrderedTests, testDependency)) {
|
||||
alreadyOrderedTests.insert(testDependency);
|
||||
this->OrderedTests.push_back(testDependency);
|
||||
}
|
||||
}
|
||||
|
||||
alreadySortedTests.insert(test);
|
||||
this->SortedTests.push_back(test);
|
||||
alreadyOrderedTests.insert(test);
|
||||
this->OrderedTests.push_back(test);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1255,9 +1263,7 @@ void cmCTestMultiProcessHandler::PrintOutputAsJson()
|
||||
// Don't worry if this fails, we are only showing the test list, not
|
||||
// running the tests
|
||||
cmWorkingDirectory workdir(p.Directory);
|
||||
cmCTestRunTest testRun(*this);
|
||||
testRun.SetIndex(p.Index);
|
||||
testRun.SetTestProperties(&p);
|
||||
cmCTestRunTest testRun(*this, p.Index);
|
||||
testRun.ComputeArguments();
|
||||
|
||||
// Skip tests not available in this configuration.
|
||||
@ -1294,9 +1300,7 @@ void cmCTestMultiProcessHandler::PrintTestList()
|
||||
// running the tests
|
||||
cmWorkingDirectory workdir(p.Directory);
|
||||
|
||||
cmCTestRunTest testRun(*this);
|
||||
testRun.SetIndex(p.Index);
|
||||
testRun.SetTestProperties(&p);
|
||||
cmCTestRunTest testRun(*this, p.Index);
|
||||
testRun.ComputeArguments(); // logs the command in verbose mode
|
||||
|
||||
if (!p.Labels.empty()) // print the labels
|
||||
@ -1390,17 +1394,15 @@ void cmCTestMultiProcessHandler::CheckResume()
|
||||
|
||||
void cmCTestMultiProcessHandler::RemoveTest(int index)
|
||||
{
|
||||
this->EraseTest(index);
|
||||
this->ErasePendingTest(index);
|
||||
this->Properties.erase(index);
|
||||
this->TestRunningMap[index] = false;
|
||||
this->TestFinishMap[index] = true;
|
||||
this->Completed++;
|
||||
}
|
||||
|
||||
int cmCTestMultiProcessHandler::FindMaxIndex()
|
||||
{
|
||||
int max = 0;
|
||||
for (auto const& i : this->Tests) {
|
||||
for (auto const& i : this->PendingTests) {
|
||||
if (i.first > max) {
|
||||
max = i.first;
|
||||
}
|
||||
@ -1414,7 +1416,7 @@ bool cmCTestMultiProcessHandler::CheckCycles()
|
||||
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
|
||||
"Checking test dependency graph..." << std::endl,
|
||||
this->Quiet);
|
||||
for (auto const& it : this->Tests) {
|
||||
for (auto const& it : this->PendingTests) {
|
||||
// DFS from each element to itself
|
||||
int root = it.first;
|
||||
std::set<int> visited;
|
||||
@ -1424,7 +1426,7 @@ bool cmCTestMultiProcessHandler::CheckCycles()
|
||||
int test = s.top();
|
||||
s.pop();
|
||||
if (visited.insert(test).second) {
|
||||
for (auto const& d : this->Tests[test]) {
|
||||
for (auto const& d : this->PendingTests[test].Depends) {
|
||||
if (d == root) {
|
||||
// cycle exists
|
||||
cmCTestLog(
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#include "cmConfigure.h" // IWYU pragma: keep
|
||||
|
||||
#include <cstddef>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
@ -38,7 +39,11 @@ public:
|
||||
struct TestSet : public std::set<int>
|
||||
{
|
||||
};
|
||||
struct TestMap : public std::map<int, TestSet>
|
||||
struct TestInfo
|
||||
{
|
||||
TestSet Depends;
|
||||
};
|
||||
struct TestMap : public std::map<int, TestInfo>
|
||||
{
|
||||
};
|
||||
struct TestList : public std::vector<int>
|
||||
@ -57,7 +62,7 @@ public:
|
||||
cmCTestMultiProcessHandler();
|
||||
virtual ~cmCTestMultiProcessHandler();
|
||||
// Set the tests
|
||||
void SetTests(TestMap& tests, PropertiesMap& properties);
|
||||
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 SetTestLoad(unsigned long load);
|
||||
@ -99,7 +104,7 @@ public:
|
||||
|
||||
void SetQuiet(bool b) { this->Quiet = b; }
|
||||
|
||||
void CheckResourcesAvailable();
|
||||
void CheckResourceAvailability();
|
||||
|
||||
protected:
|
||||
// Start the next test or tests as many as are allowed by
|
||||
@ -124,7 +129,7 @@ protected:
|
||||
|
||||
// Removes the checkpoint file
|
||||
void MarkFinished();
|
||||
void EraseTest(int index);
|
||||
void ErasePendingTest(int index);
|
||||
void FinishTestProcess(std::unique_ptr<cmCTestRunTest> runner, bool started);
|
||||
|
||||
static void OnTestLoadRetryCB(uv_timer_t* timer);
|
||||
@ -146,18 +151,19 @@ protected:
|
||||
void LockResources(int index);
|
||||
void UnlockResources(int index);
|
||||
|
||||
enum class ResourceAllocationError
|
||||
enum class ResourceAvailabilityError
|
||||
{
|
||||
NoResourceType,
|
||||
InsufficientResources,
|
||||
};
|
||||
|
||||
bool Complete();
|
||||
bool AllocateResources(int index);
|
||||
bool TryAllocateResources(
|
||||
int index,
|
||||
std::map<std::string, std::vector<cmCTestBinPackerAllocation>>&
|
||||
allocations,
|
||||
std::map<std::string, ResourceAllocationError>* errors = nullptr);
|
||||
std::map<std::string, ResourceAvailabilityError>* errors = nullptr);
|
||||
void DeallocateResources(int index);
|
||||
bool AllResourcesAvailable();
|
||||
bool InitResourceAllocator(std::string& error);
|
||||
@ -170,9 +176,10 @@ protected:
|
||||
cm::optional<std::size_t> ResourceSpecSetupTest;
|
||||
bool HasInvalidGeneratedResourceSpec;
|
||||
|
||||
// map from test number to set of depend tests
|
||||
TestMap Tests;
|
||||
TestList SortedTests;
|
||||
// Tests pending selection to start. They may have dependencies.
|
||||
TestMap PendingTests;
|
||||
// List of pending test indexes, ordered by cost.
|
||||
std::list<int> OrderedTests;
|
||||
// Total number of tests we'll be running
|
||||
size_t Total;
|
||||
// Number of tests that are complete
|
||||
@ -183,8 +190,6 @@ protected:
|
||||
bool StopTimePassed = false;
|
||||
// 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, std::string> TestOutput;
|
||||
std::vector<std::string>* Passed;
|
||||
std::vector<std::string>* Failed;
|
||||
@ -193,14 +198,14 @@ protected:
|
||||
std::map<int,
|
||||
std::vector<std::map<std::string, std::vector<ResourceAllocation>>>>
|
||||
AllocatedResources;
|
||||
std::map<int, std::map<std::string, ResourceAllocationError>>
|
||||
ResourceAllocationErrors;
|
||||
std::map<int, std::map<std::string, ResourceAvailabilityError>>
|
||||
ResourceAvailabilityErrors;
|
||||
cmCTestResourceAllocator ResourceAllocator;
|
||||
std::vector<cmCTestTestHandler::cmCTestTestResult>* TestResults;
|
||||
size_t ParallelLevel; // max number of process that can be run at once
|
||||
unsigned long TestLoad;
|
||||
unsigned long FakeLoadForTesting;
|
||||
uv_loop_t Loop;
|
||||
cm::uv_loop_ptr Loop;
|
||||
cm::uv_timer_ptr TestLoadRetryTimer;
|
||||
cmCTestTestHandler* TestHandler;
|
||||
cmCTest* CTest;
|
||||
|
||||
@ -25,13 +25,17 @@
|
||||
#include "cmProcess.h"
|
||||
#include "cmStringAlgorithms.h"
|
||||
#include "cmSystemTools.h"
|
||||
#include "cmUVHandlePtr.h"
|
||||
#include "cmWorkingDirectory.h"
|
||||
|
||||
cmCTestRunTest::cmCTestRunTest(cmCTestMultiProcessHandler& multiHandler)
|
||||
cmCTestRunTest::cmCTestRunTest(cmCTestMultiProcessHandler& multiHandler,
|
||||
int index)
|
||||
: MultiTestHandler(multiHandler)
|
||||
, Index(index)
|
||||
, CTest(MultiTestHandler.CTest)
|
||||
, TestHandler(MultiTestHandler.TestHandler)
|
||||
, TestProperties(MultiTestHandler.Properties[Index])
|
||||
{
|
||||
this->CTest = multiHandler.CTest;
|
||||
this->TestHandler = multiHandler.TestHandler;
|
||||
}
|
||||
|
||||
void cmCTestRunTest::CheckOutput(std::string const& line)
|
||||
@ -161,7 +165,7 @@ cmCTestRunTest::EndTestResult cmCTestRunTest::EndTest(size_t completed,
|
||||
reason = "Invalid resource spec file";
|
||||
forceFail = true;
|
||||
} else {
|
||||
this->MultiTestHandler.CheckResourcesAvailable();
|
||||
this->MultiTestHandler.CheckResourceAvailability();
|
||||
}
|
||||
}
|
||||
std::ostringstream outputStream;
|
||||
@ -887,7 +891,7 @@ bool cmCTestRunTest::ForkProcess()
|
||||
this->TestResult.Environment.erase(this->TestResult.Environment.length() -
|
||||
1);
|
||||
|
||||
return this->TestProcess->StartProcess(this->MultiTestHandler.Loop,
|
||||
return this->TestProcess->StartProcess(*this->MultiTestHandler.Loop,
|
||||
&this->TestProperties->Affinity);
|
||||
}
|
||||
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
class cmCTestRunTest
|
||||
{
|
||||
public:
|
||||
explicit cmCTestRunTest(cmCTestMultiProcessHandler& multiHandler);
|
||||
explicit cmCTestRunTest(cmCTestMultiProcessHandler& multiHandler, int index);
|
||||
|
||||
void SetNumberOfRuns(int n)
|
||||
{
|
||||
@ -33,18 +33,12 @@ public:
|
||||
}
|
||||
|
||||
void SetRepeatMode(cmCTest::Repeat r) { this->RepeatMode = r; }
|
||||
void SetTestProperties(cmCTestTestHandler::cmCTestTestProperties* prop)
|
||||
{
|
||||
this->TestProperties = prop;
|
||||
}
|
||||
|
||||
cmCTestTestHandler::cmCTestTestProperties* GetTestProperties()
|
||||
{
|
||||
return this->TestProperties;
|
||||
}
|
||||
|
||||
void SetIndex(int i) { this->Index = i; }
|
||||
|
||||
int GetIndex() { return this->Index; }
|
||||
|
||||
void AddFailedDependency(const std::string& failedTest)
|
||||
@ -124,16 +118,15 @@ private:
|
||||
// Returns "completed/total Test #Index: "
|
||||
std::string GetTestPrefix(size_t completed, size_t total) const;
|
||||
|
||||
cmCTestTestHandler::cmCTestTestProperties* TestProperties;
|
||||
// Pointer back to the "parent"; the handler that invoked this test run
|
||||
cmCTestTestHandler* TestHandler;
|
||||
cmCTest* CTest;
|
||||
std::unique_ptr<cmProcess> TestProcess;
|
||||
std::string ProcessOutput;
|
||||
// The test results
|
||||
cmCTestTestHandler::cmCTestTestResult TestResult;
|
||||
cmCTestMultiProcessHandler& MultiTestHandler;
|
||||
int Index;
|
||||
cmCTest* CTest;
|
||||
cmCTestTestHandler* TestHandler;
|
||||
cmCTestTestHandler::cmCTestTestProperties* TestProperties;
|
||||
|
||||
std::unique_ptr<cmProcess> TestProcess;
|
||||
std::string ProcessOutput;
|
||||
cmCTestTestHandler::cmCTestTestResult TestResult;
|
||||
std::set<std::string> FailedDependencies;
|
||||
std::string StartTime;
|
||||
std::string ActualCommand;
|
||||
|
||||
@ -1381,15 +1381,15 @@ bool cmCTestTestHandler::ProcessDirectory(std::vector<std::string>& passed,
|
||||
}
|
||||
}
|
||||
}
|
||||
tests[p.Index] = depends;
|
||||
tests[p.Index].Depends = depends;
|
||||
properties[p.Index] = &p;
|
||||
}
|
||||
parallel->SetResourceSpecFile(this->ResourceSpecFile);
|
||||
parallel->SetTests(tests, properties);
|
||||
parallel->SetTests(std::move(tests), std::move(properties));
|
||||
parallel->SetPassFailVectors(&passed, &failed);
|
||||
this->TestResults.clear();
|
||||
parallel->SetTestResults(&this->TestResults);
|
||||
parallel->CheckResourcesAvailable();
|
||||
parallel->CheckResourceAvailability();
|
||||
|
||||
if (this->CTest->ShouldPrintLabels()) {
|
||||
parallel->PrintLabels();
|
||||
|
||||
@ -229,19 +229,22 @@ function(run_TestLoad name load)
|
||||
file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/CTestTestfile.cmake" "
|
||||
add_test(TestLoad1 \"${CMAKE_COMMAND}\" -E echo \"test of --test-load\")
|
||||
set_tests_properties(TestLoad1 PROPERTIES PROCESSORS 2)
|
||||
add_test(TestLoad2 \"${CMAKE_COMMAND}\" -E echo \"test of --test-load\")
|
||||
set_tests_properties(TestLoad2 PROPERTIES PROCESSORS 2)
|
||||
")
|
||||
run_cmake_command(${name} ${CMAKE_CTEST_COMMAND} -VV -j2 --test-load ${load})
|
||||
run_cmake_command(${name} ${CMAKE_CTEST_COMMAND} -VV -j8 --test-load ${load})
|
||||
endfunction()
|
||||
|
||||
# Tests for the --test-load feature of ctest
|
||||
#
|
||||
# Spoof a load average value to make these tests more reliable.
|
||||
set(ENV{__CTEST_FAKE_LOAD_AVERAGE_FOR_TESTING} 5)
|
||||
set(ENV{__CTEST_FAKE_LOAD_AVERAGE_FOR_TESTING} 7)
|
||||
|
||||
# Verify that new tests are not started when the load average exceeds
|
||||
# our threshold and that they then run once the load average drops.
|
||||
run_TestLoad(test-load-wait 3)
|
||||
run_TestLoad(test-load-wait0 5)
|
||||
run_TestLoad(test-load-wait1 8)
|
||||
|
||||
# Verify that warning message is displayed but tests still start when
|
||||
# an invalid argument is given.
|
||||
@ -249,7 +252,7 @@ run_TestLoad(test-load-invalid 'two')
|
||||
|
||||
# Verify that new tests are started when the load average falls below
|
||||
# our threshold.
|
||||
run_TestLoad(test-load-pass 10)
|
||||
run_TestLoad(test-load-pass 12)
|
||||
|
||||
unset(ENV{__CTEST_FAKE_LOAD_AVERAGE_FOR_TESTING})
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ Test project [^
|
||||
]*/Tests/RunCMake/CTestCommandLine/TestLoad(
|
||||
[^*][^
|
||||
]*)*
|
||||
\*\*\*\*\* WAITING, System Load: 5, Max Allowed Load: 3, Smallest test TestLoad[1-2] requires 1\*\*\*\*\*
|
||||
\*\*\*\*\* WAITING, System Load: 7, Max Allowed Load: 5\*\*\*\*\*
|
||||
test 1
|
||||
Start 1: TestLoad1
|
||||
+(
|
||||
21
Tests/RunCMake/CTestCommandLine/test-load-wait1-stdout.txt
Normal file
21
Tests/RunCMake/CTestCommandLine/test-load-wait1-stdout.txt
Normal file
@ -0,0 +1,21 @@
|
||||
Test project [^
|
||||
]*/Tests/RunCMake/CTestCommandLine/TestLoad(
|
||||
[^*][^
|
||||
]*)*
|
||||
\*\*\*\*\* WAITING, System Load: 7, Max Allowed Load: 8, Smallest test TestLoad[1-2] requires 2\*\*\*\*\*
|
||||
test 1
|
||||
Start 1: TestLoad1
|
||||
+(
|
||||
[^*][^
|
||||
]*)*
|
||||
test 2
|
||||
Start 2: TestLoad2
|
||||
+(
|
||||
[^*][^
|
||||
]*)*
|
||||
1/2 Test #[1-2]: TestLoad[1-2] ........................ Passed +[0-9.]+ sec(
|
||||
[^*][^
|
||||
]*)*
|
||||
2/2 Test #[1-2]: TestLoad[1-2] ........................ Passed +[0-9.]+ sec
|
||||
+
|
||||
100% tests passed, 0 tests failed out of 2
|
||||
@ -1,8 +1,8 @@
|
||||
Test project [^
|
||||
]*/Tests/RunCMake/ctest_test/TestLoadWait-build(
|
||||
]*/Tests/RunCMake/ctest_test/CTestTestLoadWait0-build(
|
||||
[^*][^
|
||||
]*)*
|
||||
\*\*\*\*\* WAITING, System Load: 5, Max Allowed Load: 2, Smallest test RunCMakeVersion requires 1\*\*\*\*\*
|
||||
\*\*\*\*\* WAITING, System Load: 7, Max Allowed Load: 6\*\*\*\*\*
|
||||
test 1
|
||||
Start 1: RunCMakeVersion
|
||||
+(
|
||||
15
Tests/RunCMake/ctest_test/CTestTestLoadWait1-stdout.txt
Normal file
15
Tests/RunCMake/ctest_test/CTestTestLoadWait1-stdout.txt
Normal file
@ -0,0 +1,15 @@
|
||||
Test project [^
|
||||
]*/Tests/RunCMake/ctest_test/CTestTestLoadWait1-build(
|
||||
[^*][^
|
||||
]*)*
|
||||
\*\*\*\*\* WAITING, System Load: 7, Max Allowed Load: 8, Smallest test RunCMakeVersion requires 2\*\*\*\*\*
|
||||
test 1
|
||||
Start 1: RunCMakeVersion
|
||||
+(
|
||||
[^*][^
|
||||
]*)*
|
||||
1/1 Test #1: RunCMakeVersion .................. Passed +[0-9.]+ sec
|
||||
+
|
||||
100% tests passed, 0 tests failed out of 1
|
||||
+
|
||||
Total Test time \(real\) = +[0-9.]+ sec$
|
||||
@ -14,16 +14,20 @@ run_ctest_test(TestQuiet QUIET)
|
||||
# Tests for the 'Test Load' feature of ctest
|
||||
#
|
||||
# Spoof a load average value to make these tests more reliable.
|
||||
set(ENV{__CTEST_FAKE_LOAD_AVERAGE_FOR_TESTING} 5)
|
||||
set(ENV{__CTEST_FAKE_LOAD_AVERAGE_FOR_TESTING} 7)
|
||||
set(RunCTest_VERBOSE_FLAG -VV)
|
||||
set(CASE_CMAKELISTS_SUFFIX_CODE [[
|
||||
set_property(TEST RunCMakeVersion PROPERTY PROCESSORS 2)
|
||||
]])
|
||||
|
||||
# Verify that new tests are started when the load average falls below
|
||||
# our threshold.
|
||||
run_ctest_test(TestLoadPass TEST_LOAD 6)
|
||||
run_ctest_test(TestLoadPass TEST_LOAD 8)
|
||||
|
||||
# Verify that new tests are not started when the load average exceeds
|
||||
# our threshold and that they then run once the load average drops.
|
||||
run_ctest_test(TestLoadWait TEST_LOAD 2)
|
||||
run_ctest_test(TestLoadWait0 TEST_LOAD 4 PARALLEL_LEVEL 8)
|
||||
run_ctest_test(TestLoadWait1 TEST_LOAD 8 PARALLEL_LEVEL 8)
|
||||
|
||||
# Verify that when an invalid "TEST_LOAD" value is given, a warning
|
||||
# message is displayed and the value is ignored.
|
||||
@ -31,13 +35,15 @@ run_ctest_test(TestLoadInvalid TEST_LOAD "ERR1")
|
||||
|
||||
# Verify that new tests are started when the load average falls below
|
||||
# our threshold.
|
||||
set(CASE_CTEST_TEST_LOAD 7)
|
||||
set(CASE_CTEST_TEST_LOAD 9)
|
||||
run_ctest_test(CTestTestLoadPass)
|
||||
|
||||
# Verify that new tests are not started when the load average exceeds
|
||||
# our threshold and that they then run once the load average drops.
|
||||
set(CASE_CTEST_TEST_LOAD 4)
|
||||
run_ctest_test(CTestTestLoadWait)
|
||||
set(CASE_CTEST_TEST_LOAD 6)
|
||||
run_ctest_test(CTestTestLoadWait0 PARALLEL_LEVEL 8)
|
||||
set(CASE_CTEST_TEST_LOAD 8)
|
||||
run_ctest_test(CTestTestLoadWait1 PARALLEL_LEVEL 8)
|
||||
|
||||
# Verify that when an invalid "CTEST_TEST_LOAD" value is given,
|
||||
# a warning message is displayed and the value is ignored.
|
||||
@ -51,6 +57,7 @@ run_ctest_test(TestLoadOrder TEST_LOAD "ERR4")
|
||||
|
||||
unset(ENV{__CTEST_FAKE_LOAD_AVERAGE_FOR_TESTING})
|
||||
unset(CASE_CTEST_TEST_LOAD)
|
||||
unset(CASE_CMAKELISTS_SUFFIX_CODE)
|
||||
unset(RunCTest_VERBOSE_FLAG)
|
||||
|
||||
block()
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
Test project [^
|
||||
]*/Tests/RunCMake/ctest_test/CTestTestLoadWait-build(
|
||||
]*/Tests/RunCMake/ctest_test/TestLoadWait0-build(
|
||||
[^*][^
|
||||
]*)*
|
||||
\*\*\*\*\* WAITING, System Load: 5, Max Allowed Load: 4, Smallest test RunCMakeVersion requires 1\*\*\*\*\*
|
||||
\*\*\*\*\* WAITING, System Load: 7, Max Allowed Load: 4\*\*\*\*\*
|
||||
test 1
|
||||
Start 1: RunCMakeVersion
|
||||
+(
|
||||
15
Tests/RunCMake/ctest_test/TestLoadWait1-stdout.txt
Normal file
15
Tests/RunCMake/ctest_test/TestLoadWait1-stdout.txt
Normal file
@ -0,0 +1,15 @@
|
||||
Test project [^
|
||||
]*/Tests/RunCMake/ctest_test/TestLoadWait1-build(
|
||||
[^*][^
|
||||
]*)*
|
||||
\*\*\*\*\* WAITING, System Load: 7, Max Allowed Load: 8, Smallest test RunCMakeVersion requires 2\*\*\*\*\*
|
||||
test 1
|
||||
Start 1: RunCMakeVersion
|
||||
+(
|
||||
[^*][^
|
||||
]*)*
|
||||
1/1 Test #1: RunCMakeVersion .................. Passed +[0-9.]+ sec
|
||||
+
|
||||
100% tests passed, 0 tests failed out of 1
|
||||
+
|
||||
Total Test time \(real\) = +[0-9.]+ sec$
|
||||
Loading…
x
Reference in New Issue
Block a user