Implements --gmock_catch_leaked_mocks and Mock::AllowLeak.

This commit is contained in:
zhanyong.wan 2009-04-22 22:25:31 +00:00
parent 1c8eb1c059
commit df35a763b9
17 changed files with 681 additions and 43 deletions

View File

@ -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

View File

@ -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<F>& RegisterOwner(const void* mock_obj) {
{
MutexLock l(&g_gmock_mutex);
mock_obj_ = mock_obj;
}
Mock::Register(mock_obj, this);
return *::testing::internal::down_cast<FunctionMocker<F>*>(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<F>& AddNewDefaultActionSpec(
const char* file, int line,
const ArgumentMatcherTuple& m) {
Mock::RegisterUseByOnCallOrExpectCall(MockObject(), file, line);
default_actions_.push_back(DefaultActionSpec<F>(file, line, m));
return default_actions_.back();
}
// Adds and returns an expectation spec for this mock function.
// L < g_gmock_mutex
Expectation<F>& AddNewExpectation(
const char* file, int line,
const ArgumentMatcherTuple& m) {
Mock::RegisterUseByOnCallOrExpectCall(MockObject(), file, line);
const linked_ptr<Expectation<F> > expectation(
new Expectation<F>(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)

View File

@ -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

View File

@ -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) \

View File

@ -0,0 +1,205 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Name="gmock-spec-builders_test"
ProjectGUID="{46972604-5BE0-4493-BAE3-878DB825FDCB}"
RootNamespace="gmockspecbuilders_test"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
InheritedPropertySheets=".\gmock_config.vsprops"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="../include"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
InheritedPropertySheets=".\gmock_config.vsprops"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories="../include"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="0"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
<ProjectReference
ReferencedProjectIdentifier="{34681F0D-CE45-415D-B5F2-5C662DFE3BD5}"
RelativePathToProject=".\gmock.vcproj"
/>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath="..\test\gmock-spec-builders_test.cc"
>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@ -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

View File

@ -172,8 +172,8 @@
</Configurations>
<References>
<ProjectReference
ReferencedProjectIdentifier="{E4EF614B-30DF-4954-8C53-580A0BF6B589}"
RelativePathToProject=".\gmock_main.vcproj"
ReferencedProjectIdentifier="{34681F0D-CE45-415D-B5F2-5C662DFE3BD5}"
RelativePathToProject=".\gmock.vcproj"
/>
</References>
<Files>

View File

@ -226,10 +226,6 @@
RelativePath="..\test\gmock-printers_test.cc"
>
</File>
<File
RelativePath="..\test\gmock-spec-builders_test.cc"
>
</File>
<File
RelativePath="..\test\gmock_test.cc"
>

View File

@ -36,9 +36,17 @@
#include <gmock/gmock-spec-builders.h>
#include <stdlib.h>
#include <iostream> // NOLINT
#include <map>
#include <set>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#if GTEST_OS_CYGWIN || GTEST_OS_LINUX || GTEST_OS_MAC
#include <unistd.h> // NOLINT
#endif
namespace testing {
namespace internal {
@ -148,10 +156,77 @@ void ReportUninterestingCall(CallReaction reaction, const string& msg) {
namespace {
typedef std::set<internal::UntypedFunctionMockerBase*> FunctionMockers;
typedef std::map<const void*, FunctionMockers> 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<const void*, MockObjectState> 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();

View File

@ -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

View File

@ -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();
}

84
test/gmock_leak_test.py Executable file
View File

@ -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()

95
test/gmock_leak_test_.cc Normal file
View File

@ -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 <gmock/gmock.h>
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

View File

@ -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)

View File

@ -40,6 +40,7 @@
#include <gtest/gtest.h>
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();
}

View File

@ -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.

View File

@ -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.
}