Почему 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
.
Ответы
Ответ 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();