From 32e94ae25842ed2104868e1fb8a822cafac3c18a Mon Sep 17 00:00:00 2001 From: Aviad Ariel Date: Wed, 28 May 2025 00:43:30 -0700 Subject: [PATCH] gmock verbosity from env --- googlemock/CMakeLists.txt | 2 + .../include/gmock/internal/gmock-port.h | 26 +++- googlemock/src/gmock.cc | 117 ++++++++++----- googlemock/test/BUILD.bazel | 7 + googlemock/test/gmock_env_test.cc | 142 ++++++++++++++++++ 5 files changed, 255 insertions(+), 39 deletions(-) create mode 100644 googlemock/test/gmock_env_test.cc diff --git a/googlemock/CMakeLists.txt b/googlemock/CMakeLists.txt index 99b2411f3..a77b4c040 100644 --- a/googlemock/CMakeLists.txt +++ b/googlemock/CMakeLists.txt @@ -153,6 +153,8 @@ if (gmock_build_tests) cxx_test(gmock-spec-builders_test gmock_main) cxx_test(gmock_link_test gmock_main test/gmock_link2_test.cc) cxx_test(gmock_test gmock_main) + cxx_test(gmock_env_test gmock_main) + if (DEFINED GTEST_HAS_PTHREAD) cxx_test(gmock_stress_test gmock) diff --git a/googlemock/include/gmock/internal/gmock-port.h b/googlemock/include/gmock/internal/gmock-port.h index 42d36d2f1..eacf4bce4 100644 --- a/googlemock/include/gmock/internal/gmock-port.h +++ b/googlemock/include/gmock/internal/gmock-port.h @@ -57,6 +57,23 @@ #include "gmock/internal/custom/gmock-port.h" #include "gtest/internal/gtest-port.h" +namespace testing { +namespace internal { + +// Returns the name of the environment variable corresponding to the +// given flag. For example, FlagToEnvVar("foo") will return +// "GMOCK_FOO". +GTEST_API_ std::string FlagToEnvVar(const char* flag); + +// Reads and returns the string environment variable corresponding to +// the given flag; if it's not set, returns default_value. +GTEST_API_ const char* StringFromGMockEnv( + const char* flag, + const char* default_value); + +} // namespace internal +} // namespace testing + #if defined(GTEST_HAS_ABSL) && !defined(GTEST_NO_ABSL_FLAGS) #include "absl/flags/declare.h" #include "absl/flags/flag.h" @@ -68,6 +85,9 @@ #error "At least Visual C++ 2015 (14.0) is required to compile Google Mock." #endif +// Defines the flag prefixes for Google Mock. +#define GMOCK_FLAG_PREFIX_ "gmock_" + // Macro for referencing flags. This is public as we want the user to // use this syntax to reference Google Mock flags. #define GMOCK_FLAG_NAME_(name) gmock_##name @@ -96,7 +116,7 @@ #define GMOCK_FLAG_SET(name, value) \ (void)(::absl::SetFlag(&GMOCK_FLAG(name), value)) -#else // defined(GTEST_HAS_ABSL) && !defined(GTEST_NO_ABSL_FLAGS) +#else // defined(GTEST_HAS_ABSL) && !defined(GTEST_NO_ABSL_FLAGS) // Macros for defining flags. #define GMOCK_DEFINE_bool_(name, default_val, doc) \ @@ -135,6 +155,6 @@ #define GMOCK_FLAG_GET(name) ::testing::GMOCK_FLAG(name) #define GMOCK_FLAG_SET(name, value) (void)(::testing::GMOCK_FLAG(name) = value) -#endif // defined(GTEST_HAS_ABSL) && !defined(GTEST_NO_ABSL_FLAGS) +#endif // defined(GTEST_HAS_ABSL) && !defined(GTEST_NO_ABSL_FLAGS) -#endif // GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PORT_H_ +#endif // GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PORT_H_ diff --git a/googlemock/src/gmock.cc b/googlemock/src/gmock.cc index b5e714da7..74fbfac41 100644 --- a/googlemock/src/gmock.cc +++ b/googlemock/src/gmock.cc @@ -33,23 +33,59 @@ #include "gmock/internal/gmock-port.h" -GMOCK_DEFINE_bool_(catch_leaked_mocks, true, - "true if and only if Google Mock should report leaked " - "mock objects as failures."); +namespace testing { +namespace internal { -GMOCK_DEFINE_string_(verbose, testing::internal::kWarningVerbosity, - "Controls how verbose Google Mock's output is." - " Valid values:\n" - " info - prints all messages.\n" - " warning - prints warnings and errors.\n" - " error - prints errors only."); +// Returns the name of the environment variable corresponding to the +// given flag. For example, FlagToEnvVar("foo") will return +// "GMOCK_FOO". +std::string FlagToEnvVar(const char* flag) { + const std::string full_flag = std::string(GMOCK_FLAG_PREFIX_) + flag; -GMOCK_DEFINE_int32_(default_mock_behavior, 1, - "Controls the default behavior of mocks." - " Valid values:\n" - " 0 - by default, mocks act as NiceMocks.\n" - " 1 - by default, mocks act as NaggyMocks.\n" - " 2 - by default, mocks act as StrictMocks."); + std::string env_var; + for (size_t i = 0; i != full_flag.length(); i++) { + env_var += toupper(full_flag.c_str()[i]); + } + + return env_var; +} + +// Reads and returns the string environment variable corresponding to +// the given flag; if it's not set, returns default_value. +const char* StringFromGMockEnv(const char* flag, const char* default_value) { + const std::string env_var = FlagToEnvVar(flag); + const char* const value = posix::GetEnv(env_var.c_str()); + return value == nullptr ? default_value : value; +} + +} // namespace internal +} // namespace testing + +GMOCK_DEFINE_bool_( + catch_leaked_mocks, + true, + "true if and only if Google Mock should report leaked " + "mock objects as failures."); + +GMOCK_DEFINE_string_( + verbose, + testing::internal::StringFromGMockEnv( + "verbose", + testing::internal::kWarningVerbosity), + "Controls how verbose Google Mock's output is." + " Valid values:\n" + " info - prints all messages.\n" + " warning - prints warnings and errors.\n" + " error - prints errors only."); + +GMOCK_DEFINE_int32_( + default_mock_behavior, + 1, + "Controls the default behavior of mocks." + " Valid values:\n" + " 0 - by default, mocks act as NiceMocks.\n" + " 1 - by default, mocks act as NaggyMocks.\n" + " 2 - by default, mocks act as StrictMocks."); namespace testing { namespace internal { @@ -59,16 +95,20 @@ namespace internal { // "=value" part can be omitted. // // Returns the value of the flag, or NULL if the parsing failed. -static const char* ParseGoogleMockFlagValue(const char* str, - const char* flag_name, - bool def_optional) { +static const char* ParseGoogleMockFlagValue( + const char* str, + const char* flag_name, + bool def_optional) { // str and flag must not be NULL. - if (str == nullptr || flag_name == nullptr) return nullptr; + if (str == nullptr || flag_name == nullptr) + return nullptr; // The flag must start with "--gmock_". - const std::string flag_name_str = std::string("--gmock_") + flag_name; + const std::string flag_name_str = + std::string("--") + GMOCK_FLAG_PREFIX_ + flag_name; const size_t flag_name_len = flag_name_str.length(); - if (strncmp(str, flag_name_str.c_str(), flag_name_len) != 0) return nullptr; + if (strncmp(str, flag_name_str.c_str(), flag_name_len) != 0) + return nullptr; // Skips the flag name. const char* flag_end = str + flag_name_len; @@ -81,7 +121,8 @@ static const char* ParseGoogleMockFlagValue(const char* str, // If def_optional is true and there are more characters after the // flag name, or if def_optional is false, there must be a '=' after // the flag name. - if (flag_end[0] != '=') return nullptr; + if (flag_end[0] != '=') + return nullptr; // Returns the string after "=". return flag_end + 1; @@ -92,13 +133,14 @@ static const char* ParseGoogleMockFlagValue(const char* str, // // On success, stores the value of the flag in *value, and returns // true. On failure, returns false without changing *value. -static bool ParseGoogleMockFlag(const char* str, const char* flag_name, - bool* value) { +static bool +ParseGoogleMockFlag(const char* str, const char* flag_name, bool* value) { // Gets the value of the flag as a string. const char* const value_str = ParseGoogleMockFlagValue(str, flag_name, true); // Aborts if the parsing failed. - if (value_str == nullptr) return false; + if (value_str == nullptr) + return false; // Converts the string value to a bool. *value = !(*value_str == '0' || *value_str == 'f' || *value_str == 'F'); @@ -111,30 +153,32 @@ static bool ParseGoogleMockFlag(const char* str, const char* flag_name, // On success, stores the value of the flag in *value, and returns // true. On failure, returns false without changing *value. template -static bool ParseGoogleMockFlag(const char* str, const char* flag_name, - String* value) { +static bool +ParseGoogleMockFlag(const char* str, const char* flag_name, String* value) { // Gets the value of the flag as a string. const char* const value_str = ParseGoogleMockFlagValue(str, flag_name, false); // Aborts if the parsing failed. - if (value_str == nullptr) return false; + if (value_str == nullptr) + return false; // Sets *value to the value of the flag. *value = value_str; return true; } -static bool ParseGoogleMockFlag(const char* str, const char* flag_name, - int32_t* value) { +static bool +ParseGoogleMockFlag(const char* str, const char* flag_name, int32_t* value) { // Gets the value of the flag as a string. const char* const value_str = ParseGoogleMockFlagValue(str, flag_name, true); // Aborts if the parsing failed. - if (value_str == nullptr) return false; + if (value_str == nullptr) + return false; // Sets *value to the value of the flag. - return ParseInt32(Message() << "The value of flag --" << flag_name, value_str, - value); + return ParseInt32( + Message() << "The value of flag --" << flag_name, value_str, value); } // The internal implementation of InitGoogleMock(). @@ -146,7 +190,8 @@ void InitGoogleMockImpl(int* argc, CharType** argv) { // Makes sure Google Test is initialized. InitGoogleTest() is // idempotent, so it's fine if the user has already called it. InitGoogleTest(argc, argv); - if (*argc <= 0) return; + if (*argc <= 0) + return; for (int i = 1; i != *argc; i++) { const std::string arg_string = StreamableToString(argv[i]); @@ -187,7 +232,7 @@ void InitGoogleMockImpl(int* argc, CharType** argv) { } } -} // namespace internal +} // namespace internal // Initializes Google Mock. This must be called before running the // tests. In particular, it parses a command line for the flags that @@ -222,4 +267,4 @@ GTEST_API_ void InitGoogleMock() { internal::InitGoogleMockImpl(&argc, argv); } -} // namespace testing +} // namespace testing diff --git a/googlemock/test/BUILD.bazel b/googlemock/test/BUILD.bazel index d4297c80f..1d119b322 100644 --- a/googlemock/test/BUILD.bazel +++ b/googlemock/test/BUILD.bazel @@ -116,3 +116,10 @@ cc_test( srcs = ["gmock_test.cc"], deps = ["//:gtest_main"], ) + +cc_test( + name = "gmock_env_test", + size = "small", + srcs = ["gmock_env_test.cc"], + deps = ["//:gtest_main"], +) diff --git a/googlemock/test/gmock_env_test.cc b/googlemock/test/gmock_env_test.cc new file mode 100644 index 000000000..0d3ef6ce1 --- /dev/null +++ b/googlemock/test/gmock_env_test.cc @@ -0,0 +1,142 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Tests for the StringFromGMockEnv() function that enables setting +// verbosity level from the GMOCK_VERBOSE environment variable. + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +// The StringFromGMockEnv() function is declared in gmock-port.h and implemented +// in gmock.cc. We test it indirectly through the GMOCK_FLAG(verbose) which is +// initialized with this function. + +namespace { + +// Function to set environment variable for testing +void SetEnv(const char* name, const char* value) { +#ifdef _WIN32 + _putenv_s(name, value); +#else + setenv(name, value, 1); +#endif +} + +// Function to unset environment variable for testing +void UnsetEnv(const char* name) { +#ifdef _WIN32 + _putenv_s(name, ""); +#else + unsetenv(name); +#endif +} + +// Test fixture for environment variable tests +class GMockVerbosityEnvTest : public ::testing::Test { + protected: + void SetUp() override { + // Save the original verbose flag value + original_verbose_ = GMOCK_FLAG_GET(verbose); + + // Unset the GMOCK_VERBOSE environment variable to start with a clean state + UnsetEnv("GMOCK_VERBOSE"); + } + + void TearDown() override { + // Restore the original verbose flag value + GMOCK_FLAG_SET(verbose, original_verbose_); + + // Clean up the environment variable + UnsetEnv("GMOCK_VERBOSE"); + } + + std::string original_verbose_; +}; + +// Tests that when GMOCK_VERBOSE is not set, the default value is used +TEST_F(GMockVerbosityEnvTest, DefaultValueWhenEnvNotSet) { + // Re-initialize Google Mock to pick up the environment variable + // This will call InitGoogleMock which will use StringFromGMockEnv() + int argc = 1; + const char* argv[] = {"test_program"}; + testing::InitGoogleMock(&argc, const_cast(argv)); + + // The default value should be "warning" + EXPECT_EQ(GMOCK_FLAG_GET(verbose), "warning"); +} + +// Tests that when GMOCK_VERBOSE is set to "info", that value is used +TEST_F(GMockVerbosityEnvTest, InfoValueWhenEnvSet) { + // Set the environment variable + SetEnv("GMOCK_VERBOSE", "info"); + + // Re-initialize Google Mock to pick up the environment variable + int argc = 1; + const char* argv[] = {"test_program"}; + testing::InitGoogleMock(&argc, const_cast(argv)); + + // The value should be "info" as set in the environment + EXPECT_EQ(GMOCK_FLAG_GET(verbose), "info"); +} + +// Tests that when GMOCK_VERBOSE is set to "error", that value is used +TEST_F(GMockVerbosityEnvTest, ErrorValueWhenEnvSet) { + // Set the environment variable + SetEnv("GMOCK_VERBOSE", "error"); + + // Re-initialize Google Mock to pick up the environment variable + int argc = 1; + const char* argv[] = {"test_program"}; + testing::InitGoogleMock(&argc, const_cast(argv)); + + // The value should be "error" as set in the environment + EXPECT_EQ(GMOCK_FLAG_GET(verbose), "error"); +} + +// Tests that command line flags take precedence over environment variables +TEST_F(GMockVerbosityEnvTest, CommandLineFlagOverridesEnv) { + // Set the environment variable + SetEnv("GMOCK_VERBOSE", "info"); + + // Set up command line arguments with the --gmock_verbose flag + int argc = 2; + const char* argv[] = {"test_program", "--gmock_verbose=error"}; + testing::InitGoogleMock(&argc, const_cast(argv)); + + // The value should be "error" from the command line, not "info" from the + // environment + EXPECT_EQ(GMOCK_FLAG_GET(verbose), "error"); +} + +} // namespace + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}