web-dev-qa-db-ja.com

GoogleMockがshared_ptrをリークしているのはなぜですか?

テストにGoogleMock/GoogleTestを使用していますが、マッチャーがモックへのshared_ptrをパラメーターとして持っていて、同じshared_ptrでEXPECTが呼び出されると、奇妙な動作が見られます。問題のあるコード:

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

このテストを実行すると、

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.

なぜこれが起こるのかについて何か考えはありますか? Mock::AllowLeakを使用する必要はありません。

28
bruno nery

これは、pを_shared_ptr_として保持し、InSequenceと、期待を宣言した順序を使用した結果です。

あなたが電話するとき

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

pの_use_count_を増やします。リーク検出が通過するためには、pTESTの終わり(またはその前)に破棄される必要があります。

ここでの問題は、内部的に、gmockが前の期待値へのポインターを保持することにより、必要なモック呼び出しシーケンスの記録を維持することです。したがって、EXPECT_CALL(*p, myMethod());を呼び出すと、前の期待値へのポインターのコピーが取得されます。

これは、pが終了したときに、TESTのデストラクタへの呼び出しをブロックする効果があります。

これを回避するために、私はあなたの最善の策は電話することだと思います

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

TESTを終了する直前。これにより、pの期待値がクリアされます。これには、前提条件の期待値が含まれます。これにより、pのデストラクタを正しく呼び出すことができます。

または、模擬呼び出しの順序が重要でない場合は、_InSequence dummy;_を削除するだけで、pのデストラクタも実行できます。


余談ですが、コードにはいくつかの問題があります。

  • 基本構造体には仮想デストラクタが必要です
  • _MyClass::myMethod_は、gmockの関数がそれをオーバーライドできるようにするために仮想である必要があります
  • p->myMethod(p);p->myMethod();である必要があります
31
Fraser