/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ #pragma once #include "cmConfigure.h" // IWYU pragma: keep #include #include #include #include #include #include #include #include #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 { }; struct TestInfo { TestSet Depends; }; struct TestMap : public std::map { }; struct TestList : public std::vector { }; struct PropertiesMap : public std::map { }; 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 level); void SetTestLoad(unsigned long load); virtual void RunTests(); void PrintOutputAsJson(); void PrintTestList(); void PrintLabels(); void SetPassFailVectors(std::vector* passed, std::vector* failed) { this->Passed = passed; this->Failed = failed; } void SetTestResults(std::vector* r) { this->TestResults = r; } cmCTestTestHandler* GetTestHandler() { return this->TestHandler; } void SetRepeatMode(cmCTest::Repeat mode, int count) { this->RepeatMode = mode; this->RepeatCount = count; } void SetResourceSpecFile(const std::string& 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(std::string const& name); void CreateTestCostList(); void GetAllTestDependencies(int test, TestList& dependencies); void CreateSerialTestCostList(); void CreateParallelTestCostList(); // Removes the checkpoint file void MarkFinished(); void FinishTestProcess(std::unique_ptr 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>& allocations, std::map* 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 ResourceSpecSetupTest; bool HasInvalidGeneratedResourceSpec = false; // Tests pending selection to start. They may have dependencies. TestMap PendingTests; // List of pending test indexes, ordered by cost. std::list 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 ProcessorsAvailable; size_t HaveAffinity; bool StopTimePassed = false; // list of test properties (indices concurrent to the test map) PropertiesMap Properties; std::map TestOutput; std::vector* Passed; std::vector* Failed; std::vector LastTestsFailed; std::set ProjectResourcesLocked; std::map>>> AllocatedResources; std::map> ResourceAvailabilityErrors; cmCTestResourceAllocator ResourceAllocator; std::vector* 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 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 JobServerClient; // List of tests that are queued to run when a token is available. std::list 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; };