mirror of
https://github.com/Kitware/CMake.git
synced 2026-08-07 08:10:48 +00:00
ctest: add PRESET option for ctest_test() and ctest_memcheck()
Allow users to specify a test preset when using CTest dashboard scripts. Fixes #27630
This commit is contained in:
@@ -26,6 +26,7 @@ Perform the :ref:`CTest MemCheck Step` as a :ref:`Dashboard Client`.
|
|||||||
[CAPTURE_CMAKE_ERROR <result-var>]
|
[CAPTURE_CMAKE_ERROR <result-var>]
|
||||||
[REPEAT <mode>:<n>]
|
[REPEAT <mode>:<n>]
|
||||||
[OUTPUT_JUNIT <file>]
|
[OUTPUT_JUNIT <file>]
|
||||||
|
[PRESET <preset>]
|
||||||
[DEFECT_COUNT <defect-count-var>]
|
[DEFECT_COUNT <defect-count-var>]
|
||||||
[QUIET]
|
[QUIET]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ Perform the :ref:`CTest Test Step` as a :ref:`Dashboard Client`.
|
|||||||
[CAPTURE_CMAKE_ERROR <result-var>]
|
[CAPTURE_CMAKE_ERROR <result-var>]
|
||||||
[REPEAT <mode>:<n>]
|
[REPEAT <mode>:<n>]
|
||||||
[OUTPUT_JUNIT <file>]
|
[OUTPUT_JUNIT <file>]
|
||||||
|
[PRESET <preset>]
|
||||||
[QUIET]
|
[QUIET]
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -190,6 +191,15 @@ The options are:
|
|||||||
of the tests. See the CTest :ref:`CoverageTool <ctest-CoverageTool>`
|
of the tests. See the CTest :ref:`CoverageTool <ctest-CoverageTool>`
|
||||||
setting for details.
|
setting for details.
|
||||||
|
|
||||||
|
``PRESET <preset>``
|
||||||
|
.. versionadded:: 4.4
|
||||||
|
|
||||||
|
Specify a :manual:`preset <cmake-presets(7)>` to use when running tests.
|
||||||
|
Any value set in the CTest script will take priority over a corresponding
|
||||||
|
setting from the preset. For example, the ``INCLUDE`` argument will override
|
||||||
|
the :preset:`filter.include.name <testPresets.filter.include.name>` setting
|
||||||
|
from the chosen preset.
|
||||||
|
|
||||||
``QUIET``
|
``QUIET``
|
||||||
.. versionadded:: 3.3
|
.. versionadded:: 3.3
|
||||||
|
|
||||||
|
|||||||
7
Help/release/dev/ctest-script-preset-arg.rst
Normal file
7
Help/release/dev/ctest-script-preset-arg.rst
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
ctest-script-preset-arg
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
* The :command:`ctest_configure`, :command:`ctest_build`,
|
||||||
|
:command:`ctest_test`, and :command:`ctest_memcheck` commands gained
|
||||||
|
``PRESET`` arguments to support using :manual:`presets <cmake-presets(7)>`
|
||||||
|
for their :ref:`Dashboard Client` steps.
|
||||||
@@ -6,25 +6,60 @@
|
|||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <ratio>
|
#include <ratio>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <cm/memory>
|
#include <cm/memory>
|
||||||
|
#include <cm/optional>
|
||||||
|
|
||||||
|
#include "cmArgumentParserTypes.h"
|
||||||
|
#include "cmCMakePresetsGraph.h"
|
||||||
#include "cmCTest.h"
|
#include "cmCTest.h"
|
||||||
#include "cmCTestGenericHandler.h"
|
#include "cmCTestGenericHandler.h"
|
||||||
#include "cmCTestTestHandler.h"
|
#include "cmCTestTestHandler.h"
|
||||||
#include "cmDuration.h"
|
#include "cmDuration.h"
|
||||||
#include "cmExecutionStatus.h"
|
#include "cmExecutionStatus.h"
|
||||||
|
#include "cmJSONState.h"
|
||||||
#include "cmMakefile.h"
|
#include "cmMakefile.h"
|
||||||
#include "cmStringAlgorithms.h"
|
#include "cmStringAlgorithms.h"
|
||||||
#include "cmValue.h"
|
#include "cmValue.h"
|
||||||
|
|
||||||
|
using TestPreset = cmCMakePresetsGraph::TestPreset;
|
||||||
|
|
||||||
std::unique_ptr<cmCTestGenericHandler> cmCTestTestCommand::InitializeHandler(
|
std::unique_ptr<cmCTestGenericHandler> cmCTestTestCommand::InitializeHandler(
|
||||||
HandlerArguments& arguments, cmExecutionStatus& status) const
|
HandlerArguments& arguments, cmExecutionStatus& status) const
|
||||||
{
|
{
|
||||||
cmMakefile& mf = status.GetMakefile();
|
cmMakefile& mf = status.GetMakefile();
|
||||||
auto& args = static_cast<TestArguments&>(arguments);
|
auto& args = static_cast<TestArguments&>(arguments);
|
||||||
|
|
||||||
|
std::unique_ptr<cmCMakePresetsGraph> presetsGraph;
|
||||||
|
TestPreset const* expandedPreset = nullptr;
|
||||||
|
if (!args.Preset.empty()) {
|
||||||
|
std::string const sourceDirectory =
|
||||||
|
mf.GetSafeDefinition("CTEST_SOURCE_DIRECTORY");
|
||||||
|
|
||||||
|
presetsGraph = cm::make_unique<cmCMakePresetsGraph>();
|
||||||
|
if (!presetsGraph->ReadProjectPresets(sourceDirectory, "")) {
|
||||||
|
status.SetError(
|
||||||
|
cmStrCat("Could not read presets from \"", sourceDirectory,
|
||||||
|
"\": ", presetsGraph->parseState.GetErrorMessage()));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto resolveResult =
|
||||||
|
presetsGraph->ResolvePreset(args.Preset, presetsGraph->TestPresets);
|
||||||
|
auto resolveError = cmCMakePresetsGraph::FormatPresetError<TestPreset>(
|
||||||
|
resolveResult.StatusCode, resolveResult.ErrorPresetName,
|
||||||
|
sourceDirectory);
|
||||||
|
if (resolveError) {
|
||||||
|
status.SetError(*resolveError);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
expandedPreset = resolveResult.Preset;
|
||||||
|
}
|
||||||
|
|
||||||
cmValue ctestTimeout = mf.GetDefinition("CTEST_TEST_TIMEOUT");
|
cmValue ctestTimeout = mf.GetDefinition("CTEST_TEST_TIMEOUT");
|
||||||
|
|
||||||
cmDuration timeout;
|
cmDuration timeout;
|
||||||
@@ -39,12 +74,56 @@ std::unique_ptr<cmCTestGenericHandler> cmCTestTestCommand::InitializeHandler(
|
|||||||
}
|
}
|
||||||
this->CTest->SetTimeOut(timeout);
|
this->CTest->SetTimeOut(timeout);
|
||||||
|
|
||||||
cmValue resourceSpecFile = mf.GetDefinition("CTEST_RESOURCE_SPEC_FILE");
|
auto handler = this->InitializeActualHandler(args, status);
|
||||||
if (args.ResourceSpecFile.empty() && resourceSpecFile) {
|
|
||||||
args.ResourceSpecFile = *resourceSpecFile;
|
// Load settings from the preset if one was specified.
|
||||||
|
if (expandedPreset) {
|
||||||
|
cmCTestApplyTestPresetToOptions(handler->TestOptions, *expandedPreset);
|
||||||
|
|
||||||
|
if (!args.ParallelLevel && expandedPreset->Execution) {
|
||||||
|
if (auto const& jobs = expandedPreset->Execution->Jobs) {
|
||||||
|
ArgumentParser::Maybe<std::string> level;
|
||||||
|
if (jobs->has_value()) {
|
||||||
|
level = std::to_string(**jobs);
|
||||||
|
}
|
||||||
|
handler->ParallelLevel = level;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.Repeat.empty() && expandedPreset->Execution &&
|
||||||
|
expandedPreset->Execution->Repeat) {
|
||||||
|
auto const& rep = *expandedPreset->Execution->Repeat;
|
||||||
|
using RepeatMode = TestPreset::ExecutionOptions::RepeatOptions::ModeEnum;
|
||||||
|
std::string modeStr;
|
||||||
|
switch (rep.Mode) {
|
||||||
|
case RepeatMode::UntilFail:
|
||||||
|
modeStr = "UNTIL_FAIL";
|
||||||
|
break;
|
||||||
|
case RepeatMode::UntilPass:
|
||||||
|
modeStr = "UNTIL_PASS";
|
||||||
|
break;
|
||||||
|
case RepeatMode::AfterTimeout:
|
||||||
|
modeStr = "AFTER_TIMEOUT";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
handler->Repeat = cmStrCat(modeStr, ':', rep.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.TestLoad.empty() && expandedPreset->Execution &&
|
||||||
|
expandedPreset->Execution->TestLoad) {
|
||||||
|
args.TestLoad = std::to_string(*expandedPreset->Execution->TestLoad);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto handler = this->InitializeActualHandler(args, status);
|
if (args.ResourceSpecFile.empty()) {
|
||||||
|
cmValue resourceSpecFile = mf.GetDefinition("CTEST_RESOURCE_SPEC_FILE");
|
||||||
|
if (resourceSpecFile) {
|
||||||
|
args.ResourceSpecFile = *resourceSpecFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply explicitly specified ctest_test() options,
|
||||||
|
// overriding any conflicting preset values.
|
||||||
if (!args.Start.empty() || !args.End.empty() || !args.Stride.empty()) {
|
if (!args.Start.empty() || !args.End.empty() || !args.Stride.empty()) {
|
||||||
handler->TestOptions.TestsToRunInformation =
|
handler->TestOptions.TestsToRunInformation =
|
||||||
cmStrCat(args.Start, ',', args.End, ',', args.Stride);
|
cmStrCat(args.Start, ',', args.End, ',', args.Stride);
|
||||||
@@ -56,10 +135,12 @@ std::unique_ptr<cmCTestGenericHandler> cmCTestTestCommand::InitializeHandler(
|
|||||||
handler->TestOptions.IncludeRegularExpression = args.Include;
|
handler->TestOptions.IncludeRegularExpression = args.Include;
|
||||||
}
|
}
|
||||||
if (!args.ExcludeLabel.empty()) {
|
if (!args.ExcludeLabel.empty()) {
|
||||||
|
handler->TestOptions.ExcludeLabelRegularExpression.clear();
|
||||||
handler->TestOptions.ExcludeLabelRegularExpression.push_back(
|
handler->TestOptions.ExcludeLabelRegularExpression.push_back(
|
||||||
args.ExcludeLabel);
|
args.ExcludeLabel);
|
||||||
}
|
}
|
||||||
if (!args.IncludeLabel.empty()) {
|
if (!args.IncludeLabel.empty()) {
|
||||||
|
handler->TestOptions.LabelRegularExpression.clear();
|
||||||
handler->TestOptions.LabelRegularExpression.push_back(args.IncludeLabel);
|
handler->TestOptions.LabelRegularExpression.push_back(args.IncludeLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ protected:
|
|||||||
std::string OutputJUnit;
|
std::string OutputJUnit;
|
||||||
std::string CoverageTool;
|
std::string CoverageTool;
|
||||||
bool StopOnFailure = false;
|
bool StopOnFailure = false;
|
||||||
|
std::string Preset;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Args>
|
template <typename Args>
|
||||||
@@ -76,7 +77,8 @@ protected:
|
|||||||
.Bind("RESOURCE_SPEC_FILE"_s, &TestArguments::ResourceSpecFile)
|
.Bind("RESOURCE_SPEC_FILE"_s, &TestArguments::ResourceSpecFile)
|
||||||
.Bind("STOP_ON_FAILURE"_s, &TestArguments::StopOnFailure)
|
.Bind("STOP_ON_FAILURE"_s, &TestArguments::StopOnFailure)
|
||||||
.Bind("COVERAGE_TOOL"_s, &TestArguments::CoverageTool)
|
.Bind("COVERAGE_TOOL"_s, &TestArguments::CoverageTool)
|
||||||
.Bind("OUTPUT_JUNIT"_s, &TestArguments::OutputJUnit);
|
.Bind("OUTPUT_JUNIT"_s, &TestArguments::OutputJUnit)
|
||||||
|
.Bind("PRESET"_s, &TestArguments::Preset);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
26
Tests/RunCMake/ctest_memcheck/CMakePresets.json.in
Normal file
26
Tests/RunCMake/ctest_memcheck/CMakePresets.json.in
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"version": 3,
|
||||||
|
"cmakeMinimumRequired": {
|
||||||
|
"major": 3,
|
||||||
|
"minor": 21,
|
||||||
|
"patch": 0
|
||||||
|
},
|
||||||
|
"configurePresets": [
|
||||||
|
{
|
||||||
|
"name": "my-configure-preset",
|
||||||
|
"generator": "@RunCMake_GENERATOR@",
|
||||||
|
"binaryDir": "${sourceDir}/build"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"testPresets": [
|
||||||
|
{
|
||||||
|
"name": "my-include-preset",
|
||||||
|
"configurePreset": "my-configure-preset",
|
||||||
|
"filter": {
|
||||||
|
"include": {
|
||||||
|
"name": "^test1$"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -198,3 +198,19 @@ run_mc_test(DummyCudaSanitizer "${PSEUDO_CUDA_SANITIZER}")
|
|||||||
unset(CTEST_MEMCHECK_ARGS)
|
unset(CTEST_MEMCHECK_ARGS)
|
||||||
unset(CTEST_SUFFIX_CODE)
|
unset(CTEST_SUFFIX_CODE)
|
||||||
unset(CTEST_EXTRA_CODE)
|
unset(CTEST_EXTRA_CODE)
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
block()
|
||||||
|
set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/TestPresetInclude")
|
||||||
|
set(CMAKELISTS_EXTRA_CODE [[
|
||||||
|
foreach(i RANGE 1 3)
|
||||||
|
add_test(NAME test${i} COMMAND ${CMAKE_COMMAND} -E true)
|
||||||
|
endforeach()
|
||||||
|
]])
|
||||||
|
configure_file(
|
||||||
|
"${RunCMake_SOURCE_DIR}/CMakePresets.json.in"
|
||||||
|
"${RunCMake_TEST_SOURCE_DIR}/CMakePresets.json"
|
||||||
|
@ONLY)
|
||||||
|
set(CTEST_MEMCHECK_ARGS "PRESET my-include-preset")
|
||||||
|
run_mc_test(TestPresetInclude "${PSEUDO_VALGRIND}")
|
||||||
|
endblock()
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
1/1 MemCheck #[0-9]+: test1 .+ Passed +[0-9]+\.[0-9]+ sec
|
||||||
|
|
||||||
|
100% tests passed out of 1
|
||||||
|
.*
|
||||||
|
-- Processing memory checking output:
|
||||||
|
MemCheck log files can be found here:.*corresponds to test number.
|
||||||
|
.*MemoryChecker.*log
|
||||||
|
Memory checking results:
|
||||||
35
Tests/RunCMake/ctest_test/CMakePresets.json.in
Normal file
35
Tests/RunCMake/ctest_test/CMakePresets.json.in
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"version": 3,
|
||||||
|
"cmakeMinimumRequired": {
|
||||||
|
"major": 3,
|
||||||
|
"minor": 21,
|
||||||
|
"patch": 0
|
||||||
|
},
|
||||||
|
"configurePresets": [
|
||||||
|
{
|
||||||
|
"name": "my-configure-preset",
|
||||||
|
"generator": "@RunCMake_GENERATOR@",
|
||||||
|
"binaryDir": "${sourceDir}/build"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"testPresets": [
|
||||||
|
{
|
||||||
|
"name": "my-include-preset",
|
||||||
|
"configurePreset": "my-configure-preset",
|
||||||
|
"filter": {
|
||||||
|
"include": {
|
||||||
|
"name": "^test1$"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "my-exclude-preset",
|
||||||
|
"configurePreset": "my-configure-preset",
|
||||||
|
"filter": {
|
||||||
|
"exclude": {
|
||||||
|
"name": "^test3$"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -13,6 +13,29 @@ endfunction()
|
|||||||
|
|
||||||
run_ctest_test(TestQuiet QUIET)
|
run_ctest_test(TestQuiet QUIET)
|
||||||
|
|
||||||
|
block()
|
||||||
|
set(CASE_CMAKELISTS_SUFFIX_CODE [[
|
||||||
|
foreach(i RANGE 1 3)
|
||||||
|
add_test(NAME test${i} COMMAND ${CMAKE_COMMAND} -E true)
|
||||||
|
endforeach()
|
||||||
|
]])
|
||||||
|
foreach(case IN ITEMS
|
||||||
|
TestPresetBadName
|
||||||
|
TestPresetExclude
|
||||||
|
TestPresetInclude
|
||||||
|
TestPresetOverride
|
||||||
|
)
|
||||||
|
configure_file(
|
||||||
|
"${RunCMake_SOURCE_DIR}/CMakePresets.json.in"
|
||||||
|
"${RunCMake_BINARY_DIR}/${case}/CMakePresets.json"
|
||||||
|
@ONLY)
|
||||||
|
endforeach()
|
||||||
|
run_ctest_test(TestPresetBadName PRESET nonexistent-preset)
|
||||||
|
run_ctest_test(TestPresetExclude PRESET my-exclude-preset)
|
||||||
|
run_ctest_test(TestPresetInclude PRESET my-include-preset)
|
||||||
|
run_ctest_test(TestPresetOverride PRESET my-include-preset INCLUDE test2)
|
||||||
|
endblock()
|
||||||
|
|
||||||
set(CASE_CMAKELISTS_SUFFIX_CODE [[
|
set(CASE_CMAKELISTS_SUFFIX_CODE [[
|
||||||
foreach(i RANGE 1 4)
|
foreach(i RANGE 1 4)
|
||||||
add_test(NAME test${i} COMMAND ${CMAKE_COMMAND} -E true)
|
add_test(NAME test${i} COMMAND ${CMAKE_COMMAND} -E true)
|
||||||
|
|||||||
1
Tests/RunCMake/ctest_test/TestPresetBadName-result.txt
Normal file
1
Tests/RunCMake/ctest_test/TestPresetBadName-result.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
(-1|255)
|
||||||
4
Tests/RunCMake/ctest_test/TestPresetBadName-stderr.txt
Normal file
4
Tests/RunCMake/ctest_test/TestPresetBadName-stderr.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Cannot instantiate test handler ctest_test
|
||||||
|
CMake Error at [^
|
||||||
|
]*/test\.cmake:[0-9]+ \(ctest_test\):
|
||||||
|
ctest_test No such test preset
|
||||||
6
Tests/RunCMake/ctest_test/TestPresetExclude-stdout.txt
Normal file
6
Tests/RunCMake/ctest_test/TestPresetExclude-stdout.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
Start [0-9]+: test1
|
||||||
|
[0-9]+/3 Test #[0-9]+: test1 \.+ Passed +[0-9\.]+ sec
|
||||||
|
Start [0-9]+: test2
|
||||||
|
[0-9]+/3 Test #[0-9]+: test2 \.+ Passed +[0-9\.]+ sec
|
||||||
|
+
|
||||||
|
100% tests passed out of 3
|
||||||
4
Tests/RunCMake/ctest_test/TestPresetInclude-stdout.txt
Normal file
4
Tests/RunCMake/ctest_test/TestPresetInclude-stdout.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Start [0-9]+: test1
|
||||||
|
1/1 Test #[0-9]+: test1 \.+ Passed +[0-9\.]+ sec
|
||||||
|
+
|
||||||
|
100% tests passed out of 1
|
||||||
4
Tests/RunCMake/ctest_test/TestPresetOverride-stdout.txt
Normal file
4
Tests/RunCMake/ctest_test/TestPresetOverride-stdout.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Start [0-9]+: test2
|
||||||
|
1/1 Test #[0-9]+: test2 \.+ Passed +[0-9\.]+ sec
|
||||||
|
+
|
||||||
|
100% tests passed out of 1
|
||||||
Reference in New Issue
Block a user