aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCristian Adam <[email protected]>2025-09-10 17:09:15 +0200
committerCristian Adam <[email protected]>2025-09-12 07:27:46 +0000
commit89d247b93d10f4a5f3acf96aac73b5d69d54a1d6 (patch)
tree5bc7e0757d60de1fbb8439a408b71a03b42ccb56
parent49fd03a14ca691544162719f9074eab03408d31c (diff)
CMakePM: Integrate CMake test presets into "ct" locator
This way one could trigger the test presets. Change-Id: I663fba1314912c2dd7a4827aaebdd2e2b3b112a4 Reviewed-by: Christian Stenger <[email protected]>
-rw-r--r--src/plugins/cmakeprojectmanager/CMakeLists.txt2
-rw-r--r--src/plugins/cmakeprojectmanager/cmakelocatorfilter.cpp48
-rw-r--r--src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs2
-rw-r--r--src/plugins/cmakeprojectmanager/testpresetshelper.cpp153
-rw-r--r--src/plugins/cmakeprojectmanager/testpresetshelper.h16
-rw-r--r--src/plugins/cmakeprojectmanager/tests/tst_cmake_test_presets.cpp69
6 files changed, 286 insertions, 4 deletions
diff --git a/src/plugins/cmakeprojectmanager/CMakeLists.txt b/src/plugins/cmakeprojectmanager/CMakeLists.txt
index 8c2fcafaf3f..ec2113302b4 100644
--- a/src/plugins/cmakeprojectmanager/CMakeLists.txt
+++ b/src/plugins/cmakeprojectmanager/CMakeLists.txt
@@ -44,6 +44,7 @@ add_qtc_plugin(CMakeProjectManager
presetsmacros.cpp presetsmacros.h
projecttreehelper.cpp projecttreehelper.h
targethelper.cpp targethelper.h
+ testpresetshelper.cpp testpresetshelper.h
3rdparty/cmake/cmListFileCache.cxx
3rdparty/cmake/cmListFileLexer.cxx
3rdparty/cmake/cmListFileCache.h
@@ -65,6 +66,7 @@ add_qtc_test(tst_cmake_test_presets
tests/tst_cmake_test_presets.cpp
presetsparser.cpp
presetsmacros.cpp
+ testpresetshelper.cpp
)
file(GLOB_RECURSE test_cases RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} testcases/*)
diff --git a/src/plugins/cmakeprojectmanager/cmakelocatorfilter.cpp b/src/plugins/cmakeprojectmanager/cmakelocatorfilter.cpp
index af013d81d22..45fa46161f3 100644
--- a/src/plugins/cmakeprojectmanager/cmakelocatorfilter.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakelocatorfilter.cpp
@@ -7,6 +7,7 @@
#include "cmakeproject.h"
#include "cmakeprojectmanagertr.h"
#include "targethelper.h"
+#include "testpresetshelper.h"
#include <autotest/autotestconstants.h>
#include <coreplugin/actionmanager/actionmanager.h>
@@ -177,10 +178,25 @@ private:
if (!testMenu) {
Core::MessageManager::writeFlashing(
Tr::tr("AutoTest plugin needs to be loaded in order to execute tests."));
+ return;
}
ProjectExplorer::TestCaseEnvironment testEnv;
- emit buildSystem->testRunRequested(testInfo, {"--output-on-failure"}, testEnv);
+ QStringList additionalOptions;
+ if (testInfo.path.fileName() == "CMakePresets.json") {
+ const auto cbs = qobject_cast<CMakeBuildSystem *>(buildSystem);
+ auto preset = Utils::findOrDefault(
+ cbs->project()->presetsData().testPresets,
+ [testInfo](const auto &preset) { return preset.name == testInfo.name; });
+ additionalOptions = presetToCTestArgs(preset);
+
+ if (preset.environment)
+ testEnv.environment = *preset.environment;
+ } else {
+ additionalOptions << "--output-on-failure";
+ }
+
+ emit buildSystem->testRunRequested(testInfo, additionalOptions, testEnv);
}
using TestAcceptor = std::function<void(BuildSystem *, const TestCaseInfo &)>;
@@ -198,15 +214,39 @@ private:
const auto cmakeProject = qobject_cast<const CMakeProject *>(ProjectManager::startupProject());
if (!cmakeProject)
return;
+
const auto bs = qobject_cast<CMakeBuildSystem *>(cmakeProject->activeBuildSystem());
if (!bs)
return;
- for (const TestCaseInfo &testInfo : bs->testcasesInfo()) {
+ // First the test presets
+ const auto testPresets = cmakeProject->presetsData().testPresets;
+ QList<TestCaseInfo> testCasesInfo;
+ for (const auto &testPreset : testPresets) {
+ TestCaseInfo testInfo;
+ testInfo.name = testPreset.name;
+ testInfo.path = cmakeProject->projectFilePath().parentDir().pathAppended(
+ "CMakePresets.json");
+ testCasesInfo << testInfo;
+ }
+ auto presetDisplayName = [cmakeProject](const TestCaseInfo &testInfo) -> QString {
+ auto preset = Utils::findOrDefault(
+ cmakeProject->presetsData().testPresets,
+ [testInfo](const auto &preset) { return preset.name == testInfo.name; });
+ if (preset.displayName)
+ return preset.displayName.value();
+ return testInfo.name;
+ };
+
+ // Then the tests themselves
+ testCasesInfo << bs->testcasesInfo();
+
+ for (const TestCaseInfo &testInfo : testCasesInfo) {
const QRegularExpressionMatch match = regexp.match(testInfo.name);
if (match.hasMatch()) {
- const FilePath projectPath = cmakeProject->projectFilePath();
- const QString displayName = testInfo.name;
+ const QString displayName = testInfo.path.fileName() == "CMakePresets.json"
+ ? presetDisplayName(testInfo)
+ : testInfo.name;
LocatorFilterEntry entry;
entry.displayName = displayName;
if (acceptor) {
diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs
index 746e966320c..6a03ea8cf4b 100644
--- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs
+++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs
@@ -90,6 +90,8 @@ QtcPlugin {
"projecttreehelper.h",
"targethelper.cpp",
"targethelper.h",
+ "testpresetshelper.cpp",
+ "testpresetshelper.h"
]
Group {
diff --git a/src/plugins/cmakeprojectmanager/testpresetshelper.cpp b/src/plugins/cmakeprojectmanager/testpresetshelper.cpp
new file mode 100644
index 00000000000..c487ca97d8e
--- /dev/null
+++ b/src/plugins/cmakeprojectmanager/testpresetshelper.cpp
@@ -0,0 +1,153 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "presetsparser.h"
+
+namespace CMakeProjectManager::Internal {
+
+// Helper that converts a CMake test preset into a list of command‑line args.
+QStringList presetToCTestArgs(const PresetsDetails::TestPreset &preset)
+{
+ QStringList args;
+
+ if (preset.output) {
+ const PresetsDetails::Output &out = *preset.output;
+
+ if (out.shortProgress.value_or(false))
+ args << "--progress";
+
+ const QString verb = out.verbosity.value_or("default");
+ if (verb == "verbose")
+ args << "--verbose";
+ else if (verb == "extra")
+ args << "--extra-verbose";
+ else if (verb == "debug")
+ args << "--debug";
+
+ if (out.outputOnFailure.value_or(false))
+ args << "--output-on-failure";
+
+ if (out.quiet.value_or(false))
+ args << "--quiet";
+
+ if (out.outputLogFile)
+ args << "--output-log" << out.outputLogFile->toFSPathString();
+
+ if (out.outputJUnitFile)
+ args << "--output-junit" << out.outputJUnitFile->toFSPathString();
+
+ if (out.labelSummary.has_value() && !out.labelSummary.value())
+ args << "--no-label-summary";
+
+ if (out.subprojectSummary.has_value() && !out.subprojectSummary.value())
+ args << "--no-subproject-summary";
+
+ if (out.maxPassedTestOutputSize)
+ args << "--test-output-size-passed" << QString::number(*out.maxPassedTestOutputSize);
+
+ if (out.maxFailedTestOutputSize)
+ args << "--test-output-size-failed" << QString::number(*out.maxFailedTestOutputSize);
+
+ if (out.testOutputTruncation)
+ args << "--test-output-truncation" << *out.testOutputTruncation;
+
+ if (out.maxTestNameWidth)
+ args << "--max-width" << QString::number(*out.maxTestNameWidth);
+ }
+
+ if (preset.filter) {
+ const PresetsDetails::Filter &filt = *preset.filter;
+
+ if (filt.include) {
+ const PresetsDetails::Filter::Include &inc = *filt.include;
+ if (inc.name)
+ args << "--tests-regex" << *inc.name;
+ if (inc.label)
+ args << "--label-regex" << *inc.label;
+ if (inc.useUnion.value_or(false))
+ args << "--union";
+
+ if (inc.index) {
+ const PresetsDetails::Filter::Include::Index &idx = *inc.index;
+ if (idx.start)
+ args << "--start" << QString::number(*idx.start);
+ if (idx.end)
+ args << "--end" << QString::number(*idx.end);
+ if (idx.stride)
+ args << "--stride" << QString::number(*idx.stride);
+ if (idx.specificTests)
+ for (int t : *idx.specificTests)
+ args << "--specific-test" << QString::number(t);
+ }
+ }
+
+ if (filt.exclude) {
+ const PresetsDetails::Filter::Exclude &exc = *filt.exclude;
+ if (exc.name)
+ args << "--exclude-regex" << *exc.name;
+ if (exc.label)
+ args << "--label-exclude" << *exc.label;
+
+ if (exc.fixtures) {
+ const PresetsDetails::Filter::Exclude::Fixtures &f = *exc.fixtures;
+ if (f.any)
+ args << "--fixture-exclude-any" << *f.any;
+ if (f.setup)
+ args << "--fixture-exclude-setup" << *f.setup;
+ if (f.cleanup)
+ args << "--fixture-exclude-cleanup" << *f.cleanup;
+ }
+ }
+ }
+
+ if (preset.execution) {
+ const PresetsDetails::Execution &exe = *preset.execution;
+
+ if (exe.stopOnFailure.value_or(false))
+ args << "--stop-on-failure";
+
+ if (exe.enableFailover.value_or(false))
+ args << "-F";
+
+ if (exe.jobs)
+ args << "--parallel" << QString::number(*exe.jobs);
+
+ if (exe.resourceSpecFile)
+ args << "--resource-spec-file" << exe.resourceSpecFile->toFSPathString();
+
+ if (exe.testLoad)
+ args << "--test-load" << QString::number(*exe.testLoad);
+
+ if (exe.showOnly)
+ args << QString("--show-only=%1").arg(*exe.showOnly);
+
+ if (exe.repeat) {
+ const PresetsDetails::Execution::Repeat &r = *exe.repeat;
+ args << "--repeat" << r.mode << "--count" << QString::number(r.count);
+ }
+
+ if (exe.interactiveDebugging.value_or(false))
+ args << "--interactive-debug-mode=1";
+ else
+ args << "--interactive-debug-mode=0";
+
+ if (exe.scheduleRandom.value_or(false))
+ args << "--schedule-random";
+
+ if (exe.timeout)
+ args << "--timeout" << QString::number(*exe.timeout);
+
+ if (exe.noTestsAction) {
+ if (*exe.noTestsAction == "error")
+ args << "--no-tests=error";
+ else if (*exe.noTestsAction == "ignore")
+ args << "--no-tests=ignore";
+ }
+ }
+
+ if (preset.configuration)
+ args << "--build-config" << *preset.configuration;
+
+ return args;
+}
+} // namespace CMakeProjectManager::Internal
diff --git a/src/plugins/cmakeprojectmanager/testpresetshelper.h b/src/plugins/cmakeprojectmanager/testpresetshelper.h
new file mode 100644
index 00000000000..a7d11404ba1
--- /dev/null
+++ b/src/plugins/cmakeprojectmanager/testpresetshelper.h
@@ -0,0 +1,16 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <QString>
+
+namespace CMakeProjectManager::Internal {
+
+namespace PresetsDetails {
+class TestPreset;
+}
+
+QStringList presetToCTestArgs(const PresetsDetails::TestPreset &preset);
+
+} // namespace CMakeProjectManager::Internal
diff --git a/src/plugins/cmakeprojectmanager/tests/tst_cmake_test_presets.cpp b/src/plugins/cmakeprojectmanager/tests/tst_cmake_test_presets.cpp
index 1bcff91582d..63bd5f056aa 100644
--- a/src/plugins/cmakeprojectmanager/tests/tst_cmake_test_presets.cpp
+++ b/src/plugins/cmakeprojectmanager/tests/tst_cmake_test_presets.cpp
@@ -13,6 +13,7 @@
#include "../presetsmacros.h"
#include "../presetsparser.h"
+#include "../testpresetshelper.h"
using namespace CMakeProjectManager;
using namespace CMakeProjectManager::Internal;
@@ -408,6 +409,74 @@ private slots:
else
QCOMPARE(val5, QString("a:b"));
}
+
+ void testPresetToCTestArgs()
+ {
+ PresetsDetails::TestPreset p;
+ PresetsDetails::Output output;
+ output.shortProgress = true;
+ output.verbosity = "debug";
+ output.outputOnFailure = true;
+ output.outputLogFile = Utils::FilePath::fromString("/tmp/log.txt");
+ output.outputJUnitFile = Utils::FilePath::fromString("/tmp/junit.xml");
+ output.labelSummary = false;
+ output.subprojectSummary = false;
+ output.maxPassedTestOutputSize = 1024;
+ output.maxFailedTestOutputSize = 2048;
+ output.testOutputTruncation = "full";
+ output.maxTestNameWidth = 80;
+ p.output = output;
+
+ PresetsDetails::Filter filter;
+ PresetsDetails::Filter::Include include;
+ include = PresetsDetails::Filter::Include{};
+ include.name = ".*Foo.*";
+ include.label = "fast";
+ include.useUnion = true;
+ include.index = PresetsDetails::Filter::Include::Index{
+ 1, 10, 2, QList<int>{3, 5, 7}
+ };
+ filter.include = include;
+ p.filter = filter;
+
+ PresetsDetails::Execution execution;
+ execution.jobs = 4;
+ execution.showOnly = "human";
+ execution.repeat = PresetsDetails::Execution::Repeat{"count", 3};
+ execution.timeout = 30;
+ execution.noTestsAction = "error";
+ p.execution = execution;
+
+ p.configuration = "Debug";
+
+ const auto args = presetToCTestArgs(p);
+ qDebug() << args;
+
+ QVERIFY(args.contains("--progress"));
+ QVERIFY(args.contains("--debug"));
+ QVERIFY(args.contains("--output-on-failure"));
+ QVERIFY(args.contains("--output-log"));
+ QVERIFY(args.contains("--output-junit"));
+ QVERIFY(args.contains("--no-label-summary"));
+ QVERIFY(args.contains("--no-subproject-summary"));
+ QVERIFY(args.contains("--test-output-size-passed"));
+ QVERIFY(args.contains("--test-output-size-failed"));
+ QVERIFY(args.contains("--test-output-truncation"));
+ QVERIFY(args.contains("--max-width"));
+ QVERIFY(args.contains("--tests-regex"));
+ QVERIFY(args.contains("--label-regex"));
+ QVERIFY(args.contains("--union"));
+ QVERIFY(args.contains("--start"));
+ QVERIFY(args.contains("--end"));
+ QVERIFY(args.contains("--stride"));
+ QVERIFY(args.contains("--specific-test"));
+ QVERIFY(args.contains("--parallel"));
+ QVERIFY(args.contains("--show-only=human"));
+ QVERIFY(args.contains("--repeat"));
+ QVERIFY(args.contains("--timeout"));
+ QVERIFY(args.contains("--build-config"));
+ }
+
};
QTEST_GUILESS_MAIN(TestPresetsTests)