c++ - ¿Por qué GoogleMock pierde mi shared_ptr?
boost shared-ptr (1)
Uso GoogleMock / GoogleTest para probar, y estoy viendo un comportamiento extraño cuando un matcher tiene un shared_ptr a un simulacro como parámetro, y se llama a EXPECT en el mismo shared_ptr. La pieza ofensiva de código:
#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();
}
Cuando se ejecuta esta prueba, obtengo
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.
¿Alguna idea de por qué sucede esto? Preferiría no tener que usar Mock::AllowLeak
.
Esto es el resultado de mantener p
como shared_ptr
, utilizando InSequence
y el orden en el que ha declarado sus expectativas.
Cuando usted llama
EXPECT_CALL(*c, myMethod(Eq(p)));
aumentas el use_count
de p
. Para que la detección de fugas pase, p
debe destruirse en (o antes) al final de TEST
.
El problema aquí es que internamente, gmock mantiene un registro de la secuencia requerida de simulacros de llamada manteniendo un puntero a la expectativa anterior. Entonces cuando llamas a EXPECT_CALL(*p, myMethod());
, obtiene una copia del puntero a la expectativa anterior.
Esto tiene el efecto de bloquear la llamada al destructor de p
cuando finaliza TEST
.
Para evitar esto, creo que tu mejor opción es llamar
EXPECT_TRUE(Mock::VerifyAndClearExpectations(p.get()));
justo antes de salir de TEST
. Esto borra las expectativas en p
, incluida críticamente su expectativa de requisito previo, que a su vez permite que el destructor de p
se invoque correctamente.
Alternativamente, si el orden de las llamadas simuladas no es importante, simplemente eliminando el InSequence dummy;
también permitirá la ejecución del destructor de p
.
Como un aparte, su código tiene un par de problemas;
- Tus estructuras base deberían tener destructores virtuales
-
MyClass::myMethod
debe ser virtual para permitir que la función de gmock lo anule -
p->myMethod(p);
debería serp->myMethod();