From 2c181bd60ada6912414c2f59750a98d3cb3ef567 Mon Sep 17 00:00:00 2001 From: Robert Maynard Date: Thu, 9 Jul 2026 15:45:30 -0400 Subject: [PATCH] GoogleTest: Drop newlines from test names It is possible to have trailing ( and maybe leading ) newlines in a test name. This causes problems with ctest features like `tests-from-file` since they will never be an exact match. So now we sanitize names by removing these newlines --- Modules/GoogleTest/ParseTestList.cmake | 3 ++ Modules/GoogleTestAddTests.cmake | 1 + .../GoogleTest/GoogleTest-test9-stdout.txt | 15 +++++++ Tests/RunCMake/GoogleTest/GoogleTest.cmake | 9 ++++ Tests/RunCMake/GoogleTest/RunCMakeTest.cmake | 17 ++++++++ Tests/RunCMake/GoogleTest/fake_gtest.cpp | 41 +++++++++++++++++++ 6 files changed, 86 insertions(+) create mode 100644 Tests/RunCMake/GoogleTest/GoogleTest-test9-stdout.txt diff --git a/Modules/GoogleTest/ParseTestList.cmake b/Modules/GoogleTest/ParseTestList.cmake index 077e8741ec..8cf97fd464 100644 --- a/Modules/GoogleTest/ParseTestList.cmake +++ b/Modules/GoogleTest/ParseTestList.cmake @@ -84,6 +84,8 @@ macro(get_json_member_with_default json_variable member_name out_variable) if(error_param) # Member not present set(${out_variable} "") + else() + string(STRIP "${${out_variable}}" "${out_variable}") endif() endmacro() @@ -111,6 +113,7 @@ macro(parse_tests_from_json json_file per_test_callback) # 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(STRIP "${current_test_suite}" current_test_suite) string(JSON tests_json GET "${test_suite_json}" "testsuite") # Skip test suites without tests diff --git a/Modules/GoogleTestAddTests.cmake b/Modules/GoogleTestAddTests.cmake index 0774792c90..98a9c7ddfb 100644 --- a/Modules/GoogleTestAddTests.cmake +++ b/Modules/GoogleTestAddTests.cmake @@ -5,6 +5,7 @@ cmake_minimum_required(VERSION 4.2) include("${CMAKE_CURRENT_LIST_DIR}/GoogleTest/ParseTestList.cmake") macro(write_test_to_file) + # Store the gtest test name before messing with these strings set(gtest_name ${current_test_suite}.${current_test_name}) diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-test9-stdout.txt b/Tests/RunCMake/GoogleTest/GoogleTest-test9-stdout.txt new file mode 100644 index 0000000000..3b9fbdd6df --- /dev/null +++ b/Tests/RunCMake/GoogleTest/GoogleTest-test9-stdout.txt @@ -0,0 +1,15 @@ +Test project .* + *Start +[0-9]+: TEST:newline/test\.case/a!9 + *[0-9]+/[0-9]+ +Test.*TEST:newline/test\.case/a!9 .+ +Passed +[0-9\.]+ sec + *Start +[0-9]+: TEST:newline/test\.case/b!9 + *[0-9]+/[0-9]+ +Test.*TEST:newline/test\.case/b!9 .+ +Passed +[0-9\.]+ sec + *Start +[0-9]+: TEST:newline/test\.case/c!9 + *[0-9]+/[0-9]+ +Test.*TEST:newline/test\.case/c!9 .+ +Passed +[0-9\.]+ sec + *Start +[0-9]+: TEST:newline_name/suite\.case/trailing_param!9 + *[0-9]+/[0-9]+ +Test.*TEST:newline_name/suite\.case/trailing_param!9 .+ +Passed +[0-9\.]+ sec + *Start +[0-9]+: TEST:newline_name/suite\.case/leading_param!9 + *[0-9]+/[0-9]+ +Test.*TEST:newline_name/suite\.case/leading_param!9 .+ +Passed +[0-9\.]+ sec + +100% tests passed out of [0-9]+ + +Total Test time \(real\) = +[0-9\.]+ sec diff --git a/Tests/RunCMake/GoogleTest/GoogleTest.cmake b/Tests/RunCMake/GoogleTest/GoogleTest.cmake index 8c33cf666b..ac01fc326e 100644 --- a/Tests/RunCMake/GoogleTest/GoogleTest.cmake +++ b/Tests/RunCMake/GoogleTest/GoogleTest.cmake @@ -83,6 +83,15 @@ gtest_discover_tests( PROPERTIES LABELS TEST8 ) +gtest_discover_tests( + fake_gtest + TEST_PREFIX TEST: + TEST_SUFFIX !9 + TEST_FILTER newline* + EXTRA_ARGS how now "\"brown\" cow" + PROPERTIES LABELS TEST9 +) + add_executable(no_tests_defined no_tests_defined.cpp) xcode_sign_adhoc(no_tests_defined) diff --git a/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake b/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake index a910e1fdcd..6e1a84ac24 100644 --- a/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake +++ b/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake @@ -93,6 +93,23 @@ function(run_GoogleTest DISCOVERY_MODE) --no-label-summary ) + run_cmake_command(GoogleTest-test9 + ${CMAKE_CTEST_COMMAND} + -C Debug + -L TEST9 + --no-label-summary + ) + + file(WRITE "${RunCMake_TEST_BINARY_DIR}/test9-names.txt" + "TEST:newline_name/suite.case/trailing_param!9\n" + ) + run_cmake_command(GoogleTest-test9-from-file + ${CMAKE_CTEST_COMMAND} + -C Debug + --tests-from-file "${RunCMake_TEST_BINARY_DIR}/test9-names.txt" + --no-label-summary + ) + run_cmake_command(GoogleTest-test-missing ${CMAKE_CTEST_COMMAND} -C Debug diff --git a/Tests/RunCMake/GoogleTest/fake_gtest.cpp b/Tests/RunCMake/GoogleTest/fake_gtest.cpp index f034d1adb0..534dfa6095 100644 --- a/Tests/RunCMake/GoogleTest/fake_gtest.cpp +++ b/Tests/RunCMake/GoogleTest/fake_gtest.cpp @@ -29,6 +29,8 @@ int main(int argc, char** argv) !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); + bool add_newline_tests = !is_filtered || + (std::string(argv[3]).find("newline*") != std::string::npos); if (argc > 2 && std::string(argv[1]) == "--gtest_list_tests" && std::string(argv[2]).find("--gtest_output=json:") != std::string::npos) { @@ -239,6 +241,45 @@ int main(int argc, char** argv) tests += 8; } } + if (add_newline_tests) { + std::cout << "newline/test." << std::endl; + std::cout << " case/0 # GetParam() = wrapped_value" << std::endl; + std::cout << " case/1 # GetParam() = wrapped_value" << std::endl; + std::cout << " case/2 # GetParam() = wrapped_value" << std::endl; + std::cout << "newline_name/suite." << std::endl; + std::cout << " case/trailing_param" << std::endl; + std::cout << " case/leading_param" << std::endl; + + if (tests) + ostrm << ","; + ostrm << "\n" + " {\n" + " \"name\": \"newline/test\",\n" + " \"tests\": 3,\n" + " \"testsuite\": [\n" + " { \"name\": \"case/a\", \"value_param\": " + "\"wrapped_value\\n\", \"file\": \"file4.cpp\", \"line\": 1 " + "},\n" + " { \"name\": \"case/b\", \"value_param\": " + "\"\\nwrapped_value\", \"file\": \"file4.cpp\", \"line\": 2 " + "},\n" + " { \"name\": \"case/c\", \"value_param\": " + "\"\\nwrapped_value\\n\", \"file\": \"file4.cpp\", \"line\": 3 " + "}\n" + " ]\n" + " },\n" + " {\n" + " \"name\": \"newline_name/suite\",\n" + " \"tests\": 2,\n" + " \"testsuite\": [\n" + " { \"name\": \"case/trailing_param\\n\", " + "\"file\": \"file5.cpp\", \"line\": 1 },\n" + " { \"name\": \"\\ncase/leading_param\", " + "\"file\": \"file5.cpp\", \"line\": 2 }\n" + " ]\n" + " }"; + tests += 5; + } if (add_value_tests || add_typed_tests || add_dynamic_tests) { char const* both_suite_names[] = { "both_suite", "both/suite", "ns.both/suite",