2012-04-23 28 views
21

Utilizo GoogleMock/GoogleTest para realizar pruebas, 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 de código que:¿Por qué GoogleMock pierde mi 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(); 
} 

Cuando se ejecuta esta prueba, consigo

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. 

Cualquier idea de por qué sucede esto? Prefiero no tener que usar Mock::AllowLeak.

Respuesta

26

Este es el resultado de la celebración de p como shared_ptr, utilizando InSequence y el orden en el que se ha declarado sus expectativas.

Cuando se llama a

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

se aumenta 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. Por lo tanto, cuando llame al EXPECT_CALL(*p, myMethod());, obtendrá una copia del puntero a la expectativa anterior.

Esto tiene el efecto de bloquear la llamada al destructor p cuando termina TEST.

Con el fin de solucionar este problema, creo que la 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 previa, 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 eliminar InSequence dummy; también permitirá la ejecución del destructor p.


Como un lado, su código tiene un par de problemas;

  • Sus estructuras de base deben tener destructores virtuales
  • MyClass::myMethod debe ser virtual con el fin de permitir la función de gmock para anularla
  • p->myMethod(p); debe ser p->myMethod();
+0

Funciona, Fraser! Corregí el código según tus sugerencias también. –

+0

@bruno nery: ¿Qué versión de GoogleMock estás usando? –

+0

¿Qué pasaría si p se crea antes de c? No se destruiría más que al final c, se verificaron y aclararon sus expectativas, lo que conducirá a una disminución del contador de referencia de p. Después de que p se destruirá, se verificará y se destruirá por completo, ya que el contador ahora es 0. –

Cuestiones relacionadas