Pārlūkot izejas kodu

ctest: allow test output to override the 'details' field

Parse test output for <CTestDetails>...</CTestDetails>.
If found, use this value to override the default 'Details' string reported
to CDash.
Zack Galbreath 4 gadi atpakaļ
vecāks
revīzija
02f1271bdf

+ 11 - 0
Help/command/ctest_test.rst

@@ -259,3 +259,14 @@ The following example demonstrates how to upload non-image files to CDash.
 If the name of the file to upload is known at configure time, you can use the
 If the name of the file to upload is known at configure time, you can use the
 :prop_test:`ATTACHED_FILES` or :prop_test:`ATTACHED_FILES_ON_FAIL` test
 :prop_test:`ATTACHED_FILES` or :prop_test:`ATTACHED_FILES_ON_FAIL` test
 properties instead.
 properties instead.
+
+Custom Details
+""""""""""""""
+
+The following example demonstrates how to specify a custom value for the
+``Test Details`` field displayed on CDash.
+
+.. code-block:: c++
+
+   std::cout <<
+     "<CTestDetails>My Custom Details Value</CTestDetails>" << std::endl;

+ 16 - 0
Source/CTest/cmCTestRunTest.cxx

@@ -40,6 +40,22 @@ void cmCTestRunTest::CheckOutput(std::string const& line)
 {
 {
   cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
   cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
              this->GetIndex() << ": " << line << std::endl);
              this->GetIndex() << ": " << line << std::endl);
+
+  // Check for special CTest XML tags in this line of output.
+  // If any are found, this line is excluded from ProcessOutput.
+  if (!line.empty() && line.find("<CTest") != std::string::npos) {
+    if (this->TestHandler->CustomCompletionStatusRegex.find(line)) {
+      this->TestResult.CustomCompletionStatus =
+        this->TestHandler->CustomCompletionStatusRegex.match(1);
+      cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
+                 this->GetIndex() << ": "
+                                  << "Test Details changed to '"
+                                  << this->TestResult.CustomCompletionStatus
+                                  << "'" << std::endl);
+      return;
+    }
+  }
+
   this->ProcessOutput += line;
   this->ProcessOutput += line;
   this->ProcessOutput += "\n";
   this->ProcessOutput += "\n";
 
 

+ 9 - 1
Source/CTest/cmCTestTestHandler.cxx

@@ -308,6 +308,10 @@ cmCTestTestHandler::cmCTestTestHandler()
   // regex to detect each individual <DartMeasurement>...</DartMeasurement>
   // regex to detect each individual <DartMeasurement>...</DartMeasurement>
   this->DartStuff1.compile(
   this->DartStuff1.compile(
     "(<DartMeasurement[^<]*</DartMeasurement[a-zA-Z]*>)");
     "(<DartMeasurement[^<]*</DartMeasurement[a-zA-Z]*>)");
+
+  // regex to detect <CTestDetails>...</CTestDetails>
+  this->CustomCompletionStatusRegex.compile(
+    "<CTestDetails>(.*)</CTestDetails>");
 }
 }
 
 
 void cmCTestTestHandler::Initialize()
 void cmCTestTestHandler::Initialize()
@@ -1460,7 +1464,11 @@ void cmCTestTestHandler::GenerateDartOutput(cmXMLWriter& xml)
     xml.StartElement("NamedMeasurement");
     xml.StartElement("NamedMeasurement");
     xml.Attribute("type", "text/string");
     xml.Attribute("type", "text/string");
     xml.Attribute("name", "Completion Status");
     xml.Attribute("name", "Completion Status");
-    xml.Element("Value", result.CompletionStatus);
+    if (result.CustomCompletionStatus.empty()) {
+      xml.Element("Value", result.CompletionStatus);
+    } else {
+      xml.Element("Value", result.CustomCompletionStatus);
+    }
     xml.EndElement(); // NamedMeasurement
     xml.EndElement(); // NamedMeasurement
 
 
     xml.StartElement("NamedMeasurement");
     xml.StartElement("NamedMeasurement");

+ 2 - 0
Source/CTest/cmCTestTestHandler.h

@@ -175,6 +175,7 @@ public:
     std::string ExceptionStatus;
     std::string ExceptionStatus;
     bool CompressOutput;
     bool CompressOutput;
     std::string CompletionStatus;
     std::string CompletionStatus;
+    std::string CustomCompletionStatus;
     std::string Output;
     std::string Output;
     std::string DartString;
     std::string DartString;
     int TestCount;
     int TestCount;
@@ -358,6 +359,7 @@ private:
   ListOfTests TestList;
   ListOfTests TestList;
   size_t TotalNumberOfTests;
   size_t TotalNumberOfTests;
   cmsys::RegularExpression DartStuff;
   cmsys::RegularExpression DartStuff;
+  cmsys::RegularExpression CustomCompletionStatusRegex;
 
 
   std::ostream* LogFile;
   std::ostream* LogFile;
 
 

+ 12 - 0
Tests/RunCMake/ctest_test/RunCMakeTest.cmake

@@ -169,3 +169,15 @@ add_test(
   run_ctest(TestMeasurements)
   run_ctest(TestMeasurements)
 endfunction()
 endfunction()
 run_measurements()
 run_measurements()
+
+# Verify that test output can override the Completion Status.
+function(run_completion_status)
+  set(CASE_CMAKELISTS_SUFFIX_CODE [[
+add_test(
+  NAME custom_details
+  COMMAND ${CMAKE_COMMAND} -E
+  echo test output\n<CTestDetails>CustomDetails</CTestDetails>\nmore output)
+  ]])
+  run_ctest(TestCompletionStatus)
+endfunction()
+run_completion_status()

+ 16 - 0
Tests/RunCMake/ctest_test/TestCompletionStatus-check.cmake

@@ -0,0 +1,16 @@
+file(READ "${RunCMake_TEST_BINARY_DIR}/Testing/TAG" _tag)
+string(REGEX REPLACE "^([^\n]*)\n.*$" "\\1" _date "${_tag}")
+file(READ "${RunCMake_TEST_BINARY_DIR}/Testing/${_date}/Test.xml" _test_contents)
+
+# Check custom completion status.
+if(NOT _test_contents MATCHES [[<Value>CustomDetails</Value>]])
+  string(APPEND RunCMake_TEST_FAILED
+    "Could not find expected <Value>CustomDetails</Value> in Test.xml")
+endif()
+# Check test output.
+if(NOT _test_contents MATCHES "test output")
+  string(APPEND RunCMake_TEST_FAILED "Could not find expected string 'test output' in Test.xml")
+endif()
+if(NOT _test_contents MATCHES "more output")
+  string(APPEND RunCMake_TEST_FAILED "Could not find expected string 'more output' in Test.xml")
+endif()