Skip to content
Snippets Groups Projects
Commit e7bb5ede authored by zhanyong.wan's avatar zhanyong.wan
Browse files

Improves the error message for leaked mocks to include the test name (by Zhanyong Wan).

parent 125783fb
No related branches found
No related tags found
No related merge requests found
......@@ -166,16 +166,16 @@ inline To down_cast(From* f) { // so we only accept pointers
return static_cast<To>(f);
}
// The GMOCK_COMPILE_ASSERT macro can be used to verify that a compile time
// The GMOCK_COMPILE_ASSERT_ macro can be used to verify that a compile time
// expression is true. For example, you could use it to verify the
// size of a static array:
//
// GMOCK_COMPILE_ASSERT(ARRAYSIZE(content_type_names) == CONTENT_NUM_TYPES,
// content_type_names_incorrect_size);
// GMOCK_COMPILE_ASSERT_(ARRAYSIZE(content_type_names) == CONTENT_NUM_TYPES,
// content_type_names_incorrect_size);
//
// or to make sure a struct is smaller than a certain size:
//
// GMOCK_COMPILE_ASSERT(sizeof(foo) < 128, foo_too_large);
// GMOCK_COMPILE_ASSERT_(sizeof(foo) < 128, foo_too_large);
//
// The second argument to the macro is the name of the variable. If
// the expression is false, most compilers will issue a warning/error
......
......@@ -168,6 +168,8 @@ struct MockObjectState {
// invoked on this mock object.
const char* first_used_file;
int first_used_line;
::std::string first_used_test_case;
::std::string first_used_test;
bool leakable; // true iff it's OK to leak the object.
FunctionMockers function_mockers; // All registered methods of the object.
};
......@@ -203,8 +205,13 @@ class MockObjectRegistry {
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 << ".";
cout << " ERROR: this mock object";
if (state.first_used_test != "") {
cout << " (used in test " << state.first_used_test_case << "."
<< state.first_used_test << ")";
}
cout << " should be deleted but never is. Its address is @"
<< it->first << ".";
leaked_count++;
}
if (leaked_count > 0) {
......@@ -357,6 +364,15 @@ void Mock::RegisterUseByOnCallOrExpectCall(
if (state.first_used_file == NULL) {
state.first_used_file = file;
state.first_used_line = line;
const TestInfo* const test_info =
UnitTest::GetInstance()->current_test_info();
if (test_info != NULL) {
// TODO(wan@google.com): record the test case name when the
// ON_CALL or EXPECT_CALL is invoked from SetUpTestCase() or
// TearDownTestCase().
state.first_used_test_case = test_info->test_case_name();
state.first_used_test = test_info->name();
}
}
}
......
......@@ -64,6 +64,7 @@ GOLDEN_NAME = 'gmock_output_test_golden.txt'
GOLDEN_PATH = os.path.join(gmock_test_utils.GetSourceDir(),
GOLDEN_NAME)
def ToUnixLineEnding(s):
"""Changes all Windows/Mac line endings in s to UNIX line endings."""
......@@ -109,15 +110,38 @@ def RemoveMemoryAddresses(output):
return re.sub(r'@\w+', '@0x#', output)
def NormalizeOutput(output):
"""Normalizes output (the output of gmock_output_test_.exe)."""
def RemoveTestNamesOfLeakedMocks(output):
"""Removes the test names of leaked mock objects from the test output."""
return re.sub(r'\(used in test .+\) ', '', output)
def GetLeakyTests(output):
"""Returns a list of test names that leak mock objects."""
# findall() returns a list of all matches of the regex in output.
# For example, if '(used in test FooTest.Bar)' is in output, the
# list will contain 'FooTest.Bar'.
return re.findall(r'\(used in test (.+)\)', output)
def GetNormalizedOutputAndLeakyTests(output):
"""Normalizes the output of gmock_output_test_.
Args:
output: The test output.
Returns:
A tuple (the normalized test output, the list of test names that have
leaked mocks).
"""
output = ToUnixLineEnding(output)
output = RemoveReportHeaderAndFooter(output)
output = NormalizeErrorMarker(output)
output = RemoveLocations(output)
output = RemoveMemoryAddresses(output)
return output
return (RemoveTestNamesOfLeakedMocks(output), GetLeakyTests(output))
def IterShellCommandOutput(cmd, stdin_string=None):
......@@ -167,9 +191,8 @@ def GetShellCommandOutput(cmd, stdin_string=None):
return string.join(lines, '')
def GetCommandOutput(cmd):
"""Runs a command and returns its output with all file location
info stripped off.
def GetNormalizedCommandOutputAndLeakyTests(cmd):
"""Runs a command and returns its normalized output and a list of leaky tests.
Args:
cmd: the shell command.
......@@ -177,22 +200,29 @@ def GetCommandOutput(cmd):
# Disables exception pop-ups on Windows.
os.environ['GTEST_CATCH_EXCEPTIONS'] = '1'
return NormalizeOutput(GetShellCommandOutput(cmd, ''))
return GetNormalizedOutputAndLeakyTests(GetShellCommandOutput(cmd, ''))
class GMockOutputTest(unittest.TestCase):
def testOutput(self):
output = GetCommandOutput(COMMAND)
(output, leaky_tests) = GetNormalizedCommandOutputAndLeakyTests(COMMAND)
golden_file = open(GOLDEN_PATH, 'rb')
golden = golden_file.read()
golden_file.close()
# The normalized output should match the golden file.
self.assertEquals(golden, output)
# The raw output should contain 2 leaked mock object errors for
# test GMockOutputTest.CatchesLeakedMocks.
self.assertEquals(['GMockOutputTest.CatchesLeakedMocks',
'GMockOutputTest.CatchesLeakedMocks'],
leaky_tests)
if __name__ == '__main__':
if sys.argv[1:] == [GENGOLDEN_FLAG]:
output = GetCommandOutput(COMMAND)
(output, _) = GetNormalizedCommandOutputAndLeakyTests(COMMAND)
golden_file = open(GOLDEN_PATH, 'wb')
golden_file.write(output)
golden_file.close()
......
......@@ -258,6 +258,16 @@ TEST_F(GMockOutputTest, CatchesLeakedMocks) {
// Both foo1 and foo2 are deliberately leaked.
}
void TestCatchesLeakedMocksInAdHocTests() {
MockFoo* foo = new MockFoo;
// Invokes EXPECT_CALL on foo.
EXPECT_CALL(*foo, Bar2(_, _));
foo->Bar2(2, 1);
// foo is deliberately leaked.
}
int main(int argc, char **argv) {
testing::InitGoogleMock(&argc, argv);
......@@ -266,5 +276,6 @@ int main(int argc, char **argv) {
testing::GMOCK_FLAG(catch_leaked_mocks) = true;
testing::GMOCK_FLAG(verbose) = "warning";
TestCatchesLeakedMocksInAdHocTests();
return RUN_ALL_TESTS();
}
......@@ -298,4 +298,5 @@ Stack trace:
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.
FILE:#: ERROR: this mock object should be deleted but never is. Its address is @0x#.
ERROR: 3 leaked mock objects found at program exit.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment