Browse Source

Merge topic 'gtest-parallel-xml'

32bc6aa9b6 GoogleTest: Add release note for XML_OUTPUT_DIR
0001339a6f GoogleTest: Add test case for XML_OUTPUT_DIR
e9ab39eb1d GoogleTest: Add XML_OUTPUT_DIR parameter

Acked-by: Kitware Robot <[email protected]>
Merge-request: !4433
Brad King 5 years ago
parent
commit
c6def2107f

+ 6 - 0
Help/release/dev/GoogleTest-XML_OUTPUT_DIR.rst

@@ -0,0 +1,6 @@
+GoogleTest-XML_OUTPUT_DIR
+-------------------------
+
+* The :module:`GoogleTest` module ``gtest_discover_test`` command
+  gained a new optional parameter ``XML_OUTPUT_DIR``. When set the JUnit XML
+  test results are stored in that directory.

+ 10 - 1
Modules/GoogleTest.cmake

@@ -151,6 +151,7 @@ same as the Google Test name (i.e. ``suite.testcase``); see also
                          [PROPERTIES name1 value1...]
                          [TEST_LIST var]
                          [DISCOVERY_TIMEOUT seconds]
+                         [XML_OUTPUT_DIR dir]
     )
 
   ``gtest_discover_tests`` sets up a post-build command on the test executable
@@ -236,6 +237,13 @@ same as the Google Test name (i.e. ``suite.testcase``); see also
       problem.  The ambiguous behavior of the ``TIMEOUT`` keyword in 3.10.1
       and 3.10.2 has not been preserved.
 
+  ``XML_OUTPUT_DIR dir``
+    If specified, the parameter is passed along with ``--gtest_output=xml:``
+    to test executable. The actual file name is the same as the test target,
+    including prefix and suffix. This should be used instead of
+    ``EXTRA_ARGS --gtest_output=xml`` to avoid race conditions writing the
+    XML result output when using parallel test execution.
+
 #]=======================================================================]
 
 # Save project's policies
@@ -372,7 +380,7 @@ function(gtest_discover_tests TARGET)
   cmake_parse_arguments(
     ""
     "NO_PRETTY_TYPES;NO_PRETTY_VALUES"
-    "TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST;DISCOVERY_TIMEOUT"
+    "TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST;DISCOVERY_TIMEOUT;XML_OUTPUT_DIR"
     "EXTRA_ARGS;PROPERTIES"
     ${ARGN}
   )
@@ -434,6 +442,7 @@ function(gtest_discover_tests TARGET)
             -D "TEST_LIST=${_TEST_LIST}"
             -D "CTEST_FILE=${ctest_tests_file}"
             -D "TEST_DISCOVERY_TIMEOUT=${_DISCOVERY_TIMEOUT}"
+            -D "TEST_XML_OUTPUT_DIR=${_XML_OUTPUT_DIR}"
             -P "${_GOOGLETEST_DISCOVER_TESTS_SCRIPT}"
     VERBATIM
   )

+ 6 - 0
Modules/GoogleTestAddTests.cmake

@@ -99,6 +99,11 @@ foreach(line ${output})
       endif()
       string(REGEX REPLACE "^DISABLED_" "" pretty_test "${pretty_test}")
       string(REGEX REPLACE "#.*" "" test "${test}")
+      if(NOT TEST_XML_OUTPUT_DIR STREQUAL "")
+        set(TEST_XML_OUTPUT_PARAM "--gtest_output=xml:${TEST_XML_OUTPUT_DIR}/${prefix}${pretty_suite}.${pretty_test}${suffix}.xml")
+      else()
+        unset(TEST_XML_OUTPUT_PARAM)
+      endif()
       # ...and add to script
       add_command(add_test
         "${prefix}${pretty_suite}.${pretty_test}${suffix}"
@@ -106,6 +111,7 @@ foreach(line ${output})
         "${TEST_EXECUTABLE}"
         "--gtest_filter=${suite}.${test}"
         "--gtest_also_run_disabled_tests"
+        ${TEST_XML_OUTPUT_PARAM}
         ${extra_args}
       )
       if(suite MATCHES "^DISABLED" OR test MATCHES "^DISABLED")

+ 4 - 0
Tests/RunCMake/GoogleTest/GoogleTestXML-result-check.cmake

@@ -0,0 +1,4 @@
+set(RESULT_FILE "${RunCMake_TEST_BINARY_DIR}/GoogleTestXML.Foo.xml")
+if(NOT EXISTS ${RESULT_FILE})
+  set(RunCMake_TEST_FAILED "Result XML file ${RESULT_FILE} was not created")
+endif()

+ 11 - 0
Tests/RunCMake/GoogleTest/GoogleTestXML.cmake

@@ -0,0 +1,11 @@
+project(test_include_dirs)
+include(CTest)
+include(GoogleTest)
+
+enable_testing()
+
+add_executable(xml_output xml_output.cpp)
+gtest_discover_tests(
+  xml_output
+  XML_OUTPUT_DIR ${CMAKE_BINARY_DIR}
+)

+ 28 - 0
Tests/RunCMake/GoogleTest/RunCMakeTest.cmake

@@ -78,4 +78,32 @@ function(run_GoogleTest)
   )
 endfunction()
 
+function(run_GoogleTestXML)
+  # Use a single build tree for a few tests without cleaning.
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/GoogleTestXML-build)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
+  endif()
+  file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
+  file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+
+  run_cmake(GoogleTestXML)
+
+  run_cmake_command(GoogleTestXML-discovery
+  ${CMAKE_COMMAND}
+  --build .
+  --config Debug
+  --target xml_output
+  )
+
+  run_cmake_command(GoogleTestXML-result
+  ${CMAKE_CTEST_COMMAND}
+  -C Debug
+  -R GoogleTestXML
+  --no-label-summary
+  )
+endfunction()
+
 run_GoogleTest()
+run_GoogleTestXML()

+ 26 - 0
Tests/RunCMake/GoogleTest/xml_output.cpp

@@ -0,0 +1,26 @@
+#include <fstream>
+#include <iostream>
+#include <string>
+
+int main(int argc, char** argv)
+{
+  // Note: GoogleTestXML.cmake doesn't actually depend on Google Test as such;
+  // it only mimicks the output file creation using the path passed to this
+  // test without any content
+  for (int i = 0; i < argc; i++) {
+    std::string param(argv[i]);
+    if (param.find("--gtest_list_tests") != std::string::npos) {
+      // This actually defines the name of the file passed in the 2nd run
+      std::cout << "GoogleTestXML." << std::endl;
+      std::cout << "  Foo" << std::endl;
+    } else if (param.find("--gtest_output=xml:") != std::string::npos) {
+      std::string::size_type split = param.find(":");
+      std::string filepath = param.substr(split + 1);
+      // The full file path is passed
+      std::ofstream ostrm(filepath.c_str(), std::ios::binary);
+      ostrm << "--gtest_output=xml: mockup file\n";
+    }
+  }
+
+  return 0;
+}