From aca0b57db43b3de1848773625164199ae6e369de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Ferenc=20Nagy-Egri?= Date: Sun, 4 Jan 2026 18:13:50 +0100 Subject: [PATCH] GoogleTest: Add DISCOVERY_USE_TEST_ENV --- Modules/GoogleTest.cmake | 82 +++++++++++++++++++++++++++- Tests/GoogleTest/Test/CMakeLists.txt | 13 +++++ Tests/GoogleTest/Test/main5.cxx | 13 +++++ 3 files changed, 107 insertions(+), 1 deletion(-) diff --git a/Modules/GoogleTest.cmake b/Modules/GoogleTest.cmake index 3e67346530..757a0ef921 100644 --- a/Modules/GoogleTest.cmake +++ b/Modules/GoogleTest.cmake @@ -181,6 +181,7 @@ same as the Google Test name (i.e. ``suite.testcase``); see also [XML_OUTPUT_DIR dir] [DISCOVERY_MODE ] [DISCOVERY_EXTRA_ARGS args...] + [DISCOVERY_USE_TEST_ENV] ) .. versionadded:: 3.10 @@ -337,6 +338,59 @@ same as the Google Test name (i.e. ``suite.testcase``); see also pass lists as property values. Do note that doing so the chosen character cannot appear in any of the property values. + ``DISCOVERY_USE_TEST_ENV`` + .. versionadded:: 4.3 + + By default, only test execution uses the environment described by the + :prop_test:`ENVIRONMENT` and :prop_test`ENVIRONMENT_MODIFICATION` test + properties. This option tells the module to also apply these properties to + the discovery step, regardless of ``DISCOVERY_MODE``. + +Examples +======== + +``DISCOVERY_USE_TEST_ENV`` allows describing test environments where even +discovery depends on environment modifications. Combining it with +``LIST_SEPARATOR`` enables even more complex scenarios requiring multiple +environment modfiications. This can be crucial on Windows platforms, where +RPATH isn't available to tell the linker about DLL dependency locations in +the build tree. Without modifying the environment, one has to resort to +other methods, typically copying DLLs next to test executables with +``add_custom_command()``s. As mentioned in ``DISCOVERY_MODE``, setting the +discovery mode to ``PRE_TEST`` helps in some cases, but is of limited use. +Setting the environment as flexibly, robustly in a multi-config generator +friendly way from a CMake preset JSON or an external script is challanging. + +.. code-block:: cmake + + # Test executable depending on two SHARED libraries + # even during discovery. + add_executable(my_test my_test.cpp) + + target_link_libraries(my_test PRIVATE + shared_lib1 + shared_lib2 + GTest::gtest + GTest::gtest_main + ) + + set(LABELS label1 label2) + set(ENVIRONMENT_MODIFICATION + PATH=path_list_prepend:$ + PATH=path_list_prepend:$ + ) + set(LIST_SEPARATOR ",") + list(JOIN LABELS ${LIST_SEPARATOR} LABELS) + list(JOIN ENVIRONMENT_MODIFICATION ${LIST_SEPARATOR} ENVIRONMENT_MODIFICATION) + + gtest_discover_tests(my_test + LIST_SEPARATOR ${LIST_SEPARATOR} + PROPERTIES + LABELS "${LABELS}" + ENVIRONMENT_MODIFICATION "${ENVIRONMENT_MODIFICATION}" + DISCOVERY_USE_TEST_ENV ON + ) + #]=======================================================================] # Save project's policies @@ -562,6 +616,7 @@ function(gtest_discover_tests target) set(options NO_PRETTY_TYPES NO_PRETTY_VALUES + DISCOVERY_USE_TEST_ENV ) set(oneValueArgs TEST_PREFIX @@ -694,10 +749,34 @@ function(gtest_discover_tests target) endif() if(arg_DISCOVERY_MODE STREQUAL "POST_BUILD") + if(arg_DISCOVERY_USE_TEST_ENV) + foreach(PROP ENVIRONMENT ENVIRONMENT_MODIFICATION) + list(FIND arg_PROPERTIES ${PROP} ${PROP}_INDEX) + if(NOT ${PROP}_INDEX EQUAL -1) + math(EXPR VALUE_INDEX "${${PROP}_INDEX} + 1") + list(GET arg_PROPERTIES ${VALUE_INDEX} ${PROP}_VALUE) + string(REPLACE "${arg_LIST_SEPARATOR}" ";" ${PROP}_VALUE "${${PROP}_VALUE}") + endif() + endforeach() + # if discovery should use test env, then one of the relevant test props + # must have been set, otherwise TEST_ENV_FRAGMENT will be ill-formed. + if(NOT ENVIRONMENT_INDEX EQUAL -1 OR NOT ENVIRONMENT_MODIFICATION_INDEX EQUAL -1) + foreach(ENV_MOD_VALUE IN LISTS ENVIRONMENT_MODIFICATION_VALUE) + list(APPEND ENVIRONMENT_MODIFICATION_FRAGMENT "--modify" "${ENV_MOD_VALUE}") + endforeach() + set(TEST_ENV_FRAGMENT + -E env + ${ENVIRONMENT_VALUE} + ${ENVIRONMENT_MODIFICATION_FRAGMENT} + -- + "${CMAKE_COMMAND}" + ) + endif() + endif() add_custom_command( TARGET ${target} POST_BUILD BYPRODUCTS "${ctest_tests_file}" - COMMAND "${CMAKE_COMMAND}" + COMMAND "${CMAKE_COMMAND}" ${TEST_ENV_FRAGMENT} -D "TEST_TARGET=${target}" -D "TEST_EXECUTABLE=$" -D "TEST_EXECUTOR=${test_executor}" @@ -709,6 +788,7 @@ function(gtest_discover_tests target) -D "TEST_FILTER=${arg_TEST_FILTER}" -D "NO_PRETTY_TYPES=${arg_NO_PRETTY_TYPES}" -D "NO_PRETTY_VALUES=${arg_NO_PRETTY_VALUES}" + -D "DISCOVERY_USE_TEST_ENV=${arg_DISCOVERY_USE_TEST_ENV}" -D "LIST_SEPARATOR=${arg_LIST_SEPARATOR}" -D "TEST_LIST=${arg_TEST_LIST}" -D "CTEST_FILE=${ctest_tests_file}" diff --git a/Tests/GoogleTest/Test/CMakeLists.txt b/Tests/GoogleTest/Test/CMakeLists.txt index d7a21d3192..d410404462 100644 --- a/Tests/GoogleTest/Test/CMakeLists.txt +++ b/Tests/GoogleTest/Test/CMakeLists.txt @@ -126,3 +126,16 @@ gtest_discover_tests(test_gtest5 PROPERTIES ENVIRONMENT "${ENVIRONMENT}" ) + +# Check if DISCOVERY_USE_TEST_ENV really inherits relevant properties +add_executable(test_gtest5env main5.cxx) +target_link_libraries(test_gtest5env GTest::GTest) +target_compile_definitions(test_gtest5env PRIVATE DISCOVERY_USE_TEST_ENV) +set(ENVIRONMENT VALX=1) +set(ENVIRONMENT_MODIFICATION VALY=set:2) +gtest_discover_tests(test_gtest5env + PROPERTIES + ENVIRONMENT "${ENVIRONMENT}" + ENVIRONMENT_MODIFICATION "${ENVIRONMENT_MODIFICATION}" + DISCOVERY_USE_TEST_ENV ON +) diff --git a/Tests/GoogleTest/Test/main5.cxx b/Tests/GoogleTest/Test/main5.cxx index 24880b82c4..20b697950e 100644 --- a/Tests/GoogleTest/Test/main5.cxx +++ b/Tests/GoogleTest/Test/main5.cxx @@ -7,3 +7,16 @@ TEST(GoogleTest, Add) EXPECT_EQ(std::atoi(std::getenv("VALX")) + std::atoi(std::getenv("VALY")), 3); } + +#ifdef DISCOVERY_USE_TEST_ENV +int main(int argc, char* argv[]) +{ + if (std::getenv("VALX") == nullptr || std::getenv("VALY") == nullptr) { + std::cerr << "VALX or VALY not present in main." << std::endl; + return -1; + } + + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} +#endif