diff options
author | Frank Winklmeier <frank.winklmeier@cern.ch> | 2022-03-07 08:28:55 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2022-03-08 13:18:02 (GMT) |
commit | 140704d443e73c2dc74ac8192a109ae0c21e834a (patch) | |
tree | 7781fb36c21aa925d0442daf27ec5344d89e76f9 /Source | |
parent | 359e5b17d8edd092a1e500698af7968f96fe1d8d (diff) | |
download | CMake-140704d443e73c2dc74ac8192a109ae0c21e834a.zip CMake-140704d443e73c2dc74ac8192a109ae0c21e834a.tar.gz CMake-140704d443e73c2dc74ac8192a109ae0c21e834a.tar.bz2 |
ctest: add option for output truncation
Add `--test-output-truncation` to `ctest`. This option can be used to
customize which part of the test output is being truncated. Currently
supported values are `tail`, `middle` and `head`.
Also add equivalent `CTEST_CUSTOM_TEST_OUTPUT_TRUNCATION` variable.
Fixes: #23206
Diffstat (limited to 'Source')
-rw-r--r-- | Source/CTest/cmCTestMemCheckHandler.cxx | 3 | ||||
-rw-r--r-- | Source/CTest/cmCTestRunTest.cxx | 3 | ||||
-rw-r--r-- | Source/CTest/cmCTestTestHandler.cxx | 57 | ||||
-rw-r--r-- | Source/CTest/cmCTestTestHandler.h | 11 | ||||
-rw-r--r-- | Source/CTest/cmCTestTypes.h | 16 | ||||
-rw-r--r-- | Source/cmCMakePresetsGraph.cxx | 5 | ||||
-rw-r--r-- | Source/cmCMakePresetsGraph.h | 4 | ||||
-rw-r--r-- | Source/cmCMakePresetsGraphReadJSON.cxx | 5 | ||||
-rw-r--r-- | Source/cmCMakePresetsGraphReadJSONTestPresets.cxx | 39 | ||||
-rw-r--r-- | Source/cmCTest.cxx | 12 | ||||
-rw-r--r-- | Source/ctest.cxx | 3 |
11 files changed, 141 insertions, 17 deletions
diff --git a/Source/CTest/cmCTestMemCheckHandler.cxx b/Source/CTest/cmCTestMemCheckHandler.cxx index 6bb8e79..2d8276a 100644 --- a/Source/CTest/cmCTestMemCheckHandler.cxx +++ b/Source/CTest/cmCTestMemCheckHandler.cxx @@ -371,7 +371,8 @@ void cmCTestMemCheckHandler::GenerateCTestXML(cmXMLWriter& xml) } this->CleanTestOutput( memcheckstr, - static_cast<size_t>(this->CustomMaximumFailedTestOutputSize)); + static_cast<size_t>(this->CustomMaximumFailedTestOutputSize), + this->TestOutputTruncation); this->WriteTestResultHeader(xml, result); xml.StartElement("Results"); int memoryErrors = 0; diff --git a/Source/CTest/cmCTestRunTest.cxx b/Source/CTest/cmCTestRunTest.cxx index 6cd3b09..f594300 100644 --- a/Source/CTest/cmCTestRunTest.cxx +++ b/Source/CTest/cmCTestRunTest.cxx @@ -277,7 +277,8 @@ bool cmCTestRunTest::EndTest(size_t completed, size_t total, bool started) static_cast<size_t>( this->TestResult.Status == cmCTestTestHandler::COMPLETED ? this->TestHandler->CustomMaximumPassedTestOutputSize - : this->TestHandler->CustomMaximumFailedTestOutputSize)); + : this->TestHandler->CustomMaximumFailedTestOutputSize), + this->TestHandler->TestOutputTruncation); } this->TestResult.Reason = reason; if (this->TestHandler->LogFile) { diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx index 958c51c..a794e3d 100644 --- a/Source/CTest/cmCTestTestHandler.cxx +++ b/Source/CTest/cmCTestTestHandler.cxx @@ -281,6 +281,7 @@ cmCTestTestHandler::cmCTestTestHandler() this->CustomMaximumPassedTestOutputSize = 1 * 1024; this->CustomMaximumFailedTestOutputSize = 300 * 1024; + this->TestOutputTruncation = cmCTestTypes::TruncationMode::Tail; this->MemCheck = false; @@ -325,6 +326,7 @@ void cmCTestTestHandler::Initialize() this->CustomPostTest.clear(); this->CustomMaximumPassedTestOutputSize = 1 * 1024; this->CustomMaximumFailedTestOutputSize = 300 * 1024; + this->TestOutputTruncation = cmCTestTypes::TruncationMode::Tail; this->TestsToRun.clear(); @@ -358,6 +360,11 @@ void cmCTestTestHandler::PopulateCustomVectors(cmMakefile* mf) this->CTest->PopulateCustomInteger( mf, "CTEST_CUSTOM_MAXIMUM_FAILED_TEST_OUTPUT_SIZE", this->CustomMaximumFailedTestOutputSize); + + cmValue dval = mf->GetDefinition("CTEST_CUSTOM_TEST_OUTPUT_TRUNCATION"); + if (dval) { + this->SetTestOutputTruncation(dval); + } } int cmCTestTestHandler::PreProcessHandler() @@ -2076,6 +2083,20 @@ void cmCTestTestHandler::SetExcludeRegExp(const std::string& arg) this->ExcludeRegExp = arg; } +bool cmCTestTestHandler::SetTestOutputTruncation(const std::string& mode) +{ + if (mode == "tail") { + this->TestOutputTruncation = cmCTestTypes::TruncationMode::Tail; + } else if (mode == "middle") { + this->TestOutputTruncation = cmCTestTypes::TruncationMode::Middle; + } else if (mode == "head") { + this->TestOutputTruncation = cmCTestTypes::TruncationMode::Head; + } else { + return false; + } + return true; +} + void cmCTestTestHandler::SetTestsToRunInformation(cmValue in) { if (!in) { @@ -2094,7 +2115,8 @@ void cmCTestTestHandler::SetTestsToRunInformation(cmValue in) } } -void cmCTestTestHandler::CleanTestOutput(std::string& output, size_t length) +void cmCTestTestHandler::CleanTestOutput(std::string& output, size_t length, + cmCTestTypes::TruncationMode truncate) { if (!length || length >= output.size() || output.find("CTEST_FULL_OUTPUT") != std::string::npos) { @@ -2122,20 +2144,29 @@ void cmCTestTestHandler::CleanTestOutput(std::string& output, size_t length) return current; }; - // Truncate at given length respecting UTF-8 words + // Truncation message. + const std::string msg = + "\n[This part of the test output was removed since it " + "exceeds the threshold of " + + std::to_string(length) + " bytes.]\n"; + char const* const begin = output.c_str(); char const* const end = begin + output.size(); - char const* current = utf8_advance(begin, end, length); - output.erase(current - begin); - - // Append truncation message. - std::ostringstream msg; - msg << "...\n" - "The rest of the test output was removed since it exceeds the " - "threshold " - "of " - << length << " bytes.\n"; - output += msg.str(); + + // Erase head, middle or tail of output. + if (truncate == cmCTestTypes::TruncationMode::Head) { + char const* current = utf8_advance(begin, end, output.size() - length); + output.erase(0, current - begin); + output.insert(0, msg + "..."); + } else if (truncate == cmCTestTypes::TruncationMode::Middle) { + char const* current = utf8_advance(begin, end, length / 2); + output.erase(current - begin, output.size() - length); + output.insert(current - begin, "..." + msg + "..."); + } else { // default or "tail" + char const* current = utf8_advance(begin, end, length); + output.erase(current - begin); + output += ("..." + msg); + } } bool cmCTestTestHandler::SetTestsProperties( diff --git a/Source/CTest/cmCTestTestHandler.h b/Source/CTest/cmCTestTestHandler.h index 135e764..d0049da 100644 --- a/Source/CTest/cmCTestTestHandler.h +++ b/Source/CTest/cmCTestTestHandler.h @@ -19,6 +19,7 @@ #include "cmCTest.h" #include "cmCTestGenericHandler.h" #include "cmCTestResourceSpec.h" +#include "cmCTestTypes.h" #include "cmDuration.h" #include "cmListFileCache.h" #include "cmValue.h" @@ -32,6 +33,7 @@ class cmXMLWriter; */ class cmCTestTestHandler : public cmCTestGenericHandler { + friend class cmCTest; friend class cmCTestRunTest; friend class cmCTestMultiProcessHandler; @@ -80,6 +82,9 @@ public: this->CustomMaximumFailedTestOutputSize = n; } + //! Set test output truncation mode. Return false if unknown mode. + bool SetTestOutputTruncation(const std::string& mode); + //! pass the -I argument down void SetTestsToRunInformation(cmValue); @@ -242,8 +247,9 @@ protected: void AttachFile(cmXMLWriter& xml, std::string const& file, std::string const& name); - //! Clean test output to specified length - void CleanTestOutput(std::string& output, size_t length); + //! Clean test output to specified length and truncation mode + void CleanTestOutput(std::string& output, size_t length, + cmCTestTypes::TruncationMode truncate); cmDuration ElapsedTestingTime; @@ -258,6 +264,7 @@ protected: bool MemCheck; int CustomMaximumPassedTestOutputSize; int CustomMaximumFailedTestOutputSize; + cmCTestTypes::TruncationMode TestOutputTruncation; int MaxIndex; public: diff --git a/Source/CTest/cmCTestTypes.h b/Source/CTest/cmCTestTypes.h new file mode 100644 index 0000000..843d27a --- /dev/null +++ b/Source/CTest/cmCTestTypes.h @@ -0,0 +1,16 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +namespace cmCTestTypes { + +enum class TruncationMode +{ // Test output truncation mode + Tail, + Middle, + Head +}; +} diff --git a/Source/cmCMakePresetsGraph.cxx b/Source/cmCMakePresetsGraph.cxx index 705e2b0..39bb326 100644 --- a/Source/cmCMakePresetsGraph.cxx +++ b/Source/cmCMakePresetsGraph.cxx @@ -773,6 +773,8 @@ cmCMakePresetsGraph::TestPreset::VisitPresetInherit( parentOutput.MaxPassedTestOutputSize); InheritOptionalValue(output.MaxFailedTestOutputSize, parentOutput.MaxFailedTestOutputSize); + InheritOptionalValue(output.TestOutputTruncation, + parentOutput.TestOutputTruncation); InheritOptionalValue(output.MaxTestNameWidth, parentOutput.MaxTestNameWidth); } else { @@ -1027,6 +1029,9 @@ const char* cmCMakePresetsGraph::ResultToString(ReadFileResult result) "support."; case ReadFileResult::CYCLIC_INCLUDE: return "Cyclic include among preset files"; + case ReadFileResult::TEST_OUTPUT_TRUNCATION_UNSUPPORTED: + return "File version must be 5 or higher for testOutputTruncation " + "preset support."; } return "Unknown error"; diff --git a/Source/cmCMakePresetsGraph.h b/Source/cmCMakePresetsGraph.h index 9d6c61a..f1f8662 100644 --- a/Source/cmCMakePresetsGraph.h +++ b/Source/cmCMakePresetsGraph.h @@ -14,6 +14,8 @@ #include <cm/optional> +#include "CTest/cmCTestTypes.h" + enum class PackageResolveMode; class cmCMakePresetsGraph @@ -47,6 +49,7 @@ public: CONDITION_UNSUPPORTED, TOOLCHAIN_FILE_UNSUPPORTED, CYCLIC_INCLUDE, + TEST_OUTPUT_TRUNCATION_UNSUPPORTED, }; enum class ArchToolsetStrategy @@ -226,6 +229,7 @@ public: cm::optional<bool> SubprojectSummary; cm::optional<int> MaxPassedTestOutputSize; cm::optional<int> MaxFailedTestOutputSize; + cm::optional<cmCTestTypes::TruncationMode> TestOutputTruncation; cm::optional<int> MaxTestNameWidth; }; diff --git a/Source/cmCMakePresetsGraphReadJSON.cxx b/Source/cmCMakePresetsGraphReadJSON.cxx index 8a770b0..e68f0fa 100644 --- a/Source/cmCMakePresetsGraphReadJSON.cxx +++ b/Source/cmCMakePresetsGraphReadJSON.cxx @@ -568,6 +568,11 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile( return ReadFileResult::CONDITION_UNSUPPORTED; } + // Support for TestOutputTruncation added in version 5. + if (v < 5 && preset.Output) { + return ReadFileResult::TEST_OUTPUT_TRUNCATION_UNSUPPORTED; + } + this->TestPresetOrder.push_back(preset.Name); } diff --git a/Source/cmCMakePresetsGraphReadJSONTestPresets.cxx b/Source/cmCMakePresetsGraphReadJSONTestPresets.cxx index 4d6474a..43eccfe 100644 --- a/Source/cmCMakePresetsGraphReadJSONTestPresets.cxx +++ b/Source/cmCMakePresetsGraphReadJSONTestPresets.cxx @@ -16,6 +16,8 @@ #include "cmCMakePresetsGraphInternal.h" #include "cmJSONHelpers.h" +#include "CTest/cmCTestTypes.h" + namespace { using ReadFileResult = cmCMakePresetsGraph::ReadFileResult; using TestPreset = cmCMakePresetsGraph::TestPreset; @@ -55,6 +57,40 @@ auto const TestPresetOptionalOutputVerbosityHelper = ReadFileResult>(ReadFileResult::READ_OK, TestPresetOutputVerbosityHelper); +ReadFileResult TestPresetOutputTruncationHelper( + cmCTestTypes::TruncationMode& out, const Json::Value* value) +{ + if (!value) { + out = cmCTestTypes::TruncationMode::Tail; + return ReadFileResult::READ_OK; + } + + if (!value->isString()) { + return ReadFileResult::INVALID_PRESET; + } + + if (value->asString() == "tail") { + out = cmCTestTypes::TruncationMode::Tail; + return ReadFileResult::READ_OK; + } + + if (value->asString() == "middle") { + out = cmCTestTypes::TruncationMode::Middle; + return ReadFileResult::READ_OK; + } + + if (value->asString() == "head") { + out = cmCTestTypes::TruncationMode::Head; + return ReadFileResult::READ_OK; + } + + return ReadFileResult::INVALID_PRESET; +} + +auto const TestPresetOptionalTruncationHelper = + cmJSONOptionalHelper<cmCTestTypes::TruncationMode, ReadFileResult>( + ReadFileResult::READ_OK, TestPresetOutputTruncationHelper); + auto const TestPresetOptionalOutputHelper = cmJSONOptionalHelper<TestPreset::OutputOptions, ReadFileResult>( ReadFileResult::READ_OK, @@ -83,6 +119,9 @@ auto const TestPresetOptionalOutputHelper = .Bind("maxFailedTestOutputSize"_s, &TestPreset::OutputOptions::MaxFailedTestOutputSize, cmCMakePresetsGraphInternal::PresetOptionalIntHelper, false) + .Bind("testOutputTruncation"_s, + &TestPreset::OutputOptions::TestOutputTruncation, + TestPresetOptionalTruncationHelper, false) .Bind("maxTestNameWidth"_s, &TestPreset::OutputOptions::MaxTestNameWidth, cmCMakePresetsGraphInternal::PresetOptionalIntHelper, false)); diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx index a1e920e..710b4d7 100644 --- a/Source/cmCTest.cxx +++ b/Source/cmCTest.cxx @@ -2036,6 +2036,13 @@ bool cmCTest::HandleCommandLineArguments(size_t& i, "Invalid value for '--test-output-size-failed': " << args[i] << "\n"); } + } else if (this->CheckArgument(arg, "--test-output-truncation"_s) && + i < args.size() - 1) { + i++; + if (!this->Impl->TestHandler.SetTestOutputTruncation(args[i])) { + errormsg = "Invalid value for '--test-output-truncation': " + args[i]; + return false; + } } else if (this->CheckArgument(arg, "-N"_s, "--show-only")) { this->Impl->ShowOnly = true; } else if (cmHasLiteralPrefix(arg, "--show-only=")) { @@ -2464,6 +2471,11 @@ bool cmCTest::SetArgsFromPreset(const std::string& presetName, *expandedPreset->Output->MaxFailedTestOutputSize); } + if (expandedPreset->Output->TestOutputTruncation) { + this->Impl->TestHandler.TestOutputTruncation = + *expandedPreset->Output->TestOutputTruncation; + } + if (expandedPreset->Output->MaxTestNameWidth) { this->Impl->MaxTestNameWidth = *expandedPreset->Output->MaxTestNameWidth; } diff --git a/Source/ctest.cxx b/Source/ctest.cxx index cad27fa..363f473 100644 --- a/Source/ctest.cxx +++ b/Source/ctest.cxx @@ -44,6 +44,9 @@ static const char* cmDocumentationOptions[][2] = { { "--test-output-size-failed <size>", "Limit the output for failed tests " "to <size> bytes" }, + { "--test-output-truncation <mode>", + "Truncate 'tail' (default), 'middle' or 'head' of test output once " + "maximum output size is reached" }, { "-F", "Enable failover." }, { "-j <jobs>, --parallel <jobs>", "Run the tests in parallel using the " |