summaryrefslogtreecommitdiffstats
path: root/Source/cmCTest.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'Source/cmCTest.cxx')
-rw-r--r--Source/cmCTest.cxx340
1 files changed, 339 insertions, 1 deletions
diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx
index 6c1071d..4bea0d5 100644
--- a/Source/cmCTest.cxx
+++ b/Source/cmCTest.cxx
@@ -18,6 +18,7 @@
#include <vector>
#include <cm/memory>
+#include <cm/optional>
#include <cm/string_view>
#include <cmext/algorithm>
#include <cmext/string_view>
@@ -38,6 +39,7 @@
# include <unistd.h> // IWYU pragma: keep
#endif
+#include "cmCMakePresetsFile.h"
#include "cmCTestBuildAndTestHandler.h"
#include "cmCTestBuildHandler.h"
#include "cmCTestConfigureHandler.h"
@@ -2257,6 +2259,311 @@ bool cmCTest::AddVariableDefinition(const std::string& arg)
return false;
}
+void cmCTest::SetPersistentOptionIfNotEmpty(const std::string& value,
+ const std::string& optionName)
+{
+ if (!value.empty()) {
+ this->GetTestHandler()->SetPersistentOption(optionName, value.c_str());
+ this->GetMemCheckHandler()->SetPersistentOption(optionName, value.c_str());
+ }
+}
+
+bool cmCTest::SetArgsFromPreset(const std::string& presetName,
+ bool listPresets)
+{
+ const auto workingDirectory = cmSystemTools::GetCurrentWorkingDirectory();
+
+ cmCMakePresetsFile settingsFile;
+ auto result = settingsFile.ReadProjectPresets(workingDirectory);
+ if (result != cmCMakePresetsFile::ReadFileResult::READ_OK) {
+ cmSystemTools::Error(cmStrCat("Could not read presets from ",
+ workingDirectory, ": ",
+ cmCMakePresetsFile::ResultToString(result)));
+ return false;
+ }
+
+ if (listPresets) {
+ settingsFile.PrintTestPresetList();
+ return true;
+ }
+
+ auto presetPair = settingsFile.TestPresets.find(presetName);
+ if (presetPair == settingsFile.TestPresets.end()) {
+ cmSystemTools::Error(cmStrCat("No such test preset in ", workingDirectory,
+ ": \"", presetName, '"'));
+ settingsFile.PrintTestPresetList();
+ return false;
+ }
+
+ if (presetPair->second.Unexpanded.Hidden) {
+ cmSystemTools::Error(cmStrCat("Cannot use hidden test preset in ",
+ workingDirectory, ": \"", presetName, '"'));
+ settingsFile.PrintTestPresetList();
+ return false;
+ }
+
+ auto const& expandedPreset = presetPair->second.Expanded;
+ if (!expandedPreset) {
+ cmSystemTools::Error(cmStrCat("Could not evaluate test preset \"",
+ presetName, "\": Invalid macro expansion"));
+ settingsFile.PrintTestPresetList();
+ return false;
+ }
+
+ auto configurePresetPair =
+ settingsFile.ConfigurePresets.find(expandedPreset->ConfigurePreset);
+ if (configurePresetPair == settingsFile.ConfigurePresets.end()) {
+ cmSystemTools::Error(cmStrCat("No such configure preset in ",
+ workingDirectory, ": \"",
+ expandedPreset->ConfigurePreset, '"'));
+ settingsFile.PrintConfigurePresetList();
+ return false;
+ }
+
+ if (configurePresetPair->second.Unexpanded.Hidden) {
+ cmSystemTools::Error(cmStrCat("Cannot use hidden configure preset in ",
+ workingDirectory, ": \"",
+ expandedPreset->ConfigurePreset, '"'));
+ settingsFile.PrintConfigurePresetList();
+ return false;
+ }
+
+ auto const& expandedConfigurePreset = configurePresetPair->second.Expanded;
+ if (!expandedConfigurePreset) {
+ cmSystemTools::Error(cmStrCat("Could not evaluate configure preset \"",
+ expandedPreset->ConfigurePreset,
+ "\": Invalid macro expansion"));
+ return false;
+ }
+
+ auto presetEnvironment = expandedPreset->Environment;
+ for (auto const& var : presetEnvironment) {
+ if (var.second) {
+ cmSystemTools::PutEnv(cmStrCat(var.first, '=', *var.second));
+ }
+ }
+
+ if (!expandedPreset->Configuration.empty()) {
+ this->SetConfigType(expandedPreset->Configuration);
+ }
+
+ // Set build directory to value specified by the configure preset.
+ this->AddCTestConfigurationOverwrite(
+ cmStrCat("BuildDirectory=", expandedConfigurePreset->BinaryDir));
+ for (const auto& kvp : expandedPreset->OverwriteConfigurationFile) {
+ this->AddCTestConfigurationOverwrite(kvp);
+ }
+
+ if (expandedPreset->Output) {
+ this->Impl->TestProgressOutput =
+ expandedPreset->Output->ShortProgress.value_or(false);
+
+ if (expandedPreset->Output->Verbosity) {
+ const auto& verbosity = *expandedPreset->Output->Verbosity;
+ switch (verbosity) {
+ case cmCMakePresetsFile::TestPreset::OutputOptions::VerbosityEnum::
+ Extra:
+ this->Impl->ExtraVerbose = true;
+ // intentional fallthrough
+ case cmCMakePresetsFile::TestPreset::OutputOptions::VerbosityEnum::
+ Verbose:
+ this->Impl->Verbose = true;
+ break;
+ case cmCMakePresetsFile::TestPreset::OutputOptions::VerbosityEnum::
+ Default:
+ default:
+ // leave default settings
+ break;
+ }
+ }
+
+ this->Impl->Debug = expandedPreset->Output->Debug.value_or(false);
+ this->Impl->ShowLineNumbers =
+ expandedPreset->Output->Debug.value_or(false);
+ this->Impl->OutputTestOutputOnTestFailure =
+ expandedPreset->Output->OutputOnFailure.value_or(false);
+ this->Impl->Quiet = expandedPreset->Output->Quiet.value_or(false);
+
+ if (!expandedPreset->Output->OutputLogFile.empty()) {
+ this->SetOutputLogFileName(expandedPreset->Output->OutputLogFile);
+ }
+
+ this->Impl->LabelSummary =
+ expandedPreset->Output->LabelSummary.value_or(true);
+ this->Impl->SubprojectSummary =
+ expandedPreset->Output->SubprojectSummary.value_or(true);
+
+ if (expandedPreset->Output->MaxPassedTestOutputSize) {
+ this->Impl->TestHandler.SetTestOutputSizePassed(
+ *expandedPreset->Output->MaxPassedTestOutputSize);
+ }
+
+ if (expandedPreset->Output->MaxFailedTestOutputSize) {
+ this->Impl->TestHandler.SetTestOutputSizeFailed(
+ *expandedPreset->Output->MaxFailedTestOutputSize);
+ }
+
+ if (expandedPreset->Output->MaxTestNameWidth) {
+ this->Impl->MaxTestNameWidth = *expandedPreset->Output->MaxTestNameWidth;
+ }
+ }
+
+ if (expandedPreset->Filter) {
+ if (expandedPreset->Filter->Include) {
+ this->SetPersistentOptionIfNotEmpty(
+ expandedPreset->Filter->Include->Name, "IncludeRegularExpression");
+ this->SetPersistentOptionIfNotEmpty(
+ expandedPreset->Filter->Include->Label, "LabelRegularExpression");
+
+ if (expandedPreset->Filter->Include->Index) {
+ if (expandedPreset->Filter->Include->Index->IndexFile.empty()) {
+ const auto& start = expandedPreset->Filter->Include->Index->Start;
+ const auto& end = expandedPreset->Filter->Include->Index->End;
+ const auto& stride = expandedPreset->Filter->Include->Index->Stride;
+ std::string indexOptions;
+ indexOptions += (start ? std::to_string(*start) : "") + ",";
+ indexOptions += (end ? std::to_string(*end) : "") + ",";
+ indexOptions += (stride ? std::to_string(*stride) : "") + ",";
+ indexOptions +=
+ cmJoin(expandedPreset->Filter->Include->Index->SpecificTests, ",");
+
+ this->SetPersistentOptionIfNotEmpty(indexOptions,
+ "TestsToRunInformation");
+ } else {
+ this->SetPersistentOptionIfNotEmpty(
+ expandedPreset->Filter->Include->Index->IndexFile,
+ "TestsToRunInformation");
+ }
+ }
+
+ if (expandedPreset->Filter->Include->UseUnion.value_or(false)) {
+ this->GetTestHandler()->SetPersistentOption("UseUnion", "true");
+ this->GetMemCheckHandler()->SetPersistentOption("UseUnion", "true");
+ }
+ }
+
+ if (expandedPreset->Filter->Exclude) {
+ this->SetPersistentOptionIfNotEmpty(
+ expandedPreset->Filter->Exclude->Name, "ExcludeRegularExpression");
+ this->SetPersistentOptionIfNotEmpty(
+ expandedPreset->Filter->Exclude->Label,
+ "ExcludeLabelRegularExpression");
+
+ if (expandedPreset->Filter->Exclude->Fixtures) {
+ this->SetPersistentOptionIfNotEmpty(
+ expandedPreset->Filter->Exclude->Fixtures->Any,
+ "ExcludeFixtureRegularExpression");
+ this->SetPersistentOptionIfNotEmpty(
+ expandedPreset->Filter->Exclude->Fixtures->Setup,
+ "ExcludeFixtureSetupRegularExpression");
+ this->SetPersistentOptionIfNotEmpty(
+ expandedPreset->Filter->Exclude->Fixtures->Cleanup,
+ "ExcludeFixtureCleanupRegularExpression");
+ }
+ }
+ }
+
+ if (expandedPreset->Execution) {
+ this->Impl->StopOnFailure =
+ expandedPreset->Execution->StopOnFailure.value_or(false);
+ this->Impl->Failover =
+ expandedPreset->Execution->EnableFailover.value_or(false);
+
+ if (expandedPreset->Execution->Jobs) {
+ auto jobs = *expandedPreset->Execution->Jobs;
+ this->SetParallelLevel(jobs);
+ this->Impl->ParallelLevelSetInCli = true;
+ }
+
+ this->SetPersistentOptionIfNotEmpty(
+ expandedPreset->Execution->ResourceSpecFile, "ResourceSpecFile");
+
+ if (expandedPreset->Execution->TestLoad) {
+ auto testLoad = *expandedPreset->Execution->TestLoad;
+ this->SetTestLoad(testLoad);
+ }
+
+ if (expandedPreset->Execution->ShowOnly) {
+ this->Impl->ShowOnly = true;
+
+ switch (*expandedPreset->Execution->ShowOnly) {
+ case cmCMakePresetsFile::TestPreset::ExecutionOptions::ShowOnlyEnum::
+ JsonV1:
+ this->Impl->Quiet = true;
+ this->Impl->OutputAsJson = true;
+ this->Impl->OutputAsJsonVersion = 1;
+ break;
+ case cmCMakePresetsFile::TestPreset::ExecutionOptions::ShowOnlyEnum::
+ Human:
+ // intentional fallthrough (human is the default)
+ default:
+ break;
+ }
+ }
+
+ if (expandedPreset->Execution->RerunFailed.value_or(false)) {
+ this->GetTestHandler()->SetPersistentOption("RerunFailed", "true");
+ this->GetMemCheckHandler()->SetPersistentOption("RerunFailed", "true");
+ }
+
+ if (expandedPreset->Execution->Repeat) {
+ this->Impl->RepeatCount = expandedPreset->Execution->Repeat->Count;
+ switch (expandedPreset->Execution->Repeat->Mode) {
+ case cmCMakePresetsFile::TestPreset::ExecutionOptions::RepeatOptions::
+ ModeEnum::UntilFail:
+ this->Impl->RepeatMode = cmCTest::Repeat::UntilFail;
+ break;
+ case cmCMakePresetsFile::TestPreset::ExecutionOptions::RepeatOptions::
+ ModeEnum::UntilPass:
+ this->Impl->RepeatMode = cmCTest::Repeat::UntilPass;
+ break;
+ case cmCMakePresetsFile::TestPreset::ExecutionOptions::RepeatOptions::
+ ModeEnum::AfterTimeout:
+ this->Impl->RepeatMode = cmCTest::Repeat::AfterTimeout;
+ break;
+ default:
+ // should never default since mode is required
+ return false;
+ }
+ }
+
+ if (expandedPreset->Execution->InteractiveDebugging) {
+ this->Impl->InteractiveDebugMode =
+ *expandedPreset->Execution->InteractiveDebugging;
+ }
+
+ if (expandedPreset->Execution->ScheduleRandom.value_or(false)) {
+ this->Impl->ScheduleType = "Random";
+ }
+
+ if (expandedPreset->Execution->Timeout) {
+ this->Impl->GlobalTimeout =
+ cmDuration(*expandedPreset->Execution->Timeout);
+ }
+
+ if (expandedPreset->Execution->NoTestsAction) {
+ switch (*expandedPreset->Execution->NoTestsAction) {
+ case cmCMakePresetsFile::TestPreset::ExecutionOptions::
+ NoTestsActionEnum::Error:
+ this->Impl->NoTestsMode = cmCTest::NoTests::Error;
+ break;
+ case cmCMakePresetsFile::TestPreset::ExecutionOptions::
+ NoTestsActionEnum::Ignore:
+ this->Impl->NoTestsMode = cmCTest::NoTests::Ignore;
+ break;
+ case cmCMakePresetsFile::TestPreset::ExecutionOptions::
+ NoTestsActionEnum::Default:
+ break;
+ default:
+ // should never default
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
// the main entry point of ctest, called from main
int cmCTest::Run(std::vector<std::string>& args, std::string* output)
{
@@ -2268,6 +2575,37 @@ int cmCTest::Run(std::vector<std::string>& args, std::string* output)
// copy the command line
cm::append(this->Impl->InitialCommandLineArguments, args);
+ // check if a test preset was specified
+
+ bool listPresets =
+ find(args.begin(), args.end(), "--list-presets") != args.end();
+ auto it = find(args.begin(), args.end(), "--preset");
+ if (listPresets || it != args.end()) {
+ std::string errormsg;
+ bool success;
+
+ if (listPresets) {
+ // If listing presets we don't need a presetName
+ success = this->SetArgsFromPreset("", listPresets);
+ } else {
+ if (++it != args.end()) {
+ auto presetName = *it;
+ success = this->SetArgsFromPreset(presetName, listPresets);
+ } else {
+ cmSystemTools::Error("'--preset' requires an argument");
+ success = false;
+ }
+ }
+
+ if (listPresets) {
+ return success ? 0 : 1;
+ }
+
+ if (!success) {
+ return 1;
+ }
+ }
+
// process the command line arguments
for (size_t i = 1; i < args.size(); ++i) {
// handle the simple commandline arguments
@@ -2339,7 +2677,7 @@ int cmCTest::Run(std::vector<std::string>& args, std::string* output)
this->Impl->ScheduleType = "Random";
}
- // pass the argument to all the handlers as well, but i may no longer be
+ // pass the argument to all the handlers as well, but it may no longer be
// set to what it was originally so I'm not sure this is working as
// intended
for (auto& handler : this->Impl->GetTestingHandlers()) {