From dea0484e4d3b6a2c50055c24c5617cd662a50c5f Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 26 Apr 2023 07:13:23 -0700 Subject: Use Abseil Flag public API for flag parsing. This change brings InitGoogleTest semantic in accordance with the official documentation: only GoogleTest flags are removed from argc/argv. The rest of the flags remains in place. We do nothing special for flags with unrecognized gunit_/gtest_ prefix and we do not report them. PiperOrigin-RevId: 527257221 Change-Id: Ibb29a1bda1a44251a4ee579c0fb5bbdfd9965c21 --- googletest/src/gtest.cc | 69 +++++++++++++++++++++++++++++--------- googletest/test/gtest_help_test.py | 11 ------ googletest/test/gtest_unittest.cc | 9 +++++ googletest_deps.bzl | 8 ++--- 4 files changed, 67 insertions(+), 30 deletions(-) diff --git a/googletest/src/gtest.cc b/googletest/src/gtest.cc index 9fdca40..fb7512c 100644 --- a/googletest/src/gtest.cc +++ b/googletest/src/gtest.cc @@ -44,6 +44,7 @@ #include // NOLINT #include #include +#include #include #include #include @@ -140,6 +141,7 @@ #endif #ifdef GTEST_HAS_ABSL +#include "absl/container/flat_hash_set.h" #include "absl/debugging/failure_signal_handler.h" #include "absl/debugging/stacktrace.h" #include "absl/debugging/symbolize.h" @@ -147,6 +149,8 @@ #include "absl/flags/usage.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_replace.h" +#include "absl/strings/string_view.h" +#include "absl/strings/strip.h" #endif // GTEST_HAS_ABSL // Checks builtin compiler feature |x| while avoiding an extra layer of #ifdefs @@ -6686,25 +6690,60 @@ void ParseGoogleTestFlagsOnlyImpl(int* argc, CharType** argv) { } // Parses the command line for Google Test flags, without initializing -// other parts of Google Test. +// other parts of Google Test. This function updates argc and argv by removing +// flags that are known to GoogleTest (including other user flags defined using +// ABSL_FLAG if GoogleTest is built with GTEST_USE_ABSL). Other arguments +// remain in place. Unrecognized flags are not reported and do not cause the +// program to exit. void ParseGoogleTestFlagsOnly(int* argc, char** argv) { #ifdef GTEST_HAS_ABSL - if (*argc > 0) { - // absl::ParseCommandLine() requires *argc > 0. - auto positional_args = absl::flags_internal::ParseCommandLineImpl( - *argc, argv, absl::flags_internal::UsageFlagsAction::kHandleUsage, - absl::flags_internal::OnUndefinedFlag::kReportUndefined); - // Any command-line positional arguments not part of any command-line flag - // (or arguments to a flag) are copied back out to argv, with the program - // invocation name at position 0, and argc is resized. This includes - // positional arguments after the flag-terminating delimiter '--'. - // See https://abseil.io/docs/cpp/guides/flags. - std::copy(positional_args.begin(), positional_args.end(), argv); - if (static_cast(positional_args.size()) < *argc) { - argv[positional_args.size()] = nullptr; - *argc = static_cast(positional_args.size()); + if (*argc <= 0) return; + + std::vector positional_args; + std::vector unrecognized_flags; + absl::ParseAbseilFlagsOnly(*argc, argv, positional_args, unrecognized_flags); + absl::flat_hash_set unrecognized; + for (const auto& flag : unrecognized_flags) { + unrecognized.insert(flag.flag_name); + } + absl::flat_hash_set positional; + for (const auto& arg : positional_args) { + positional.insert(arg); + } + + int out_pos = 1; + int in_pos = 1; + for (; in_pos < *argc; ++in_pos) { + char* arg = argv[in_pos]; + absl::string_view arg_str(arg); + if (absl::ConsumePrefix(&arg_str, "--")) { + // Flag-like argument. If the flag was unrecognized, keep it. + // If it was a GoogleTest flag, remove it. + if (unrecognized.contains(arg_str)) { + argv[out_pos++] = argv[in_pos]; + continue; + } + } + + if (arg_str.empty()) { + ++in_pos; + break; // '--' indicates that the rest of the arguments are positional + } + + // Probably a positional argument. If it is in fact positional, keep it. + // If it was a value for the flag argument, remove it. + if (positional.contains(arg)) { + argv[out_pos++] = arg; } } + + // The rest are positional args for sure. + while (in_pos < *argc) { + argv[out_pos++] = argv[in_pos++]; + } + + *argc = out_pos; + argv[out_pos] = nullptr; #else ParseGoogleTestFlagsOnlyImpl(argc, argv); #endif diff --git a/googletest/test/gtest_help_test.py b/googletest/test/gtest_help_test.py index 9261b87..85a0c33 100755 --- a/googletest/test/gtest_help_test.py +++ b/googletest/test/gtest_help_test.py @@ -54,7 +54,6 @@ PROGRAM_PATH = gtest_test_utils.GetTestExecutablePath('gtest_help_test_') FLAG_PREFIX = '--gtest_' DEATH_TEST_STYLE_FLAG = FLAG_PREFIX + 'death_test_style' STREAM_RESULT_TO_FLAG = FLAG_PREFIX + 'stream_result_to' -UNKNOWN_GTEST_PREFIXED_FLAG = FLAG_PREFIX + 'unknown_flag_for_testing' LIST_TESTS_FLAG = FLAG_PREFIX + 'list_tests' INTERNAL_FLAG_FOR_TESTING = FLAG_PREFIX + 'internal_flag_for_testing' @@ -177,16 +176,6 @@ class GTestHelpTest(gtest_test_utils.TestCase): def testPrintsHelpWithFullFlag(self): self.TestHelpFlag('--help') - def testPrintsHelpWithUnrecognizedGoogleTestFlag(self): - # The behavior is slightly different when Abseil flags is - # used. Abseil flags rejects all unknown flags, while the builtin - # GTest flags implementation interprets an unknown flag with a - # '--gtest_' prefix as a request for help. - if HAS_ABSL_FLAGS: - self.TestUnknownFlagWithAbseil(UNKNOWN_GTEST_PREFIXED_FLAG) - else: - self.TestHelpFlag(UNKNOWN_GTEST_PREFIXED_FLAG) - def testRunsTestsWithoutHelpFlag(self): """Verifies correct behavior when no help flag is specified. diff --git a/googletest/test/gtest_unittest.cc b/googletest/test/gtest_unittest.cc index 1fde827..9b4b833 100644 --- a/googletest/test/gtest_unittest.cc +++ b/googletest/test/gtest_unittest.cc @@ -6220,6 +6220,15 @@ TEST_F(ParseFlagsTest, AbseilPositionalFlags) { } #endif +TEST_F(ParseFlagsTest, UnrecognizedFlags) { + const char* argv[] = {"foo.exe", "--gtest_filter=abcd", "--other_flag", + nullptr}; + + const char* argv2[] = {"foo.exe", "--other_flag", nullptr}; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::Filter("abcd"), false); +} + #ifdef GTEST_OS_WINDOWS // Tests parsing wide strings. TEST_F(ParseFlagsTest, WideStrings) { diff --git a/googletest_deps.bzl b/googletest_deps.bzl index 7fe8096..65e56ab 100644 --- a/googletest_deps.bzl +++ b/googletest_deps.bzl @@ -15,8 +15,8 @@ def googletest_deps(): if not native.existing_rule("com_google_absl"): http_archive( - name = "com_google_absl", # 2023-02-27T15:50:25Z - sha256 = "baf8e734ac3ce213a889ce7c248b981ee1730e2093e32808e0f0a910dc985f76", - strip_prefix = "abseil-cpp-0c1114c4fb83c844c7fd74708338cca1d3d9b0dc", - urls = ["https://github.com/abseil/abseil-cpp/archive/0c1114c4fb83c844c7fd74708338cca1d3d9b0dc.zip"], + name = "com_google_absl", # 2023-04-06T14:42:25Z + sha256 = "a50452f02402262f9a61a8eedda60f76dda6b9538d36b34b55bce9f74a4d5ef8", + strip_prefix = "abseil-cpp-e73b9139ee9b853a4bd7812531442c138da09084", + urls = ["https://github.com/abseil/abseil-cpp/archive/e73b9139ee9b853a4bd7812531442c138da09084.zip"], ) -- cgit v0.12