diff --git a/Makefile.am b/Makefile.am index f70a8085..7a821a02 100644 --- a/Makefile.am +++ b/Makefile.am @@ -128,7 +128,7 @@ test_gmock_printers_test_LDADD = $(GTEST_LIBS) lib/libgmock_main.la TESTS += test/gmock-spec-builders_test check_PROGRAMS += test/gmock-spec-builders_test test_gmock_spec_builders_test_SOURCES = test/gmock-spec-builders_test.cc -test_gmock_spec_builders_test_LDADD = $(GTEST_LIBS) lib/libgmock_main.la +test_gmock_spec_builders_test_LDADD = $(GTEST_LIBS) lib/libgmock.la TESTS += test/gmock_test check_PROGRAMS += test/gmock_test @@ -147,6 +147,11 @@ dist_check_SCRIPTS = # Python modules used by multiple Python tests below. dist_check_SCRIPTS += test/gmock_test_utils.py +check_PROGRAMS += test/gmock_leak_test_ +test_gmock_leak_test__SOURCES = test/gmock_leak_test_.cc +test_gmock_leak_test__LDADD = $(GTEST_LIBS) lib/libgmock_main.la +dist_check_SCRIPTS += test/gmock_leak_test.py + check_PROGRAMS += test/gmock_output_test_ test_gmock_output_test__SOURCES = test/gmock_output_test_.cc test_gmock_output_test__LDADD = $(GTEST_LIBS) lib/libgmock_main.la @@ -155,15 +160,17 @@ EXTRA_DIST += test/gmock_output_test_golden.txt # Enable all the python driven tests when we can run them. if HAVE_PYTHON -TESTS += test/gmock_output_test.py +TESTS += \ + test/gmock_leak_test.py \ + test/gmock_output_test.py endif # Nonstandard package files for distribution. EXTRA_DIST += \ - CHANGES \ - CONTRIBUTORS \ - make/Makefile \ - src/gmock-all.cc + CHANGES \ + CONTRIBUTORS \ + make/Makefile \ + src/gmock-all.cc # Pump scripts for generating Google Mock headers. # TODO(chandlerc@google.com): automate the generation of *.h from *.h.pump. @@ -199,4 +206,5 @@ EXTRA_DIST += \ msvc/gmock_link_test.vcproj \ msvc/gmock_main.vcproj \ msvc/gmock_output_test_.vcproj \ + msvc/gmock-spec-builders_test.vcproj \ msvc/gmock_test.vcproj diff --git a/include/gmock/gmock-spec-builders.h b/include/gmock/gmock-spec-builders.h index 3b4c0853..0fc43d6d 100644 --- a/include/gmock/gmock-spec-builders.h +++ b/include/gmock/gmock-spec-builders.h @@ -246,6 +246,10 @@ class Mock { public: // The following public methods can be called concurrently. + // Tells Google Mock to ignore mock_obj when checking for leaked + // mock objects. + static void AllowLeak(const void* mock_obj); + // Verifies and clears all expectations on the given mock object. // If the expectations aren't satisfied, generates one or more // Google Test non-fatal failures and returns false. @@ -311,6 +315,13 @@ class Mock { static void Register(const void* mock_obj, internal::UntypedFunctionMockerBase* mocker); + // Tells Google Mock where in the source code mock_obj is used in an + // ON_CALL or EXPECT_CALL. In case mock_obj is leaked, this + // information helps the user identify which object it is. + // L < g_gmock_mutex + static void RegisterUseByOnCallOrExpectCall( + const void* mock_obj, const char* file, int line); + // Unregisters a mock method; removes the owning mock object from // the registry when the last mock method associated with it has // been unregistered. This is called only in the destructor of @@ -1081,7 +1092,12 @@ class FunctionMockerBase : public UntypedFunctionMockerBase { // Registers this function mocker and the mock object owning it; // returns a reference to the function mocker object. This is only // called by the ON_CALL() and EXPECT_CALL() macros. + // L < g_gmock_mutex FunctionMocker& RegisterOwner(const void* mock_obj) { + { + MutexLock l(&g_gmock_mutex); + mock_obj_ = mock_obj; + } Mock::Register(mock_obj, this); return *::testing::internal::down_cast*>(this); } @@ -1155,17 +1171,21 @@ class FunctionMockerBase : public UntypedFunctionMockerBase { } // Adds and returns a default action spec for this mock function. + // L < g_gmock_mutex DefaultActionSpec& AddNewDefaultActionSpec( const char* file, int line, const ArgumentMatcherTuple& m) { + Mock::RegisterUseByOnCallOrExpectCall(MockObject(), file, line); default_actions_.push_back(DefaultActionSpec(file, line, m)); return default_actions_.back(); } // Adds and returns an expectation spec for this mock function. + // L < g_gmock_mutex Expectation& AddNewExpectation( const char* file, int line, const ArgumentMatcherTuple& m) { + Mock::RegisterUseByOnCallOrExpectCall(MockObject(), file, line); const linked_ptr > expectation( new Expectation(this, file, line, m)); expectations_.push_back(expectation); @@ -1314,10 +1334,13 @@ class FunctionMockerBase : public UntypedFunctionMockerBase { } } - // Address of the mock object this mock method belongs to. + // Address of the mock object this mock method belongs to. Only + // valid after this mock method has been called or + // ON_CALL/EXPECT_CALL has been invoked on it. const void* mock_obj_; // Protected by g_gmock_mutex. - // Name of the function being mocked. + // Name of the function being mocked. Only valid after this mock + // method has been called. const char* name_; // Protected by g_gmock_mutex. // The current spec (either default action spec or expectation spec) diff --git a/include/gmock/gmock.h b/include/gmock/gmock.h index 41d175f1..22e70287 100644 --- a/include/gmock/gmock.h +++ b/include/gmock/gmock.h @@ -68,6 +68,7 @@ namespace testing { // Declares Google Mock flags that we want a user to use programmatically. +GMOCK_DECLARE_bool_(catch_leaked_mocks); GMOCK_DECLARE_string_(verbose); // Initializes Google Mock. This must be called before running the diff --git a/include/gmock/internal/gmock-port.h b/include/gmock/internal/gmock-port.h index cb352192..b98cb113 100644 --- a/include/gmock/internal/gmock-port.h +++ b/include/gmock/internal/gmock-port.h @@ -242,6 +242,21 @@ typedef ::wstring wstring; typedef ::std::wstring wstring; #endif // GTEST_HAS_GLOBAL_WSTRING +// Prints the file location in the format native to the compiler. +inline void FormatFileLocation(const char* file, int line, ::std::ostream* os) { + if (file == NULL) + file = "unknown file"; + if (line < 0) { + *os << file << ":"; + } else { +#if _MSC_VER + *os << file << "(" << line << "):"; +#else + *os << file << ":" << line << ":"; +#endif + } +} + // INTERNAL IMPLEMENTATION - DO NOT USE. // // GMOCK_CHECK_ is an all mode assert. It aborts the program if the condition @@ -260,26 +275,13 @@ typedef ::std::wstring wstring; class GMockCheckProvider { public: GMockCheckProvider(const char* condition, const char* file, int line) { - FormatFileLocation(file, line); + FormatFileLocation(file, line, &::std::cerr); ::std::cerr << " ERROR: Condition " << condition << " failed. "; } ~GMockCheckProvider() { ::std::cerr << ::std::endl; abort(); } - void FormatFileLocation(const char* file, int line) { - if (file == NULL) - file = "unknown file"; - if (line < 0) { - ::std::cerr << file << ":"; - } else { -#if _MSC_VER - ::std::cerr << file << "(" << line << "):"; -#else - ::std::cerr << file << ":" << line << ":"; -#endif - } - } ::std::ostream& GetStream() { return ::std::cerr; } }; #define GMOCK_CHECK_(condition) \ diff --git a/msvc/gmock-spec-builders_test.vcproj b/msvc/gmock-spec-builders_test.vcproj new file mode 100755 index 00000000..84407420 --- /dev/null +++ b/msvc/gmock-spec-builders_test.vcproj @@ -0,0 +1,205 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/msvc/gmock.sln b/msvc/gmock.sln index aeb6a614..cd1502a9 100644 --- a/msvc/gmock.sln +++ b/msvc/gmock.sln @@ -11,6 +11,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gmock_output_test_", "gmock EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gmock_main", "gmock_main.vcproj", "{E4EF614B-30DF-4954-8C53-580A0BF6B589}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gmock-spec-builders_test", "gmock-spec-builders_test.vcproj", "{46972604-5BE0-4493-BAE3-878DB825FDCB}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -37,6 +39,10 @@ Global {E4EF614B-30DF-4954-8C53-580A0BF6B589}.Debug|Win32.Build.0 = Debug|Win32 {E4EF614B-30DF-4954-8C53-580A0BF6B589}.Release|Win32.ActiveCfg = Release|Win32 {E4EF614B-30DF-4954-8C53-580A0BF6B589}.Release|Win32.Build.0 = Release|Win32 + {46972604-5BE0-4493-BAE3-878DB825FDCB}.Debug|Win32.ActiveCfg = Debug|Win32 + {46972604-5BE0-4493-BAE3-878DB825FDCB}.Debug|Win32.Build.0 = Debug|Win32 + {46972604-5BE0-4493-BAE3-878DB825FDCB}.Release|Win32.ActiveCfg = Release|Win32 + {46972604-5BE0-4493-BAE3-878DB825FDCB}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/msvc/gmock_output_test_.vcproj b/msvc/gmock_output_test_.vcproj index 051dd149..8e846ec1 100644 --- a/msvc/gmock_output_test_.vcproj +++ b/msvc/gmock_output_test_.vcproj @@ -172,8 +172,8 @@ diff --git a/msvc/gmock_test.vcproj b/msvc/gmock_test.vcproj index 135b2e6c..60e1b4bc 100644 --- a/msvc/gmock_test.vcproj +++ b/msvc/gmock_test.vcproj @@ -226,10 +226,6 @@ RelativePath="..\test\gmock-printers_test.cc" > - - diff --git a/src/gmock-spec-builders.cc b/src/gmock-spec-builders.cc index 353bb2df..2bb72954 100644 --- a/src/gmock-spec-builders.cc +++ b/src/gmock-spec-builders.cc @@ -36,9 +36,17 @@ #include +#include +#include // NOLINT +#include #include +#include #include +#if GTEST_OS_CYGWIN || GTEST_OS_LINUX || GTEST_OS_MAC +#include // NOLINT +#endif + namespace testing { namespace internal { @@ -148,10 +156,77 @@ void ReportUninterestingCall(CallReaction reaction, const string& msg) { namespace { typedef std::set FunctionMockers; -typedef std::map MockObjectRegistry; -// Maps a mock object to the set of mock methods it owns. Protected -// by g_gmock_mutex. +// The current state of a mock object. Such information is needed for +// detecting leaked mock objects and explicitly verifying a mock's +// expectations. +struct MockObjectState { + MockObjectState() + : first_used_file(NULL), first_used_line(-1), leakable(false) {} + + // Where in the source file an ON_CALL or EXPECT_CALL is first + // invoked on this mock object. + const char* first_used_file; + int first_used_line; + bool leakable; // true iff it's OK to leak the object. + FunctionMockers function_mockers; // All registered methods of the object. +}; + +// A global registry holding the state of all mock objects that are +// alive. A mock object is added to this registry the first time +// Mock::AllowLeak(), ON_CALL(), or EXPECT_CALL() is called on it. It +// is removed from the registry in the mock object's destructor. +class MockObjectRegistry { + public: + // Maps a mock object (identified by its address) to its state. + typedef std::map StateMap; + + // This destructor will be called when a program exits, after all + // tests in it have been run. By then, there should be no mock + // object alive. Therefore we report any living object as test + // failure, unless the user explicitly asked us to ignore it. + ~MockObjectRegistry() { + using ::std::cout; + + if (!GMOCK_FLAG(catch_leaked_mocks)) + return; + + int leaked_count = 0; + for (StateMap::const_iterator it = states_.begin(); it != states_.end(); + ++it) { + if (it->second.leakable) // The user said it's fine to leak this object. + continue; + + // TODO(wan@google.com): Print the type of the leaked object. + // This can help the user identify the leaked object. + cout << "\n"; + const MockObjectState& state = it->second; + internal::FormatFileLocation( + state.first_used_file, state.first_used_line, &cout); + cout << " ERROR: this mock object should be deleted but never is. " + << "Its address is @" << it->first << "."; + leaked_count++; + } + if (leaked_count > 0) { + cout << "\nERROR: " << leaked_count + << " leaked mock " << (leaked_count == 1 ? "object" : "objects") + << " found at program exit.\n"; + cout.flush(); + ::std::cerr.flush(); + // RUN_ALL_TESTS() has already returned when this destructor is + // called. Therefore we cannot use the normal Google Test + // failure reporting mechanism. + _exit(1); // We cannot call exit() as it is not reentrant and + // may already have been called. + } + } + + StateMap& states() { return states_; } + private: + StateMap states_; +}; + +// Protected by g_gmock_mutex. MockObjectRegistry g_mock_object_registry; // Maps a mock object to the reaction Google Mock should have when an @@ -208,6 +283,14 @@ internal::CallReaction Mock::GetReactionOnUninterestingCalls( internal::WARN : g_uninteresting_call_reaction[mock_obj]; } +// Tells Google Mock to ignore mock_obj when checking for leaked mock +// objects. +// L < g_gmock_mutex +void Mock::AllowLeak(const void* mock_obj) { + internal::MutexLock l(&internal::g_gmock_mutex); + g_mock_object_registry.states()[mock_obj].leakable = true; +} + // Verifies and clears all expectations on the given mock object. If // the expectations aren't satisfied, generates one or more Google // Test non-fatal failures and returns false. @@ -233,7 +316,7 @@ bool Mock::VerifyAndClear(void* mock_obj) { // L >= g_gmock_mutex bool Mock::VerifyAndClearExpectationsLocked(void* mock_obj) { internal::g_gmock_mutex.AssertHeld(); - if (g_mock_object_registry.count(mock_obj) == 0) { + if (g_mock_object_registry.states().count(mock_obj) == 0) { // No EXPECT_CALL() was set on the given mock object. return true; } @@ -241,7 +324,8 @@ bool Mock::VerifyAndClearExpectationsLocked(void* mock_obj) { // Verifies and clears the expectations on each mock method in the // given mock object. bool expectations_met = true; - FunctionMockers& mockers = g_mock_object_registry[mock_obj]; + FunctionMockers& mockers = + g_mock_object_registry.states()[mock_obj].function_mockers; for (FunctionMockers::const_iterator it = mockers.begin(); it != mockers.end(); ++it) { if (!(*it)->VerifyAndClearExpectationsLocked()) { @@ -259,7 +343,21 @@ bool Mock::VerifyAndClearExpectationsLocked(void* mock_obj) { void Mock::Register(const void* mock_obj, internal::UntypedFunctionMockerBase* mocker) { internal::MutexLock l(&internal::g_gmock_mutex); - g_mock_object_registry[mock_obj].insert(mocker); + g_mock_object_registry.states()[mock_obj].function_mockers.insert(mocker); +} + +// Tells Google Mock where in the source code mock_obj is used in an +// ON_CALL or EXPECT_CALL. In case mock_obj is leaked, this +// information helps the user identify which object it is. +// L < g_gmock_mutex +void Mock::RegisterUseByOnCallOrExpectCall( + const void* mock_obj, const char* file, int line) { + internal::MutexLock l(&internal::g_gmock_mutex); + MockObjectState& state = g_mock_object_registry.states()[mock_obj]; + if (state.first_used_file == NULL) { + state.first_used_file = file; + state.first_used_line = line; + } } // Unregisters a mock method; removes the owning mock object from the @@ -269,13 +367,14 @@ void Mock::Register(const void* mock_obj, // L >= g_gmock_mutex void Mock::UnregisterLocked(internal::UntypedFunctionMockerBase* mocker) { internal::g_gmock_mutex.AssertHeld(); - for (MockObjectRegistry::iterator it = g_mock_object_registry.begin(); - it != g_mock_object_registry.end(); ++it) { - FunctionMockers& mockers = it->second; + for (MockObjectRegistry::StateMap::iterator it = + g_mock_object_registry.states().begin(); + it != g_mock_object_registry.states().end(); ++it) { + FunctionMockers& mockers = it->second.function_mockers; if (mockers.erase(mocker) > 0) { // mocker was in mockers and has been just removed. if (mockers.empty()) { - g_mock_object_registry.erase(it); + g_mock_object_registry.states().erase(it); } return; } @@ -287,14 +386,15 @@ void Mock::UnregisterLocked(internal::UntypedFunctionMockerBase* mocker) { void Mock::ClearDefaultActionsLocked(void* mock_obj) { internal::g_gmock_mutex.AssertHeld(); - if (g_mock_object_registry.count(mock_obj) == 0) { + if (g_mock_object_registry.states().count(mock_obj) == 0) { // No ON_CALL() was set on the given mock object. return; } // Clears the default actions for each mock method in the given mock // object. - FunctionMockers& mockers = g_mock_object_registry[mock_obj]; + FunctionMockers& mockers = + g_mock_object_registry.states()[mock_obj].function_mockers; for (FunctionMockers::const_iterator it = mockers.begin(); it != mockers.end(); ++it) { (*it)->ClearDefaultActionsLocked(); diff --git a/src/gmock.cc b/src/gmock.cc index c017917d..dc9d3d22 100644 --- a/src/gmock.cc +++ b/src/gmock.cc @@ -34,6 +34,15 @@ namespace testing { +// TODO(wan@google.com): support using environment variables to +// control the flag values, like what Google Test does. + +// TODO(wan@google.com): change the default value to true after people +// have a chance to fix their leaked mocks. +GMOCK_DEFINE_bool_(catch_leaked_mocks, false, + "true iff Google Mock should report leaked mock objects " + "as failures."); + GMOCK_DEFINE_string_(verbose, internal::kWarningVerbosity, "Controls how verbose Google Mock's output is." " Valid values:\n" @@ -76,6 +85,24 @@ static const char* ParseGoogleMockFlagValue(const char* str, return flag_end + 1; } +// Parses a string for a Google Mock bool flag, in the form of +// "--gmock_flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +static bool ParseGoogleMockBoolFlag(const char* str, const char* flag, + bool* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseGoogleMockFlagValue(str, flag, true); + + // Aborts if the parsing failed. + if (value_str == NULL) return false; + + // Converts the string value to a bool. + *value = !(*value_str == '0' || *value_str == 'f' || *value_str == 'F'); + return true; +} + // Parses a string for a Google Mock string flag, in the form of // "--gmock_flag=value". // @@ -110,7 +137,9 @@ void InitGoogleMockImpl(int* argc, CharType** argv) { const char* const arg = arg_string.c_str(); // Do we see a Google Mock flag? - if (ParseGoogleMockStringFlag(arg, "verbose", &GMOCK_FLAG(verbose))) { + if (ParseGoogleMockBoolFlag(arg, "catch_leaked_mocks", + &GMOCK_FLAG(catch_leaked_mocks)) || + ParseGoogleMockStringFlag(arg, "verbose", &GMOCK_FLAG(verbose))) { // Yes. Shift the remainder of the argv list left by one. Note // that argv has (*argc + 1) elements, the last one always being // NULL. The following loop moves the trailing NULL element as diff --git a/test/gmock-spec-builders_test.cc b/test/gmock-spec-builders_test.cc index 287a63e2..e8c39028 100644 --- a/test/gmock-spec-builders_test.cc +++ b/test/gmock-spec-builders_test.cc @@ -1612,6 +1612,43 @@ TEST_F(GMockVerboseFlagTest, InvalidFlagIsTreatedAsWarning) { #endif // 0 +TEST(AllowLeakTest, AllowsLeakingUnusedMockObject) { + MockA* a = new MockA; + Mock::AllowLeak(a); +} + +TEST(AllowLeakTest, CanBeCalledBeforeOnCall) { + MockA* a = new MockA; + Mock::AllowLeak(a); + ON_CALL(*a, DoA(_)).WillByDefault(Return()); + a->DoA(0); +} + +TEST(AllowLeakTest, CanBeCalledAfterOnCall) { + MockA* a = new MockA; + ON_CALL(*a, DoA(_)).WillByDefault(Return()); + Mock::AllowLeak(a); +} + +TEST(AllowLeakTest, CanBeCalledBeforeExpectCall) { + MockA* a = new MockA; + Mock::AllowLeak(a); + EXPECT_CALL(*a, DoA(_)); + a->DoA(0); +} + +TEST(AllowLeakTest, CanBeCalledAfterExpectCall) { + MockA* a = new MockA; + EXPECT_CALL(*a, DoA(_)).Times(AnyNumber()); + Mock::AllowLeak(a); +} + +TEST(AllowLeakTest, WorksWhenBothOnCallAndExpectCallArePresent) { + MockA* a = new MockA; + ON_CALL(*a, DoA(_)).WillByDefault(Return()); + EXPECT_CALL(*a, DoA(_)).Times(AnyNumber()); + Mock::AllowLeak(a); +} // Tests that we can verify and clear a mock object's expectations // when none of its methods has expectations. @@ -1916,3 +1953,14 @@ void Helper(MockC* c) { } } // namespace + +int main(int argc, char **argv) { + testing::InitGoogleMock(&argc, argv); + + // Ensures that the tests pass no matter what value of + // --gmock_catch_leaked_mocks and --gmock_verbose the user specifies. + testing::GMOCK_FLAG(catch_leaked_mocks) = true; + testing::GMOCK_FLAG(verbose) = testing::internal::kWarningVerbosity; + + return RUN_ALL_TESTS(); +} diff --git a/test/gmock_leak_test.py b/test/gmock_leak_test.py new file mode 100755 index 00000000..51358f06 --- /dev/null +++ b/test/gmock_leak_test.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python +# +# Copyright 2009, 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 that leaked mock objects can be caught be Google Mock.""" + +__author__ = 'wan@google.com (Zhanyong Wan)' + +import gmock_test_utils +import os +import unittest + +IS_WINDOWS = os.name == 'nt' + +if IS_WINDOWS: + # TODO(wan@google.com): test the opt build too. We should do it + # when Vlad Losev's work on Google Test's Python test driver is + # done, such that we can reuse the work. + PROGRAM = r'..\build.dbg\gmock_leak_test_.exe' +else: + PROGRAM = 'gmock_leak_test_' + +PROGRAM_PATH = os.path.join(gmock_test_utils.GetBuildDir(), PROGRAM) +TEST_WITH_EXPECT_CALL = PROGRAM_PATH + ' --gtest_filter=*ExpectCall*' +TEST_WITH_ON_CALL = PROGRAM_PATH + ' --gtest_filter=*OnCall*' +TEST_MULTIPLE_LEAKS = PROGRAM_PATH + ' --gtest_filter=*MultipleLeaked*' + + +class GMockLeakTest(unittest.TestCase): + + def testDoesNotCatchLeakedMockByDefault(self): + self.assertEquals(0, os.system(TEST_WITH_EXPECT_CALL)) + self.assertEquals(0, os.system(TEST_WITH_ON_CALL)) + + def testDoesNotCatchLeakedMockWhenDisabled(self): + self.assertEquals( + 0, os.system(TEST_WITH_EXPECT_CALL + ' --gmock_catch_leaked_mocks=0')) + self.assertEquals( + 0, os.system(TEST_WITH_ON_CALL + ' --gmock_catch_leaked_mocks=0')) + + def testCatchesLeakedMockWhenEnabled(self): + self.assertNotEqual( + os.system(TEST_WITH_EXPECT_CALL + ' --gmock_catch_leaked_mocks'), 0) + self.assertNotEqual( + os.system(TEST_WITH_ON_CALL + ' --gmock_catch_leaked_mocks'), 0) + + def testCatchesLeakedMockWhenEnabledWithExplictFlagValue(self): + self.assertNotEqual( + os.system(TEST_WITH_EXPECT_CALL + ' --gmock_catch_leaked_mocks=1'), 0) + + def testCatchesMultipleLeakedMocks(self): + self.assertNotEqual( + os.system(TEST_MULTIPLE_LEAKS + ' --gmock_catch_leaked_mocks'), 0) + + +if __name__ == '__main__': + gmock_test_utils.Main() diff --git a/test/gmock_leak_test_.cc b/test/gmock_leak_test_.cc new file mode 100644 index 00000000..157bd7ec --- /dev/null +++ b/test/gmock_leak_test_.cc @@ -0,0 +1,95 @@ +// Copyright 2009, 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. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Mock - a framework for writing C++ mock classes. +// +// This program is for verifying that a leaked mock object can be +// caught by Google Mock's leak detector. + +#include + +namespace { + +using ::testing::Return; + +class FooInterface { + public: + virtual ~FooInterface() {} + virtual void DoThis() = 0; +}; + +class MockFoo : public FooInterface { + public: + MOCK_METHOD0(DoThis, void()); +}; + +TEST(LeakTest, LeakedMockWithExpectCallCausesFailureWhenLeakCheckingIsEnabled) { + MockFoo* foo = new MockFoo; + + EXPECT_CALL(*foo, DoThis()); + foo->DoThis(); + + // In order to test the leak detector, we deliberately leak foo. + + // Makes sure Google Mock's leak detector can change the exit code + // to 1 even when the code is already exiting with 0. + exit(0); +} + +TEST(LeakTest, LeakedMockWithOnCallCausesFailureWhenLeakCheckingIsEnabled) { + MockFoo* foo = new MockFoo; + + ON_CALL(*foo, DoThis()).WillByDefault(Return()); + + // In order to test the leak detector, we deliberately leak foo. + + // Makes sure Google Mock's leak detector can change the exit code + // to 1 even when the code is already exiting with 0. + exit(0); +} + +TEST(LeakTest, CatchesMultipleLeakedMockObjects) { + MockFoo* foo1 = new MockFoo; + MockFoo* foo2 = new MockFoo; + + ON_CALL(*foo1, DoThis()).WillByDefault(Return()); + EXPECT_CALL(*foo2, DoThis()); + foo2->DoThis(); + + // In order to test the leak detector, we deliberately leak foo1 and + // foo2. + + // Makes sure Google Mock's leak detector can change the exit code + // to 1 even when the code is already exiting with 0. + exit(0); +} + +} // namespace diff --git a/test/gmock_output_test.py b/test/gmock_output_test.py index f7f37abb..2e992190 100755 --- a/test/gmock_output_test.py +++ b/test/gmock_output_test.py @@ -59,7 +59,7 @@ else: PROGRAM = 'gmock_output_test_' PROGRAM_PATH = os.path.join(gmock_test_utils.GetBuildDir(), PROGRAM) -COMMAND = PROGRAM_PATH + ' --gtest_stack_trace_depth=0' +COMMAND = PROGRAM_PATH + ' --gtest_stack_trace_depth=0 --gtest_print_time=0' GOLDEN_NAME = 'gmock_output_test_golden.txt' GOLDEN_PATH = os.path.join(gmock_test_utils.GetSourceDir(), GOLDEN_NAME) diff --git a/test/gmock_output_test_.cc b/test/gmock_output_test_.cc index bb56b7cd..c97bc78c 100644 --- a/test/gmock_output_test_.cc +++ b/test/gmock_output_test_.cc @@ -40,6 +40,7 @@ #include using testing::_; +using testing::AnyNumber; using testing::Ge; using testing::InSequence; using testing::Ref; @@ -239,3 +240,31 @@ TEST_F(GMockOutputTest, ExplicitActionsRunOutWithDefaultAction) { foo_.Bar2(2, 2); foo_.Bar2(1, 1); // Explicit actions in EXPECT_CALL run out. } + +TEST_F(GMockOutputTest, CatchesLeakedMocks) { + MockFoo* foo1 = new MockFoo; + MockFoo* foo2 = new MockFoo; + + // Invokes ON_CALL on foo1. + ON_CALL(*foo1, Bar(_, _, _)).WillByDefault(Return('a')); + + // Invokes EXPECT_CALL on foo2. + EXPECT_CALL(*foo2, Bar2(_, _)); + EXPECT_CALL(*foo2, Bar2(1, _)); + EXPECT_CALL(*foo2, Bar3(_, _)).Times(AnyNumber()); + foo2->Bar2(2, 1); + foo2->Bar2(1, 1); + + // Both foo1 and foo2 are deliberately leaked. +} + +int main(int argc, char **argv) { + testing::InitGoogleMock(&argc, argv); + + // Ensures that the tests pass no matter what value of + // --gmock_catch_leaked_mocks and --gmock_verbose the user specifies. + testing::GMOCK_FLAG(catch_leaked_mocks) = true; + testing::GMOCK_FLAG(verbose) = "warning"; + + return RUN_ALL_TESTS(); +} diff --git a/test/gmock_output_test_golden.txt b/test/gmock_output_test_golden.txt index 374e6659..50ef7b75 100644 --- a/test/gmock_output_test_golden.txt +++ b/test/gmock_output_test_golden.txt @@ -1,4 +1,3 @@ -Running main() from gmock_main.cc [ RUN ] GMockOutputTest.ExpectedCall FILE:#: EXPECT_CALL(foo_, Bar2(0, _)) invoked @@ -280,6 +279,8 @@ Called 2 times, but only 1 WillOnce() is specified - taking default action speci FILE:#: Stack trace: [ OK ] GMockOutputTest.ExplicitActionsRunOutWithDefaultAction +[ RUN ] GMockOutputTest.CatchesLeakedMocks +[ OK ] GMockOutputTest.CatchesLeakedMocks [ FAILED ] GMockOutputTest.UnexpectedCall [ FAILED ] GMockOutputTest.UnexpectedCallToVoidFunction [ FAILED ] GMockOutputTest.ExcessiveCall @@ -294,3 +295,7 @@ Stack trace: [ FAILED ] GMockOutputTest.UnexpectedCallWithDefaultAction [ FAILED ] GMockOutputTest.ExcessiveCallWithDefaultAction + +FILE:#: ERROR: this mock object should be deleted but never is. Its address is @0x#. +FILE:#: ERROR: this mock object should be deleted but never is. Its address is @0x#. +ERROR: 2 leaked mock objects found at program exit. diff --git a/test/gmock_test.cc b/test/gmock_test.cc index 63c3fe8d..0c832607 100644 --- a/test/gmock_test.cc +++ b/test/gmock_test.cc @@ -246,3 +246,10 @@ TEST(WideInitGoogleMockTest, CallsInitGoogleTest) { TestInitGoogleMock(argv, new_argv, "error"); EXPECT_EQ(old_init_gtest_count + 1, g_init_gtest_count); } + +// Makes sure Google Mock flags can be accessed in code. +TEST(FlagTest, IsAccessibleInCode) { + bool dummy = testing::GMOCK_FLAG(catch_leaked_mocks) && + testing::GMOCK_FLAG(verbose) == ""; + dummy = dummy; // Avoids the "unused local variable" warning. +}