Merge topic 'ctest-fixture-repeat'

5cced02b9d CTest: Repeat test fixtures with the tests that require them
4fcf3dd296 CTest: Ignore a checkpoint entry for a test that is not pending
32b25b94d6 CTest: Track one run number for a repeating test

Acked-by: Kitware Robot <kwrobot@kitware.com>
Acked-by: buildbot <buildbot@kitware.com>
Merge-request: !11638
This commit is contained in:
Brad King
2026-09-22 10:01:43 -04:00
committed by Kitware Robot
76 changed files with 1343 additions and 45 deletions
+5
View File
@@ -89,6 +89,11 @@ the provided regular expression and replacement strings.
replacement strings and may reference capture groups from
``DISCOVERY_MATCH``.
.. versionchanged:: 4.5
If the properties make the discovered tests part of a test fixture, and
they do not set :prop_test:`FIXTURE_REPEAT_MODE`, policy :policy:`CMP0224`
determines the behavior when the :ctest-option:`--repeat` option is used.
CTest executes the discovery step to obtain the list of tests and then runs
each discovered test using the command-line produced by ``COMMAND`` together
with ``TEST_ARGS``. The pass/fail behavior of each discovered test follows
+1
View File
@@ -100,6 +100,7 @@ Policies Introduced by CMake 4.5
.. toctree::
:maxdepth: 1
CMP0224: Test fixtures use AROUND_EACH_REPEAT mode by default with ctest --repeat. </policy/CMP0224>
CMP0223: An empty path is not a prefix of any path. </policy/CMP0223>
CMP0222: The if() command supports path prefix tests using PATH_IS_PREFIX operator. </policy/CMP0222>
CMP0221: cmake_host_system_information() DISTRIB_* queries read the host os-release. </policy/CMP0221>
+1
View File
@@ -614,6 +614,7 @@ Properties on Tests
/prop_test/ENVIRONMENT
/prop_test/ENVIRONMENT_MODIFICATION
/prop_test/FAIL_REGULAR_EXPRESSION
/prop_test/FIXTURE_REPEAT_MODE
/prop_test/FIXTURES_CLEANUP
/prop_test/FIXTURES_REQUIRED
/prop_test/FIXTURES_SETUP
+46
View File
@@ -0,0 +1,46 @@
CMP0224
-------
.. versionadded:: 4.5
Test fixtures use ``AROUND_EACH_REPEAT`` mode by default with
:option:`ctest --repeat`.
When :manual:`ctest(1)` repeats tests with its :ctest-option:`--repeat`
option, CMake 4.4 and below repeated fixture setup and cleanup tests along
with the tests that require them. Each test, including the fixture tests,
ran all of its repetitions before the next test started::
setup (xN) -> test (xN) -> cleanup (xN)
CMake 4.5 and above prefer to repeat the fixture as a whole, so that each
repetition of a test is surrounded by its own fixture setup and cleanup::
(setup -> test -> cleanup) xN
This policy provides compatibility with projects that rely on the old
behavior. It determines the default behavior when the
:prop_test:`FIXTURE_REPEAT_MODE` test property is not set. The ``OLD``
behavior for this policy is ``EACH_TEST_SEPARATELY``. The ``NEW`` behavior
for this policy is ``AROUND_EACH_REPEAT``.
The property describes a fixture rather than one test, so setting it on any
one of a fixture's setup or cleanup tests is enough to make this policy
irrelevant to that fixture.
Tests are not always created during the configure step by :command:`add_test`.
The :command:`discover_tests` and :command:`gtest_discover_tests` commands
defer test discovery to the build or test step, but they apply the policy
setting as recorded when they were called. If a project adds tests through
its own script listed in :prop_dir:`TEST_INCLUDE_FILES`, it should set
:prop_test:`FIXTURE_REPEAT_MODE` on its fixture tests explicitly.
.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 4.5
.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn by default
.. include:: include/STANDARD_ADVICE.rst
See documentation of the
:variable:`CMAKE_POLICY_WARNING_CMP0224 <CMAKE_POLICY_WARNING_CMP<NNNN>>`
variable to control the warning.
.. include:: include/DEPRECATED.rst
+10 -4
View File
@@ -20,10 +20,10 @@ primary effects:
set require any fixture listed in ``FIXTURES_CLEANUP``.
A cleanup test can have multiple fixtures listed in its ``FIXTURES_CLEANUP``
property. It will execute only once for the whole CTest run, not once for each
fixture. A fixture can also have more than one cleanup test defined. If there
are multiple cleanup tests for a fixture, projects can control their order with
the usual :prop_test:`DEPENDS` test property if necessary.
property. It will execute once for all of them, not once for each fixture. A
fixture can also have more than one cleanup test defined. If there are multiple
cleanup tests for a fixture, projects can control their order with the usual
:prop_test:`DEPENDS` test property if necessary.
A cleanup test is allowed to require other fixtures, but not any fixture listed
in its ``FIXTURES_CLEANUP`` property. For example:
@@ -45,5 +45,11 @@ in its ``FIXTURES_CLEANUP`` property. For example:
Cleanup tests will execute even if setup or regular tests for that fixture fail
or are skipped.
.. versionchanged:: 4.5
The :prop_test:`FIXTURE_REPEAT_MODE` property selects how a fixture behaves
when :manual:`ctest(1)` repeats tests with :ctest-option:`--repeat`. See
also :policy:`CMP0224`.
See :prop_test:`FIXTURES_REQUIRED` for a more complete discussion of how to use
test fixtures.
+6
View File
@@ -26,6 +26,12 @@ behavior can be overridden with the :option:`-FS <ctest -FS>`,
:option:`-FC <ctest -FC>` and :option:`-FA <ctest -FA>` command line options to
:manual:`ctest(1)` if desired.
.. versionchanged:: 4.5
The :prop_test:`FIXTURE_REPEAT_MODE` property selects how a fixture behaves
when :manual:`ctest(1)` repeats tests with :ctest-option:`--repeat`. See
also :policy:`CMP0224`.
Since setup and cleanup tasks are also tests, they can have an ordering
specified by the :prop_test:`DEPENDS` test property just like any other tests.
This can be exploited to implement setup or cleanup using multiple tests for a
+10 -4
View File
@@ -20,10 +20,10 @@ primary effects:
set require any fixture listed in ``FIXTURES_SETUP``.
A setup test can have multiple fixtures listed in its ``FIXTURES_SETUP``
property. It will execute only once for the whole CTest run, not once for each
fixture. A fixture can also have more than one setup test defined. If there are
multiple setup tests for a fixture, projects can control their order with the
usual :prop_test:`DEPENDS` test property if necessary.
property. It will execute once for all of them, not once for each fixture. A
fixture can also have more than one setup test defined. If there are multiple
setup tests for a fixture, projects can control their order with the usual
:prop_test:`DEPENDS` test property if necessary.
A setup test is allowed to require other fixtures, but not any fixture listed
in its ``FIXTURES_SETUP`` property. For example:
@@ -46,5 +46,11 @@ If any of a fixture's setup tests fail, none of the tests listing that fixture
in its :prop_test:`FIXTURES_REQUIRED` property will be run. Cleanup tests will,
however, still be executed.
.. versionchanged:: 4.5
The :prop_test:`FIXTURE_REPEAT_MODE` property selects how a fixture behaves
when :manual:`ctest(1)` repeats tests with :ctest-option:`--repeat`. See
also :policy:`CMP0224`.
See :prop_test:`FIXTURES_REQUIRED` for a more complete discussion of how to use
test fixtures.
+87
View File
@@ -0,0 +1,87 @@
FIXTURE_REPEAT_MODE
-------------------
.. versionadded:: 4.5
Specifies how a test fixture behaves when :manual:`ctest(1)` repeats tests
with its :ctest-option:`--repeat` option.
The property describes the fixture rather than the test carrying it, so
setting it on any one of the fixture's setup or cleanup tests is enough. It
has no effect on a test that has neither a :prop_test:`FIXTURES_SETUP` nor a
:prop_test:`FIXTURES_CLEANUP` property, although :manual:`ctest(1)` still
rejects a value it does not recognize.
The value must be one of the following:
``AROUND_ALL_REPEATS``
The fixture runs once, around all repetitions of the tests requiring it::
setup -> test -> test -> test -> cleanup
``AROUND_EACH_REPEAT``
The fixture repeats with the tests requiring it, so that every repetition
of a test gets a fresh setup and its own cleanup::
setup -> test -> cleanup -> setup -> test -> cleanup -> ...
``EACH_TEST_SEPARATELY``
No test is treated specially: every test, including the fixture's own setup
and cleanup tests, runs all of its repetitions before the next test starts::
setup -> setup -> setup -> test -> test -> test -> cleanup -> ...
This is the behavior of CMake 4.4 and below.
If the property is not set on any of a fixture's setup or cleanup tests, the
behavior is determined by policy :policy:`CMP0224`.
Fixtures that repeat together must agree on the mode. All of a fixture's own
setup and cleanup tests must request the same mode, and so must any two
fixtures that share a test, since one test cannot repeat with one fixture and
not with another it takes part in. :manual:`ctest(1)` reports an error and
runs nothing if they disagree.
In ``AROUND_EACH_REPEAT`` mode the fixture's tests repeat as a unit, so
:ctest-option:`--repeat` applies its condition to the unit as a whole.
``until-fail`` repeats while every test in the unit passes, ``until-pass``
repeats while any of them does not pass, and ``after-timeout`` repeats while
any of them times out. A test that requires two fixtures in
``AROUND_EACH_REPEAT`` mode makes those fixtures repeat together.
Only the last repetition of the unit is reported, as for a test repeating on
its own, and a test that lists one of the unit's tests in its
:prop_test:`DEPENDS` property runs after that last repetition. If
:manual:`ctest(1)` is interrupted part-way through the repetitions,
:ctest-option:`-F` resumes by running the whole unit again.
Example
^^^^^^^
.. code-block:: cmake
add_test(NAME start_server COMMAND start_server)
set_tests_properties(start_server PROPERTIES
FIXTURES_SETUP Server
FIXTURE_REPEAT_MODE AROUND_EACH_REPEAT)
add_test(NAME query_server COMMAND query_server)
set_tests_properties(query_server PROPERTIES FIXTURES_REQUIRED Server)
add_test(NAME stop_server COMMAND stop_server)
set_tests_properties(stop_server PROPERTIES FIXTURES_CLEANUP Server)
With ``ctest --repeat until-fail:3``, this runs a fresh server for each
attempt at ``query_server``::
start_server -> query_server -> stop_server
start_server -> query_server -> stop_server
start_server -> query_server -> stop_server
See Also
^^^^^^^^
* :prop_test:`FIXTURES_SETUP`
* :prop_test:`FIXTURES_CLEANUP`
* :prop_test:`FIXTURES_REQUIRED`
* :policy:`CMP0224`
@@ -0,0 +1,6 @@
ctest-fixture-repeat
--------------------
* The :prop_test:`FIXTURE_REPEAT_MODE` test property was added to select how
a test fixture behaves when :manual:`ctest(1)` repeats tests with its
:ctest-option:`--repeat` option. See policy :policy:`CMP0224`.
@@ -44,6 +44,8 @@ only for the policies that do not warn by default:
policy :policy:`CMP0206`.
* ``CMAKE_POLICY_WARNING_CMP0220`` controls the warning for
policy :policy:`CMP0220`.
* ``CMAKE_POLICY_WARNING_CMP0224`` controls the warning for
policy :policy:`CMP0224`.
This variable should not be set by a project in CMake code. Project
developers running CMake may set this variable in their cache to
+16
View File
@@ -256,6 +256,11 @@ same as the Google Test name (i.e. ``suite.testcase``); see also
Specifies additional properties to be set on all tests discovered by this
invocation of ``gtest_discover_tests()``.
.. versionchanged:: 4.5
If the properties make the discovered tests part of a test fixture, and
they do not set :prop_test:`FIXTURE_REPEAT_MODE`, policy :policy:`CMP0224`
determines the behavior when the :ctest-option:`--repeat` option is used.
``TEST_LIST var``
Make the list of tests available in the variable ``var``, rather than the
default ``<target>_TESTS``. This can be useful when the same test
@@ -595,6 +600,17 @@ function(gtest_discover_tests target)
endif()
endif()
# For policy CMP0224, only NEW needs carrying through. With nothing
# recorded, ctest already uses the EACH_TEST_SEPARATELY behavior of
# CMake 4.4 and below.
cmake_policy(GET CMP0224 _CMP0224
PARENT_SCOPE # undocumented, do not use outside of CMake
)
if(_CMP0224 STREQUAL "NEW")
list(APPEND arg_PROPERTIES
_CMAKE_DEFAULT_FIXTURE_REPEAT_MODE AROUND_EACH_REPEAT)
endif()
get_property(test_launcher
TARGET ${target}
PROPERTY TEST_LAUNCHER
+318 -9
View File
@@ -9,11 +9,15 @@
#include <cstddef> // IWYU pragma: keep
#include <cstdlib>
#include <functional>
#include <initializer_list>
#include <iomanip>
#include <iostream>
#include <list>
#include <map>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
@@ -21,6 +25,7 @@
#include <cm/memory>
#include <cm/optional>
#include <cm/string_view>
#include <cm/vector>
#include <cmext/algorithm>
#include <cm3p/json/value.h>
@@ -42,6 +47,7 @@
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmUVJobServerClient.h"
#include "cmUnreachable.h"
#include "cmWorkingDirectory.h"
namespace {
@@ -95,6 +101,36 @@ cm::optional<CostEntry> splitCostLine(cm::string_view line)
return CostEntry{ line.substr(0, pos1), prev, cost };
}
std::string FixtureRepeatModeString(cmCTestTestHandler::FixtureRepeatMode mode)
{
switch (mode) {
case cmCTestTestHandler::FixtureRepeatMode::AroundAllRepeats:
return "AROUND_ALL_REPEATS";
case cmCTestTestHandler::FixtureRepeatMode::AroundEachRepeat:
return "AROUND_EACH_REPEAT";
case cmCTestTestHandler::FixtureRepeatMode::EachTestSeparately:
return "EACH_TEST_SEPARATELY";
}
CM_UNREACHABLE;
}
// A fixture and everything ctest needs to decide how it repeats.
struct Fixture
{
// The fixture's own setup and cleanup tests.
std::set<int> SetupAndCleanupTests;
// Those tests and the tests requiring the fixture, which together are
// everything that repeats with it in AROUND_EACH_REPEAT mode.
std::set<int> AllTests;
// The modes the setup and cleanup tests request. Each is mapped to one
// test that requests it, so that a report of conflicting modes can name
// the tests responsible.
std::map<cmCTestTestHandler::FixtureRepeatMode, int> RequestedModes;
// The mode CMake recorded as the CMP0224 default, taken from the first
// setup or cleanup test that carries one.
cm::optional<cmCTestTestHandler::FixtureRepeatMode> DefaultMode;
};
}
namespace cmsys {
@@ -144,7 +180,8 @@ bool cmCTestMultiProcessHandler::SetTests(TestMap tests,
this->HasCycles = !this->CheckCycles();
this->HasInvalidGeneratedResourceSpec =
!this->CheckGeneratedResourceSpec();
if (this->HasCycles || this->HasInvalidGeneratedResourceSpec) {
if (this->HasCycles || this->HasInvalidGeneratedResourceSpec ||
!this->ComputeFixtureRepetition()) {
return false;
}
this->CreateTestCostList();
@@ -152,6 +189,158 @@ bool cmCTestMultiProcessHandler::SetTests(TestMap tests,
return true;
}
bool cmCTestMultiProcessHandler::ComputeFixtureRepetition()
{
using Mode = cmCTestTestHandler::FixtureRepeatMode;
if (this->RepeatMode == cmCTest::Repeat::Never || this->RepeatCount <= 1) {
return true;
}
// Collect the tests taking part in each fixture and the modes its own
// setup and cleanup tests ask for.
std::map<std::string, Fixture> fixtures;
for (auto const& p : this->Properties) {
int const test = p.first;
auto const& props = *p.second;
for (std::string const& name : props.FixturesRequired) {
fixtures[name].AllTests.insert(test);
}
for (std::set<std::string> const* own :
{ &props.FixturesSetup, &props.FixturesCleanup }) {
for (std::string const& name : *own) {
Fixture& fixture = fixtures[name];
fixture.SetupAndCleanupTests.insert(test);
fixture.AllTests.insert(test);
if (props.RequestedFixtureRepeatMode) {
fixture.RequestedModes.emplace(*props.RequestedFixtureRepeatMode,
test);
} else if (!fixture.DefaultMode) {
fixture.DefaultMode = props.DefaultFixtureRepeatMode;
}
}
}
}
// Resolve each fixture's mode. A mode requested by one of its own tests
// applies to the whole fixture, so its own tests must agree.
std::map<std::string, Mode> fixtureMode;
for (auto const& fi : fixtures) {
Fixture const& fixture = fi.second;
if (fixture.RequestedModes.size() > 1) {
std::string e =
cmStrCat("Error: the setup and cleanup tests of fixture \"", fi.first,
"\" request conflicting FIXTURE_REPEAT_MODE values:\n");
for (auto const& r : fixture.RequestedModes) {
e += cmStrCat(" \"", this->GetName(r.second), "\" requests ",
FixtureRepeatModeString(r.first), '\n');
}
e += "All setup and cleanup tests of a fixture must request the same "
"mode.\n";
cmCTestLog(this->CTest, ERROR_MESSAGE, e);
return false;
}
fixtureMode[fi.first] = !fixture.RequestedModes.empty()
? fixture.RequestedModes.begin()->first
: fixture.DefaultMode.value_or(Mode::EachTestSeparately);
}
// Fixtures that share a test repeat together, so they must agree as well.
// Otherwise a test could repeat with one fixture after another fixture it
// takes part in has been cleaned up.
for (auto const& p : this->Properties) {
auto const& props = *p.second;
std::map<Mode, std::string> modes;
for (std::set<std::string> const* used :
{ &props.FixturesSetup, &props.FixturesCleanup,
&props.FixturesRequired }) {
for (std::string const& name : *used) {
modes.emplace(fixtureMode[name], name);
}
}
if (modes.size() > 1) {
std::string e = cmStrCat("Error: test \"", this->GetName(p.first),
"\" takes part in fixtures with conflicting "
"FIXTURE_REPEAT_MODE values:\n");
for (auto const& m : modes) {
e += cmStrCat(" fixture \"", m.second, "\" uses ",
FixtureRepeatModeString(m.first), '\n');
}
e += "Fixtures that share a test must use the same mode.\n";
cmCTestLog(this->CTest, ERROR_MESSAGE, e);
return false;
}
}
// Record what the resolved modes ask of each fixture's tests.
for (auto const& fi : fixtures) {
switch (fixtureMode[fi.first]) {
case Mode::AroundAllRepeats:
this->TestsRunOnce.insert(fi.second.SetupAndCleanupTests.begin(),
fi.second.SetupAndCleanupTests.end());
break;
case Mode::AroundEachRepeat:
this->AddRepeatGroup(fi.second.AllTests);
break;
case Mode::EachTestSeparately:
break;
}
}
// Record what each group needs to repeat itself.
for (auto& group : this->RepeatGroups) {
group.RepetitionsLeft = this->RepeatCount - 1;
group.Unfinished = group.Tests.size();
for (auto& t : group.Tests) {
// Keep only dependencies within the group. Dependencies on other
// tests are satisfied once, by the group's first repetition.
for (int depend : this->PendingTests[t.first].Depends) {
if (group.Tests.count(depend) != 0) {
t.second.insert(depend);
}
}
}
}
return true;
}
void cmCTestMultiProcessHandler::AddRepeatGroup(std::set<int> const& tests)
{
// Reuse the group of any test that is already in one, so that fixtures
// sharing a test end up repeating together.
int groupNumber = -1;
for (int test : tests) {
auto const gi = this->RepeatGroupOfTest.find(test);
if (gi != this->RepeatGroupOfTest.end()) {
groupNumber = gi->second;
break;
}
}
if (groupNumber < 0) {
groupNumber = static_cast<int>(this->RepeatGroups.size());
this->RepeatGroups.emplace_back();
}
RepeatGroup& group = this->RepeatGroups[groupNumber];
for (int test : tests) {
auto const gi = this->RepeatGroupOfTest.find(test);
if (gi == this->RepeatGroupOfTest.end()) {
this->RepeatGroupOfTest[test] = groupNumber;
group.Tests[test];
} else if (gi->second != groupNumber) {
// Move the tests of the other group over. It is left behind empty in
// RepeatGroups, where no test refers to it any more.
RepeatGroup& other = this->RepeatGroups[gi->second];
for (auto const& t : other.Tests) {
this->RepeatGroupOfTest[t.first] = groupNumber;
}
group.Tests.insert(other.Tests.begin(), other.Tests.end());
other.Tests.clear();
}
}
}
// Set the max number of tests that can be run at the same time.
void cmCTestMultiProcessHandler::SetParallelLevel(cm::optional<size_t> level)
{
@@ -273,8 +462,15 @@ void cmCTestMultiProcessHandler::StartTestProcess(int test)
auto testRun = cm::make_unique<cmCTestRunTest>(*this, test);
if (this->RepeatMode != cmCTest::Repeat::Never) {
testRun->SetRepeatMode(this->RepeatMode);
testRun->SetNumberOfRuns(this->RepeatCount);
auto const gi = this->RepeatGroupOfTest.find(test);
if (gi != this->RepeatGroupOfTest.end()) {
// The group repeats the test, so report the repetition it is running.
int const left = this->RepeatGroups[gi->second].RepetitionsLeft;
testRun->SetRunNumber(this->RepeatCount - left, this->RepeatCount);
} else if (this->TestsRunOnce.count(test) == 0) {
testRun->SetRepeatMode(this->RepeatMode);
testRun->SetRunNumber(1, this->RepeatCount);
}
}
if (this->UseResourceSpec) {
testRun->SetUseAllocatedResources(true);
@@ -829,19 +1025,36 @@ void cmCTestMultiProcessHandler::FinishTestProcess(
this->Failed->push_back(properties->Name);
}
for (auto& t : this->PendingTests) {
t.second.Depends.erase(test);
// Let the tests waiting on this one start. A test that its fixture
// repeats releases only the other tests of its group; the tests outside
// it have to wait for the group's last repetition, so FinishRepeatGroup
// releases those.
auto const group = this->RepeatGroupOfTest.find(test);
bool const repeatsWithFixture = group != this->RepeatGroupOfTest.end();
for (auto& pending : this->PendingTests) {
if (repeatsWithFixture) {
auto const pendingGroup = this->RepeatGroupOfTest.find(pending.first);
if (pendingGroup == this->RepeatGroupOfTest.end() ||
pendingGroup->second != group->second) {
continue;
}
}
pending.second.Depends.erase(test);
}
// A test killed by the interrupt (e.g. Ctrl+C) never truly finished, so do
// not record it in the checkpoint; otherwise `ctest -F` would skip it when
// resuming this interrupted run.
if (cmInstrumentationInterrupt::PendingInterruptSignal() == 0) {
// resuming this interrupted run. A test that its fixture repeats is
// recorded by FinishRepeatGroup instead, once its group is done.
if (cmInstrumentationInterrupt::PendingInterruptSignal() == 0 &&
!repeatsWithFixture) {
this->WriteCheckpoint(test);
}
this->DeallocateResources(test);
this->UnlockResources(test);
this->FinishRepeatGroupTest(test, testResult.TestStatus);
runner.reset();
if (this->JobServerClient) {
@@ -850,6 +1063,90 @@ void cmCTestMultiProcessHandler::FinishTestProcess(
this->StartNextTestsOnIdle();
}
void cmCTestMultiProcessHandler::FinishRepeatGroupTest(int test,
int testStatus)
{
auto const gi = this->RepeatGroupOfTest.find(test);
if (gi == this->RepeatGroupOfTest.end()) {
return;
}
RepeatGroup& group = this->RepeatGroups[gi->second];
group.AllCompleted &= testStatus == cmCTestTestHandler::COMPLETED;
group.AnyTimedOut |= testStatus == cmCTestTestHandler::TIMEOUT;
if (--group.Unfinished > 0) {
return;
}
// The group finished a repetition. Repeat it under the same conditions
// that make an individual test repeat. See cmCTestRunTest::NeedsToRepeat.
bool const conditionMet =
(this->RepeatMode == cmCTest::Repeat::UntilFail && group.AllCompleted) ||
(this->RepeatMode == cmCTest::Repeat::UntilPass && !group.AllCompleted) ||
(this->RepeatMode == cmCTest::Repeat::AfterTimeout && group.AnyTimedOut);
bool const runEnding = this->StopTimePassed ||
(this->CheckStopOnFailure() && !this->Failed->empty()) ||
cmInstrumentationInterrupt::PendingInterruptSignal() != 0;
if (group.RepetitionsLeft == 0 || !conditionMet || runEnding) {
this->FinishRepeatGroup(group);
return;
}
group.RepetitionsLeft--;
group.Unfinished = group.Tests.size();
group.AllCompleted = true;
group.AnyTimedOut = false;
this->RequeueRepeatGroup(group);
}
void cmCTestMultiProcessHandler::FinishRepeatGroup(RepeatGroup const& group)
{
// The group has run its last repetition, so record its tests the way a
// test repeating on its own is recorded once it stops repeating: in the
// checkpoint, unless the interrupt cut the group short, and as satisfying
// the tests outside the group that were waiting on them.
bool const interrupted =
cmInstrumentationInterrupt::PendingInterruptSignal() != 0;
for (auto const& t : group.Tests) {
if (!interrupted) {
this->WriteCheckpoint(t.first);
}
for (auto& pending : this->PendingTests) {
pending.second.Depends.erase(t.first);
}
}
}
void cmCTestMultiProcessHandler::RequeueRepeatGroup(RepeatGroup const& group)
{
// Only the last repetition of the group counts, as for a test repeating on
// its own, so drop the results of the repetition just finished. Leaving a
// failure behind would also keep the tests requiring the fixture from
// running again, since their dependency on it has to have succeeded.
for (auto const& t : group.Tests) {
std::string const& name = this->Properties[t.first]->Name;
cm::erase(*this->Passed, name);
cm::erase(*this->Failed, name);
}
cm::erase_if(*this->TestResults,
[&group](cmCTestTestHandler::cmCTestTestResult const& result) {
return group.Tests.count(result.TestCount) != 0;
});
this->Completed -= group.Tests.size();
for (auto const& t : group.Tests) {
TestInfo info;
info.Depends = t.second;
this->PendingTests[t.first] = std::move(info);
this->OrderedTests.push_back(t.first);
}
cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Re-queued " << group.Tests.size()
<< " tests to repeat their fixture"
<< std::endl,
this->Quiet);
}
void cmCTestMultiProcessHandler::UpdateCostData()
{
std::string fname = this->CTest->GetCostDataFile();
@@ -1256,6 +1553,13 @@ static Json::Value DumpCTestProperties(
raw ? rawProperties["FIXTURES_SETUP"]
: DumpToJsonArray(testProperties.FixturesSetup)));
}
if (testProperties.RequestedFixtureRepeatMode) {
properties.append(
DumpCTestProperty("FIXTURE_REPEAT_MODE",
raw ? rawProperties["FIXTURE_REPEAT_MODE"]
: Json::Value(FixtureRepeatModeString(
*testProperties.RequestedFixtureRepeatMode))));
}
if (!testProperties.GeneratedResourceSpecFile.empty()) {
properties.append(
DumpCTestProperty("GENERATED_RESOURCE_SPEC_FILE",
@@ -1638,8 +1942,13 @@ void cmCTestMultiProcessHandler::CheckResume()
void cmCTestMultiProcessHandler::RemoveTest(int index)
{
this->OrderedTests.erase(
std::find(this->OrderedTests.begin(), this->OrderedTests.end(), index));
auto const oi =
std::find(this->OrderedTests.begin(), this->OrderedTests.end(), index);
if (oi == this->OrderedTests.end()) {
// The checkpoint names a test this run does not have pending.
return;
}
this->OrderedTests.erase(oi);
this->PendingTests.erase(index);
this->Properties.erase(index);
this->Completed++;
+40
View File
@@ -177,6 +177,46 @@ private:
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.
+27 -21
View File
@@ -391,6 +391,7 @@ cmCTestRunTest::EndTestResult cmCTestRunTest::EndTest(size_t completed,
}
cmCTestRunTest::EndTestResult testResult;
testResult.Passed = passed || skipped;
testResult.TestStatus = this->TestResult.Status;
if (res == cmProcess::State::Expired &&
this->TestProcess->GetTimeoutReason() ==
cmProcess::TimeoutReason::StopTime) {
@@ -424,8 +425,7 @@ bool cmCTestRunTest::StartAgain(std::unique_ptr<cmCTestRunTest> runner,
bool cmCTestRunTest::NeedsToRepeat()
{
this->NumberOfRunsLeft--;
if (this->NumberOfRunsLeft == 0) {
if (this->RunNumber == this->RunCount) {
return false;
}
// If a test is marked as NOT_RUN it will not be repeated
@@ -433,9 +433,8 @@ bool cmCTestRunTest::NeedsToRepeat()
if (this->TestResult.Status == cmCTestTestHandler::NOT_RUN) {
return false;
}
// if number of runs left is not 0, and we are running until
// we find a failed (or passed) test, then return true so the test can be
// restarted
// The test has runs left, so run it again if we are running until we find
// a failed (or passed) test.
if ((this->RepeatMode == cmCTest::Repeat::UntilFail &&
this->TestResult.Status == cmCTestTestHandler::COMPLETED) ||
(this->RepeatMode == cmCTest::Repeat::UntilPass &&
@@ -443,6 +442,7 @@ bool cmCTestRunTest::NeedsToRepeat()
(this->RepeatMode == cmCTest::Repeat::AfterTimeout &&
this->TestResult.Status == cmCTestTestHandler::TIMEOUT)) {
this->RunAgain = true;
this->RunNumber++;
return true;
}
return false;
@@ -559,11 +559,10 @@ bool cmCTestRunTest::StartTest(size_t completed, size_t total)
{
this->TotalNumberOfTests = total; // save for rerun case
std::string runIterationSuffix{};
if (this->NumberOfRunsTotal > 1) {
std::string runIterationSuffix;
if (this->RunCount > 1) {
runIterationSuffix =
cmStrCat(" (run ", 1 + this->NumberOfRunsTotal - this->NumberOfRunsLeft,
'/', this->NumberOfRunsTotal, ')');
cmStrCat(" (run ", this->RunNumber, '/', this->RunCount, ')');
}
if (!this->CTest->GetTestProgressOutput()) {
cmCTestLog(
@@ -999,18 +998,25 @@ void cmCTestRunTest::WriteLogOutputTop(size_t completed, size_t total)
{
std::ostringstream outputStream;
// If this is the last or only run of this test, or progress output is
// requested, then print out completed / total.
// Only issue is if a test fails and we are running until fail
// then it will never print out the completed / total, same would
// got for run until pass. Trick is when this is called we don't
// yet know if we are passing or failing.
bool const progressOnLast =
(this->RepeatMode != cmCTest::Repeat::UntilPass &&
this->RepeatMode != cmCTest::Repeat::AfterTimeout);
if ((progressOnLast && this->NumberOfRunsLeft == 1) ||
(!progressOnLast && this->NumberOfRunsLeft == this->NumberOfRunsTotal) ||
this->CTest->GetTestProgressOutput()) {
// Print "completed/total" on the run whose result is the one recorded for
// the test, and blanks on its other runs. Which run that is has to be
// decided before the run finishes: with until-fail it is the last run, and
// with until-pass and after-timeout the repetitions may end early, so it
// is the first. A test that its fixture repeats records every run.
bool countThisRun = true;
switch (this->RepeatMode) {
case cmCTest::Repeat::Never:
break;
case cmCTest::Repeat::UntilFail:
countThisRun = this->RunNumber == this->RunCount;
break;
case cmCTest::Repeat::UntilPass:
CM_FALLTHROUGH;
case cmCTest::Repeat::AfterTimeout:
countThisRun = this->RunNumber == 1;
break;
}
if (countThisRun || this->CTest->GetTestProgressOutput()) {
outputStream << std::setw(getNumWidth(total)) << completed << "/";
outputStream << std::setw(getNumWidth(total)) << total << " ";
}
+12 -6
View File
@@ -30,10 +30,13 @@ class cmCTestRunTest
public:
explicit cmCTestRunTest(cmCTestMultiProcessHandler& multiHandler, int index);
void SetNumberOfRuns(int n)
// Report this run as run `number` of `count` in the test's "(run N/M)"
// suffix. A test repeating on its own starts at run 1 and counts up
// itself; a test repeated by its fixture is told which run it is.
void SetRunNumber(int number, int count)
{
this->NumberOfRunsLeft = n;
this->NumberOfRunsTotal = n;
this->RunNumber = number;
this->RunCount = count;
}
void SetRepeatMode(cmCTest::Repeat r) { this->RepeatMode = r; }
@@ -73,6 +76,7 @@ public:
{
bool Passed = false;
bool StopTimePassed = false;
int TestStatus = cmCTestTestHandler::NOT_RUN;
};
// launch the test process, return whether it started correctly
@@ -157,10 +161,12 @@ private:
std::vector<std::map<
std::string, std::vector<cmCTestMultiProcessHandler::ResourceAllocation>>>
AllocatedResources;
// Never unless this test repeats itself. A test repeated by its fixture
// leaves this Never; the fixture decides when to repeat.
cmCTest::Repeat RepeatMode = cmCTest::Repeat::Never;
int NumberOfRunsLeft = 1; // default to 1 run of the test
int NumberOfRunsTotal = 1; // default to 1 run of the test
bool RunAgain = false; // default to not having to run again
int RunNumber = 1; // which run of the test this is
int RunCount = 1; // how many runs it may be given
bool RunAgain = false;
bool UseLLVMCov = false;
size_t TotalNumberOfTests;
};
+26
View File
@@ -2351,6 +2351,27 @@ std::string cmCTestTestHandler::cmCTestTestProperties::GetStampFile()
return cmCryptoHash(cmCryptoHash::AlgoMD5).HashString(this->Name) + ".stamp";
}
namespace {
cm::optional<cmCTestTestHandler::FixtureRepeatMode> ParseFixtureRepeatMode(
cmCTestTestHandler::cmCTestTestProperties& rt, std::string const& val)
{
using Mode = cmCTestTestHandler::FixtureRepeatMode;
if (val == "AROUND_ALL_REPEATS"_s) {
return Mode::AroundAllRepeats;
}
if (val == "AROUND_EACH_REPEAT"_s) {
return Mode::AroundEachRepeat;
}
if (val == "EACH_TEST_SEPARATELY"_s) {
return Mode::EachTestSeparately;
}
rt.AppendError(cmStrCat("FIXTURE_REPEAT_MODE \"", val,
"\" not recognized. Must be AROUND_ALL_REPEATS, "
"AROUND_EACH_REPEAT, or EACH_TEST_SEPARATELY."));
return cm::nullopt;
}
}
bool cmCTestTestHandler::SetTestsProperties(
std::vector<std::string> const& args)
{
@@ -2433,6 +2454,11 @@ bool cmCTestTestHandler::SetTestsProperties(
cmList lval{ val };
rt.FixturesRequired.insert(lval.begin(), lval.end());
} else if (key == "FIXTURE_REPEAT_MODE"_s) {
rt.RawProperties[key] = val;
rt.RequestedFixtureRepeatMode = ParseFixtureRepeatMode(rt, val);
} else if (key == "_CMAKE_DEFAULT_FIXTURE_REPEAT_MODE"_s) {
rt.DefaultFixtureRepeatMode = ParseFixtureRepeatMode(rt, val);
} else if (key == "TIMEOUT"_s) {
rt.RawProperties[key] = val;
rt.Timeout = cmDuration(atof(val.c_str()));
+13
View File
@@ -143,6 +143,15 @@ public:
std::string Name;
};
// How ctest --repeat treats a fixture. See the FIXTURE_REPEAT_MODE test
// property and policy CMP0224.
enum class FixtureRepeatMode
{
AroundAllRepeats, // the fixture runs once, around all repetitions
AroundEachRepeat, // the fixture repeats with the tests requiring it
EachTestSeparately // every test repeats on its own
};
struct cmCTestTestProperties
{
void AppendError(cm::string_view err);
@@ -221,6 +230,10 @@ public:
std::set<std::string> FixturesRequired;
std::string FixturesRequiredRaw;
std::set<std::string> RequireSuccessDepends;
// For the fixtures this test sets up or cleans up. A mode requested by
// any of a fixture's own tests wins over the default recorded for them.
cm::optional<FixtureRepeatMode> RequestedFixtureRepeatMode;
cm::optional<FixtureRepeatMode> DefaultFixtureRepeatMode;
std::vector<std::vector<cmCTestTestResourceRequirement>> ResourceGroups;
std::string ResourceGroupsRaw;
std::string GeneratedResourceSpecFile;
+25
View File
@@ -8,6 +8,7 @@
#include <vector>
#include <cm/memory>
#include <cm/string_view>
#include <cmext/string_view>
#include "cmArgumentParser.h"
@@ -17,6 +18,7 @@
#include "cmListFileCache.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmPolicies.h"
#include "cmScriptGenerator.h"
#include "cmTestDiscovery.h"
#include "cmTestGenerator.h"
@@ -89,6 +91,17 @@ private:
cmListFileBacktrace Backtrace;
};
bool SetsFixtureRepeatMode(std::vector<std::string> const& properties)
{
for (std::size_t i = 0; i < properties.size(); i += 2) {
if (properties[i] == "FIXTURE_REPEAT_MODE"_s ||
properties[i] == "_CMAKE_DEFAULT_FIXTURE_REPEAT_MODE"_s) {
return true;
}
}
return false;
}
} // namespace
bool cmDiscoverTestsCommand(std::vector<std::string> const& args,
@@ -121,6 +134,18 @@ bool cmDiscoverTestsCommand(std::vector<std::string> const& args,
}
cmMakefile& mf = status.GetMakefile();
// The discovered tests are created while ctest runs, too late for the
// policy to reach them. Only NEW needs carrying through. With nothing
// recorded, ctest already uses the EACH_TEST_SEPARATELY behavior of
// CMake 4.4 and below.
if (mf.GetPolicyStatus(cmPolicies::CMP0224) == cmPolicies::NEW &&
!SetsFixtureRepeatMode(arguments.TestProperties)) {
arguments.TestProperties.emplace_back(
"_CMAKE_DEFAULT_FIXTURE_REPEAT_MODE");
arguments.TestProperties.emplace_back("AROUND_EACH_REPEAT");
}
mf.AddTestGenerator(cm::make_unique<DiscoveryGenerator>(std::move(arguments),
mf.GetBacktrace()));
return true;
+24
View File
@@ -1574,6 +1574,11 @@ void cmGlobalGenerator::AddCMP0068WarnTarget(std::string const& target)
this->CMP0068WarnTargets.insert(target);
}
void cmGlobalGenerator::AddCMP0224WarnTest(std::string const& test)
{
this->CMP0224WarnTests.insert(test);
}
bool cmGlobalGenerator::ShouldWarnCMP0210(std::string const& lang)
{
return this->WarnedCMP0210Languages.insert(lang).second;
@@ -1694,6 +1699,9 @@ bool cmGlobalGenerator::Compute()
// clear targets to issue warning CMP0068 for
this->CMP0068WarnTargets.clear();
// clear tests to issue warning CMP0224 for
this->CMP0224WarnTests.clear();
// Check whether this generator is allowed to run.
if (!this->CheckALLOW_DUPLICATE_CUSTOM_TARGETS()) {
return false;
@@ -1974,6 +1982,22 @@ void cmGlobalGenerator::Generate()
this->GetCMakeInstance()->IssueDiagnostic(cmDiagnostics::CMD_POLICY,
w.str());
}
if (!this->CMP0224WarnTests.empty()) {
std::ostringstream w;
/* clang-format off */
w <<
cmPolicies::GetPolicyWarning(cmPolicies::CMP0224) << "\n"
"For compatibility with older versions of CMake, the fixtures of the "
"following tests will use EACH_TEST_SEPARATELY mode:\n"
;
/* clang-format on */
for (std::string const& t : this->CMP0224WarnTests) {
w << ' ' << t << '\n';
}
this->GetCMakeInstance()->IssueDiagnostic(cmDiagnostics::CMD_POLICY,
w.str());
}
}
#if !defined(CMAKE_BOOTSTRAP)
+4
View File
@@ -708,6 +708,7 @@ public:
cmExportBuildFileGenerator* GetExportedTargetsFile(
std::string const& filename) const;
void AddCMP0068WarnTarget(std::string const& target);
void AddCMP0224WarnTest(std::string const& test);
virtual bool SupportsShortObjectNames() const;
bool UseShortObjectNames(
@@ -1055,6 +1056,9 @@ private:
// track targets to issue CMP0068 warning for.
std::set<std::string> CMP0068WarnTargets;
// track tests to issue CMP0224 warning for.
std::set<std::string> CMP0224WarnTests;
std::unordered_set<std::string> WarnedCMP0210Languages;
std::unordered_set<std::string> WarnedExperimental;
+5 -1
View File
@@ -672,7 +672,11 @@ class cmMakefile;
"PATH_IS_PREFIX operator.", \
4, 5, 0, WARN) \
SELECT(POLICY, CMP0223, "An empty path is not a prefix of any path.", 4, 5, \
0, WARN)
0, WARN) \
SELECT(POLICY, CMP0224, \
"Test fixtures use AROUND_EACH_REPEAT mode by default with " \
"ctest --repeat.", \
4, 5, 0, WARN)
#define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
#define CM_FOR_EACH_POLICY_ID(POLICY) \
+1
View File
@@ -13,6 +13,7 @@ cmTest::cmTest(cmMakefile* mf)
: Backtrace(mf->GetBacktrace())
, PolicyStatusCMP0158(mf->GetPolicyStatus(cmPolicies::CMP0158))
, PolicyStatusCMP0178(mf->GetPolicyStatus(cmPolicies::CMP0178))
, PolicyStatusCMP0224(mf->GetPolicyStatus(cmPolicies::CMP0224))
{
this->Makefile = mf;
this->OldStyle = true;
+7
View File
@@ -83,6 +83,12 @@ public:
this->PolicyStatusCMP0178 = p;
}
/** Get the CMP0224 policy setting */
cmPolicies::PolicyStatus GetCMP0224() const
{
return this->PolicyStatusCMP0224;
}
/** Set/Get whether lists in command lines should be expanded. */
bool GetCommandExpandLists() const;
void SetCommandExpandLists(bool b);
@@ -100,4 +106,5 @@ private:
cmListFileBacktrace Backtrace;
cmPolicies::PolicyStatus PolicyStatusCMP0158;
cmPolicies::PolicyStatus PolicyStatusCMP0178;
cmPolicies::PolicyStatus PolicyStatusCMP0224;
};
+36
View File
@@ -314,6 +314,7 @@ void cmTestGenerator::GenerateScriptForConfig(std::ostream& os,
os << " _CMAKE_TEST_BUILD_DEPENDS "
<< cmScriptGenerator::Quote(depList.to_string());
}
this->GenerateDefaultFixtureRepeatMode(os);
os << ' ';
this->GenerateBacktrace(os, this->Test->GetBacktrace());
os << ")\n";
@@ -367,11 +368,46 @@ void cmTestGenerator::GenerateOldStyle(std::ostream& fout, Indent indent)
for (auto const& i : this->Test->GetProperties().GetList()) {
fout << " " << i.first << " " << cmScriptGenerator::Quote(i.second);
}
this->GenerateDefaultFixtureRepeatMode(fout);
fout << ' ';
this->GenerateBacktrace(fout, this->Test->GetBacktrace());
fout << ")\n";
}
void cmTestGenerator::GenerateDefaultFixtureRepeatMode(std::ostream& os)
{
// Nothing to choose for a test that is not part of a fixture, or that
// names a mode itself.
if (this->Test->GetProperty("FIXTURE_REPEAT_MODE") ||
(!this->Test->GetProperty("FIXTURES_SETUP") &&
!this->Test->GetProperty("FIXTURES_CLEANUP"))) {
return;
}
// Write the mode the policy chose into the test file, so that ctest reads
// a mode rather than the policy settings behind it. Only NEW needs
// writing: with nothing written, ctest already uses the
// EACH_TEST_SEPARATELY behavior of CMake 4.4 and below.
switch (this->Test->GetCMP0224()) {
case cmPolicies::WARN:
// Warn only on request. Fixtures are common, and the choice of mode
// matters only to those who run ctest --repeat. Collect the tests
// rather than warning about each: a project that sets its fixtures up
// in an add_test() wrapper would fill the console.
if (this->Test->GetMakefile()->PolicyOptionalWarningEnabled(
"CMAKE_POLICY_WARNING_CMP0224")) {
this->LG->GetGlobalGenerator()->AddCMP0224WarnTest(
this->Test->GetName());
}
CM_FALLTHROUGH;
case cmPolicies::OLD:
break;
case cmPolicies::NEW:
os << " _CMAKE_DEFAULT_FIXTURE_REPEAT_MODE AROUND_EACH_REPEAT";
break;
}
}
void cmTestGenerator::GenerateBacktrace(std::ostream& os,
cmListFileBacktrace bt)
{
+1
View File
@@ -67,6 +67,7 @@ private:
protected:
void GenerateBacktrace(std::ostream& os, cmListFileBacktrace bt);
void GenerateDefaultFixtureRepeatMode(std::ostream& os);
void GenerateCommand(std::ostream& os,
std::vector<std::string> const& command,
std::string const& config, bool expand,
@@ -104,6 +104,84 @@ endfunction()
run_repeat_until_fail_tests(--repeat-until-fail 3)
run_repeat_until_fail_tests(--repeat until-fail:3)
function(run_repeat_fixture_test case)
# Each case runs one fixture around one test with `--repeat until-fail:3`.
# The FIXTURE_REPEAT_MODE property and policy CMP0224 decide whether the
# fixture repeats with the test, around it, or on its own.
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/repeat-fixture-${case}-build)
run_cmake_with_options(repeat-fixture-${case}-cmake ${ARGN})
set(RunCMake_TEST_NO_CLEAN 1)
run_cmake_command(repeat-fixture-${case}-ctest
${CMAKE_CTEST_COMMAND} -C Debug --repeat until-fail:3
)
endfunction()
# CMP0224 selects the default mode for fixture tests with no explicit mode.
# Its warning is off by default, so the WARN case asks for it.
run_repeat_fixture_test(default) # not set, and silent
run_repeat_fixture_test(warn -DCMAKE_POLICY_WARNING_CMP0224=ON)
run_repeat_fixture_test(old) # OLD: EACH_TEST_SEPARATELY
run_repeat_fixture_test(new) # NEW: AROUND_EACH_REPEAT
# An explicit FIXTURE_REPEAT_MODE property applies whatever the policy says.
run_repeat_fixture_test(around-all)
run_repeat_fixture_test(around-each)
run_repeat_fixture_test(separately)
# The property describes the fixture, so one of its tests may carry it alone.
run_repeat_fixture_test(setup-only)
run_repeat_fixture_test(cleanup-only)
# Fixtures that share a test repeat as one unit.
run_repeat_fixture_test(merge)
# A test depending on one inside the group waits for the last repetition.
run_repeat_fixture_test(depends)
block()
# A repeat group records its tests in the checkpoint once, when it stops
# repeating, so that `ctest -F` re-runs an interrupted group from its first
# repetition. A checkpoint naming a test twice, or naming one this run
# does not have, must not upset the resume either.
set(RunCMake_TEST_BINARY_DIR
${RunCMake_BINARY_DIR}/repeat-fixture-resume-build)
run_cmake(repeat-fixture-resume-cmake)
set(RunCMake_TEST_NO_CLEAN 1)
file(WRITE "${RunCMake_TEST_BINARY_DIR}/Testing/Temporary/CTestCheckpoint.txt"
"1\n2\n3\n1\n99\n")
run_cmake_command(repeat-fixture-resume-ctest
${CMAKE_CTEST_COMMAND} -C Debug -F --repeat until-fail:3
)
endblock()
function(run_repeat_fixture_until_pass_test)
set(RunCMake_TEST_BINARY_DIR
${RunCMake_BINARY_DIR}/repeat-fixture-until-pass-build)
run_cmake(repeat-fixture-until-pass-cmake)
set(RunCMake_TEST_NO_CLEAN 1)
run_cmake_command(repeat-fixture-until-pass-ctest
${CMAKE_CTEST_COMMAND} -C Debug --repeat until-pass:3
)
endfunction()
run_repeat_fixture_until_pass_test()
function(run_repeat_fixture_conflict_test case)
# Fixtures that repeat together must agree on the mode; ctest refuses to
# run when they do not.
set(RunCMake_TEST_BINARY_DIR
${RunCMake_BINARY_DIR}/repeat-fixture-conflict-${case}-build)
run_cmake(repeat-fixture-conflict-${case}-cmake)
set(RunCMake_TEST_NO_CLEAN 1)
run_cmake_command(repeat-fixture-conflict-${case}-ctest
${CMAKE_CTEST_COMMAND} -C Debug --repeat until-fail:3
)
endfunction()
run_repeat_fixture_conflict_test(fixture) # one fixture, disagreeing tests
run_repeat_fixture_conflict_test(shared) # two fixtures sharing a test
function(run_repeat_fixture_bad_mode_test)
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/repeat-fixture-bad-mode-build)
run_cmake(repeat-fixture-bad-mode-cmake)
set(RunCMake_TEST_NO_CLEAN 1)
run_cmake_command(repeat-fixture-bad-mode-ctest ${CMAKE_CTEST_COMMAND} -C Debug)
endfunction()
run_repeat_fixture_bad_mode_test()
block()
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/rerun)
set(RunCMake_TEST_NO_CLEAN 1)
@@ -0,0 +1,2 @@
set(FIXTURE_REPEAT_MODE AROUND_ALL_REPEATS)
include(${CMAKE_CURRENT_LIST_DIR}/repeat-fixture-common.cmake)
@@ -0,0 +1,15 @@
^Test project .*/Tests/RunCMake/CTestCommandLine/repeat-fixture-around-all-build
Start 1: fixture_setup
1/3 Test #1: fixture_setup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: test_with_fixture \(run 1/3\)
Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: test_with_fixture \(run 2/3\)
Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: test_with_fixture \(run 3/3\)
2/3 Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 3: fixture_cleanup
3/3 Test #3: fixture_cleanup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
+
100% tests passed out of 3
+
Total Test time \(real\) = +[0-9\.]+ sec$
@@ -0,0 +1,2 @@
set(FIXTURE_REPEAT_MODE AROUND_EACH_REPEAT)
include(${CMAKE_CURRENT_LIST_DIR}/repeat-fixture-common.cmake)
@@ -0,0 +1,23 @@
^Test project .*/Tests/RunCMake/CTestCommandLine/repeat-fixture-around-each-build
Start 1: fixture_setup \(run 1/3\)
1/3 Test #1: fixture_setup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: test_with_fixture \(run 1/3\)
2/3 Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 3: fixture_cleanup \(run 1/3\)
3/3 Test #3: fixture_cleanup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 1: fixture_setup \(run 2/3\)
1/3 Test #1: fixture_setup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: test_with_fixture \(run 2/3\)
2/3 Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 3: fixture_cleanup \(run 2/3\)
3/3 Test #3: fixture_cleanup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 1: fixture_setup \(run 3/3\)
1/3 Test #1: fixture_setup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: test_with_fixture \(run 3/3\)
2/3 Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 3: fixture_cleanup \(run 3/3\)
3/3 Test #3: fixture_cleanup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
+
100% tests passed out of 3
+
Total Test time \(real\) = +[0-9\.]+ sec$
@@ -0,0 +1,2 @@
set(FIXTURE_REPEAT_MODE BOGUS)
include(${CMAKE_CURRENT_LIST_DIR}/repeat-fixture-common.cmake)
@@ -0,0 +1 @@
Errors while running CTest
@@ -0,0 +1,19 @@
^Test project .*/Tests/RunCMake/CTestCommandLine/repeat-fixture-bad-mode-build
Start 1: fixture_setup
FIXTURE_REPEAT_MODE "BOGUS" not recognized\. Must be AROUND_ALL_REPEATS, AROUND_EACH_REPEAT, or EACH_TEST_SEPARATELY\.
1/3 Test #1: fixture_setup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\*\*\*Not Run +[0-9\.]+ sec
Start 2: test_with_fixture
Failed test dependencies: fixture_setup
2/3 Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\*\*\*Not Run +[0-9\.]+ sec
Start 3: fixture_cleanup
FIXTURE_REPEAT_MODE "BOGUS" not recognized\. Must be AROUND_ALL_REPEATS, AROUND_EACH_REPEAT, or EACH_TEST_SEPARATELY\.
3/3 Test #3: fixture_cleanup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\*\*\*Not Run +[0-9\.]+ sec
+
0% tests passed, 3 tests failed out of 3
+
Total Test time \(real\) = +[0-9\.]+ sec
+
The following tests FAILED:
[ ]+1 - fixture_setup \(Not Run\)
[ ]+2 - test_with_fixture \(Not Run\)
[ ]+3 - fixture_cleanup \(Not Run\)$
@@ -0,0 +1,5 @@
# The mode set on the cleanup test governs the whole fixture, so the setup
# test must not repeat on its own.
set(FIXTURE_REPEAT_MODE AROUND_ALL_REPEATS)
set(FIXTURE_REPEAT_TESTS fixture_cleanup)
include(${CMAKE_CURRENT_LIST_DIR}/repeat-fixture-common.cmake)
@@ -0,0 +1,15 @@
^Test project .*/Tests/RunCMake/CTestCommandLine/repeat-fixture-cleanup-only-build
Start 1: fixture_setup
1/3 Test #1: fixture_setup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: test_with_fixture \(run 1/3\)
Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: test_with_fixture \(run 2/3\)
Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: test_with_fixture \(run 3/3\)
2/3 Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 3: fixture_cleanup
3/3 Test #3: fixture_cleanup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
+
100% tests passed out of 3
+
Total Test time \(real\) = +[0-9\.]+ sec$
@@ -0,0 +1,20 @@
enable_testing()
add_test(NAME fixture_setup COMMAND ${CMAKE_COMMAND} -E true)
set_tests_properties(fixture_setup PROPERTIES FIXTURES_SETUP MyFixture)
add_test(NAME test_with_fixture COMMAND ${CMAKE_COMMAND} -E true)
set_tests_properties(test_with_fixture PROPERTIES FIXTURES_REQUIRED MyFixture)
add_test(NAME fixture_cleanup COMMAND ${CMAKE_COMMAND} -E true)
set_tests_properties(fixture_cleanup PROPERTIES FIXTURES_CLEANUP MyFixture)
# The mode applies to the whole fixture, so cases that check that setting it
# on one of its tests is enough name only that test in FIXTURE_REPEAT_TESTS.
if(FIXTURE_REPEAT_MODE)
if(NOT FIXTURE_REPEAT_TESTS)
set(FIXTURE_REPEAT_TESTS fixture_setup fixture_cleanup)
endif()
set_tests_properties(${FIXTURE_REPEAT_TESTS} PROPERTIES
FIXTURE_REPEAT_MODE ${FIXTURE_REPEAT_MODE})
endif()
@@ -0,0 +1,10 @@
# The setup and cleanup tests of one fixture disagree on the mode.
enable_testing()
add_test(NAME fixture_setup COMMAND ${CMAKE_COMMAND} -E true)
add_test(NAME fixture_cleanup COMMAND ${CMAKE_COMMAND} -E true)
add_test(NAME test_with_fixture COMMAND ${CMAKE_COMMAND} -E true)
set_tests_properties(fixture_setup PROPERTIES
FIXTURES_SETUP MyFixture FIXTURE_REPEAT_MODE AROUND_EACH_REPEAT)
set_tests_properties(fixture_cleanup PROPERTIES
FIXTURES_CLEANUP MyFixture FIXTURE_REPEAT_MODE AROUND_ALL_REPEATS)
set_tests_properties(test_with_fixture PROPERTIES FIXTURES_REQUIRED MyFixture)
@@ -0,0 +1,7 @@
Error: the setup and cleanup tests of fixture "MyFixture" request conflicting FIXTURE_REPEAT_MODE values:
"fixture_cleanup" requests AROUND_ALL_REPEATS
"fixture_setup" requests AROUND_EACH_REPEAT
All setup and cleanup tests of a fixture must request the same mode\.
Errors while running CTest
Output from these tests are in: .*/Testing/Temporary/LastTest\.log
Use "--rerun-failed --output-on-failure" to re-run the failed cases verbosely\.
@@ -0,0 +1 @@
^Test project .*/Tests/RunCMake/CTestCommandLine/repeat-fixture-conflict-fixture-build$
@@ -0,0 +1,16 @@
# Two fixtures disagree on the mode, and one test takes part in both.
enable_testing()
add_test(NAME setupA COMMAND ${CMAKE_COMMAND} -E true)
add_test(NAME cleanupA COMMAND ${CMAKE_COMMAND} -E true)
add_test(NAME setupB COMMAND ${CMAKE_COMMAND} -E true)
add_test(NAME cleanupB COMMAND ${CMAKE_COMMAND} -E true)
add_test(NAME needs_both COMMAND ${CMAKE_COMMAND} -E true)
set_tests_properties(setupA PROPERTIES FIXTURES_SETUP A
FIXTURE_REPEAT_MODE AROUND_EACH_REPEAT)
set_tests_properties(cleanupA PROPERTIES FIXTURES_CLEANUP A
FIXTURE_REPEAT_MODE AROUND_EACH_REPEAT)
set_tests_properties(setupB PROPERTIES FIXTURES_SETUP B
FIXTURE_REPEAT_MODE AROUND_ALL_REPEATS)
set_tests_properties(cleanupB PROPERTIES FIXTURES_CLEANUP B
FIXTURE_REPEAT_MODE AROUND_ALL_REPEATS)
set_tests_properties(needs_both PROPERTIES FIXTURES_REQUIRED "A;B")
@@ -0,0 +1,7 @@
Error: test "needs_both" takes part in fixtures with conflicting FIXTURE_REPEAT_MODE values:
fixture "B" uses AROUND_ALL_REPEATS
fixture "A" uses AROUND_EACH_REPEAT
Fixtures that share a test must use the same mode\.
Errors while running CTest
Output from these tests are in: .*/Testing/Temporary/LastTest\.log
Use "--rerun-failed --output-on-failure" to re-run the failed cases verbosely\.
@@ -0,0 +1 @@
^Test project .*/Tests/RunCMake/CTestCommandLine/repeat-fixture-conflict-shared-build$
@@ -0,0 +1 @@
include(${CMAKE_CURRENT_LIST_DIR}/repeat-fixture-common.cmake)
@@ -0,0 +1,23 @@
^Test project .*/Tests/RunCMake/CTestCommandLine/repeat-fixture-default-build
Start 1: fixture_setup \(run 1/3\)
Test #1: fixture_setup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 1: fixture_setup \(run 2/3\)
Test #1: fixture_setup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 1: fixture_setup \(run 3/3\)
1/3 Test #1: fixture_setup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: test_with_fixture \(run 1/3\)
Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: test_with_fixture \(run 2/3\)
Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: test_with_fixture \(run 3/3\)
2/3 Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 3: fixture_cleanup \(run 1/3\)
Test #3: fixture_cleanup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 3: fixture_cleanup \(run 2/3\)
Test #3: fixture_cleanup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 3: fixture_cleanup \(run 3/3\)
3/3 Test #3: fixture_cleanup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
+
100% tests passed out of 3
+
Total Test time \(real\) = +[0-9\.]+ sec$
@@ -0,0 +1,7 @@
# A test outside a repeat group that DEPENDS on a test inside it has to wait
# for the group's last repetition, not its first.
cmake_policy(SET CMP0224 NEW)
include(${CMAKE_CURRENT_LIST_DIR}/repeat-fixture-common.cmake)
add_test(NAME after_fixture COMMAND ${CMAKE_COMMAND} -E true)
set_tests_properties(after_fixture PROPERTIES DEPENDS test_with_fixture)
@@ -0,0 +1,29 @@
^Test project .*/Tests/RunCMake/CTestCommandLine/repeat-fixture-depends-build
Start 1: fixture_setup \(run 1/3\)
1/4 Test #1: fixture_setup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: test_with_fixture \(run 1/3\)
2/4 Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 3: fixture_cleanup \(run 1/3\)
3/4 Test #3: fixture_cleanup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 1: fixture_setup \(run 2/3\)
1/4 Test #1: fixture_setup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: test_with_fixture \(run 2/3\)
2/4 Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 3: fixture_cleanup \(run 2/3\)
3/4 Test #3: fixture_cleanup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 1: fixture_setup \(run 3/3\)
1/4 Test #1: fixture_setup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: test_with_fixture \(run 3/3\)
2/4 Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 3: fixture_cleanup \(run 3/3\)
3/4 Test #3: fixture_cleanup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 4: after_fixture \(run 1/3\)
Test #4: after_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 4: after_fixture \(run 2/3\)
Test #4: after_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 4: after_fixture \(run 3/3\)
4/4 Test #4: after_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
+
100% tests passed out of 4
+
Total Test time \(real\) = +[0-9\.]+ sec$
@@ -0,0 +1,18 @@
# Fixture C is required by a test of fixture A and by a test of fixture B, so
# all three repeat as one unit. The DEPENDS chain fixes the order in which
# the unit runs its tests.
enable_testing()
cmake_policy(SET CMP0224 NEW)
foreach(f A B C)
add_test(NAME setup${f} COMMAND ${CMAKE_COMMAND} -E true)
set_tests_properties(setup${f} PROPERTIES FIXTURES_SETUP ${f})
endforeach()
set_tests_properties(setupB PROPERTIES DEPENDS setupA)
set_tests_properties(setupC PROPERTIES DEPENDS setupB)
add_test(NAME testA COMMAND ${CMAKE_COMMAND} -E true)
set_tests_properties(testA PROPERTIES FIXTURES_REQUIRED "A;C")
add_test(NAME testB COMMAND ${CMAKE_COMMAND} -E true)
set_tests_properties(testB PROPERTIES FIXTURES_REQUIRED "B;C" DEPENDS testA)
@@ -0,0 +1,35 @@
^Test project .*/Tests/RunCMake/CTestCommandLine/repeat-fixture-merge-build
Start 1: setupA \(run 1/3\)
1/5 Test #1: setupA \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: setupB \(run 1/3\)
2/5 Test #2: setupB \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 3: setupC \(run 1/3\)
3/5 Test #3: setupC \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 4: testA \(run 1/3\)
4/5 Test #4: testA \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 5: testB \(run 1/3\)
5/5 Test #5: testB \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 1: setupA \(run 2/3\)
1/5 Test #1: setupA \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: setupB \(run 2/3\)
2/5 Test #2: setupB \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 3: setupC \(run 2/3\)
3/5 Test #3: setupC \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 4: testA \(run 2/3\)
4/5 Test #4: testA \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 5: testB \(run 2/3\)
5/5 Test #5: testB \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 1: setupA \(run 3/3\)
1/5 Test #1: setupA \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: setupB \(run 3/3\)
2/5 Test #2: setupB \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 3: setupC \(run 3/3\)
3/5 Test #3: setupC \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 4: testA \(run 3/3\)
4/5 Test #4: testA \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 5: testB \(run 3/3\)
5/5 Test #5: testB \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
+
100% tests passed out of 5
+
Total Test time \(real\) = +[0-9\.]+ sec$
@@ -0,0 +1,2 @@
cmake_policy(SET CMP0224 NEW)
include(${CMAKE_CURRENT_LIST_DIR}/repeat-fixture-common.cmake)
@@ -0,0 +1,23 @@
^Test project .*/Tests/RunCMake/CTestCommandLine/repeat-fixture-new-build
Start 1: fixture_setup \(run 1/3\)
1/3 Test #1: fixture_setup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: test_with_fixture \(run 1/3\)
2/3 Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 3: fixture_cleanup \(run 1/3\)
3/3 Test #3: fixture_cleanup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 1: fixture_setup \(run 2/3\)
1/3 Test #1: fixture_setup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: test_with_fixture \(run 2/3\)
2/3 Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 3: fixture_cleanup \(run 2/3\)
3/3 Test #3: fixture_cleanup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 1: fixture_setup \(run 3/3\)
1/3 Test #1: fixture_setup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: test_with_fixture \(run 3/3\)
2/3 Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 3: fixture_cleanup \(run 3/3\)
3/3 Test #3: fixture_cleanup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
+
100% tests passed out of 3
+
Total Test time \(real\) = +[0-9\.]+ sec$
@@ -0,0 +1,2 @@
cmake_policy(SET CMP0224 OLD)
include(${CMAKE_CURRENT_LIST_DIR}/repeat-fixture-common.cmake)
@@ -0,0 +1,23 @@
^Test project .*/Tests/RunCMake/CTestCommandLine/repeat-fixture-old-build
Start 1: fixture_setup \(run 1/3\)
Test #1: fixture_setup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 1: fixture_setup \(run 2/3\)
Test #1: fixture_setup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 1: fixture_setup \(run 3/3\)
1/3 Test #1: fixture_setup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: test_with_fixture \(run 1/3\)
Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: test_with_fixture \(run 2/3\)
Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: test_with_fixture \(run 3/3\)
2/3 Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 3: fixture_cleanup \(run 1/3\)
Test #3: fixture_cleanup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 3: fixture_cleanup \(run 2/3\)
Test #3: fixture_cleanup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 3: fixture_cleanup \(run 3/3\)
3/3 Test #3: fixture_cleanup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
+
100% tests passed out of 3
+
Total Test time \(real\) = +[0-9\.]+ sec$
@@ -0,0 +1,5 @@
# `ctest -F` resumes from the checkpoint that records the tests already run.
cmake_policy(SET CMP0224 NEW)
include(${CMAKE_CURRENT_LIST_DIR}/repeat-fixture-common.cmake)
add_test(NAME not_yet_run COMMAND ${CMAKE_COMMAND} -E true)
@@ -0,0 +1,11 @@
^Test project .*/Tests/RunCMake/CTestCommandLine/repeat-fixture-resume-build
Start 4: not_yet_run \(run 1/3\)
Test #4: not_yet_run \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 4: not_yet_run \(run 2/3\)
Test #4: not_yet_run \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 4: not_yet_run \(run 3/3\)
4/4 Test #4: not_yet_run \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
+
100% tests passed out of 1
+
Total Test time \(real\) = +[0-9\.]+ sec$
@@ -0,0 +1,2 @@
set(FIXTURE_REPEAT_MODE EACH_TEST_SEPARATELY)
include(${CMAKE_CURRENT_LIST_DIR}/repeat-fixture-common.cmake)
@@ -0,0 +1,23 @@
^Test project .*/Tests/RunCMake/CTestCommandLine/repeat-fixture-separately-build
Start 1: fixture_setup \(run 1/3\)
Test #1: fixture_setup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 1: fixture_setup \(run 2/3\)
Test #1: fixture_setup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 1: fixture_setup \(run 3/3\)
1/3 Test #1: fixture_setup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: test_with_fixture \(run 1/3\)
Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: test_with_fixture \(run 2/3\)
Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: test_with_fixture \(run 3/3\)
2/3 Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 3: fixture_cleanup \(run 1/3\)
Test #3: fixture_cleanup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 3: fixture_cleanup \(run 2/3\)
Test #3: fixture_cleanup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 3: fixture_cleanup \(run 3/3\)
3/3 Test #3: fixture_cleanup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
+
100% tests passed out of 3
+
Total Test time \(real\) = +[0-9\.]+ sec$
@@ -0,0 +1,6 @@
# The mode set on the setup test governs the whole fixture, so the default
# CMP0224 records for the cleanup test must not override or conflict with it.
cmake_policy(SET CMP0224 NEW)
set(FIXTURE_REPEAT_MODE AROUND_ALL_REPEATS)
set(FIXTURE_REPEAT_TESTS fixture_setup)
include(${CMAKE_CURRENT_LIST_DIR}/repeat-fixture-common.cmake)
@@ -0,0 +1,15 @@
^Test project .*/Tests/RunCMake/CTestCommandLine/repeat-fixture-setup-only-build
Start 1: fixture_setup
1/3 Test #1: fixture_setup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: test_with_fixture \(run 1/3\)
Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: test_with_fixture \(run 2/3\)
Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: test_with_fixture \(run 3/3\)
2/3 Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 3: fixture_cleanup
3/3 Test #3: fixture_cleanup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
+
100% tests passed out of 3
+
Total Test time \(real\) = +[0-9\.]+ sec$
@@ -0,0 +1,18 @@
# The fixture's setup test fails on its first run. With `--repeat until-pass`
# the whole unit runs again, and only the repetition that passed is reported.
cmake_policy(SET CMP0224 NEW)
enable_testing()
set(TEST_OUTPUT_FILE "${CMAKE_CURRENT_BINARY_DIR}/test_output.txt")
file(WRITE "${TEST_OUTPUT_FILE}" "0")
add_test(NAME fixture_setup COMMAND ${CMAKE_COMMAND}
"-DTEST_OUTPUT_FILE=${TEST_OUTPUT_FILE}"
-P "${CMAKE_CURRENT_SOURCE_DIR}/test1-pass.cmake")
set_tests_properties(fixture_setup PROPERTIES FIXTURES_SETUP MyFixture)
add_test(NAME test_with_fixture COMMAND ${CMAKE_COMMAND} -E true)
set_tests_properties(test_with_fixture PROPERTIES FIXTURES_REQUIRED MyFixture)
add_test(NAME fixture_cleanup COMMAND ${CMAKE_COMMAND} -E true)
set_tests_properties(fixture_cleanup PROPERTIES FIXTURES_CLEANUP MyFixture)
@@ -0,0 +1,18 @@
^Test project .*/Tests/RunCMake/CTestCommandLine/repeat-fixture-until-pass-build
Start 1: fixture_setup \(run 1/3\)
1/3 Test #1: fixture_setup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\*\*\*Failed +[0-9\.]+ sec
Start 2: test_with_fixture \(run 1/3\)
Failed test dependencies: fixture_setup
2/3 Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\*\*\*Not Run +[0-9\.]+ sec
Start 3: fixture_cleanup \(run 1/3\)
3/3 Test #3: fixture_cleanup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 1: fixture_setup \(run 2/3\)
1/3 Test #1: fixture_setup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: test_with_fixture \(run 2/3\)
2/3 Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 3: fixture_cleanup \(run 2/3\)
3/3 Test #3: fixture_cleanup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
+
100% tests passed out of 3
+
Total Test time \(real\) = +[0-9\.]+ sec$
@@ -0,0 +1,14 @@
CMake Warning \(policy\):
Policy CMP0224 is not set: Test fixtures use AROUND_EACH_REPEAT mode by
default with ctest --repeat\. Run "cmake --help-policy CMP0224" for policy
details\. Use the cmake_policy command to set the policy and suppress this
warning\.
For compatibility with older versions of CMake, the fixtures of the
following tests will use EACH_TEST_SEPARATELY mode:
fixture_cleanup
fixture_setup
This warning is for project developers\. Use -Wno-author or -Wno-policy to
suppress it\.
@@ -0,0 +1 @@
include(${CMAKE_CURRENT_LIST_DIR}/repeat-fixture-common.cmake)
@@ -0,0 +1,23 @@
^Test project .*/Tests/RunCMake/CTestCommandLine/repeat-fixture-warn-build
Start 1: fixture_setup \(run 1/3\)
Test #1: fixture_setup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 1: fixture_setup \(run 2/3\)
Test #1: fixture_setup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 1: fixture_setup \(run 3/3\)
1/3 Test #1: fixture_setup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: test_with_fixture \(run 1/3\)
Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: test_with_fixture \(run 2/3\)
Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: test_with_fixture \(run 3/3\)
2/3 Test #2: test_with_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 3: fixture_cleanup \(run 1/3\)
Test #3: fixture_cleanup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 3: fixture_cleanup \(run 2/3\)
Test #3: fixture_cleanup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 3: fixture_cleanup \(run 3/3\)
3/3 Test #3: fixture_cleanup \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
+
100% tests passed out of 3
+
Total Test time \(real\) = +[0-9\.]+ sec$
@@ -0,0 +1,2 @@
set(expect_mode "AROUND_EACH_REPEAT")
include(${CMAKE_CURRENT_LIST_DIR}/FixtureRepeatMode-verify.cmake)
@@ -0,0 +1,2 @@
cmake_policy(SET CMP0224 NEW)
include(${CMAKE_CURRENT_LIST_DIR}/FixtureRepeatMode-common.cmake)
@@ -0,0 +1,2 @@
set(expect_mode "")
include(${CMAKE_CURRENT_LIST_DIR}/FixtureRepeatMode-verify.cmake)
@@ -0,0 +1,2 @@
cmake_policy(SET CMP0224 OLD)
include(${CMAKE_CURRENT_LIST_DIR}/FixtureRepeatMode-common.cmake)
@@ -0,0 +1,9 @@
# The discovered tests are created by a script that ctest runs, so check the
# generated file that carries their properties.
add_executable(example IMPORTED)
set_property(TARGET example PROPERTY IMPORTED_LOCATION
${CMAKE_CURRENT_BINARY_DIR}/example)
include(GoogleTest)
gtest_discover_tests(example DISCOVERY_MODE PRE_TEST
PROPERTIES FIXTURES_SETUP ExampleFixture
)
@@ -0,0 +1,23 @@
# Verify whether the generated discovery scripts carry the mode chosen by
# policy CMP0224 through to the tests they create. ${expect_mode} is the
# mode expected, or empty if nothing should be recorded. A multi-config
# generator writes one script per configuration.
set(property "_CMAKE_DEFAULT_FIXTURE_REPEAT_MODE")
file(GLOB discovery_scripts
"${RunCMake_TEST_BINARY_DIR}/example_*_discovery.cmake")
if(NOT discovery_scripts)
set(RunCMake_TEST_FAILED "No test discovery script was generated.")
return()
endif()
foreach(discovery_script IN LISTS discovery_scripts)
file(READ "${discovery_script}" content)
if(expect_mode STREQUAL "")
if(content MATCHES "${property}")
set(RunCMake_TEST_FAILED
"${property} should not appear in ${discovery_script}:\n${content}")
endif()
elseif(NOT content MATCHES "${property}[^A-Za-z_]+${expect_mode}")
set(RunCMake_TEST_FAILED
"${property} ${expect_mode} missing from ${discovery_script}:\n${content}")
endif()
endforeach()
@@ -13,6 +13,10 @@ endif()
run_cmake(PropertiesOdd)
# Policy CMP0224 has to reach the tests that discovery creates.
run_cmake(FixtureRepeatMode-NEW)
run_cmake(FixtureRepeatMode-OLD)
function(run_GoogleTest DISCOVERY_MODE)
# Use a single build tree for a few tests without cleaning.
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/GoogleTest-build)
@@ -28,6 +28,17 @@ block()
run_cmake(bad-test-properties-cmake)
endblock()
block()
# Policy CMP0224 has to reach the tests that discovery creates.
set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/fixture-repeat-mode-build")
run_cmake(fixture-repeat-mode)
set(RunCMake_TEST_NO_CLEAN 1)
run_cmake_command(fixture-repeat-mode-build
${CMAKE_COMMAND} --build . --config Debug)
run_cmake_command(fixture-repeat-mode-test
${CMAKE_CTEST_COMMAND} -C Debug --repeat until-fail:2)
endblock()
function(run_case CASE)
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${CASE}-build)
run_cmake(${CASE})
@@ -0,0 +1,13 @@
^Test project .*/Tests/RunCMake/discover_tests/fixture-repeat-mode-build
Start 1: setup_case_one \(run 1/2\)
1/2 Test #1: setup_case_one \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: needs_fixture \(run 1/2\)
2/2 Test #2: needs_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 1: setup_case_one \(run 2/2\)
1/2 Test #1: setup_case_one \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
Start 2: needs_fixture \(run 2/2\)
2/2 Test #2: needs_fixture \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. Passed +[0-9\.]+ sec
+
100% tests passed out of 2
+
Total Test time \(real\) = +[0-9\.]+ sec$
@@ -0,0 +1,20 @@
# Tests created by discovery cannot be reached by policy CMP0224 when it is
# read, so discover_tests() records the mode the policy chose for them.
cmake_policy(SET CMP0224 NEW)
enable_language(C)
enable_testing()
add_executable(fake_discovery fake_discovery.c)
discover_tests(COMMAND fake_discovery
DISCOVERY_ARGS --list_tests
DISCOVERY_MATCH "^(case_one),LBL1$"
TEST_NAME "setup_\\1"
TEST_ARGS "\\1"
TEST_PROPERTIES
FIXTURES_SETUP DiscoveredFixture
)
add_test(NAME needs_fixture COMMAND ${CMAKE_COMMAND} -E true)
set_tests_properties(needs_fixture PROPERTIES
FIXTURES_REQUIRED DiscoveredFixture)