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:
Brad King 2023-11-20 13:16:05 +00:00 committed by Kitware Robot
commit 6818925b9a
13 changed files with 226 additions and 161 deletions

View File

@ -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(

View File

@ -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;

View File

@ -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);
}

View File

@ -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;

View File

@ -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();

View File

@ -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})

View File

@ -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
+(

View 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

View File

@ -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
+(

View 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$

View File

@ -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()

View File

@ -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
+(

View 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$