diff options
author | Cristian Adam <[email protected]> | 2025-09-10 17:09:15 +0200 |
---|---|---|
committer | Cristian Adam <[email protected]> | 2025-09-12 07:27:46 +0000 |
commit | 89d247b93d10f4a5f3acf96aac73b5d69d54a1d6 (patch) | |
tree | 5bc7e0757d60de1fbb8439a408b71a03b42ccb56 | |
parent | 49fd03a14ca691544162719f9074eab03408d31c (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]>
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) |