Why is GoogleMock leaking my shared_ptr?

bruno nery picture bruno nery · Apr 23, 2012 · Viewed 18k times · Source

I use GoogleMock/GoogleTest for testing, and I'm seeing some strange behavior when a matcher has a shared_ptr to a mock as a parameter, and EXPECT is called on the same shared_ptr. The offending piece of code:

#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
using namespace boost;
using namespace testing;

struct MyParameter
{
    virtual ~MyParameter() {}
    virtual void myMethod() = 0;
};

struct MyParameterMock : public MyParameter
{
    MOCK_METHOD0(myMethod, void());
};

struct MyClass
{
    virtual ~MyClass() {}
    virtual void myMethod(shared_ptr<MyParameter> p) {}
};

struct MyClassMock : public MyClass
{
    MOCK_METHOD1(myMethod, void(shared_ptr<MyParameter>));
};

TEST(LeakTest, GoogleMockLeaksMatchedPointer)
{
    shared_ptr<MyClassMock> c = make_shared<MyClassMock>();
    shared_ptr<MyParameterMock> p = make_shared<MyParameterMock>();
    {
        InSequence dummy;
        EXPECT_CALL(*c, myMethod(Eq(p)));
        EXPECT_CALL(*p, myMethod());
    }
    c->myMethod(p);
    p->myMethod();
}

When this test is run, I get

leak_ptr_mock.cpp:37: ERROR: this mock object (used in test LeakTest.GoogleMockLeaksMatchedPointer) should be deleted but never is. Its address is @0x9309544.
ERROR: 1 leaked mock object found at program exit.

Any idea of why this happens? I rather not have to use Mock::AllowLeak.

Answer

Fraser picture Fraser · Apr 24, 2012

This is a result of holding p as a shared_ptr, using InSequence and the order in which you have declared your expectations.

When you call

    EXPECT_CALL(*c, myMethod(Eq(p)));

you increase the use_count of p. In order for the leak detection to pass, p must be destroyed at (or before) the end of TEST.

The problem here is that internally, gmock maintains a record of the required mock call sequence by holding a pointer to the preceding expectation. So when you call EXPECT_CALL(*p, myMethod());, it gets a copy of the pointer to the previous expectation.

This then has the effect of blocking the call to p's destructor when TEST ends.

In order to work around this, I think your best bet is to call

    EXPECT_TRUE(Mock::VerifyAndClearExpectations(p.get()));

just before you exit TEST. This clears the expectations on p, including critically its prerequisite expectation, which in turn allows the destructor of p to be invoked correctly.

Alternatively, if the order of the mock calls is unimportant, simply removing InSequence dummy; will also allow p's destructor to execute.


As an aside, your code has a couple of issues;

  • Your base structs should have virtual destructors
  • MyClass::myMethod should be virtual in order to allow gmock's function to override it
  • p->myMethod(p); should be p->myMethod();