From e79506fadf8b9dd2d8c603e6a61af5b06eea25af Mon Sep 17 00:00:00 2001 From: Tyler Yankee Date: Tue, 14 Jul 2026 09:31:17 -0400 Subject: [PATCH 1/2] GoogleTest: Fix gtest_discover_tests race on POST_BUILD of multiple targets Incorporate the logic from commit 6680df042e (GoogleTest: Avoid POST_BUILD race condition for gtest_discover_tests(), 2025-10-26, v4.2.0-rc2~15^2) into the JSON test discovery mechanism introduced in commit 1cdceae8e3 (GoogleTest: Parse discovered test list from JSON output if supported, 2025-05-02, v4.2.0-rc1~533^2~2). Add a regression test. Fixes: #27939, #27319, #27972 --- Modules/GoogleTest.cmake | 1 + ...Test-discovery-post-build-race-check.cmake | 18 +++++++++++++ .../GoogleTestDiscoveryPostBuildRace.cmake | 27 +++++++++++++++++++ Tests/RunCMake/GoogleTest/RunCMakeTest.cmake | 26 ++++++++++++++++++ 4 files changed, 72 insertions(+) create mode 100644 Tests/RunCMake/GoogleTest/GoogleTest-discovery-post-build-race-check.cmake create mode 100644 Tests/RunCMake/GoogleTest/GoogleTestDiscoveryPostBuildRace.cmake diff --git a/Modules/GoogleTest.cmake b/Modules/GoogleTest.cmake index 1a27f002cc..ed11c195c5 100644 --- a/Modules/GoogleTest.cmake +++ b/Modules/GoogleTest.cmake @@ -733,6 +733,7 @@ function(gtest_discover_tests target) " NOT \"${ctest_tests_file}\" IS_NEWER_THAN \"\${CMAKE_CURRENT_LIST_FILE}\")\n" " include(\"${CMAKE_ROOT}/Modules/GoogleTestAddTests.cmake\")" "\n" " gtest_discover_tests_impl(" "\n" + " TEST_TARGET" " [==[${target}]==]" "\n" " TEST_EXECUTABLE" " [==[$]==]" "\n" " TEST_EXECUTOR" " [==[${test_executor}]==]" "\n" " TEST_WORKING_DIR" " [==[${arg_WORKING_DIRECTORY}]==]" "\n" diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-discovery-post-build-race-check.cmake b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-post-build-race-check.cmake new file mode 100644 index 0000000000..47ac65b085 --- /dev/null +++ b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-post-build-race-check.cmake @@ -0,0 +1,18 @@ +if(NOT DEFINED binary_dir) + message(FATAL_ERROR "binary_dir is required") +endif() +if(NOT DEFINED target_count) + message(FATAL_ERROR "target_count is required") +endif() + +file(GLOB discovery_json_files "${binary_dir}/cmake_test_discovery_*.json") +list(LENGTH discovery_json_files discovery_json_count) + +if(NOT discovery_json_count EQUAL target_count) + message(FATAL_ERROR + "Expected ${target_count} unique discovery JSON files, found ${discovery_json_count}. " + "Likely shared discovery scratch file collision in POST_BUILD discovery." + ) +endif() + +message(STATUS "Found ${discovery_json_count}/${target_count} unique discovery JSON files") diff --git a/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryPostBuildRace.cmake b/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryPostBuildRace.cmake new file mode 100644 index 0000000000..bc5696f65e --- /dev/null +++ b/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryPostBuildRace.cmake @@ -0,0 +1,27 @@ +enable_language(CXX) +include(GoogleTest) + +enable_testing() + +include(xcode_sign_adhoc.cmake) + +set(race_target_count 16) +set(race_targets) + +foreach(i RANGE 1 ${race_target_count}) + set(t race_fake_gtest_${i}) + add_executable(${t} fake_gtest.cpp) + xcode_sign_adhoc(${t}) + # Avoid timeouts on low-resource hardware at high parallelism. + gtest_discover_tests(${t} DISCOVERY_MODE POST_BUILD DISCOVERY_TIMEOUT 30) + list(APPEND race_targets ${t}) +endforeach() + +add_custom_target(check_discovery_json_files + COMMAND ${CMAKE_COMMAND} + -Dbinary_dir=${CMAKE_CURRENT_BINARY_DIR} + -Dtarget_count=${race_target_count} + -P ${CMAKE_CURRENT_LIST_DIR}/GoogleTest-discovery-post-build-race-check.cmake + DEPENDS ${race_targets} + VERBATIM +) diff --git a/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake b/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake index a910e1fdcd..8ebcfb8894 100644 --- a/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake +++ b/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake @@ -357,6 +357,27 @@ function(run_GoogleTest_discovery_flush_script DISCOVERY_MODE) ) endfunction() +function(run_GoogleTest_discovery_post_build_race) + # Use a single build tree for this test without cleaning. + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/GoogleTest-discovery-post-build-race) + 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(GoogleTestDiscoveryPostBuildRace) + + run_cmake_command(GoogleTest-discovery-post-build-race-build + ${CMAKE_COMMAND} + --build . + --config Debug + -j 16 + --target check_discovery_json_files + ) +endfunction() + function(run_GoogleTest_discovery_test_list_scoped DISCOVERY_MODE) # Use a single build tree for a few tests without cleaning. set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/GoogleTest-discovery-test-list-scoped-build) @@ -516,3 +537,8 @@ endblock() run_GoogleTest_LegacyParser() run_GoogleTest_DEF_SOURCE_LINE() + +# This test is meaningful only for generators which support parallel builds. +if (NOT RunCMake_GENERATOR MATCHES "(Borland|NMake|Watcom)") + run_GoogleTest_discovery_post_build_race() +endif() From fc1b218b3715b113b0d382e15c286098a5c6ce5f Mon Sep 17 00:00:00 2001 From: Tyler Yankee Date: Tue, 14 Jul 2026 09:31:17 -0400 Subject: [PATCH 2/2] GoogleTest: Fix gtest_discover_tests race on POST_BUILD of multiple targets Restore the logic from commit 6680df042e (GoogleTest: Avoid POST_BUILD race condition for gtest_discover_tests(), 2025-10-26, v4.2.0-rc2~15^2) that was lost in the refactoring in commit 3748ca9fc7 (GoogleTest: generate single discovery script, 2026-04-08, v4.4.0-rc1~302^2~1). Add a regression test. Fixes: #27939, #27319, #27972 --- Modules/GoogleTest.cmake | 1 + ...Test-discovery-post-build-race-check.cmake | 18 +++++++++++++ .../GoogleTestDiscoveryPostBuildRace.cmake | 27 +++++++++++++++++++ Tests/RunCMake/GoogleTest/RunCMakeTest.cmake | 26 ++++++++++++++++++ 4 files changed, 72 insertions(+) create mode 100644 Tests/RunCMake/GoogleTest/GoogleTest-discovery-post-build-race-check.cmake create mode 100644 Tests/RunCMake/GoogleTest/GoogleTestDiscoveryPostBuildRace.cmake diff --git a/Modules/GoogleTest.cmake b/Modules/GoogleTest.cmake index 68f25206fb..61c2bc2ae6 100644 --- a/Modules/GoogleTest.cmake +++ b/Modules/GoogleTest.cmake @@ -685,6 +685,7 @@ function(gtest_discover_tests target) string(CONCAT discovery_content "include(\"${CMAKE_ROOT}/Modules/GoogleTestAddTests.cmake\")" "\n" "gtest_discover_tests_impl(" "\n" + " TEST_TARGET" " [==[${target}]==]" "\n" " TEST_EXECUTABLE" " [==[$]==]" "\n" " TEST_EXECUTOR" " [==[${test_executor}]==]" "\n" " TEST_WORKING_DIR" " [==[${arg_WORKING_DIRECTORY}]==]" "\n" diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-discovery-post-build-race-check.cmake b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-post-build-race-check.cmake new file mode 100644 index 0000000000..47ac65b085 --- /dev/null +++ b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-post-build-race-check.cmake @@ -0,0 +1,18 @@ +if(NOT DEFINED binary_dir) + message(FATAL_ERROR "binary_dir is required") +endif() +if(NOT DEFINED target_count) + message(FATAL_ERROR "target_count is required") +endif() + +file(GLOB discovery_json_files "${binary_dir}/cmake_test_discovery_*.json") +list(LENGTH discovery_json_files discovery_json_count) + +if(NOT discovery_json_count EQUAL target_count) + message(FATAL_ERROR + "Expected ${target_count} unique discovery JSON files, found ${discovery_json_count}. " + "Likely shared discovery scratch file collision in POST_BUILD discovery." + ) +endif() + +message(STATUS "Found ${discovery_json_count}/${target_count} unique discovery JSON files") diff --git a/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryPostBuildRace.cmake b/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryPostBuildRace.cmake new file mode 100644 index 0000000000..bc5696f65e --- /dev/null +++ b/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryPostBuildRace.cmake @@ -0,0 +1,27 @@ +enable_language(CXX) +include(GoogleTest) + +enable_testing() + +include(xcode_sign_adhoc.cmake) + +set(race_target_count 16) +set(race_targets) + +foreach(i RANGE 1 ${race_target_count}) + set(t race_fake_gtest_${i}) + add_executable(${t} fake_gtest.cpp) + xcode_sign_adhoc(${t}) + # Avoid timeouts on low-resource hardware at high parallelism. + gtest_discover_tests(${t} DISCOVERY_MODE POST_BUILD DISCOVERY_TIMEOUT 30) + list(APPEND race_targets ${t}) +endforeach() + +add_custom_target(check_discovery_json_files + COMMAND ${CMAKE_COMMAND} + -Dbinary_dir=${CMAKE_CURRENT_BINARY_DIR} + -Dtarget_count=${race_target_count} + -P ${CMAKE_CURRENT_LIST_DIR}/GoogleTest-discovery-post-build-race-check.cmake + DEPENDS ${race_targets} + VERBATIM +) diff --git a/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake b/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake index a910e1fdcd..8ebcfb8894 100644 --- a/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake +++ b/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake @@ -357,6 +357,27 @@ function(run_GoogleTest_discovery_flush_script DISCOVERY_MODE) ) endfunction() +function(run_GoogleTest_discovery_post_build_race) + # Use a single build tree for this test without cleaning. + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/GoogleTest-discovery-post-build-race) + 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(GoogleTestDiscoveryPostBuildRace) + + run_cmake_command(GoogleTest-discovery-post-build-race-build + ${CMAKE_COMMAND} + --build . + --config Debug + -j 16 + --target check_discovery_json_files + ) +endfunction() + function(run_GoogleTest_discovery_test_list_scoped DISCOVERY_MODE) # Use a single build tree for a few tests without cleaning. set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/GoogleTest-discovery-test-list-scoped-build) @@ -516,3 +537,8 @@ endblock() run_GoogleTest_LegacyParser() run_GoogleTest_DEF_SOURCE_LINE() + +# This test is meaningful only for generators which support parallel builds. +if (NOT RunCMake_GENERATOR MATCHES "(Borland|NMake|Watcom)") + run_GoogleTest_discovery_post_build_race() +endif()