Merge topic 'gtest_discover_tests_cross_compile_support'

3b4838b57f GoogleTest: Add tests for MultiConfig discovery in PRE_TEST mode
1ba4cb565e GoogleTest: Parameterize tests to check PRE_TEST/POST_BUILD discovery mode
75e82a13db GoogleTest: Add new DISCOVERY_MODE option to gtest_discover_tests
889a7146ff GoogleTestAddTests: Refactor into callable method

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !4078
diff --git a/Modules/GoogleTest.cmake b/Modules/GoogleTest.cmake
index 1d4398e..975ce6c 100644
--- a/Modules/GoogleTest.cmake
+++ b/Modules/GoogleTest.cmake
@@ -152,6 +152,7 @@
                          [TEST_LIST var]
                          [DISCOVERY_TIMEOUT seconds]
                          [XML_OUTPUT_DIR dir]
+                         [DISCOVERY_MODE <POST_BUILD|PRE_TEST>]
     )
 
   ``gtest_discover_tests`` sets up a post-build command on the test executable
@@ -244,6 +245,22 @@
     ``EXTRA_ARGS --gtest_output=xml`` to avoid race conditions writing the
     XML result output when using parallel test execution.
 
+  ``DISCOVERY_MODE``
+    Provides greater control over when ``gtest_discover_tests``performs test
+    discovery. By default, ``POST_BUILD`` sets up a post-build command
+    to perform test discovery at build time. In certain scenarios, like
+    cross-compiling, this ``POST_BUILD`` behavior is not desirable.
+    By contrast, ``PRE_TEST`` delays test discovery until just prior to test
+    execution. This way test discovery occurs in the target environment
+    where the test has a better chance at finding appropriate runtime
+    dependencies.
+
+    ``DISCOVERY_MODE`` defaults to the value of the
+    ``CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE`` variable if it is not
+    passed when calling ``gtest_discover_tests``. This provides a mechanism
+    for globally selecting a preferred test discovery behavior without having
+    to modify each call site.
+
 #]=======================================================================]
 
 # Save project's policies
@@ -376,11 +393,12 @@
 endfunction()
 
 #------------------------------------------------------------------------------
+
 function(gtest_discover_tests TARGET)
   cmake_parse_arguments(
     ""
     "NO_PRETTY_TYPES;NO_PRETTY_VALUES"
-    "TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST;DISCOVERY_TIMEOUT;XML_OUTPUT_DIR"
+    "TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST;DISCOVERY_TIMEOUT;XML_OUTPUT_DIR;DISCOVERY_MODE"
     "EXTRA_ARGS;PROPERTIES"
     ${ARGN}
   )
@@ -394,6 +412,12 @@
   if(NOT _DISCOVERY_TIMEOUT)
     set(_DISCOVERY_TIMEOUT 5)
   endif()
+  if(NOT _DISCOVERY_MODE)
+    if(NOT CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE)
+      set(CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE "POST_BUILD")
+    endif()
+    set(_DISCOVERY_MODE ${CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE})
+  endif()
 
   get_property(
     has_counter
@@ -425,35 +449,86 @@
     TARGET ${TARGET}
     PROPERTY CROSSCOMPILING_EMULATOR
   )
-  add_custom_command(
-    TARGET ${TARGET} POST_BUILD
-    BYPRODUCTS "${ctest_tests_file}"
-    COMMAND "${CMAKE_COMMAND}"
-            -D "TEST_TARGET=${TARGET}"
-            -D "TEST_EXECUTABLE=$<TARGET_FILE:${TARGET}>"
-            -D "TEST_EXECUTOR=${crosscompiling_emulator}"
-            -D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}"
-            -D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}"
-            -D "TEST_PROPERTIES=${_PROPERTIES}"
-            -D "TEST_PREFIX=${_TEST_PREFIX}"
-            -D "TEST_SUFFIX=${_TEST_SUFFIX}"
-            -D "NO_PRETTY_TYPES=${_NO_PRETTY_TYPES}"
-            -D "NO_PRETTY_VALUES=${_NO_PRETTY_VALUES}"
-            -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
-  )
 
-  file(WRITE "${ctest_include_file}"
-    "if(EXISTS \"${ctest_tests_file}\")\n"
-    "  include(\"${ctest_tests_file}\")\n"
-    "else()\n"
-    "  add_test(${TARGET}_NOT_BUILT ${TARGET}_NOT_BUILT)\n"
-    "endif()\n"
-  )
+  if(_DISCOVERY_MODE STREQUAL "POST_BUILD")
+    add_custom_command(
+      TARGET ${TARGET} POST_BUILD
+      BYPRODUCTS "${ctest_tests_file}"
+      COMMAND "${CMAKE_COMMAND}"
+              -D "TEST_TARGET=${TARGET}"
+              -D "TEST_EXECUTABLE=$<TARGET_FILE:${TARGET}>"
+              -D "TEST_EXECUTOR=${crosscompiling_emulator}"
+              -D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}"
+              -D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}"
+              -D "TEST_PROPERTIES=${_PROPERTIES}"
+              -D "TEST_PREFIX=${_TEST_PREFIX}"
+              -D "TEST_SUFFIX=${_TEST_SUFFIX}"
+              -D "NO_PRETTY_TYPES=${_NO_PRETTY_TYPES}"
+              -D "NO_PRETTY_VALUES=${_NO_PRETTY_VALUES}"
+              -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
+    )
+
+    file(WRITE "${ctest_include_file}"
+      "if(EXISTS \"${ctest_tests_file}\")\n"
+      "  include(\"${ctest_tests_file}\")\n"
+      "else()\n"
+      "  add_test(${TARGET}_NOT_BUILT ${TARGET}_NOT_BUILT)\n"
+      "endif()\n"
+    )
+  elseif(_DISCOVERY_MODE STREQUAL "PRE_TEST")
+
+    get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL
+        PROPERTY GENERATOR_IS_MULTI_CONFIG
+    )
+
+    if(GENERATOR_IS_MULTI_CONFIG)
+      set(ctest_tests_file "${ctest_file_base}_tests-$<CONFIG>.cmake")
+    endif()
+
+    string(CONCAT ctest_include_content
+      "if(EXISTS \"$<TARGET_FILE:${TARGET}>\")"                                    "\n"
+      "  if(\"$<TARGET_FILE:${TARGET}>\" IS_NEWER_THAN \"${ctest_tests_file}\")"   "\n"
+      "    include(GoogleTestAddTests)"                                            "\n"
+      "    gtest_discover_tests_impl("                                             "\n"
+      "      TEST_EXECUTABLE"        " [==[" "$<TARGET_FILE:${TARGET}>"   "]==]"   "\n"
+      "      TEST_EXECUTOR"          " [==[" "${crosscompiling_emulator}" "]==]"   "\n"
+      "      TEST_WORKING_DIR"       " [==[" "${_WORKING_DIRECTORY}"      "]==]"   "\n"
+      "      TEST_EXTRA_ARGS"        " [==[" "${_EXTRA_ARGS}"             "]==]"   "\n"
+      "      TEST_PROPERTIES"        " [==[" "${_PROPERTIES}"             "]==]"   "\n"
+      "      TEST_PREFIX"            " [==[" "${_TEST_PREFIX}"            "]==]"   "\n"
+      "      TEST_SUFFIX"            " [==[" "${_TEST_SUFFIX}"            "]==]"   "\n"
+      "      NO_PRETTY_TYPES"        " [==[" "${_NO_PRETTY_TYPES}"        "]==]"   "\n"
+      "      NO_PRETTY_VALUES"       " [==[" "${_NO_PRETTY_VALUES}"       "]==]"   "\n"
+      "      TEST_LIST"              " [==[" "${_TEST_LIST}"              "]==]"   "\n"
+      "      CTEST_FILE"             " [==[" "${ctest_tests_file}"        "]==]"   "\n"
+      "      TEST_DISCOVERY_TIMEOUT" " [==[" "${_DISCOVERY_TIMEOUT}"      "]==]"   "\n"
+      "      TEST_XML_OUTPUT_DIR"    " [==[" "${_XML_OUTPUT_DIR}"         "]==]"   "\n"
+      "    )"                                                                      "\n"
+      "  endif()"                                                                  "\n"
+      "  include(\"${ctest_tests_file}\")"                                         "\n"
+      "else()"                                                                     "\n"
+      "  add_test(${TARGET}_NOT_BUILT ${TARGET}_NOT_BUILT)"                        "\n"
+      "endif()"                                                                    "\n"
+    )
+
+    if(GENERATOR_IS_MULTI_CONFIG)
+      foreach(_config ${CMAKE_CONFIGURATION_TYPES})
+        file(GENERATE OUTPUT "${ctest_file_base}_include-${_config}.cmake" CONTENT "${ctest_include_content}" CONDITION $<CONFIG:${_config}>)
+      endforeach()
+      file(WRITE "${ctest_include_file}" "include(\"${ctest_file_base}_include-\${CTEST_CONFIGURATION_TYPE}.cmake\")")
+    else()
+      file(GENERATE OUTPUT "${ctest_file_base}_include.cmake" CONTENT "${ctest_include_content}")
+      file(WRITE "${ctest_include_file}" "include(\"${ctest_file_base}_include.cmake\")")
+    endif()
+
+  else()
+    message(SEND_ERROR "Unknown DISCOVERY_MODE: ${_DISCOVERY_MODE}")
+  endif()
 
   # Add discovered tests to directory TEST_INCLUDE_FILES
   set_property(DIRECTORY
diff --git a/Modules/GoogleTestAddTests.cmake b/Modules/GoogleTestAddTests.cmake
index 753319f..65af4c2 100644
--- a/Modules/GoogleTestAddTests.cmake
+++ b/Modules/GoogleTestAddTests.cmake
@@ -3,21 +3,12 @@
 
 cmake_minimum_required(VERSION ${CMAKE_VERSION})
 
-set(prefix "${TEST_PREFIX}")
-set(suffix "${TEST_SUFFIX}")
-set(extra_args ${TEST_EXTRA_ARGS})
-set(properties ${TEST_PROPERTIES})
-set(script)
-set(suite)
-set(tests)
-set(tests_buffer)
-
-# Overwrite possibly existing ${CTEST_FILE} with empty file
+# Overwrite possibly existing ${_CTEST_FILE} with empty file
 set(flush_tests_MODE WRITE)
 
-# Flushes script to ${CTEST_FILE}
+# Flushes script to ${_CTEST_FILE}
 macro(flush_script)
-  file(${flush_tests_MODE} "${CTEST_FILE}" "${script}")
+  file(${flush_tests_MODE} "${_CTEST_FILE}" "${script}")
   set(flush_tests_MODE APPEND)
 
   set(script "")
@@ -48,98 +39,137 @@
   unset(_script_len)
 endmacro()
 
-# Run test executable to get list of available tests
-if(NOT EXISTS "${TEST_EXECUTABLE}")
-  message(FATAL_ERROR
-    "Specified test executable does not exist.\n"
-    "  Path: '${TEST_EXECUTABLE}'"
-  )
-endif()
-execute_process(
-  COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" --gtest_list_tests
-  WORKING_DIRECTORY "${TEST_WORKING_DIR}"
-  TIMEOUT ${TEST_DISCOVERY_TIMEOUT}
-  OUTPUT_VARIABLE output
-  RESULT_VARIABLE result
-)
-if(NOT ${result} EQUAL 0)
-  string(REPLACE "\n" "\n    " output "${output}")
-  message(FATAL_ERROR
-    "Error running test executable.\n"
-    "  Path: '${TEST_EXECUTABLE}'\n"
-    "  Result: ${result}\n"
-    "  Output:\n"
-    "    ${output}\n"
-  )
-endif()
+function(gtest_discover_tests_impl)
 
-string(REPLACE "\n" ";" output "${output}")
+  cmake_parse_arguments(
+    ""
+    ""
+    "NO_PRETTY_TYPES;NO_PRETTY_VALUES;TEST_EXECUTABLE;TEST_EXECUTOR;TEST_WORKING_DIR;TEST_PREFIX;TEST_SUFFIX;TEST_LIST;CTEST_FILE;TEST_DISCOVERY_TIMEOUT;TEST_XML_OUTPUT_DIR"
+    "TEST_EXTRA_ARGS;TEST_PROPERTIES"
+    ${ARGN}
+  )
 
-# 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 "#" AND NOT NO_PRETTY_TYPES)
-        string(REGEX REPLACE "/[0-9]\\.+ +#.*= +" "/" pretty_suite "${line}")
+  set(prefix "${_TEST_PREFIX}")
+  set(suffix "${_TEST_SUFFIX}")
+  set(extra_args ${_TEST_EXTRA_ARGS})
+  set(properties ${_TEST_PROPERTIES})
+  set(script)
+  set(suite)
+  set(tests)
+  set(tests_buffer)
+
+  # Run test executable to get list of available tests
+  if(NOT EXISTS "${_TEST_EXECUTABLE}")
+    message(FATAL_ERROR
+      "Specified test executable does not exist.\n"
+      "  Path: '${_TEST_EXECUTABLE}'"
+    )
+  endif()
+  execute_process(
+    COMMAND ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" --gtest_list_tests
+    WORKING_DIRECTORY "${_TEST_WORKING_DIR}"
+    TIMEOUT ${_TEST_DISCOVERY_TIMEOUT}
+    OUTPUT_VARIABLE output
+    RESULT_VARIABLE result
+  )
+  if(NOT ${result} EQUAL 0)
+    string(REPLACE "\n" "\n    " output "${output}")
+    message(FATAL_ERROR
+      "Error running test executable.\n"
+      "  Path: '${_TEST_EXECUTABLE}'\n"
+      "  Result: ${result}\n"
+      "  Output:\n"
+      "    ${output}\n"
+    )
+  endif()
+
+  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 "#" AND NOT _NO_PRETTY_TYPES)
+          string(REGEX REPLACE "/[0-9]\\.+ +#.*= +" "/" pretty_suite "${line}")
+        else()
+          set(pretty_suite "${suite}")
+        endif()
+        string(REGEX REPLACE "^DISABLED_" "" pretty_suite "${pretty_suite}")
       else()
-        set(pretty_suite "${suite}")
-      endif()
-      string(REGEX REPLACE "^DISABLED_" "" pretty_suite "${pretty_suite}")
-    else()
-      # Test name; strip spaces and comments to get just the name...
-      string(REGEX REPLACE " +" "" test "${line}")
-      if(test MATCHES "#" AND NOT 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 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}"
-        ${TEST_EXECUTOR}
-        "${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")
+        # Test name; strip spaces and comments to get just the name...
+        string(REGEX REPLACE " +" "" test "${line}")
+        if(test MATCHES "#" AND NOT _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 "${_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}"
+          ${_TEST_EXECUTOR}
+          "${_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")
+          add_command(set_tests_properties
+            "${prefix}${pretty_suite}.${pretty_test}${suffix}"
+            PROPERTIES DISABLED TRUE
+          )
+        endif()
         add_command(set_tests_properties
           "${prefix}${pretty_suite}.${pretty_test}${suffix}"
-          PROPERTIES DISABLED TRUE
+          PROPERTIES
+          WORKING_DIRECTORY "${_TEST_WORKING_DIR}"
+          ${properties}
         )
-      endif()
-      add_command(set_tests_properties
-        "${prefix}${pretty_suite}.${pretty_test}${suffix}"
-        PROPERTIES
-        WORKING_DIRECTORY "${TEST_WORKING_DIR}"
-        ${properties}
-      )
-      list(APPEND tests_buffer "${prefix}${pretty_suite}.${pretty_test}${suffix}")
-      list(LENGTH tests_buffer tests_buffer_length)
-      if(${tests_buffer_length} GREATER "250")
-        flush_tests_buffer()
+        list(APPEND tests_buffer "${prefix}${pretty_suite}.${pretty_test}${suffix}")
+        list(LENGTH tests_buffer tests_buffer_length)
+        if(${tests_buffer_length} GREATER "250")
+          flush_tests_buffer()
+        endif()
       endif()
     endif()
-  endif()
-endforeach()
+  endforeach()
 
 
-# Create a list of all discovered tests, which users may use to e.g. set
-# properties on the tests
-flush_tests_buffer()
-add_command(set ${TEST_LIST} ${tests})
+  # Create a list of all discovered tests, which users may use to e.g. set
+  # properties on the tests
+  flush_tests_buffer()
+  add_command(set ${_TEST_LIST} ${tests})
 
-# Write CTest script
-flush_script()
+  # Write CTest script
+  flush_script()
+
+endfunction()
+
+if(CMAKE_SCRIPT_MODE_FILE)
+  gtest_discover_tests_impl(
+    NO_PRETTY_TYPES ${NO_PRETTY_TYPES}
+    NO_PRETTY_VALUES ${NO_PRETTY_VALUES}
+    TEST_EXECUTABLE ${TEST_EXECUTABLE}
+    TEST_EXECUTOR ${TEST_EXECUTOR}
+    TEST_WORKING_DIR ${TEST_WORKING_DIR}
+    TEST_PREFIX ${TEST_PREFIX}
+    TEST_SUFFIX ${TEST_SUFFIX}
+    TEST_LIST ${TEST_LIST}
+    CTEST_FILE ${CTEST_FILE}
+    TEST_DISCOVERY_TIMEOUT ${TEST_DISCOVERY_TIMEOUT}
+    TEST_XML_OUTPUT_DIR ${TEST_XML_OUTPUT_DIR}
+    TEST_EXTRA_ARGS ${TEST_EXTRA_ARGS}
+    TEST_PROPERTIES ${TEST_PROPERTIES}
+  )
+endif()
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-configuration-debug-stdout.txt b/Tests/RunCMake/GoogleTest/GoogleTest-configuration-debug-stdout.txt
new file mode 100644
index 0000000..1f5d1a5
--- /dev/null
+++ b/Tests/RunCMake/GoogleTest/GoogleTest-configuration-debug-stdout.txt
@@ -0,0 +1,5 @@
+Test project .*GoogleTest-discovery-multi-config
+[ \t]*Test #[0-9]+: configuration\.case_release \(Disabled\)
+[ \t]*Test #[0-9]+: configuration\.case_debug
++
+Total Tests: 2
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-configuration-release-stdout.txt b/Tests/RunCMake/GoogleTest/GoogleTest-configuration-release-stdout.txt
new file mode 100644
index 0000000..4f91664
--- /dev/null
+++ b/Tests/RunCMake/GoogleTest/GoogleTest-configuration-release-stdout.txt
@@ -0,0 +1,5 @@
+Test project .*GoogleTest-discovery-multi-config
+[ \t]*Test #[0-9]+: configuration\.case_release
+[ \t]*Test #[0-9]+: configuration\.case_debug \(Disabled\)
++
+Total Tests: 2
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-discovery-timeout-build-result.txt b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-POST_BUILD-timeout-build-result.txt
similarity index 100%
rename from Tests/RunCMake/GoogleTest/GoogleTest-discovery-timeout-build-result.txt
rename to Tests/RunCMake/GoogleTest/GoogleTest-discovery-POST_BUILD-timeout-build-result.txt
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-discovery-timeout-build-stdout.txt b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-POST_BUILD-timeout-build-stdout.txt
similarity index 100%
rename from Tests/RunCMake/GoogleTest/GoogleTest-discovery-timeout-build-stdout.txt
rename to Tests/RunCMake/GoogleTest/GoogleTest-discovery-POST_BUILD-timeout-build-stdout.txt
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-discovery-timeout-test-result.txt b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-POST_BUILD-timeout-test-result.txt
similarity index 100%
rename from Tests/RunCMake/GoogleTest/GoogleTest-discovery-timeout-test-result.txt
rename to Tests/RunCMake/GoogleTest/GoogleTest-discovery-POST_BUILD-timeout-test-result.txt
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-discovery-timeout-test-stderr.txt b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-POST_BUILD-timeout-test-stderr.txt
similarity index 100%
rename from Tests/RunCMake/GoogleTest/GoogleTest-discovery-timeout-test-stderr.txt
rename to Tests/RunCMake/GoogleTest/GoogleTest-discovery-POST_BUILD-timeout-test-stderr.txt
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-discovery-timeout-test-stdout.txt b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-POST_BUILD-timeout-test-stdout.txt
similarity index 93%
rename from Tests/RunCMake/GoogleTest/GoogleTest-discovery-timeout-test-stdout.txt
rename to Tests/RunCMake/GoogleTest/GoogleTest-discovery-POST_BUILD-timeout-test-stdout.txt
index d4c4e7b..d9de3f8 100644
--- a/Tests/RunCMake/GoogleTest/GoogleTest-discovery-timeout-test-stdout.txt
+++ b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-POST_BUILD-timeout-test-stdout.txt
@@ -1,4 +1,4 @@
-Test project .*GoogleTest-build
+Test project .*GoogleTest-discovery-timeout
 [ \t]*Start [0-9]+: discovery_timeout_test_NOT_BUILT
 Could not find executable discovery_timeout_test_NOT_BUILT
 Looked in the following places:
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-discovery-PRE_TEST-timeout-test-stderr.txt b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-PRE_TEST-timeout-test-stderr.txt
new file mode 100644
index 0000000..75afe4a
--- /dev/null
+++ b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-PRE_TEST-timeout-test-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at .*GoogleTestAddTests.cmake:[0-9]+ \(message\):
+[ \t]*Error running test executable.
++
+[ \t]*Path: '.*discovery_timeout_test(\.exe)?'
+[ \t]*Result: Process terminated due to timeout
+[ \t]*Output:
+[ \t]*timeout.
+[ \t]*case
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-discovery-PRE_TEST-timeout-test-stdout.txt b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-PRE_TEST-timeout-test-stdout.txt
new file mode 100644
index 0000000..d65061f
--- /dev/null
+++ b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-PRE_TEST-timeout-test-stdout.txt
@@ -0,0 +1 @@
+Test project .*GoogleTest-discovery-timeout
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest.cmake b/Tests/RunCMake/GoogleTest/GoogleTest.cmake
index 31808c6..4bc6b9d 100644
--- a/Tests/RunCMake/GoogleTest/GoogleTest.cmake
+++ b/Tests/RunCMake/GoogleTest/GoogleTest.cmake
@@ -49,11 +49,3 @@
   DISCOVERY_TIMEOUT 20
   PROPERTIES TIMEOUT 2
 )
-
-add_executable(discovery_timeout_test timeout_test.cpp)
-target_compile_definitions(discovery_timeout_test PRIVATE discoverySleepSec=10)
-gtest_discover_tests(
-  discovery_timeout_test
-  TEST_PREFIX discovery_
-  DISCOVERY_TIMEOUT 2
-)
diff --git a/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryMultiConfig.cmake b/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryMultiConfig.cmake
new file mode 100644
index 0000000..1919dc1
--- /dev/null
+++ b/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryMultiConfig.cmake
@@ -0,0 +1,14 @@
+project(test_include_dirs)
+include(CTest)
+include(GoogleTest)
+
+enable_testing()
+
+add_executable(configuration_gtest configuration_gtest.cpp)
+target_compile_definitions(configuration_gtest PRIVATE $<$<CONFIG:Debug>:DEBUG=1>)
+
+gtest_discover_tests(
+  configuration_gtest
+  PROPERTIES LABELS CONFIG
+  DISCOVERY_MODE PRE_TEST
+)
diff --git a/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryTimeout.cmake b/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryTimeout.cmake
new file mode 100644
index 0000000..7398faf
--- /dev/null
+++ b/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryTimeout.cmake
@@ -0,0 +1,14 @@
+project(test_include_dirs)
+include(CTest)
+include(GoogleTest)
+
+enable_testing()
+
+add_executable(discovery_timeout_test timeout_test.cpp)
+target_compile_definitions(discovery_timeout_test PRIVATE discoverySleepSec=10)
+gtest_discover_tests(
+  discovery_timeout_test
+  TEST_PREFIX discovery_
+  DISCOVERY_TIMEOUT 2
+  DISCOVERY_MODE ${DISCOVERY_MODE}
+)
diff --git a/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake b/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake
index 8070512..6b9d458 100644
--- a/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake
+++ b/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake
@@ -1,6 +1,6 @@
 include(RunCMake)
 
-function(run_GoogleTest)
+function(run_GoogleTest DISCOVERY_MODE)
   # Use a single build tree for a few tests without cleaning.
   set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/GoogleTest-build)
   set(RunCMake_TEST_NO_CLEAN 1)
@@ -10,7 +10,7 @@
   file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
   file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
 
-  run_cmake(GoogleTest)
+  run_cmake_with_options(GoogleTest -DCMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE=${DISCOVERY_MODE})
 
   run_cmake_command(GoogleTest-build
     ${CMAKE_COMMAND}
@@ -26,15 +26,6 @@
     --target property_timeout_test
   )
 
-  set(RunCMake_TEST_OUTPUT_MERGE 1)
-  run_cmake_command(GoogleTest-discovery-timeout-build
-    ${CMAKE_COMMAND}
-    --build .
-    --config Debug
-    --target discovery_timeout_test
-  )
-  set(RunCMake_TEST_OUTPUT_MERGE 0)
-
   run_cmake_command(GoogleTest-test1
     ${CMAKE_CTEST_COMMAND}
     -C Debug
@@ -69,16 +60,9 @@
     -R property_timeout\\.case_with_discovery
     --no-label-summary
   )
-
-  run_cmake_command(GoogleTest-discovery-timeout-test
-    ${CMAKE_CTEST_COMMAND}
-    -C Debug
-    -R discovery_timeout_test
-    --no-label-summary
-  )
 endfunction()
 
-function(run_GoogleTestXML)
+function(run_GoogleTestXML DISCOVERY_MODE)
   # 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)
@@ -88,7 +72,7 @@
   file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
   file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
 
-  run_cmake(GoogleTestXML)
+  run_cmake_with_options(GoogleTestXML -DCMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE=${DISCOVERY_MODE})
 
   run_cmake_command(GoogleTestXML-discovery
   ${CMAKE_COMMAND}
@@ -105,5 +89,80 @@
   )
 endfunction()
 
-run_GoogleTest()
-run_GoogleTestXML()
+function(run_GoogleTest_discovery_timeout DISCOVERY_MODE)
+  # Use a single build tree for a few tests without cleaning.
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/GoogleTest-discovery-timeout)
+  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_with_options(GoogleTestDiscoveryTimeout -DDISCOVERY_MODE=${DISCOVERY_MODE})
+
+  set(RunCMake_TEST_OUTPUT_MERGE 1)
+  run_cmake_command(GoogleTest-discovery-${DISCOVERY_MODE}-timeout-build
+    ${CMAKE_COMMAND}
+    --build .
+    --config Debug
+    --target discovery_timeout_test
+  )
+  set(RunCMake_TEST_OUTPUT_MERGE 0)
+
+  run_cmake_command(GoogleTest-discovery-${DISCOVERY_MODE}-timeout-test
+    ${CMAKE_CTEST_COMMAND}
+    -C Debug
+    -R discovery_timeout_test
+    --no-label-sumary
+  )
+endfunction()
+
+function(run_GoogleTest_discovery_multi_config)
+  # Use a single build tree for a few tests without cleaning.
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/GoogleTest-discovery-multi-config)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
+  file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+
+  run_cmake(GoogleTestDiscoveryMultiConfig)
+
+  run_cmake_command(GoogleTest-build-release
+    ${CMAKE_COMMAND}
+    --build .
+    --config Release
+    --target configuration_gtest
+  )
+  run_cmake_command(GoogleTest-build-debug
+    ${CMAKE_COMMAND}
+    --build .
+    --config Debug
+    --target configuration_gtest
+  )
+  run_cmake_command(GoogleTest-configuration-release
+    ${CMAKE_CTEST_COMMAND}
+    -C Release
+    -L CONFIG
+    -N
+  )
+  run_cmake_command(GoogleTest-configuration-debug
+    ${CMAKE_CTEST_COMMAND}
+    -C Debug
+    -L CONFIG
+    -N
+  )
+
+endfunction()
+
+foreach(DISCOVERY_MODE POST_BUILD PRE_TEST)
+  message("Testing ${DISCOVERY_MODE} discovery mode via CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE global override...")
+  run_GoogleTest(${DISCOVERY_MODE})
+  run_GoogleTestXML(${DISCOVERY_MODE})
+  message("Testing ${DISCOVERY_MODE} discovery mode via DISCOVERY_MODE option...")
+  run_GoogleTest_discovery_timeout(${DISCOVERY_MODE})
+endforeach()
+
+if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  message("Testing PRE_TEST discovery multi configuration...")
+  run_GoogleTest_discovery_multi_config()
+endif()
diff --git a/Tests/RunCMake/GoogleTest/configuration_gtest.cpp b/Tests/RunCMake/GoogleTest/configuration_gtest.cpp
new file mode 100644
index 0000000..3cbb134
--- /dev/null
+++ b/Tests/RunCMake/GoogleTest/configuration_gtest.cpp
@@ -0,0 +1,23 @@
+#include <iostream>
+#include <string>
+
+int main(int argc, char** argv)
+{
+  // Note: GoogleTest.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 && std::string(argv[1]) == "--gtest_list_tests") {
+    std::cout << "configuration." << std::endl;
+#ifdef DEBUG
+    std::cout << "  DISABLED_case_release" << std::endl;
+    std::cout << "  case_debug" << std::endl;
+#else
+    std::cout << "  case_release" << std::endl;
+    std::cout << "  DISABLED_case_debug" << std::endl;
+#endif
+    return 0;
+  }
+
+  return 1;
+}
diff --git a/Tests/RunCMake/GoogleTest/timeout_test.cpp b/Tests/RunCMake/GoogleTest/timeout_test.cpp
index b8ad055..5506269 100644
--- a/Tests/RunCMake/GoogleTest/timeout_test.cpp
+++ b/Tests/RunCMake/GoogleTest/timeout_test.cpp
@@ -4,9 +4,10 @@
 #  include <unistd.h>
 #endif
 
-#include <iostream>
 #include <string>
 
+#include <stdio.h>
+
 void sleepFor(unsigned seconds)
 {
 #if defined(_WIN32)
@@ -23,8 +24,8 @@
   // 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") {
-    std::cout << "timeout." << std::endl;
-    std::cout << "  case" << std::endl;
+    printf("timeout.\n  case\n");
+    fflush(stdout);
 #ifdef discoverySleepSec
     sleepFor(discoverySleepSec);
 #endif