mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-25 04:09:36 +03:00
When ctest repeats tests with its --repeat option, it repeats each test on its own. A fixture's setup and cleanup tests therefore run all of their repetitions back to back, and the tests they bracket repeat inside a single setup/cleanup pair: setup -> setup -> test -> test -> cleanup -> cleanup Add a FIXTURE_REPEAT_MODE test property to select how a fixture behaves when its tests are repeated: * AROUND_ALL_REPEATS: the fixture runs once, around all repetitions of the tests requiring it. * AROUND_EACH_REPEAT: the fixture and the tests requiring it repeat together, so every repetition gets a fresh setup and its own cleanup. * EACH_TEST_SEPARATELY: every test repeats on its own, as before. The property describes the fixture rather than the test carrying it, so setting it on any one of a fixture's setup or cleanup tests is enough. In AROUND_EACH_REPEAT mode the tests of a fixture form a repeat group that ctest re-queues as a whole once every test in it has finished. The --repeat condition then applies to the group the way it applies to an individual test: until-fail repeats while the whole group passes, until-pass repeats while any of it does not, and after-timeout repeats while any of it times out. Fixtures that share a test repeat together, so a test requiring two of them still runs once per repetition. A group is recorded the way a repeating test is: only once it stops repeating, and with the results of its last repetition. A group that until-pass makes pass therefore reports a pass rather than the failure that made it repeat, a test that DEPENDS on one of the group's tests waits for the last repetition rather than the first, and `ctest -F` resumes an interrupted group by running it again from the beginning. Fixtures that repeat together have to agree on the mode: a test cannot repeat with one fixture but not with another it takes part in, and a fixture whose setup and cleanup tests disagree has no coherent behavior. Report an error and run nothing in those cases rather than pick an order in which a test repeats after a fixture it requires has been cleaned up. Add policy CMP0224 to select AROUND_EACH_REPEAT as the default for fixtures whose setup and cleanup tests choose no mode themselves. Record the mode the policy chose in the generated test file under its own _CMAKE_DEFAULT_FIXTURE_REPEAT_MODE keyword, so that ctest reads a mode rather than the policy settings behind it, and so that a mode requested on one of a fixture's tests wins over the default recorded for its siblings. Only NEW needs recording: with nothing recorded, ctest already uses the behavior of CMake 4.4 and below. Fixtures are common, and the choice of mode matters only to those who run ctest --repeat, so warn about the unset policy only when the CMAKE_POLICY_WARNING_CMP0224 variable asks for it. discover_tests() and gtest_discover_tests() create their tests while ctest runs or at build time, too late for the policy to reach them, so carry the setting in effect at their call sites through to the tests they create. Report the repetition a grouped test belongs to in the "(run N/M)" suffix of its "Start" line, as ctest already does for a test repeating on its own. Co-authored-by: Tyler Yankee <tyler.yankee@kitware.com> Fixes: #21438
275 lines
8.2 KiB
C++
275 lines
8.2 KiB
C++
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file LICENSE.rst or https://cmake.org/licensing for details. */
|
|
#pragma once
|
|
|
|
#include "cmConfigure.h" // IWYU pragma: keep
|
|
|
|
#include <cstddef>
|
|
#include <list>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <set>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <cm/optional>
|
|
#include <cm/string_view>
|
|
|
|
#include "cmCTest.h"
|
|
#include "cmCTestResourceAllocator.h"
|
|
#include "cmCTestResourceSpec.h"
|
|
#include "cmCTestTestHandler.h"
|
|
#include "cmUVHandlePtr.h"
|
|
#include "cmUVJobServerClient.h"
|
|
|
|
struct cmCTestBinPackerAllocation;
|
|
class cmCTestRunTest;
|
|
|
|
/** \class cmCTestMultiProcessHandler
|
|
* \brief run parallel ctest
|
|
*
|
|
* cmCTestMultiProcessHandler
|
|
*/
|
|
class cmCTestMultiProcessHandler
|
|
{
|
|
friend class TestComparator;
|
|
friend class cmCTestRunTest;
|
|
|
|
public:
|
|
struct TestSet : public std::set<int>
|
|
{
|
|
};
|
|
struct TestInfo
|
|
{
|
|
TestSet Depends;
|
|
};
|
|
struct TestMap : public std::map<int, TestInfo>
|
|
{
|
|
};
|
|
struct TestList : public std::vector<int>
|
|
{
|
|
};
|
|
struct PropertiesMap
|
|
: public std::map<int, cmCTestTestHandler::cmCTestTestProperties*>
|
|
{
|
|
};
|
|
struct ResourceAllocation
|
|
{
|
|
std::string Id;
|
|
unsigned int Slots;
|
|
};
|
|
|
|
cmCTestMultiProcessHandler(cmCTest* ctest, cmCTestTestHandler* handler);
|
|
virtual ~cmCTestMultiProcessHandler();
|
|
// Set the tests
|
|
bool SetTests(TestMap tests, PropertiesMap properties);
|
|
// Set the max number of tests that can be run at the same time.
|
|
void SetParallelLevel(cm::optional<size_t> level);
|
|
void SetTestLoad(unsigned long load);
|
|
virtual void RunTests();
|
|
void PrintOutputAsJson();
|
|
void PrintTestList();
|
|
void PrintLabels();
|
|
|
|
void SetPassFailVectors(std::vector<std::string>* passed,
|
|
std::vector<std::string>* failed)
|
|
{
|
|
this->Passed = passed;
|
|
this->Failed = failed;
|
|
}
|
|
void SetTestResults(std::vector<cmCTestTestHandler::cmCTestTestResult>* r)
|
|
{
|
|
this->TestResults = r;
|
|
}
|
|
|
|
cmCTestTestHandler* GetTestHandler() { return this->TestHandler; }
|
|
|
|
void SetRepeatMode(cmCTest::Repeat mode, int count)
|
|
{
|
|
this->RepeatMode = mode;
|
|
this->RepeatCount = count;
|
|
}
|
|
|
|
void SetResourceSpecFile(std::string const& resourceSpecFile)
|
|
{
|
|
this->ResourceSpecFile = resourceSpecFile;
|
|
}
|
|
|
|
void SetQuiet(bool b) { this->Quiet = b; }
|
|
|
|
void CheckResourceAvailability();
|
|
|
|
protected:
|
|
// Start the next test or tests as many as are allowed by
|
|
// ParallelLevel
|
|
void StartNextTests();
|
|
void StartTestProcess(int test);
|
|
void StartTest(int test);
|
|
// Mark the checkpoint for the given test
|
|
void WriteCheckpoint(int index);
|
|
|
|
void UpdateCostData();
|
|
void ReadCostData();
|
|
// Return index of a test based on its name
|
|
int SearchByName(cm::string_view name);
|
|
|
|
void CreateTestCostList();
|
|
|
|
void GetAllTestDependencies(int test, TestList& dependencies);
|
|
void CreateSerialTestCostList();
|
|
|
|
void CreateParallelTestCostList();
|
|
|
|
// Removes the checkpoint file
|
|
void MarkFinished();
|
|
void FinishTestProcess(std::unique_ptr<cmCTestRunTest> runner, bool started);
|
|
|
|
void StartNextTestsOnIdle();
|
|
void StartNextTestsOnTimer();
|
|
|
|
void RemoveTest(int index);
|
|
// Check if we need to resume an interrupted test set
|
|
void CheckResume();
|
|
// Check if there are any circular dependencies
|
|
bool CheckCycles();
|
|
int FindMaxIndex();
|
|
inline size_t GetProcessorsUsed(int index);
|
|
std::string GetName(int index);
|
|
|
|
bool CheckStopOnFailure();
|
|
|
|
bool CheckStopTimePassed();
|
|
void SetStopTimePassed();
|
|
|
|
void InitializeLoop();
|
|
void FinalizeLoop();
|
|
|
|
bool ResourceLocksAvailable(int test);
|
|
void LockResources(int index);
|
|
void UnlockResources(int index);
|
|
|
|
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, ResourceAvailabilityError>* errors = nullptr);
|
|
void DeallocateResources(int index);
|
|
bool AllResourcesAvailable();
|
|
bool InitResourceAllocator(std::string& error);
|
|
bool CheckGeneratedResourceSpec();
|
|
|
|
private:
|
|
cmCTest* CTest;
|
|
cmCTestTestHandler* TestHandler;
|
|
|
|
bool UseResourceSpec = false;
|
|
cmCTestResourceSpec ResourceSpec;
|
|
std::string ResourceSpecFile;
|
|
std::string ResourceSpecSetupFixture;
|
|
cm::optional<std::size_t> ResourceSpecSetupTest;
|
|
bool HasInvalidGeneratedResourceSpec = false;
|
|
|
|
// Tests that ctest --repeat repeats as a unit because they take part in a
|
|
// fixture using AROUND_EACH_REPEAT mode.
|
|
struct RepeatGroup
|
|
{
|
|
// The group's tests, each mapped to its dependencies within the group.
|
|
// Dependencies on tests outside the group are satisfied by the first
|
|
// repetition and are not restored for the later ones.
|
|
std::map<int, TestSet> Tests;
|
|
// Repetitions still to run after the current one.
|
|
int RepetitionsLeft = 0;
|
|
// Tests that have not finished the current repetition.
|
|
std::size_t Unfinished = 0;
|
|
// Whether every test finished the current repetition successfully.
|
|
bool AllCompleted = true;
|
|
// Whether any test timed out during the current repetition.
|
|
bool AnyTimedOut = false;
|
|
};
|
|
std::vector<RepeatGroup> RepeatGroups;
|
|
// Index into RepeatGroups of each test that belongs to a group.
|
|
std::map<int, int> RepeatGroupOfTest;
|
|
// Fixture setup and cleanup tests that run only once, because their
|
|
// fixture brackets all repetitions of the tests requiring it.
|
|
std::set<int> TestsRunOnce;
|
|
|
|
// Work out how ctest --repeat repeats each test of a fixture, filling in
|
|
// the members above. Returns false if fixtures that repeat together
|
|
// disagree on the mode.
|
|
bool ComputeFixtureRepetition();
|
|
// Put the given tests in one repeat group, merging any group they already
|
|
// belong to.
|
|
void AddRepeatGroup(std::set<int> const& tests);
|
|
// Record the result of a test belonging to a repeat group, and re-queue
|
|
// the group if it just finished a repetition and another one is due.
|
|
void FinishRepeatGroupTest(int test, int testStatus);
|
|
// Record the group's tests once it has run its last repetition.
|
|
void FinishRepeatGroup(RepeatGroup const& group);
|
|
// Run the group's tests again, dropping the results of the repetition it
|
|
// just finished.
|
|
void RequeueRepeatGroup(RepeatGroup const& group);
|
|
|
|
// 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 = 0;
|
|
// Number of tests that are complete
|
|
size_t Completed = 0;
|
|
size_t RunningCount = 0;
|
|
std::set<size_t> ProcessorsAvailable;
|
|
size_t HaveAffinity;
|
|
bool StopTimePassed = false;
|
|
// list of test properties (indices concurrent to the test map)
|
|
PropertiesMap Properties;
|
|
std::map<int, std::string> TestOutput;
|
|
std::vector<std::string>* Passed;
|
|
std::vector<std::string>* Failed;
|
|
std::vector<std::string> LastTestsFailed;
|
|
std::set<std::string> ProjectResourcesLocked;
|
|
std::map<int,
|
|
std::vector<std::map<std::string, std::vector<ResourceAllocation>>>>
|
|
AllocatedResources;
|
|
std::map<int, std::map<std::string, ResourceAvailabilityError>>
|
|
ResourceAvailabilityErrors;
|
|
cmCTestResourceAllocator ResourceAllocator;
|
|
std::vector<cmCTestTestHandler::cmCTestTestResult>* TestResults;
|
|
|
|
// Get the maximum number of processors that may be used at once.
|
|
size_t GetParallelLevel() const;
|
|
|
|
// With no '-j' option, default to serial testing.
|
|
cm::optional<size_t> ParallelLevel = 1;
|
|
|
|
// Fallback parallelism limit when '-j' is given with no value.
|
|
size_t ParallelLevelDefault;
|
|
|
|
// 'make' jobserver client. If connected, we acquire a token
|
|
// for each test before running its process.
|
|
cm::optional<cmUVJobServerClient> JobServerClient;
|
|
// List of tests that are queued to run when a token is available.
|
|
std::list<int> JobServerQueuedTests;
|
|
// Callback invoked when a token is received.
|
|
void JobServerReceivedToken();
|
|
|
|
unsigned long TestLoad = 0;
|
|
unsigned long FakeLoadForTesting = 0;
|
|
cm::uv_loop_ptr Loop;
|
|
cm::uv_idle_ptr StartNextTestsOnIdle_;
|
|
cm::uv_timer_ptr StartNextTestsOnTimer_;
|
|
bool HasCycles = false;
|
|
cmCTest::Repeat RepeatMode = cmCTest::Repeat::Never;
|
|
int RepeatCount = 1;
|
|
bool Quiet = false;
|
|
bool SerialTestRunning = false;
|
|
};
|