mirror of
https://github.com/google/googletest.git
synced 2025-12-08 01:36:50 +08:00
Merge 106d3264a42822deb7e02f9f305fe1d9369d474a into 6ec14dfd8c409d05fba94e18e3a02df35b874353
This commit is contained in:
commit
32454943ab
@ -219,6 +219,13 @@ verified:
|
||||
Mock::AllowLeak(&mock_obj);
|
||||
```
|
||||
|
||||
Furthermore you can perform instant leak checks. This may help tracing down leaking
|
||||
mock objects when running a larger test suite:
|
||||
|
||||
```cpp
|
||||
Mock::CheckLeakInstant();
|
||||
```
|
||||
|
||||
## Mock Classes
|
||||
|
||||
gMock defines a convenient mock class template
|
||||
|
||||
@ -370,6 +370,11 @@ class GTEST_API_ Mock {
|
||||
static void AllowLeak(const void* mock_obj)
|
||||
GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
|
||||
|
||||
// Tells Google Mock to instantly check for leftover mock objects and report
|
||||
// them if there are.
|
||||
static void CheckLeakInstant(void)
|
||||
GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
|
||||
|
||||
// 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.
|
||||
|
||||
@ -456,6 +456,10 @@ static CallReaction intToCallReaction(int mock_behavior) {
|
||||
|
||||
namespace {
|
||||
|
||||
class MockObjectRegistry;
|
||||
bool ReportMockObjectRegistryLeaks(MockObjectRegistry const& reg,
|
||||
std::string& msg);
|
||||
|
||||
typedef std::set<internal::UntypedFunctionMockerBase*> FunctionMockers;
|
||||
|
||||
// The current state of a mock object. Such information is needed for
|
||||
@ -485,49 +489,19 @@ class MockObjectRegistry {
|
||||
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
|
||||
// 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() {
|
||||
if (!GMOCK_FLAG_GET(catch_leaked_mocks)) return;
|
||||
internal::MutexLock l(&internal::g_gmock_mutex);
|
||||
|
||||
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;
|
||||
|
||||
// FIXME: Print the type of the leaked object.
|
||||
// This can help the user identify the leaked object.
|
||||
std::cout << "\n";
|
||||
const MockObjectState& state = it->second;
|
||||
std::cout << internal::FormatFileLocation(state.first_used_file,
|
||||
state.first_used_line);
|
||||
std::cout << " ERROR: this mock object";
|
||||
if (!state.first_used_test.empty()) {
|
||||
std::cout << " (used in test " << state.first_used_test_suite << "."
|
||||
<< state.first_used_test << ")";
|
||||
}
|
||||
std::cout << " should be deleted but never is. Its address is @"
|
||||
<< it->first << ".";
|
||||
leaked_count++;
|
||||
}
|
||||
if (leaked_count > 0) {
|
||||
std::cout << "\nERROR: " << leaked_count << " leaked mock "
|
||||
<< (leaked_count == 1 ? "object" : "objects")
|
||||
<< " found at program exit. Expectations on a mock object are "
|
||||
"verified when the object is destructed. Leaking a mock "
|
||||
"means that its expectations aren't verified, which is "
|
||||
"usually a test bug. If you really intend to leak a mock, "
|
||||
"you can suppress this error using "
|
||||
"testing::Mock::AllowLeak(mock_object), or you may use a "
|
||||
"fake or stub instead of a mock.\n";
|
||||
std::cout.flush();
|
||||
::std::cerr.flush();
|
||||
std::string msg{};
|
||||
if (!ReportMockObjectRegistryLeaks(*this, msg)) {
|
||||
// RUN_ALL_TESTS() has already returned when this destructor is
|
||||
// called. Therefore we cannot use the normal Google Test
|
||||
// failure reporting mechanism.
|
||||
std::cout << msg;
|
||||
std::cout.flush();
|
||||
::std::cerr.flush();
|
||||
#ifdef GTEST_OS_QURT
|
||||
qurt_exception_raise_fatal();
|
||||
#else
|
||||
@ -537,12 +511,68 @@ class MockObjectRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
StateMap const& states() const { return states_; }
|
||||
StateMap& states() { return states_; }
|
||||
|
||||
private:
|
||||
StateMap states_;
|
||||
};
|
||||
|
||||
// Checks the given MockObjectRegistry for leaks (i.e. MockObjectStates objects
|
||||
// which are still in the given MockObjectRegistry and are not marked as
|
||||
// leakable) and returns a report in msg. Furthermore it returns false if leaks
|
||||
// were determined and true otherwise.
|
||||
// NOTE: It is the callers job to make calls
|
||||
// on "reg" safe, e.g. locking its mutex (if there is any).
|
||||
bool ReportMockObjectRegistryLeaks(MockObjectRegistry const& reg,
|
||||
std::string& msg) {
|
||||
// In case user specified to ignore leaks via a cl flag return true.
|
||||
if (!GMOCK_FLAG_GET(catch_leaked_mocks)) return true;
|
||||
|
||||
typedef std::map<const void*, MockObjectState> StateMap;
|
||||
|
||||
int leaked_count = 0;
|
||||
for (StateMap::const_iterator it = reg.states().begin();
|
||||
it != reg.states().end(); ++it) {
|
||||
if (it->second.leakable) // The user said it's fine to leak this object.
|
||||
continue;
|
||||
|
||||
// FIXME: Print the type of the leaked object.
|
||||
// This can help the user identify the leaked object.
|
||||
msg += "\n";
|
||||
const MockObjectState& state = it->second;
|
||||
msg += internal::FormatFileLocation(state.first_used_file,
|
||||
state.first_used_line);
|
||||
msg += " ERROR: this mock object";
|
||||
if (!state.first_used_test.empty()) {
|
||||
msg += " (used in test " + state.first_used_test_suite + "." +
|
||||
state.first_used_test + ")";
|
||||
}
|
||||
msg += " should be deleted but never is. Its address is @";
|
||||
std::ostringstream oss;
|
||||
oss << it->first;
|
||||
std::string address = oss.str();
|
||||
msg += address + ".";
|
||||
|
||||
leaked_count++;
|
||||
}
|
||||
if (leaked_count > 0) {
|
||||
msg += "\nERROR: " + std::to_string(leaked_count) + " leaked mock " +
|
||||
(leaked_count == 1 ? "object" : "objects") +
|
||||
" found at program exit. Expectations on a mock object are "
|
||||
"verified when the object is destructed. Leaking a mock "
|
||||
"means that its expectations aren't verified, which is "
|
||||
"usually a test bug. If you really intend to leak a mock, "
|
||||
"you can suppress this error using "
|
||||
"testing::Mock::AllowLeak(mock_object), or you may use a "
|
||||
"fake or stub instead of a mock.\n";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Protected by g_gmock_mutex.
|
||||
MockObjectRegistry g_mock_object_registry;
|
||||
|
||||
@ -615,6 +645,23 @@ void Mock::AllowLeak(const void* mock_obj)
|
||||
g_mock_object_registry.states()[mock_obj].leakable = true;
|
||||
}
|
||||
|
||||
// Tells Google Mock to instantly check for leftover mock objects and report
|
||||
// them if there are.
|
||||
void Mock::CheckLeakInstant(void)
|
||||
GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
|
||||
internal::MutexLock l(&internal::g_gmock_mutex);
|
||||
|
||||
std::string msg{};
|
||||
// Check for leftover mock objects at this point.
|
||||
if (!ReportMockObjectRegistryLeaks(g_mock_object_registry, msg)) {
|
||||
// If there are leftover (leaked) mock objects, fail the current test and
|
||||
// report all leaks.
|
||||
auto const& first_state = g_mock_object_registry.states().begin();
|
||||
testing::internal::Expect(false, first_state->second.first_used_file,
|
||||
first_state->second.first_used_line, msg);
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
@ -37,6 +37,8 @@ PROGRAM_PATH = gmock_test_utils.GetTestExecutablePath('gmock_leak_test_')
|
||||
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*']
|
||||
TEST_INSTANT_LEAK = [PROGRAM_PATH, '--gtest_filter=*InstantLeak*']
|
||||
TEST_INSTANT_NO_LEAK = [PROGRAM_PATH, '--gtest_filter=*InstantNoLeak*']
|
||||
|
||||
environ = gmock_test_utils.environ
|
||||
SetEnvVar = gmock_test_utils.SetEnvVar
|
||||
@ -108,6 +110,28 @@ class GMockLeakTest(gmock_test_utils.TestCase):
|
||||
).exit_code,
|
||||
)
|
||||
|
||||
def testInstantLeakCheck(self):
|
||||
self.assertNotEqual(
|
||||
0,
|
||||
gmock_test_utils.Subprocess(
|
||||
TEST_INSTANT_LEAK + ['--gmock_catch_leaked_mocks=1'], env=environ
|
||||
).exit_code,
|
||||
)
|
||||
self.assertEqual(
|
||||
0,
|
||||
gmock_test_utils.Subprocess(
|
||||
TEST_INSTANT_LEAK + ['--gmock_catch_leaked_mocks=0'], env=environ
|
||||
).exit_code,
|
||||
)
|
||||
|
||||
def testInstantNoLeak(self):
|
||||
self.assertEqual(
|
||||
0,
|
||||
gmock_test_utils.Subprocess(
|
||||
TEST_INSTANT_NO_LEAK + ['--gmock_catch_leaked_mocks=1'], env=environ
|
||||
).exit_code,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
gmock_test_utils.Main()
|
||||
|
||||
@ -96,4 +96,46 @@ TEST(LeakTest, CatchesMultipleLeakedMockObjects) {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
TEST(LeakTest, InstantNoLeak) {
|
||||
MockFoo* foo = new MockFoo;
|
||||
|
||||
EXPECT_CALL(*foo, DoThis());
|
||||
foo->DoThis();
|
||||
|
||||
delete foo;
|
||||
// Since foo is properly deleted instant leak check should not see a leaked
|
||||
// mock object and therefore not fail the test.
|
||||
testing::Mock::CheckLeakInstant();
|
||||
}
|
||||
|
||||
TEST(LeakTest, InstantLeak) {
|
||||
MockFoo* foo = new MockFoo;
|
||||
|
||||
EXPECT_CALL(*foo, DoThis());
|
||||
foo->DoThis();
|
||||
|
||||
// At this point foo is still allocated. Calling the instant leak check should
|
||||
// detect it and fail the test.
|
||||
testing::Mock::CheckLeakInstant();
|
||||
|
||||
// Free foo in order to not fail the end of program leak check.
|
||||
delete foo;
|
||||
}
|
||||
|
||||
TEST(LeakTest, InstantNoLeakAllowed) {
|
||||
MockFoo* foo = new MockFoo;
|
||||
testing::Mock::AllowLeak(foo);
|
||||
|
||||
EXPECT_CALL(*foo, DoThis());
|
||||
foo->DoThis();
|
||||
|
||||
// At this point foo is still allocated However since we made foo a leakable
|
||||
// mock object with AllowLeak() the instant leak check should ignore it and
|
||||
// pass the test.
|
||||
testing::Mock::CheckLeakInstant();
|
||||
|
||||
// Free foo in order to not fail the end of program leak check.
|
||||
delete foo;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -166,12 +166,16 @@ class GMockOutputTest(gmock_test_utils.TestCase):
|
||||
# The normalized output should match the golden file.
|
||||
self.assertEqual(golden, output)
|
||||
|
||||
# The raw output should contain 2 leaked mock object errors for
|
||||
# The raw output should contain 4 leaked mock object errors for
|
||||
# test GMockOutputTest.CatchesLeakedMocks.
|
||||
# Two from the at end of program check and two from the instant leak check
|
||||
# within GMockOutputTest.CatchesLeakedMocks.
|
||||
self.assertEqual(
|
||||
[
|
||||
'GMockOutputTest.CatchesLeakedMocks',
|
||||
'GMockOutputTest.CatchesLeakedMocks',
|
||||
'GMockOutputTest.CatchesLeakedMocks',
|
||||
'GMockOutputTest.CatchesLeakedMocks',
|
||||
],
|
||||
leaky_tests,
|
||||
)
|
||||
|
||||
@ -251,6 +251,8 @@ TEST_F(GMockOutputTest, CatchesLeakedMocks) {
|
||||
foo2->Bar2(1, 1);
|
||||
|
||||
// Both foo1 and foo2 are deliberately leaked.
|
||||
// Call the instant leak check in order to validate it's output.
|
||||
testing::Mock::CheckLeakInstant();
|
||||
}
|
||||
|
||||
MATCHER_P2(IsPair, first, second, "") {
|
||||
|
||||
@ -304,7 +304,15 @@ FILE:#:
|
||||
Stack trace:
|
||||
[ OK ] GMockOutputTest.ExplicitActionsRunOutWithDefaultAction
|
||||
[ RUN ] GMockOutputTest.CatchesLeakedMocks
|
||||
[ OK ] GMockOutputTest.CatchesLeakedMocks
|
||||
FILE:#: Failure
|
||||
|
||||
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#.
|
||||
FILE:#: ERROR: this mock object should be deleted but never is. Its address is @0x#.
|
||||
ERROR: 3 leaked mock objects found at program exit. Expectations on a mock object are verified when the object is destructed. Leaking a mock means that its expectations aren't verified, which is usually a test bug. If you really intend to leak a mock, you can suppress this error using testing::Mock::AllowLeak(mock_object), or you may use a fake or stub instead of a mock.
|
||||
|
||||
|
||||
[ FAILED ] GMockOutputTest.CatchesLeakedMocks
|
||||
[ RUN ] GMockOutputTest.PrintsMatcher
|
||||
FILE:#: Failure
|
||||
Value of: (std::pair<int, bool>(42, true))
|
||||
@ -326,6 +334,7 @@ Expected: is pair (first: is >= 48, second: true)
|
||||
[ FAILED ] GMockOutputTest.MismatchArgumentsAndWith
|
||||
[ FAILED ] GMockOutputTest.UnexpectedCallWithDefaultAction
|
||||
[ FAILED ] GMockOutputTest.ExcessiveCallWithDefaultAction
|
||||
[ FAILED ] GMockOutputTest.CatchesLeakedMocks
|
||||
[ FAILED ] GMockOutputTest.PrintsMatcher
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user