Pārlūkot izejas kodu

Merge topic 'gtest-use-json-output-for-discovery'

088b0af2f9 GoogleTest: Fix naming of tests with named parameters
3f780c3fde GoogleTest: Set DEF_SOURCE_LINE on tests if file and line are known
1cdceae8e3 GoogleTest: Parse discovered test list from JSON output if supported
07d14c21a9 GoogleTest: Add tests for NO_PRETTY_VALUES and NO_PRETTY_TYPES
54bb974b27 GoogleTest: Add tests for named parameters

Acked-by: Kitware Robot <[email protected]>
Acked-by: Dennis Lambe <[email protected]>
Merge-request: !10739
Brad King 4 mēneši atpakaļ
vecāks
revīzija
64b35e8fcb
25 mainītis faili ar 1205 papildinājumiem un 162 dzēšanām
  1. 12 0
      Help/release/dev/gtest-use-json-output-for-discovery.rst
  2. 263 126
      Modules/GoogleTestAddTests.cmake
  3. 8 0
      Tests/RunCMake/GoogleTest/GoogleTest-DEF_SOURCE_LINE-stdout.txt
  4. 20 9
      Tests/RunCMake/GoogleTest/GoogleTest-discovery-arg-change-typed-stdout.txt
  5. 28 0
      Tests/RunCMake/GoogleTest/GoogleTest-test1-stdout.txt
  6. 28 0
      Tests/RunCMake/GoogleTest/GoogleTest-test2-stdout.txt
  7. 22 0
      Tests/RunCMake/GoogleTest/GoogleTest-test4-stdout.txt
  8. 87 0
      Tests/RunCMake/GoogleTest/GoogleTest-test5-stdout.txt
  9. 45 0
      Tests/RunCMake/GoogleTest/GoogleTest-test6-stdout.txt
  10. 21 0
      Tests/RunCMake/GoogleTest/GoogleTest-test7-stdout.txt
  11. 31 0
      Tests/RunCMake/GoogleTest/GoogleTest.cmake
  12. 11 0
      Tests/RunCMake/GoogleTest/GoogleTestDefSourceLine.cmake
  13. 16 0
      Tests/RunCMake/GoogleTest/GoogleTestLegacyParser.cmake
  14. 81 0
      Tests/RunCMake/GoogleTest/RunCMakeTest.cmake
  15. 16 0
      Tests/RunCMake/GoogleTest/WorkDirWithSpaces.cmake
  16. 37 1
      Tests/RunCMake/GoogleTest/configuration_gtest.cpp
  17. 219 13
      Tests/RunCMake/GoogleTest/fake_gtest.cpp
  18. 41 2
      Tests/RunCMake/GoogleTest/flush_script_test.cpp
  19. 32 1
      Tests/RunCMake/GoogleTest/launcher_test.c
  20. 28 1
      Tests/RunCMake/GoogleTest/skip_test.cpp
  21. 35 3
      Tests/RunCMake/GoogleTest/test_list_extra_args.cpp
  22. 34 1
      Tests/RunCMake/GoogleTest/test_list_test.cpp
  23. 21 4
      Tests/RunCMake/GoogleTest/test_workdir.cpp
  24. 32 1
      Tests/RunCMake/GoogleTest/timeout_test.cpp
  25. 37 0
      Tests/RunCMake/GoogleTest/xml_output.cpp

+ 12 - 0
Help/release/dev/gtest-use-json-output-for-discovery.rst

@@ -0,0 +1,12 @@
+gtest-use-json-output-for-discovery
+-----------------------------------
+
+* The :command:`gtest_discover_tests()` command from the :module:`GoogleTest`
+  module now sets the ``DEF_SOURCE_LINE`` test property for each discovered
+  test if gtest supports the ``--gtest_output=json`` option.  This test
+  property is used by some IDEs to locate the source for each test.
+* The :command:`gtest_discover_tests()` command from the :module:`GoogleTest`
+  module previously parsed certain type-parameterized test names incorrectly.
+  Their names ended up with raw characters from gtest's output and were
+  very obviously misparsed.  Those names are now parsed correctly, so projects
+  may see different test names to before for affected tests.

+ 263 - 126
Modules/GoogleTestAddTests.cmake

@@ -43,6 +43,241 @@ function(escape_square_brackets output bracket placeholder placeholder_var outpu
   endif()
 endfunction()
 
+macro(write_test_to_file)
+  # Store the gtest test name before messing with these strings
+  set(gtest_name ${current_test_suite}.${current_test_name})
+
+  set(pretty_test_suite ${current_test_suite})
+  set(pretty_test_name ${current_test_name})
+
+  # Handle disabled tests
+  set(maybe_DISABLED "")
+  if(pretty_test_suite MATCHES "^DISABLED_" OR pretty_test_name MATCHES "^DISABLED_")
+    set(maybe_DISABLED DISABLED YES)
+    string(REGEX REPLACE "^DISABLED_" "" pretty_test_suite "${pretty_test_suite}")
+    string(REGEX REPLACE "^DISABLED_" "" pretty_test_name "${pretty_test_name}")
+  endif()
+
+  if (NOT current_test_value_param STREQUAL "" AND NOT arg_NO_PRETTY_VALUES)
+    # Remove value param name, if any, from test name
+    string(REGEX REPLACE "^(.+)/.+$" "\\1" pretty_test_name "${pretty_test_name}")
+    set(pretty_test_name "${pretty_test_name}/${current_test_value_param}")
+  endif()
+
+  if(NOT current_test_type_param STREQUAL "")
+    # Parse type param name from suite name
+    if(pretty_test_suite MATCHES "^(.+)/(.+)$")
+      set(pretty_test_suite "${CMAKE_MATCH_1}")
+      set(current_type_param_name "${CMAKE_MATCH_2}")
+    else()
+      set(current_type_param_name "")
+    endif()
+    if (NOT arg_NO_PRETTY_TYPES)
+      string(APPEND pretty_test_name  "<${current_test_type_param}>")
+    elseif(NOT current_type_param_name STREQUAL "")
+        string(APPEND pretty_test_name "<${current_type_param_name}>")
+    endif()
+  endif()
+
+  set(test_name_template "@prefix@@pretty_test_suite@.@pretty_test_name@@suffix@")
+  string(CONFIGURE "${test_name_template}" testname)
+
+  if(NOT "${arg_TEST_XML_OUTPUT_DIR}" STREQUAL "")
+    set(TEST_XML_OUTPUT_PARAM "--gtest_output=xml:${arg_TEST_XML_OUTPUT_DIR}/${prefix}${gtest_name}${suffix}.xml")
+  else()
+    set(TEST_XML_OUTPUT_PARAM "")
+  endif()
+
+  # unescape []
+  if(open_sb)
+    string(REPLACE "${open_sb}" "[" testname "${testname}")
+  endif()
+  if(close_sb)
+    string(REPLACE "${close_sb}" "]" testname "${testname}")
+  endif()
+  set(guarded_testname "${open_guard}${testname}${close_guard}")
+  # Add to script. Do not use add_command() here because it messes up the
+  # handling of empty values when forwarding arguments, and we need to
+  # preserve those carefully for arg_TEST_EXECUTOR and arg_EXTRA_ARGS.
+  string(APPEND script "add_test(${guarded_testname} ${launcherArgs}")
+  foreach(arg IN ITEMS
+    "${arg_TEST_EXECUTABLE}"
+    "--gtest_filter=${gtest_name}"
+    "--gtest_also_run_disabled_tests"
+    ${TEST_XML_OUTPUT_PARAM}
+  )
+
+    if(arg MATCHES "[^-./:a-zA-Z0-9_]")
+      string(APPEND script " [==[${arg}]==]")
+    else()
+      string(APPEND script " ${arg}")
+    endif()
+  endforeach()
+
+  if(arg_TEST_EXTRA_ARGS)
+    list(JOIN arg_TEST_EXTRA_ARGS "]==] [==[" extra_args)
+    string(APPEND script " [==[${extra_args}]==]")
+  endif()
+  string(APPEND script ")\n")
+
+  set(maybe_LOCATION "")
+  if(NOT current_test_file STREQUAL "" AND NOT current_test_line STREQUAL "")
+    set(maybe_LOCATION DEF_SOURCE_LINE "${current_test_file}:${current_test_line}")
+  endif()
+
+  add_command(set_tests_properties
+    "${guarded_testname}"
+    PROPERTIES
+      ${maybe_DISABLED}
+      ${maybe_LOCATION}
+      WORKING_DIRECTORY "${arg_TEST_WORKING_DIR}"
+      SKIP_REGULAR_EXPRESSION "\\[  SKIPPED \\]"
+      ${arg_TEST_PROPERTIES}
+  )
+
+  # possibly unbalanced square brackets render lists invalid so skip such
+  # tests in ${arg_TEST_LIST}
+  if(NOT "${testname}" MATCHES [=[(\[|\])]=])
+    # escape ;
+    string(REPLACE [[;]] [[\\;]] testname "${testname}")
+    list(APPEND tests_buffer "${testname}")
+    list(LENGTH tests_buffer tests_buffer_length)
+    if(tests_buffer_length GREATER "250")
+      # Chunk updates to the final "tests" variable, keeping the
+      # "tests_buffer" variable that we append each test to relatively
+      # small. This mitigates worsening performance impacts for the
+      # corner case of having many thousands of tests.
+      list(APPEND tests "${tests_buffer}")
+      set(tests_buffer "")
+    endif()
+  endif()
+
+  # If we've built up a sizable script so far, write it out as a chunk now
+  # so we don't accumulate a massive string to write at the end
+  string(LENGTH "${script}" script_len)
+  if(${script_len} GREATER "50000")
+    file(APPEND "${arg_CTEST_FILE}" "${script}")
+    set(script "")
+  endif()
+endmacro()
+
+macro(parse_tests_from_output)
+  generate_testname_guards("${output}" open_guard close_guard)
+  escape_square_brackets("${output}" "[" "__osb" open_sb output)
+  escape_square_brackets("${output}" "]" "__csb" close_sb output)
+
+  # Preserve semicolon in test-parameters
+  string(REPLACE [[;]] [[\;]] output "${output}")
+  string(REPLACE "\n" ";" output "${output}")
+
+  # Command line output doesn't contain information about the file and line number of the tests
+  set(current_test_file "")
+  set(current_test_line "")
+
+  # Parse output
+  foreach(line ${output})
+    # Skip header
+    if(line MATCHES "gtest_main\\.cc")
+      continue()
+    endif()
+
+    if(line STREQUAL "")
+      continue()
+    endif()
+
+    # Do we have a module name or a test name?
+    if(NOT line MATCHES "^  ")
+      set(current_test_type_param "")
+
+      # Module; remove trailing '.' to get just the name...
+      string(REGEX REPLACE "\\.( *#.*)?$" "" current_test_suite "${line}")
+      if(line MATCHES "# *TypeParam = (.*)$")
+        set(current_test_type_param "${CMAKE_MATCH_1}")
+      endif()
+    else()
+      string(STRIP "${line}" test)
+      string(REGEX REPLACE " ( *#.*)?$" "" current_test_name "${test}")
+
+      set(current_test_value_param "")
+      if(line MATCHES "# *GetParam\\(\\) = (.*)$")
+        set(current_test_value_param "${CMAKE_MATCH_1}")
+      endif()
+
+      write_test_to_file()
+    endif()
+  endforeach()
+endmacro()
+
+macro(get_json_member_with_default json_variable member_name out_variable)
+  string(JSON ${out_variable}
+    ERROR_VARIABLE error_param
+    GET "${${json_variable}}" "${member_name}"
+  )
+  if(error_param)
+    # Member not present
+    set(${out_variable} "")
+  endif()
+endmacro()
+
+macro(parse_tests_from_json json_file)
+  if(NOT EXISTS "${json_file}")
+    message(FATAL_ERROR "Missing expected JSON file with test list: ${json_file}")
+  endif()
+
+  file(READ "${json_file}" test_json)
+  string(JSON test_suites_json GET "${test_json}" "testsuites")
+
+  # Return if there are no testsuites
+  string(JSON len_test_suites LENGTH "${test_suites_json}")
+  if(len_test_suites LESS_EQUAL 0)
+    return()
+  endif()
+
+  set(open_sb)
+  set(close_sb)
+
+  math(EXPR upper_limit_test_suite_range "${len_test_suites} - 1")
+
+  foreach(index_test_suite RANGE ${upper_limit_test_suite_range})
+    string(JSON test_suite_json GET "${test_suites_json}" ${index_test_suite})
+
+    # "suite" is expected to be set in write_test_to_file(). When parsing the
+    # plain text output, "suite" is expected to be the original suite name
+    # before accounting for pretty names. This may be used to construct the
+    # name of XML output results files.
+    string(JSON current_test_suite GET "${test_suite_json}" "name")
+    string(JSON tests_json GET "${test_suite_json}" "testsuite")
+
+    # Skip test suites without tests
+    string(JSON len_tests LENGTH "${tests_json}")
+    if(len_tests LESS_EQUAL 0)
+      continue()
+    endif()
+
+    math(EXPR upper_limit_test_range "${len_tests} - 1")
+    foreach(index_test RANGE ${upper_limit_test_range})
+      string(JSON test_json GET "${tests_json}" ${index_test})
+
+      string(JSON len_test_parameters LENGTH "${test_json}")
+      if(len_test_parameters LESS_EQUAL 0)
+        continue()
+      endif()
+
+      get_json_member_with_default(test_json "name" current_test_name)
+      get_json_member_with_default(test_json "file" current_test_file)
+      get_json_member_with_default(test_json "line" current_test_line)
+      get_json_member_with_default(test_json "value_param" current_test_value_param)
+      get_json_member_with_default(test_json "type_param" current_test_type_param)
+
+      generate_testname_guards(
+        "${current_test_suite}${current_test_name}${current_test_value_param}${current_test_type_param}"
+        open_guard close_guard
+      )
+      write_test_to_file()
+    endforeach()
+  endforeach()
+endmacro()
+
 function(gtest_discover_tests_impl)
 
   set(options "")
@@ -74,19 +309,15 @@ function(gtest_discover_tests_impl)
   set(prefix "${arg_TEST_PREFIX}")
   set(suffix "${arg_TEST_SUFFIX}")
   set(script)
-  set(suite)
   set(tests)
   set(tests_buffer "")
 
   # If a file at ${arg_CTEST_FILE} already exists, we overwrite it.
-  # For performance reasons, we write to this file in chunks, and this variable
-  # is updated to APPEND after the first write.
-  set(file_write_mode WRITE)
+  file(REMOVE "${arg_CTEST_FILE}")
 
+  set(filter)
   if(arg_TEST_FILTER)
     set(filter "--gtest_filter=${arg_TEST_FILTER}")
-  else()
-    set(filter)
   endif()
 
   # CMP0178 has already been handled in gtest_discover_tests(), so we only need
@@ -113,15 +344,25 @@ function(gtest_discover_tests_impl)
     set(discovery_extra_args "[==[${discovery_extra_args}]==]")
   endif()
 
+  set(json_file "${arg_TEST_WORKING_DIR}/cmake_test_discovery.json")
+
+  # Remove json file to make sure we don't pick up an outdated one
+  file(REMOVE "${json_file}")
+
   cmake_language(EVAL CODE
     "execute_process(
-      COMMAND ${launcherArgs} [==[${arg_TEST_EXECUTABLE}]==] --gtest_list_tests ${filter} ${discovery_extra_args}
+      COMMAND ${launcherArgs} [==[${arg_TEST_EXECUTABLE}]==]
+        --gtest_list_tests
+        [==[--gtest_output=json:${json_file}]==]
+        ${filter}
+        ${discovery_extra_args}
       WORKING_DIRECTORY [==[${arg_TEST_WORKING_DIR}]==]
       TIMEOUT ${arg_TEST_DISCOVERY_TIMEOUT}
       OUTPUT_VARIABLE output
       RESULT_VARIABLE result
     )"
   )
+
   if(NOT ${result} EQUAL 0)
     string(REPLACE "\n" "\n    " output "${output}")
     if(arg_TEST_EXECUTOR)
@@ -139,124 +380,21 @@ function(gtest_discover_tests_impl)
     )
   endif()
 
-  generate_testname_guards("${output}" open_guard close_guard)
-  escape_square_brackets("${output}" "[" "__osb" open_sb output)
-  escape_square_brackets("${output}" "]" "__csb" close_sb output)
-  # Preserve semicolon in test-parameters
-  string(REPLACE [[;]] [[\;]] output "${output}")
-  string(REPLACE "\n" ";" output "${output}")
-
-  # Parse output
-  foreach(line ${output})
-    # Skip header
-    if(NOT line MATCHES "gtest_main\\.cc")
-      # Do we have a module name or a test name?
-      if(NOT line MATCHES "^  ")
-        # Module; remove trailing '.' to get just the name...
-        string(REGEX REPLACE "\\.( *#.*)?$" "" suite "${line}")
-        if(line MATCHES "#")
-          string(REGEX REPLACE "/[0-9].*" "" pretty_suite "${line}")
-          if(NOT arg_NO_PRETTY_TYPES)
-            string(REGEX REPLACE ".*/[0-9]+[ .#]+TypeParam = (.*)" "\\1" type_parameter "${line}")
-          else()
-            string(REGEX REPLACE ".*/([0-9]+)[ .#]+TypeParam = .*" "\\1" type_parameter "${line}")
-          endif()
-          set(test_name_template "@prefix@@pretty_suite@.@pretty_test@<@type_parameter@>@suffix@")
-        else()
-          set(pretty_suite "${suite}")
-          set(test_name_template "@prefix@@pretty_suite@.@pretty_test@@suffix@")
-        endif()
-        string(REGEX REPLACE "^DISABLED_" "" pretty_suite "${pretty_suite}")
-      else()
-        string(STRIP "${line}" test)
-        if(test MATCHES "#" AND NOT arg_NO_PRETTY_VALUES)
-          string(REGEX REPLACE "/[0-9]+[ #]+GetParam\\(\\) = " "/" pretty_test "${test}")
-        else()
-          string(REGEX REPLACE " +#.*" "" pretty_test "${test}")
-        endif()
-        string(REGEX REPLACE "^DISABLED_" "" pretty_test "${pretty_test}")
-        string(REGEX REPLACE " +#.*" "" test "${test}")
-        if(NOT "${arg_TEST_XML_OUTPUT_DIR}" STREQUAL "")
-          set(TEST_XML_OUTPUT_PARAM "--gtest_output=xml:${arg_TEST_XML_OUTPUT_DIR}/${prefix}${suite}.${test}${suffix}.xml")
-        else()
-          unset(TEST_XML_OUTPUT_PARAM)
-        endif()
-
-        string(CONFIGURE "${test_name_template}" testname)
-        # unescape []
-        if(open_sb)
-          string(REPLACE "${open_sb}" "[" testname "${testname}")
-        endif()
-        if(close_sb)
-          string(REPLACE "${close_sb}" "]" testname "${testname}")
-        endif()
-        set(guarded_testname "${open_guard}${testname}${close_guard}")
-
-        # Add to script. Do not use add_command() here because it messes up the
-        # handling of empty values when forwarding arguments, and we need to
-        # preserve those carefully for arg_TEST_EXECUTOR and arg_EXTRA_ARGS.
-        string(APPEND script "add_test(${guarded_testname} ${launcherArgs}")
-        foreach(arg IN ITEMS
-          "${arg_TEST_EXECUTABLE}"
-          "--gtest_filter=${suite}.${test}"
-          "--gtest_also_run_disabled_tests"
-          ${TEST_XML_OUTPUT_PARAM}
-          )
-          if(arg MATCHES "[^-./:a-zA-Z0-9_]")
-            string(APPEND script " [==[${arg}]==]")
-          else()
-            string(APPEND script " ${arg}")
-          endif()
-        endforeach()
-        if(arg_TEST_EXTRA_ARGS)
-          list(JOIN arg_TEST_EXTRA_ARGS "]==] [==[" extra_args)
-          string(APPEND script " [==[${extra_args}]==]")
-        endif()
-        string(APPEND script ")\n")
-
-        set(maybe_disabled "")
-        if(suite MATCHES "^DISABLED_" OR test MATCHES "^DISABLED_")
-          set(maybe_disabled DISABLED TRUE)
-        endif()
-
-        add_command(set_tests_properties
-          "${guarded_testname}"
-          PROPERTIES
-          ${maybe_disabled}
-          WORKING_DIRECTORY "${arg_TEST_WORKING_DIR}"
-          SKIP_REGULAR_EXPRESSION "\\[  SKIPPED \\]"
-          ${arg_TEST_PROPERTIES}
-        )
-
-        # possibly unbalanced square brackets render lists invalid so skip such
-        # tests in ${arg_TEST_LIST}
-        if(NOT "${testname}" MATCHES [=[(\[|\])]=])
-          # escape ;
-          string(REPLACE [[;]] [[\\;]] testname "${testname}")
-          list(APPEND tests_buffer "${testname}")
-          list(LENGTH tests_buffer tests_buffer_length)
-          if(tests_buffer_length GREATER "250")
-            # Chunk updates to the final "tests" variable, keeping the
-            # "tests_buffer" variable that we append each test to relatively
-            # small. This mitigates worsening performance impacts for the
-            # corner case of having many thousands of tests.
-            list(APPEND tests "${tests_buffer}")
-            set(tests_buffer "")
-          endif()
-        endif()
-      endif()
-
-      # If we've built up a sizable script so far, write it out as a chunk now
-      # so we don't accumulate a massive string to write at the end
-      string(LENGTH "${script}" script_len)
-      if(${script_len} GREATER "50000")
-        file(${file_write_mode} "${arg_CTEST_FILE}" "${script}")
-        set(file_write_mode APPEND)
-        set(script "")
-      endif()
+  if(EXISTS "${json_file}")
+    parse_tests_from_json("${json_file}")
+  else()
+    # gtest < 1.8.1, and all gtest compiled with GTEST_HAS_FILE_SYSTEM=0, don't
+    # recognize the --gtest_output=json option, and issue a warning or error on
+    # stdout about it being unrecognized, but still return an exit code 0 for
+    # success. All versions report the test list on stdout whether
+    # --gtest_output=json is recognized or not.
 
-    endif()
-  endforeach()
+    # NOTE: Because we are calling a macro, we don't want to pass "output" as
+    # an argument because it messes up the contents passed through due to the
+    # different escaping, etc. that gets applied. We rely on it picking up the
+    # "output" variable we have already set here.
+    parse_tests_from_output()
+  endif()
 
   if(NOT tests_buffer STREQUAL "")
     list(APPEND tests "${tests_buffer}")
@@ -267,8 +405,7 @@ function(gtest_discover_tests_impl)
   add_command(set "" ${arg_TEST_LIST} "${tests}")
 
   # Write remaining content to the CTest script
-  file(${file_write_mode} "${arg_CTEST_FILE}" "${script}")
-
+  file(APPEND "${arg_CTEST_FILE}" "${script}")
 endfunction()
 
 if(CMAKE_SCRIPT_MODE_FILE)

+ 8 - 0
Tests/RunCMake/GoogleTest/GoogleTest-DEF_SOURCE_LINE-stdout.txt

@@ -0,0 +1,8 @@
+.*"tests" *:.*
+ *"name" *: *"basic.case_foo" *,.*
+ *"name" *: *"DEF_SOURCE_LINE", *
+ *"value" *: *"file1.cpp:1" *
+.*
+ *"name" *: *"basic.case_bar" *,.*
+ *"name" *: *"DEF_SOURCE_LINE", *
+ *"value" *: *"file with spaces.cpp:2" *

+ 20 - 9
Tests/RunCMake/GoogleTest/GoogleTest-discovery-arg-change-typed-stdout.txt

@@ -1,12 +1,23 @@
 Test project .*/Tests/RunCMake/GoogleTest/GoogleTest-discovery-arg-change
-  Test #[0-9]+: typed\.case<short>
-  Test #[0-9]+: typed\.case<float>
-  Test #[0-9]+: typed\.case<char>
-  Test #[0-9]+: ns\.typed\.case<short>
-  Test #[0-9]+: ns\.typed\.case<float>
-  Test #[0-9]+: ns\.typed\.case<char>
-  Test #[0-9]+: prefix/typed\.case<short>
-  Test #[0-9]+: prefix/typed\.case<float>
-  Test #[0-9]+: prefix/typed\.case<char>
+  Test +#[0-9]+: typed\.case<short>
+  Test +#[0-9]+: typed\.case<float>
+  Test +#[0-9]+: typed\.case<char>
+  Test +#[0-9]+: typed\.case<int>
+  Test +#[0-9]+: ns\.typed\.case<short>
+  Test +#[0-9]+: ns\.typed\.case<float>
+  Test +#[0-9]+: ns\.typed\.case<char>
+  Test +#[0-9]+: ns\.typed\.case<int>
+  Test +#[0-9]+: prefix/typed\.case<short>
+  Test +#[0-9]+: prefix/typed\.case<float>
+  Test +#[0-9]+: prefix/typed\.case<char>
+  Test +#[0-9]+: prefix/typed\.case<int>
+  Test +#[0-9]+: both_suite\.test/VALUE<TYPE>
+  Test +#[0-9]+: both_suite\.case/VALUE<TYPE>
+  Test +#[0-9]+: both\.test/VALUE<TYPE>
+  Test +#[0-9]+: both\.case/VALUE<TYPE>
+  Test +#[0-9]+: ns\.both\.test/VALUE<TYPE>
+  Test +#[0-9]+: ns\.both\.case/VALUE<TYPE>
+  Test +#[0-9]+: prefix/both\.test/VALUE<TYPE>
+  Test +#[0-9]+: prefix/both\.case/VALUE<TYPE>
 
 Total Tests: [0-9]+

+ 28 - 0
Tests/RunCMake/GoogleTest/GoogleTest-test1-stdout.txt

@@ -25,30 +25,42 @@ Test project .*
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:typed\.case<float>!1 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:typed\.case<char>!1
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:typed\.case<char>!1 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:typed\.case<int>!1
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:typed\.case<int>!1 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:ns\.typed\.case<short>!1
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.typed\.case<short>!1 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:ns\.typed\.case<float>!1
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.typed\.case<float>!1 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:ns\.typed\.case<char>!1
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.typed\.case<char>!1 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:ns\.typed\.case<int>!1
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.typed\.case<int>!1 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:prefix/typed\.case<short>!1
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/typed\.case<short>!1 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:prefix/typed\.case<float>!1
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/typed\.case<float>!1 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:prefix/typed\.case<char>!1
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/typed\.case<char>!1 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:prefix/typed\.case<int>!1
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/typed\.case<int>!1 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:value/test\.case/1!1
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:value/test\.case/1!1 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:value/test\.case/"foo"!1
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:value/test\.case/"foo"!1 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:value/test\.case/'c'!1
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:value/test\.case/'c'!1 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:ns\.value/test\.case/1!1
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.value/test\.case/1!1 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:ns\.value/test\.case/"foo"!1
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.value/test\.case/"foo"!1 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:ns\.value/test\.case/'c'!1
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.value/test\.case/'c'!1 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:prefix/value/test\.case/1!1
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/value/test\.case/1!1 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:prefix/value/test\.case/"foo"!1
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/value/test\.case/"foo"!1 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:prefix/value/test\.case/'c'!1
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/value/test\.case/'c'!1 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:param/special\.case/"semicolon;"!1
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:param/special\.case/"semicolon;"!1 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:param/special\.case/"backslash\\"!1
@@ -97,6 +109,22 @@ Test project .*
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/param/special\.case/"__csb___text"!1 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:prefix/param/special\.case/"S o m  e   "!1
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/param/special\.case/"S o m  e   "!1 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:both_suite\.test/VALUE<TYPE>!1
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:both_suite\.test/VALUE<TYPE>!1 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:both_suite\.case/VALUE<TYPE>!1
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:both_suite\.case/VALUE<TYPE>!1 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:both\.test/VALUE<TYPE>!1
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:both\.test/VALUE<TYPE>!1 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:both\.case/VALUE<TYPE>!1
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:both\.case/VALUE<TYPE>!1 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:ns\.both\.test/VALUE<TYPE>!1
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.both\.test/VALUE<TYPE>!1 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:ns\.both\.case/VALUE<TYPE>!1
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.both\.case/VALUE<TYPE>!1 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:prefix/both\.test/VALUE<TYPE>!1
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/both\.test/VALUE<TYPE>!1 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:prefix/both\.case/VALUE<TYPE>!1
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/both\.case/VALUE<TYPE>!1 \.+ +Passed +[0-9.]+ sec
 
 100% tests passed, 0 tests failed out of [0-9]+
 

+ 28 - 0
Tests/RunCMake/GoogleTest/GoogleTest-test2-stdout.txt

@@ -25,30 +25,42 @@ Test project .*
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:typed\.case<float>!2 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:typed\.case<char>!2
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:typed\.case<char>!2 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:typed\.case<int>!2
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:typed\.case<int>!2 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:ns\.typed\.case<short>!2
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.typed\.case<short>!2 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:ns\.typed\.case<float>!2
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.typed\.case<float>!2 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:ns\.typed\.case<char>!2
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.typed\.case<char>!2 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:ns\.typed\.case<int>!2
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.typed\.case<int>!2 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:prefix/typed\.case<short>!2
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/typed\.case<short>!2 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:prefix/typed\.case<float>!2
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/typed\.case<float>!2 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:prefix/typed\.case<char>!2
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/typed\.case<char>!2 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:prefix/typed\.case<int>!2
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/typed\.case<int>!2 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:value/test\.case/1!2
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:value/test\.case/1!2 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:value/test\.case/"foo"!2
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:value/test\.case/"foo"!2 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:value/test\.case/'c'!2
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:value/test\.case/'c'!2 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:ns\.value/test\.case/1!2
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.value/test\.case/1!2 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:ns\.value/test\.case/"foo"!2
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.value/test\.case/"foo"!2 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:ns\.value/test\.case/'c'!2
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.value/test\.case/'c'!2 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:prefix/value/test\.case/1!2
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/value/test\.case/1!2 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:prefix/value/test\.case/"foo"!2
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/value/test\.case/"foo"!2 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:prefix/value/test\.case/'c'!2
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/value/test\.case/'c'!2 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:param/special\.case/"semicolon;"!2
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:param/special\.case/"semicolon;"!2 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:param/special\.case/"backslash\\"!2
@@ -97,6 +109,22 @@ Test project .*
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/param/special\.case/"__csb___text"!2 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:prefix/param/special\.case/"S o m  e   "!2
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/param/special\.case/"S o m  e   "!2 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:both_suite\.test/VALUE<TYPE>!2
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:both_suite\.test/VALUE<TYPE>!2 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:both_suite\.case/VALUE<TYPE>!2
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:both_suite\.case/VALUE<TYPE>!2 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:both\.test/VALUE<TYPE>!2
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:both\.test/VALUE<TYPE>!2 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:both\.case/VALUE<TYPE>!2
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:both\.case/VALUE<TYPE>!2 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:ns\.both\.test/VALUE<TYPE>!2
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.both\.test/VALUE<TYPE>!2 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:ns\.both\.case/VALUE<TYPE>!2
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.both\.case/VALUE<TYPE>!2 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:prefix/both\.test/VALUE<TYPE>!2
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/both\.test/VALUE<TYPE>!2 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:prefix/both\.case/VALUE<TYPE>!2
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/both\.case/VALUE<TYPE>!2 \.+ +Passed +[0-9.]+ sec
 
 100% tests passed, 0 tests failed out of [0-9]+
 

+ 22 - 0
Tests/RunCMake/GoogleTest/GoogleTest-test4-stdout.txt

@@ -5,18 +5,40 @@ Test project .*
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:typed\.case<float>!4 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:typed\.case<char>!4
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:typed\.case<char>!4 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:typed\.case<int>!4
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:typed\.case<int>!4 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:ns\.typed\.case<short>!4
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.typed\.case<short>!4 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:ns\.typed\.case<float>!4
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.typed\.case<float>!4 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:ns\.typed\.case<char>!4
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.typed\.case<char>!4 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:ns\.typed\.case<int>!4
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.typed\.case<int>!4 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:prefix/typed\.case<short>!4
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/typed\.case<short>!4 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:prefix/typed\.case<float>!4
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/typed\.case<float>!4 \.+ +Passed +[0-9.]+ sec
  *Start +[0-9]+: TEST:prefix/typed\.case<char>!4
  *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/typed\.case<char>!4 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:prefix/typed\.case<int>!4
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/typed\.case<int>!4 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:both_suite\.test/VALUE<TYPE>!4
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:both_suite\.test/VALUE<TYPE>!4 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:both_suite\.case/VALUE<TYPE>!4
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:both_suite\.case/VALUE<TYPE>!4 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:both\.test/VALUE<TYPE>!4
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:both\.test/VALUE<TYPE>!4 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:both\.case/VALUE<TYPE>!4
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:both\.case/VALUE<TYPE>!4 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:ns\.both\.test/VALUE<TYPE>!4
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.both\.test/VALUE<TYPE>!4 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:ns\.both\.case/VALUE<TYPE>!4
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.both\.case/VALUE<TYPE>!4 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:prefix/both\.test/VALUE<TYPE>!4
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/both\.test/VALUE<TYPE>!4 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:prefix/both\.case/VALUE<TYPE>!4
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/both\.case/VALUE<TYPE>!4 \.+ +Passed +[0-9.]+ sec
 
 100% tests passed, 0 tests failed out of [0-9]+
 

+ 87 - 0
Tests/RunCMake/GoogleTest/GoogleTest-test5-stdout.txt

@@ -0,0 +1,87 @@
+Test project .*
+ *Start +[0-9]+: TEST:value/test\.case/0!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:value/test\.case/0!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:value/test\.case/1!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:value/test\.case/1!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:value/test\.case/named!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:value/test\.case/named!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:ns\.value/test\.case/0!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.value/test\.case/0!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:ns\.value/test\.case/1!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.value/test\.case/1!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:ns\.value/test\.case/named!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.value/test\.case/named!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:prefix/value/test\.case/0!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/value/test\.case/0!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:prefix/value/test\.case/1!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/value/test\.case/1!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:prefix/value/test\.case/named!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/value/test\.case/named!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:param/special\.case/0!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:param/special\.case/0!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:param/special\.case/1!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:param/special\.case/1!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:param/special\.case/2!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:param/special\.case/2!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:param/special\.case/3!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:param/special\.case/3!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:param/special\.case/4!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:param/special\.case/4!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:param/special\.case/5!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:param/special\.case/5!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:param/special\.case/6!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:param/special\.case/6!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:param/special\.case/7!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:param/special\.case/7!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:ns\.param/special\.case/0!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.param/special\.case/0!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:ns\.param/special\.case/1!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.param/special\.case/1!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:ns\.param/special\.case/2!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.param/special\.case/2!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:ns\.param/special\.case/3!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.param/special\.case/3!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:ns\.param/special\.case/4!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.param/special\.case/4!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:ns\.param/special\.case/5!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.param/special\.case/5!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:ns\.param/special\.case/6!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.param/special\.case/6!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:ns\.param/special\.case/7!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.param/special\.case/7!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:prefix/param/special\.case/0!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/param/special\.case/0!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:prefix/param/special\.case/1!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/param/special\.case/1!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:prefix/param/special\.case/2!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/param/special\.case/2!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:prefix/param/special\.case/3!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/param/special\.case/3!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:prefix/param/special\.case/4!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/param/special\.case/4!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:prefix/param/special\.case/5!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/param/special\.case/5!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:prefix/param/special\.case/6!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/param/special\.case/6!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:prefix/param/special\.case/7!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/param/special\.case/7!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:both_suite\.test<TYPE>!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:both_suite\.test<TYPE>!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:both_suite\.case/test<TYPE>!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:both_suite\.case/test<TYPE>!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:both\.test<TYPE>!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:both\.test<TYPE>!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:both\.case/test<TYPE>!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:both\.case/test<TYPE>!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:ns\.both\.test<TYPE>!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.both\.test<TYPE>!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:ns\.both\.case/test<TYPE>!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.both\.case/test<TYPE>!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:prefix/both\.test<TYPE>!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/both\.test<TYPE>!5 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:prefix/both\.case/test<TYPE>!5
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/both\.case/test<TYPE>!5 \.+ +Passed +[0-9.]+ sec
+
+100% tests passed, 0 tests failed out of [0-9]+
+
+Total Test time \(real\) = +[0-9.]+ sec

+ 45 - 0
Tests/RunCMake/GoogleTest/GoogleTest-test6-stdout.txt

@@ -0,0 +1,45 @@
+Test project .*
+ *Start +[0-9]+: TEST:typed\.case<0>!6
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:typed\.case<0>!6 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:typed\.case<1>!6
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:typed\.case<1>!6 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:typed\.case<42>!6
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:typed\.case<42>!6 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:typed.case<named>!6
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:typed.case<named>!6 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:ns\.typed\.case<0>!6
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.typed\.case<0>!6 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:ns\.typed\.case<1>!6
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.typed\.case<1>!6 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:ns\.typed\.case<42>!6
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.typed\.case<42>!6 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:ns\.typed.case<named>!6
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.typed.case<named>!6 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:prefix/typed\.case<0>!6
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/typed\.case<0>!6 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:prefix/typed\.case<1>!6
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/typed\.case<1>!6 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:prefix/typed\.case<42>!6
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/typed\.case<42>!6 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:prefix/typed.case<named>!6
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/typed.case<named>!6 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:both_suite\.test/VALUE!6
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:both_suite\.test/VALUE!6 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:both_suite\.case/VALUE!6
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:both_suite\.case/VALUE!6 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:both\.test/VALUE<suite>!6
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:both\.test/VALUE<suite>!6 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:both\.case/VALUE<suite>!6
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:both\.case/VALUE<suite>!6 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:ns\.both\.test/VALUE<suite>!6
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.both\.test/VALUE<suite>!6 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:ns\.both\.case/VALUE<suite>!6
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.both\.case/VALUE<suite>!6 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:prefix/both\.test/VALUE<suite>!6
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/both\.test/VALUE<suite>!6 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:prefix/both\.case/VALUE<suite>!6
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/both\.case/VALUE<suite>!6 \.+ +Passed +[0-9.]+ sec
+
+100% tests passed, 0 tests failed out of [0-9]+
+
+Total Test time \(real\) = +[0-9.]+ sec

+ 21 - 0
Tests/RunCMake/GoogleTest/GoogleTest-test7-stdout.txt

@@ -0,0 +1,21 @@
+Test project .*
+ *Start +[0-9]+: TEST:both_suite\.test!7
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:both_suite\.test!7 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:both_suite\.case/test!7
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:both_suite\.case/test!7 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:both\.test<suite>!7
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:both\.test<suite>!7 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:both\.case/test<suite>!7
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:both\.case/test<suite>!7 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:ns\.both\.test<suite>!7
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.both\.test<suite>!7 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:ns\.both\.case/test<suite>!7
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:ns\.both\.case/test<suite>!7 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:prefix/both\.test<suite>!7
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/both\.test<suite>!7 \.+ +Passed +[0-9.]+ sec
+ *Start +[0-9]+: TEST:prefix/both\.case/test<suite>!7
+ *[0-9]+/[0-9]+ +Test +#[0-9]+: TEST:prefix/both\.case/test<suite>!7 \.+ +Passed +[0-9.]+ sec
+
+100% tests passed, 0 tests failed out of [0-9]+
+
+Total Test time \(real\) = +[0-9.]+ sec

+ 31 - 0
Tests/RunCMake/GoogleTest/GoogleTest.cmake

@@ -42,6 +42,37 @@ gtest_discover_tests(
   PROPERTIES LABELS TEST4
 )
 
+gtest_discover_tests(
+  fake_gtest
+  TEST_PREFIX TEST:
+  TEST_SUFFIX !5
+  TEST_FILTER value*
+  EXTRA_ARGS how now "\"brown\" cow"
+  PROPERTIES LABELS TEST5
+  NO_PRETTY_VALUES
+)
+
+gtest_discover_tests(
+  fake_gtest
+  TEST_PREFIX TEST:
+  TEST_SUFFIX !6
+  TEST_FILTER typed*
+  EXTRA_ARGS how now "\"brown\" cow"
+  PROPERTIES LABELS TEST6
+  NO_PRETTY_TYPES
+)
+
+gtest_discover_tests(
+  fake_gtest
+  TEST_PREFIX TEST:
+  TEST_SUFFIX !7
+  TEST_FILTER dynamic*
+  EXTRA_ARGS how now "\"brown\" cow"
+  PROPERTIES LABELS TEST7
+  NO_PRETTY_VALUES
+  NO_PRETTY_TYPES
+)
+
 add_executable(no_tests_defined no_tests_defined.cpp)
 xcode_sign_adhoc(no_tests_defined)
 

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

@@ -0,0 +1,11 @@
+enable_language(CXX)
+include(GoogleTest)
+
+enable_testing()
+
+include(xcode_sign_adhoc.cmake)
+
+add_executable(fake_gtest fake_gtest.cpp)
+xcode_sign_adhoc(fake_gtest)
+
+gtest_discover_tests(fake_gtest)

+ 16 - 0
Tests/RunCMake/GoogleTest/GoogleTestLegacyParser.cmake

@@ -0,0 +1,16 @@
+enable_language(CXX)
+include(GoogleTest)
+
+enable_testing()
+
+include(xcode_sign_adhoc.cmake)
+
+add_executable(fake_gtest fake_gtest.cpp)
+xcode_sign_adhoc(fake_gtest)
+
+gtest_discover_tests(
+  fake_gtest
+  TEST_PREFIX TEST:
+  TEST_SUFFIX !1
+  EXTRA_ARGS how now "\"brown\" cow"
+)

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

@@ -65,6 +65,27 @@ function(run_GoogleTest DISCOVERY_MODE)
     --no-label-summary
   )
 
+  run_cmake_command(GoogleTest-test5
+    ${CMAKE_CTEST_COMMAND}
+    -C Debug
+    -L TEST5
+    --no-label-summary
+  )
+
+  run_cmake_command(GoogleTest-test6
+    ${CMAKE_CTEST_COMMAND}
+    -C Debug
+    -L TEST6
+    --no-label-summary
+  )
+
+  run_cmake_command(GoogleTest-test7
+    ${CMAKE_CTEST_COMMAND}
+    -C Debug
+    -L TEST7
+    --no-label-summary
+  )
+
   run_cmake_command(GoogleTest-test-missing
     ${CMAKE_CTEST_COMMAND}
     -C Debug
@@ -381,6 +402,63 @@ function(run_GoogleTest_discovery_test_list_extra_args DISCOVERY_MODE)
   )
 endfunction()
 
+function(run_GoogleTest_LegacyParser)
+  # Use a single build tree for a few tests without cleaning.
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/GoogleTestLegacyParser-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}")
+
+  set(ENV{NO_GTEST_JSON_OUTPUT} 1)
+
+  run_cmake(GoogleTestLegacyParser)
+
+  run_cmake_command(GoogleTestLegacyParser-build
+    ${CMAKE_COMMAND}
+    --build .
+    --config Debug
+    --target fake_gtest
+  )
+
+  set(RunCMake-stdout-file GoogleTest-test1-stdout.txt)
+  run_cmake_command(GoogleTestLegacyParser-test
+    ${CMAKE_CTEST_COMMAND}
+    -C Debug
+    --no-label-summary
+  )
+
+  unset(ENV{NO_GTEST_JSON_OUTPUT})
+endfunction()
+
+function(run_GoogleTest_DEF_SOURCE_LINE)
+  # Use a single build tree for a few tests without cleaning.
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/GoogleTest-DEF_SOURCE_LINE-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(GoogleTestDefSourceLine)
+
+  run_cmake_command(GoogleTest-DEF_SOURCE_LINE-build
+    ${CMAKE_COMMAND}
+    --build .
+    --config Debug
+    --target fake_gtest
+  )
+  run_cmake_command(GoogleTest-DEF_SOURCE_LINE
+    ${CMAKE_CTEST_COMMAND}
+    -C Debug
+    -R "^basic\\.case_"
+    --show-only=json-v1
+  )
+endfunction()
+
 foreach(DISCOVERY_MODE POST_BUILD PRE_TEST)
   message(STATUS "Testing ${DISCOVERY_MODE} discovery mode via CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE global override...")
   run_GoogleTest(${DISCOVERY_MODE})
@@ -428,3 +506,6 @@ block(SCOPE_FOR VARIABLES)
     --output-on-failure
   )
 endblock()
+
+run_GoogleTest_LegacyParser()
+run_GoogleTest_DEF_SOURCE_LINE()

+ 16 - 0
Tests/RunCMake/GoogleTest/WorkDirWithSpaces.cmake

@@ -14,6 +14,22 @@ WorkDirWithSpaces.
   test1
   test2
 ]=])
+file(WRITE ${workdir}/test_list_output.json [=[
+{
+    "tests": 2,
+    "name": "AllTests",
+    "testsuites": [
+        {
+            "name": "WorkDirWithSpaces",
+            "tests": 2,
+            "testsuite": [
+                { "name": "test1", "file": "file.cpp", "line": 42 },
+                { "name": "test2", "file": "file.cpp", "line": 43 }
+            ]
+        }
+    ]
+}
+]=])
 file(WRITE ${workdir}/test_output.txt [=[
 Some output text for the test.
 ]=])

+ 37 - 1
Tests/RunCMake/GoogleTest/configuration_gtest.cpp

@@ -1,3 +1,5 @@
+#include <cstdlib>
+#include <fstream>
 #include <iostream>
 #include <string>
 
@@ -7,15 +9,49 @@ int main(int argc, char** argv)
   // it only requires that we produces output in the expected format when
   // invoked with --gtest_list_tests. Thus, we fake that here. This allows us
   // to test the module without actually needing Google Test.
-  if (argc > 1 && std::string(argv[1]) == "--gtest_list_tests") {
+  if (argc > 2 && std::string(argv[1]) == "--gtest_list_tests" &&
+      std::string(argv[2]).find("--gtest_output=json:") != std::string::npos) {
     std::cout << "configuration." << std::endl;
 #ifdef DEBUG
     std::cout << "  DISABLED_case_release" << std::endl;
     std::cout << "  case_debug" << std::endl;
+    std::string release_name("DISABLED_case_release");
+    std::string debug_name("case_debug");
 #else
     std::cout << "  case_release" << std::endl;
     std::cout << "  DISABLED_case_debug" << std::endl;
+    std::string release_name("case_release");
+    std::string debug_name("DISABLED_case_debug");
 #endif
+
+    std::string output_param(argv[2]);
+    std::string::size_type split = output_param.find(":");
+    std::string filepath = output_param.substr(split + 1);
+    // The full file path is passed
+    std::ofstream ostrm(filepath.c_str(), std::ios::binary);
+    if (!ostrm) {
+      std::cerr << "Failed to create file: " << filepath.c_str() << std::endl;
+      return 1;
+    }
+
+    ostrm << "{\n"
+             "    \"tests\": 2,\n"
+             "    \"name\": \"AllTests\",\n"
+             "    \"testsuites\": [\n"
+             "        {\n"
+             "            \"name\": \"configuration\",\n"
+             "            \"tests\": 2,\n"
+             "            \"testsuite\": [\n"
+             "                { \"name\": \""
+          << release_name
+          << "\", \"file\": \"file.cpp\", \"line\": 42 },\n"
+             "                { \"name\": \""
+          << debug_name
+          << "\", \"file\": \"file.cpp\", \"line\": 43 }\n"
+             "            ]\n"
+             "        }\n"
+             "    ]\n"
+             "}";
     return 0;
   }
 

+ 219 - 13
Tests/RunCMake/GoogleTest/fake_gtest.cpp

@@ -1,6 +1,16 @@
+#define _CRT_SECURE_NO_WARNINGS // work around 'getenv' deprecation on WIN32
+
+#include <cstdlib>
+#include <fstream>
 #include <iostream>
 #include <string>
 
+#if defined(_MSC_VER) && _MSC_VER < 1310
+#  define GETENV ::getenv
+#else
+#  define GETENV std::getenv
+#endif
+
 #define ARRAY_SIZE(a) sizeof(a) / sizeof(*a)
 
 int main(int argc, char** argv)
@@ -10,30 +20,100 @@ int main(int argc, char** argv)
   // invoked with --gtest_list_tests. Thus, we fake that here. This allows us
   // to test the module without actually needing Google Test.
   bool is_filtered =
-    argc > 2 && std::string(argv[2]).find("--gtest_filter=") == 0;
-  bool is_basic_only =
-    is_filtered && std::string(argv[2]).find("basic*") != std::string::npos;
-  bool is_typed_only =
-    is_filtered && std::string(argv[2]).find("typed*") != std::string::npos;
-
-  if (argc > 1 && std::string(argv[1]) == "--gtest_list_tests") {
-    if (!is_typed_only) {
-      char const* basic_suite_names[] = { "basic.", "ns.basic." };
+    argc > 3 && std::string(argv[3]).find("--gtest_filter=") == 0;
+  bool add_basic_tests =
+    !is_filtered || (std::string(argv[3]).find("basic*") != std::string::npos);
+  bool add_typed_tests =
+    !is_filtered || (std::string(argv[3]).find("typed*") != std::string::npos);
+  bool add_value_tests =
+    !is_filtered || (std::string(argv[3]).find("value*") != std::string::npos);
+  bool add_dynamic_tests = !is_filtered ||
+    (std::string(argv[3]).find("dynamic*") != std::string::npos);
+
+  if (argc > 2 && std::string(argv[1]) == "--gtest_list_tests" &&
+      std::string(argv[2]).find("--gtest_output=json:") != std::string::npos) {
+    std::ofstream ostrm;
+    if (!GETENV("NO_GTEST_JSON_OUTPUT")) {
+      std::string output_param(argv[2]);
+      std::string::size_type split = output_param.find(":");
+      std::string filepath = output_param.substr(split + 1);
+      // The full file path is passed
+      ostrm.open(filepath.c_str(), std::ios::out | std::ios::binary);
+      if (!ostrm) {
+        std::cerr << "Failed to create file: " << filepath.c_str()
+                  << std::endl;
+        return 1;
+      }
+    } else {
+      std::cout << "WARNING: An old-style warning." << std::endl;
+      std::cout << "[ WARNING ] A new-style warning." << std::endl;
+      std::cout << "[  ERROR  ] A new-style error." << std::endl;
+    }
+
+    int tests = 0;
+    ostrm << "{\n"
+             "    \"name\": \"AllTests\",\n"
+             "    \"testsuites\": [";
+
+    if (add_basic_tests) {
+      char const* basic_suite_names[] = { "basic", "ns.basic" };
       for (size_t i = 0; i < ARRAY_SIZE(basic_suite_names); i++) {
-        std::cout << basic_suite_names[i] << std::endl;
+        std::cout << basic_suite_names[i] << '.' << std::endl;
         std::cout << "  case_foo" << std::endl;
         std::cout << "  case_bar" << std::endl;
         std::cout << "  DISABLED_disabled_case" << std::endl;
         std::cout << "  DISABLEDnot_really_case" << std::endl;
+
+        if (tests)
+          ostrm << ",";
+        ostrm << "\n"
+                 "        {\n"
+                 "            \"name\": \""
+              << basic_suite_names[i]
+              << "\",\n"
+                 "            \"tests\": 4,\n"
+                 "            \"testsuite\": [\n"
+                 "                { \"name\": \"case_foo\", \"file\": "
+                 "\"file1.cpp\", \"line\": 1 },\n"
+                 "                { \"name\": \"case_bar\", \"file\": "
+                 "\"file with spaces.cpp\", \"line\": 2 },\n"
+                 "                { \"name\": \"DISABLED_disabled_case\", "
+                 "\"file\": \"file1.cpp\", \"line\": 3 },\n"
+                 "                { \"name\": \"DISABLEDnot_really_case\", "
+                 "\"file\": \"file1.cpp\", \"line\": 4 }\n"
+                 "            ]\n"
+                 "        }";
+        tests += 4;
       }
     }
-    if (!is_basic_only && !is_typed_only) {
+    if (!is_filtered) {
       std::cout << "DISABLED_disabled." << std::endl;
       std::cout << "  case" << std::endl;
       std::cout << "DISABLEDnotreally." << std::endl;
       std::cout << "  case" << std::endl;
+
+      if (tests)
+        ostrm << ",";
+      ostrm << "\n"
+               "        {\n"
+               "            \"name\": \"DISABLED_disabled\",\n"
+               "            \"tests\": 1,\n"
+               "            \"testsuite\": [\n"
+               "                { \"name\": \"case\", \"file\": "
+               "\"file2.cpp\", \"line\": 1 }\n"
+               "            ]\n"
+               "        },\n"
+               "        {\n"
+               "            \"name\": \"DISABLEDnotreally\",\n"
+               "            \"tests\": 1,\n"
+               "            \"testsuite\": [\n"
+               "                { \"name\": \"case\", \"file\": "
+               "\"file2.cpp\", \"line\": 2 }\n"
+               "            ]\n"
+               "        }";
+      tests += 2;
     }
-    if (!is_basic_only) {
+    if (add_typed_tests) {
       char const* typed_suite_names[] = { "typed", "ns.typed",
                                           "prefix/typed" };
       for (size_t i = 0; i < ARRAY_SIZE(typed_suite_names); i++) {
@@ -43,15 +123,75 @@ int main(int argc, char** argv)
         std::cout << "  case" << std::endl;
         std::cout << typed_suite_names[i] << "/42.  # TypeParam = char\n";
         std::cout << "  case" << std::endl;
+        std::cout << typed_suite_names[i] << "/named.  # TypeParam = int\n";
+        std::cout << "  case" << std::endl;
+
+        if (tests)
+          ostrm << ",";
+        ostrm << "\n"
+                 "        {\n"
+                 "            \"name\": \""
+              << typed_suite_names[i]
+              << "/0\",\n"
+                 "            \"tests\": 1, \"testsuite\": [ { \"name\": "
+                 "\"case\", \"type_param\": \"short\", \"file\": "
+                 "\"file3.cpp\", \"line\": 1 } ]\n"
+                 "        },\n"
+                 "        {\n"
+                 "            \"name\": \""
+              << typed_suite_names[i]
+              << "/1\",\n"
+                 "            \"tests\": 1, \"testsuite\": [ { \"name\": "
+                 "\"case\", \"type_param\": \"float\", \"file\": "
+                 "\"file3.cpp\", \"line\": 2 } ]\n"
+                 "        },\n"
+                 "        {\n"
+                 "            \"name\": \""
+              << typed_suite_names[i]
+              << "/42\",\n"
+                 "            \"tests\": 1, \"testsuite\": [ { \"name\": "
+                 "\"case\", \"type_param\": \"char\", \"file\": "
+                 "\"file3.cpp\", \"line\": 3 } ]\n"
+                 "        },\n"
+                 "        {\n"
+                 "            \"name\": \""
+              << typed_suite_names[i]
+              << "/named\",\n"
+                 "            \"tests\": 1, \"testsuite\": [ { \"name\": "
+                 "\"case\", \"type_param\": \"int\", \"file\": \"file3.cpp\", "
+                 "\"line\": 4 } ]\n"
+                 "        }";
+        tests += 4;
       }
     }
-    if (!is_basic_only && !is_typed_only) {
+    if (add_value_tests) {
       char const* value_suite_names[] = { "value", "ns.value",
                                           "prefix/value" };
       for (size_t i = 0; i < ARRAY_SIZE(value_suite_names); i++) {
         std::cout << value_suite_names[i] << "/test." << std::endl;
         std::cout << "  case/0  # GetParam() = 1" << std::endl;
         std::cout << "  case/1  # GetParam() = \"foo\"" << std::endl;
+        std::cout << "  case/named  # GetParam() = 'c'" << std::endl;
+
+        if (tests)
+          ostrm << ",";
+        ostrm
+          << "\n"
+             "        {\n"
+             "            \"name\": \""
+          << value_suite_names[i] << "/test"
+          << "\",\n"
+             "            \"tests\": 3,\n"
+             "            \"testsuite\": [\n"
+             "                { \"name\": \"case/0\", \"value_param\": \"1\", "
+             "\"file\": \"file4.cpp\", \"line\": 1 },\n"
+             "                { \"name\": \"case/1\", \"value_param\": "
+             "\"\\\"foo\\\"\", \"file\": \"file4.cpp\", \"line\": 2 },\n"
+             "                { \"name\": \"case/named\", \"value_param\": "
+             "\"'c'\", \"file\": \"file4.cpp\", \"line\": 3 }\n"
+             "            ]\n"
+             "        }";
+        tests += 3;
       }
       char const* param_suite_names[] = { "param", "ns.param",
                                           "prefix/param" };
@@ -65,8 +205,74 @@ int main(int argc, char** argv)
         std::cout << "  case/5  # GetParam() = \"__osbtext\"" << std::endl;
         std::cout << "  case/6  # GetParam() = \"__csb___text\"" << std::endl;
         std::cout << "  case/7  # GetParam() = \"S o m  e   \"" << std::endl;
+
+        ostrm
+          << ",\n"
+             "        {\n"
+             "            \"name\": \""
+          << param_suite_names[j] << "/special"
+          << "\",\n"
+             "            \"tests\": 8,\n"
+             "            \"testsuite\": [\n"
+             "                { \"name\": \"case/0\", \"value_param\": "
+             "\"\\\"semicolon;\\\"\", \"file\": \"file4.cpp\", \"line\": 1 "
+             "},\n"
+             "                { \"name\": \"case/1\", \"value_param\": "
+             "\"\\\"backslash\\\\\\\"\", \"file\": \"file4.cpp\", \"line\": 2 "
+             "},\n"
+             "                { \"name\": \"case/2\", \"value_param\": "
+             "\"\\\"${var}\\\"\", \"file\": \"file4.cpp\", \"line\": 3 },\n"
+             "                { \"name\": \"case/3\", \"value_param\": "
+             "\"'['\", \"file\": \"file4.cpp\", \"line\": 4 },\n"
+             "                { \"name\": \"case/4\", \"value_param\": "
+             "\"\\\"]]=]\\\"\", \"file\": \"file4.cpp\", \"line\": 5 },\n"
+             "                { \"name\": \"case/5\", \"value_param\": "
+             "\"\\\"__osbtext\\\"\", \"file\": \"file4.cpp\", \"line\": 5 },\n"
+             "                { \"name\": \"case/6\", \"value_param\": "
+             "\"\\\"__csb___text\\\"\", \"file\": \"file4.cpp\", \"line\": 5 "
+             "},\n"
+             "                { \"name\": \"case/7\", \"value_param\": "
+             "\"\\\"S o m  e   \\\"\", \"file\": \"file4.cpp\", \"line\": 6 "
+             "}\n"
+             "            ]\n"
+             "        }";
+        tests += 8;
+      }
+    }
+    if (add_value_tests || add_typed_tests || add_dynamic_tests) {
+      char const* both_suite_names[] = { "both_suite", "both/suite",
+                                         "ns.both/suite",
+                                         "prefix/both/suite" };
+      for (size_t k = 0; k < ARRAY_SIZE(both_suite_names); k++) {
+        std::cout << both_suite_names[k] << ".  # TypeParam = TYPE"
+                  << std::endl;
+        std::cout << "  test  # GetParam() = VALUE" << std::endl;
+        std::cout << "  case/test  # GetParam() = VALUE" << std::endl;
+
+        if (tests)
+          ostrm << ",";
+        ostrm << "\n"
+                 "          {\n"
+                 "              \"name\": \""
+              << both_suite_names[k]
+              << "\",\n"
+                 "              \"tests\": 1,\n"
+                 "              \"testsuite\": [\n"
+                 "                  { \"name\": \"test\", \"type_param\": "
+                 "\"TYPE\", \"value_param\": \"VALUE\", \"file\": "
+                 "\"file5.cpp\", \"line\": 1 },\n"
+                 "                  { \"name\": \"case/test\", "
+                 "\"type_param\": \"TYPE\", \"value_param\": \"VALUE\", "
+                 "\"file\": \"file5.cpp\", \"line\": 2 }\n"
+                 "              ]\n"
+                 "          }";
+        tests += 2;
       }
     }
+    ostrm << "\n"
+             "    ],\n"
+             "    \"tests\": "
+          << tests << "\n}";
     return 0;
   }
 

+ 41 - 2
Tests/RunCMake/GoogleTest/flush_script_test.cpp

@@ -1,3 +1,4 @@
+#include <fstream>
 #include <iostream>
 #include <string>
 
@@ -7,14 +8,52 @@ int main(int argc, char** argv)
   // it only requires that we produces output in the expected format when
   // invoked with --gtest_list_tests. Thus, we fake that here. This allows us
   // to test the module without actually needing Google Test.
-  if (argc > 1 && std::string(argv[1]) == "--gtest_list_tests") {
+  if (argc > 2 && std::string(argv[1]) == "--gtest_list_tests" &&
+      std::string(argv[2]).find("--gtest_output=json:") != std::string::npos) {
     std::cout << "flush_script_test.\n";
+
+    std::string output_param(argv[2]);
+    std::string::size_type split = output_param.find(":");
+    std::string filepath = output_param.substr(split + 1);
+    // The full file path is passed
+    std::ofstream ostrm(filepath.c_str(), std::ios::binary);
+    if (!ostrm) {
+      std::cerr << "Failed to create file: " << filepath.c_str() << std::endl;
+      return 1;
+    }
+
     size_t const flushThreshold = 50000;
     size_t const flushAfter = 4;
     size_t const testCaseNum = 3 * flushAfter;
+    ostrm << "{\n"
+             "    \"tests\": 4,\n"
+             "    \"name\": \"AllTests\",\n"
+             "    \"testsuites\": [\n"
+             "        {\n"
+             "            \"name\": \"flush_script_test\",\n"
+             "            \"tests\": 12,\n"
+             "            \"testsuite\": [";
+
     std::string testName(flushThreshold / flushAfter, 'T');
-    for (size_t i = 1; i <= testCaseNum; ++i)
+    for (size_t i = 1; i <= testCaseNum; ++i) {
       std::cout << "  t" << i << testName.c_str() << "\n";
+      if (i != 1)
+        ostrm << ",";
+      ostrm << "\n"
+               "                {\n"
+               "                  \"name\": \"t"
+            << i << testName.c_str()
+            << "\",\n"
+               "                  \"file\": \"file2.cpp\",\n"
+               "                  \"line\": 47\n"
+               "                }";
+    }
+    ostrm << "\n"
+             "            ]\n"
+             "        }\n"
+             "    ]\n"
+             "}";
   }
+
   return 0;
 }

+ 32 - 1
Tests/RunCMake/GoogleTest/launcher_test.c

@@ -8,15 +8,46 @@ TEST_F( launcher_test, test1 )
 }
 */
 
+char const gtest_output_json_flag_prefix[] = "--gtest_output=json:";
+char const json_output[] = "{\n"
+                           "    \"tests\": 1,\n"
+                           "    \"name\": \"AllTests\",\n"
+                           "    \"testsuites\": [\n"
+                           "        {\n"
+                           "            \"name\": \"launcher_test\",\n"
+                           "            \"tests\": 1,\n"
+                           "            \"testsuite\": [\n"
+                           "                { \"name\": \"test1\", \"file\": "
+                           "\"file.cpp\", \"line\": 42 }\n"
+                           "            ]\n"
+                           "        }\n"
+                           "    ]\n"
+                           "}";
+
 int main(int argc, char** argv)
 {
   /* Note: Launcher.cmake doesn't actually depend on Google Test as such;
    * it only requires that we produces output in the expected format when
    * invoked with --gtest_list_tests. Thus, we fake that here. This allows us
    * to test the module without actually needing Google Test.  */
-  if (argc > 1 && strcmp(argv[1], "--gtest_list_tests") == 0) {
+  char* filepath;
+  FILE* ofile;
+
+  if (argc > 2 && strcmp(argv[1], "--gtest_list_tests") == 0 &&
+      strncmp(argv[2], gtest_output_json_flag_prefix,
+              strlen(gtest_output_json_flag_prefix)) == 0) {
     printf("launcher_test.\n");
     printf("  test1\n");
+    filepath = strchr(argv[2], ':') + 1;
+    /* The full file path is passed */
+    ofile = fopen(filepath, "wb");
+
+    if (!ofile) {
+      fprintf(stderr, "Failed to create file: %s\n", filepath);
+      return 1;
+    }
+    fprintf(ofile, "%s", json_output);
+    fclose(ofile);
   }
 
   printf("launcher_test.test1\n");

+ 28 - 1
Tests/RunCMake/GoogleTest/skip_test.cpp

@@ -1,3 +1,4 @@
+#include <fstream>
 #include <iostream>
 #include <string>
 
@@ -14,9 +15,35 @@ int main(int argc, char** argv)
   // it only requires that we produces output in the expected format when
   // invoked with --gtest_list_tests. Thus, we fake that here. This allows us
   // to test the module without actually needing Google Test.
-  if (argc > 1 && std::string(argv[1]) == "--gtest_list_tests") {
+  if (argc > 2 && std::string(argv[1]) == "--gtest_list_tests" &&
+      std::string(argv[2]).find("--gtest_output=json:") != std::string::npos) {
     std::cout << "skip_test." << std::endl;
     std::cout << "  test1" << std::endl;
+
+    std::string output_param(argv[2]);
+    std::string::size_type split = output_param.find(":");
+    std::string filepath = output_param.substr(split + 1);
+    // The full file path is passed
+    std::ofstream ostrm(filepath.c_str(), std::ios::binary);
+    if (!ostrm) {
+      std::cerr << "Failed to create file: " << filepath.c_str() << std::endl;
+      return 1;
+    }
+    ostrm << "{\n"
+             "    \"tests\": 1,\n"
+             "    \"name\": \"AllTests\",\n"
+             "    \"testsuites\": [\n"
+             "        {\n"
+             "            \"name\": \"skip_test\",\n"
+             "            \"tests\": 1,\n"
+             "            \"testsuite\": [\n"
+             "                { \"name\": \"test1\", \"file\": \"file.cpp\", "
+             "\"line\": 42 }\n"
+             "            ]\n"
+             "        }\n"
+             "    ]\n"
+             "}";
+
     return 0;
   }
 

+ 35 - 3
Tests/RunCMake/GoogleTest/test_list_extra_args.cpp

@@ -1,3 +1,4 @@
+#include <fstream>
 #include <iostream>
 #include <string>
 
@@ -9,14 +10,45 @@ int main(int argc, char** argv)
   // to test the module without actually needing Google Test.
 
   // Simple test of DISCOVERY_EXTRA_ARGS
-  if (argc > 4 && std::string(argv[1]) == "--gtest_list_tests" &&
-      std::string(argv[2]) == "how now" && std::string(argv[3]) == "" &&
-      std::string(argv[4]) == "\"brown\" cow") {
+  if (argc > 5 && std::string(argv[1]) == "--gtest_list_tests" &&
+      std::string(argv[2]).find("--gtest_output=json:") != std::string::npos &&
+      std::string(argv[3]) == "how now" && std::string(argv[4]) == "" &&
+      std::string(argv[5]) == "\"brown\" cow") {
     std::cout << "test_list_test/test.\n";
     std::cout << "  case/0  # GetParam() = 'one'\n";
     std::cout << "  case/1  # GetParam() = 'two'\n";
     std::cout << "  case/2  # GetParam() = 'three'\n";
     std::cout << "  case/3  # GetParam() = 'four'\n";
+
+    std::string output_param(argv[2]);
+    std::string::size_type split = output_param.find(":");
+    std::string filepath = output_param.substr(split + 1);
+    // The full file path is passed
+    std::ofstream ostrm(filepath.c_str(), std::ios::binary);
+    if (!ostrm) {
+      std::cerr << "Failed to create file: " << filepath.c_str() << std::endl;
+      return 1;
+    }
+    ostrm << "{\n"
+             "    \"tests\": 4,\n"
+             "    \"name\": \"AllTests\",\n"
+             "    \"testsuites\": [\n"
+             "        {\n"
+             "            \"name\": \"test_list_test/test\",\n"
+             "            \"tests\": 4,\n"
+             "            \"testsuite\": [\n"
+             "                { \"name\": \"case/0\", \"file\": \"file.cpp\", "
+             "\"line\": 42 },\n"
+             "                { \"name\": \"case/1\", \"file\": \"file.cpp\", "
+             "\"line\": 42 },\n"
+             "                { \"name\": \"case/2\", \"file\": \"file.cpp\", "
+             "\"line\": 42 },\n"
+             "                { \"name\": \"case/3\", \"file\": \"file.cpp\", "
+             "\"line\": 42 }\n"
+             "            ]\n"
+             "        }\n"
+             "    ]\n"
+             "}";
   }
   return 0;
 }

+ 34 - 1
Tests/RunCMake/GoogleTest/test_list_test.cpp

@@ -1,3 +1,4 @@
+#include <fstream>
 #include <iostream>
 #include <string>
 
@@ -7,12 +8,44 @@ int main(int argc, char** argv)
   // it only requires that we produces output in the expected format when
   // invoked with --gtest_list_tests. Thus, we fake that here. This allows us
   // to test the module without actually needing Google Test.
-  if (argc > 1 && std::string(argv[1]) == "--gtest_list_tests") {
+  if (argc > 2 && std::string(argv[1]) == "--gtest_list_tests" &&
+      std::string(argv[2]).find("--gtest_output=json:") != std::string::npos) {
     std::cout << "test_list_test/test.\n";
     std::cout << "  case/0  # GetParam() = \"semicolon;\"\n";
     std::cout << "  case/1  # GetParam() = 'osb['\n";
     std::cout << "  case/2  # GetParam() = 'csb]'\n";
     std::cout << "  case/3  # GetParam() = 'S p a c e s'\n";
+
+    std::string output_param(argv[2]);
+    std::string::size_type split = output_param.find(":");
+    std::string filepath = output_param.substr(split + 1);
+    // The full file path is passed
+    std::ofstream ostrm(filepath.c_str(), std::ios::binary);
+    if (!ostrm) {
+      std::cerr << "Failed to create file: " << filepath.c_str() << std::endl;
+      return 1;
+    }
+    ostrm
+      << "{\n"
+         "    \"tests\": 4,\n"
+         "    \"name\": \"AllTests\",\n"
+         "    \"testsuites\": [\n"
+         "        {\n"
+         "            \"name\": \"test_list_test/test\",\n"
+         "            \"tests\": 4,\n"
+         "            \"testsuite\": [\n"
+         "                { \"name\": \"case/0\", \"value_param\": "
+         "\"\\\"semicolon;\\\"\", \"file\": \"file.cpp\", \"line\": 42 },\n"
+         "                { \"name\": \"case/1\", \"value_param\": "
+         "\"'osb['\", \"file\": \"file.cpp\", \"line\": 42 },\n"
+         "                { \"name\": \"case/2\", \"value_param\": "
+         "\"'csb]'\", \"file\": \"file.cpp\", \"line\": 42 },\n"
+         "                { \"name\": \"case/3\", \"value_param\": \"'S p a c "
+         "e s'\", \"file\": \"file.cpp\", \"line\": 42 }\n"
+         "            ]\n"
+         "        }\n"
+         "    ]\n"
+         "}";
   }
   return 0;
 }

+ 21 - 4
Tests/RunCMake/GoogleTest/test_workdir.cpp

@@ -19,13 +19,30 @@ int main(int argc, char** argv)
   // the current directory. If we are not handling spaces in the
   // working directory correctly, the files we expect won't exist.
 
-  if (argc > 1 && std::string(argv[1]) == "--gtest_list_tests") {
-    std::ifstream inFile("test_list_output.txt");
-    if (!inFile) {
+  if (argc > 2 && std::string(argv[1]) == "--gtest_list_tests" &&
+      std::string(argv[2]).find("--gtest_output=json:") != std::string::npos) {
+    std::ifstream inTestListFile("test_list_output.txt");
+    if (!inTestListFile) {
       std::cout << "ERROR: Failed to open test_list_output.txt" << std::endl;
       return 1;
     }
-    std::cout << inFile.rdbuf();
+    std::cout << inTestListFile.rdbuf();
+
+    std::ifstream inJsonFile("test_list_output.json");
+    if (!inJsonFile) {
+      std::cout << "ERROR: Failed to open test_list_output.json" << std::endl;
+      return 1;
+    }
+    std::string output_param(argv[2]);
+    std::string::size_type split = output_param.find(":");
+    std::string filepath = output_param.substr(split + 1);
+    // The full file path is passed
+    std::ofstream ostrm(filepath.c_str(), std::ios::binary);
+    if (!ostrm) {
+      std::cerr << "Failed to create file: " << filepath.c_str() << std::endl;
+      return 1;
+    }
+    ostrm << inJsonFile.rdbuf();
     return 0;
   }
 

+ 32 - 1
Tests/RunCMake/GoogleTest/timeout_test.cpp

@@ -1,3 +1,5 @@
+#define _CRT_SECURE_NO_WARNINGS // work around 'fopen' deprecation on WIN32
+
 #if defined(_WIN32)
 #  include <windows.h>
 #else
@@ -23,9 +25,38 @@ int main(int argc, char** argv)
   // it only requires that we produce output in the expected format when
   // invoked with --gtest_list_tests. Thus, we fake that here. This allows us
   // to test the module without actually needing Google Test.
-  if (argc > 1 && std::string(argv[1]) == "--gtest_list_tests") {
+  if (argc > 1 && std::string(argv[1]) == "--gtest_list_tests" &&
+      std::string(argv[2]).find("--gtest_output=json:") != std::string::npos) {
     printf("timeout.\n  case\n");
     fflush(stdout);
+
+    std::string output_param(argv[2]);
+    std::string::size_type split = output_param.find(":");
+    std::string filepath = output_param.substr(split + 1);
+    // The full file path is passed
+    FILE* ofile = fopen(filepath.c_str(), "wb");
+
+    if (!ofile) {
+      fprintf(stderr, "Failed to create file: %s\n", filepath.c_str());
+      return 1;
+    }
+    std::string json_output = "{\n"
+                              "      \"tests\": 1,\n"
+                              "      \"name\": \"AllTests\",\n"
+                              "      \"testsuites\": [\n"
+                              "          {\n"
+                              "              \"name\": \"timeout\",\n"
+                              "              \"tests\": 1,\n"
+                              "              \"testsuite\": [\n"
+                              "                  { \"name\": \"case\", "
+                              "\"file\": \"file.cpp\", \"line\": 42 }\n"
+                              "              ]\n"
+                              "          }\n"
+                              "      ]\n"
+                              "  }";
+    fprintf(ofile, "%s", json_output.c_str());
+    fclose(ofile);
+
 #ifdef discoverySleepSec
     sleepFor(discoverySleepSec);
 #endif

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

@@ -19,6 +19,43 @@ int main(int argc, char** argv)
       std::cout << "  case/0  # GetParam() = 42" << std::endl;
       std::cout << "  case/1  # GetParam() = \"string\"" << std::endl;
       std::cout << "  case/2  # GetParam() = \"path/like\"" << std::endl;
+    } else if (param.find("--gtest_output=json:") != 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);
+      if (!ostrm) {
+        std::cerr << "Failed to create file: " << filepath.c_str()
+                  << std::endl;
+        return 1;
+      }
+      ostrm
+        << "{\n"
+           "    \"tests\": 4,\n"
+           "    \"name\": \"AllTests\",\n"
+           "    \"testsuites\": [\n"
+           "        {\n"
+           "            \"name\": \"GoogleTestXML\",\n"
+           "            \"tests\": 1,\n"
+           "            \"testsuite\": [\n"
+           "                { \"name\": \"Foo\", \"file\": \"file.cpp\", "
+           "\"line\": 42 }\n"
+           "            ]\n"
+           "        },\n"
+           "        {\n"
+           "            \"name\": \"GoogleTestXMLSpecial\\/cases\",\n"
+           "            \"tests\": 3,\n"
+           "            \"testsuite\": [\n"
+           "                { \"name\": \"case\\/0\", \"value_param\": "
+           "\"42\", \"file\": \"file2.cpp\", \"line\": 47 },\n"
+           "                { \"name\": \"case\\/1\", \"value_param\": "
+           "\"\\\"string\\\"\", \"file\": \"file2.cpp\", \"line\": 47 },\n"
+           "                { \"name\": \"case\\/2\", \"value_param\": "
+           "\"\\\"path/like\\\"\", \"file\": \"file2.cpp\", \"line\": 47 }\n"
+           "            ]\n"
+           "        }\n"
+           "    ]\n"
+           "}";
     } else if (param.find("--gtest_output=xml:") != std::string::npos) {
       std::string::size_type split = param.find(":");
       std::string filepath = param.substr(split + 1);