|
|
@@ -196,8 +196,40 @@ bool cmCTestMultiProcessHandler::StartTestProcess(int test)
|
|
|
// working directory because FinishTestProcess() will try to unlock them
|
|
|
this->LockResources(test);
|
|
|
|
|
|
- if (!this->TestsHaveSufficientResources[test]) {
|
|
|
- testRun->StartFailure("Insufficient resources");
|
|
|
+ if (!this->ResourceAllocationErrors[test].empty()) {
|
|
|
+ std::ostringstream e;
|
|
|
+ e << "Insufficient resources for test " << this->Properties[test]->Name
|
|
|
+ << ":\n\n";
|
|
|
+ for (auto const& it : this->ResourceAllocationErrors[test]) {
|
|
|
+ switch (it.second) {
|
|
|
+ case ResourceAllocationError::NoResourceType:
|
|
|
+ e << " Test requested resources of type '" << it.first
|
|
|
+ << "' which does not exist\n";
|
|
|
+ break;
|
|
|
+
|
|
|
+ case ResourceAllocationError::InsufficientResources:
|
|
|
+ e << " Test requested resources of type '" << it.first
|
|
|
+ << "' in the following amounts:\n";
|
|
|
+ for (auto const& group : this->Properties[test]->ResourceGroups) {
|
|
|
+ for (auto const& requirement : group) {
|
|
|
+ if (requirement.ResourceType == it.first) {
|
|
|
+ e << " " << requirement.SlotsNeeded
|
|
|
+ << (requirement.SlotsNeeded == 1 ? " slot\n" : " slots\n");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ e << " but only the following units were available:\n";
|
|
|
+ for (auto const& res :
|
|
|
+ this->ResourceAllocator.GetResources().at(it.first)) {
|
|
|
+ e << " '" << res.first << "': " << res.second.Total
|
|
|
+ << (res.second.Total == 1 ? " slot\n" : " slots\n");
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ e << "\n";
|
|
|
+ }
|
|
|
+ e << "Resource spec file:\n\n " << this->TestHandler->ResourceSpecFile;
|
|
|
+ testRun->StartFailure(e.str(), "Insufficient resources");
|
|
|
this->FinishTestProcess(testRun, false);
|
|
|
return false;
|
|
|
}
|
|
|
@@ -205,8 +237,9 @@ bool cmCTestMultiProcessHandler::StartTestProcess(int test)
|
|
|
cmWorkingDirectory workdir(this->Properties[test]->Directory);
|
|
|
if (workdir.Failed()) {
|
|
|
testRun->StartFailure("Failed to change working directory to " +
|
|
|
- this->Properties[test]->Directory + " : " +
|
|
|
- std::strerror(workdir.GetLastResult()));
|
|
|
+ this->Properties[test]->Directory + " : " +
|
|
|
+ std::strerror(workdir.GetLastResult()),
|
|
|
+ "Failed to change working directory");
|
|
|
} else {
|
|
|
if (testRun->StartTest(this->Completed, this->Total)) {
|
|
|
// Ownership of 'testRun' has moved to another structure.
|
|
|
@@ -249,7 +282,8 @@ bool cmCTestMultiProcessHandler::AllocateResources(int index)
|
|
|
|
|
|
bool cmCTestMultiProcessHandler::TryAllocateResources(
|
|
|
int index,
|
|
|
- std::map<std::string, std::vector<cmCTestBinPackerAllocation>>& allocations)
|
|
|
+ std::map<std::string, std::vector<cmCTestBinPackerAllocation>>& allocations,
|
|
|
+ std::map<std::string, ResourceAllocationError>* errors)
|
|
|
{
|
|
|
allocations.clear();
|
|
|
|
|
|
@@ -264,18 +298,28 @@ bool cmCTestMultiProcessHandler::TryAllocateResources(
|
|
|
++processIndex;
|
|
|
}
|
|
|
|
|
|
+ bool result = true;
|
|
|
auto const& availableResources = this->ResourceAllocator.GetResources();
|
|
|
for (auto& it : allocations) {
|
|
|
if (!availableResources.count(it.first)) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- if (!cmAllocateCTestResourcesRoundRobin(availableResources.at(it.first),
|
|
|
- it.second)) {
|
|
|
- return false;
|
|
|
+ if (errors) {
|
|
|
+ (*errors)[it.first] = ResourceAllocationError::NoResourceType;
|
|
|
+ result = false;
|
|
|
+ } else {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ } else if (!cmAllocateCTestResourcesRoundRobin(
|
|
|
+ availableResources.at(it.first), it.second)) {
|
|
|
+ if (errors) {
|
|
|
+ (*errors)[it.first] = ResourceAllocationError::InsufficientResources;
|
|
|
+ result = false;
|
|
|
+ } else {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- return true;
|
|
|
+ return result;
|
|
|
}
|
|
|
|
|
|
void cmCTestMultiProcessHandler::DeallocateResources(int index)
|
|
|
@@ -316,11 +360,13 @@ bool cmCTestMultiProcessHandler::AllResourcesAvailable()
|
|
|
|
|
|
void cmCTestMultiProcessHandler::CheckResourcesAvailable()
|
|
|
{
|
|
|
- for (auto test : this->SortedTests) {
|
|
|
- std::map<std::string, std::vector<cmCTestBinPackerAllocation>> allocations;
|
|
|
- this->TestsHaveSufficientResources[test] =
|
|
|
- !this->TestHandler->UseResourceSpec ||
|
|
|
- this->TryAllocateResources(test, allocations);
|
|
|
+ if (this->TestHandler->UseResourceSpec) {
|
|
|
+ for (auto test : this->SortedTests) {
|
|
|
+ std::map<std::string, std::vector<cmCTestBinPackerAllocation>>
|
|
|
+ allocations;
|
|
|
+ this->TryAllocateResources(test, allocations,
|
|
|
+ &this->ResourceAllocationErrors[test]);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -407,7 +453,7 @@ bool cmCTestMultiProcessHandler::StartTest(int test)
|
|
|
}
|
|
|
|
|
|
// Allocate resources
|
|
|
- if (this->TestsHaveSufficientResources[test] &&
|
|
|
+ if (this->ResourceAllocationErrors[test].empty() &&
|
|
|
!this->AllocateResources(test)) {
|
|
|
this->DeallocateResources(test);
|
|
|
return false;
|