Подтвердить что ты не робот

Почему GoogleMock просачивает мой shared_ptr?

Я использую GoogleMock/GoogleTest для тестирования, и я вижу какое-то странное поведение, когда у matcher есть shared_ptr для макета как параметра, а EXPECT вызывается на том же shared_ptr. Оскорбительный фрагмент кода:

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

4b9b3361

Ответ 1

Это результат сохранения p как shared_ptr, используя InSequence и порядок, в котором вы заявили о своих ожиданиях.

Когда вы вызываете

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

вы увеличиваете use_count от p. Чтобы пройти проверку утечки, p должен быть уничтожен в (или до) конце TEST.

Проблема заключается в том, что внутренне 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();