I am testing a shared lib that contains a call to deleteLater
internally.
There is no event loop running inside the library so a requirement for the application is to have a event loop running, so that all memory is properly released.
But in the test, the object dtor is not called as expected.
For example :
void test1()
{
Foo foo;
QSignalSpy spy(&foo, SIGNAL(mySignal(Status)));
foo.do(); // should trigger mySignal
QVERIFY(spy.wait(10000)); // event loop started for 10 s max
QCOMPARE(spy.count(), 1);
QList<QVariant> sig = spy.takeFirst();
Foo::Status status = qvariant_cast<Foo::Status>(sig.at(0));
QVERIFY2(status == Foo:Ok, "Failed");
}
The class Foo
looks like this :
class Foo : public QObject
{
Q_OBJECT
// ... methods, signals, slots..
private slots:
// this call is asynchronous (depends on a network reply)
void myslot() {
//..
m_obj->deleteLater();
emit mySignal(Foo:Ok);
}
};
I have added some debug print in the dtor of m_obj and it is not called when the test1 is executed.
However, if i execute the test twice (by adding a test2 slot that is copy of test1), then it is called once.
My understanding is that when the signal is emitted it stops the spy event loop and then deleteLater
is never called.
And after that the second event loop starts in test2, it process the pending deletion from the previous test1.
Is it correct ? Thank you.
Yes, you're correct. Since the
QSignalSpy
andFoo
live in the same thread, the signalmySignal
is not delivered through the event loop but via a direct connection. Therefore, the event loop stops immediately after the signal is emitted inmyslot
. However, sincemyslot
was called by the same event loop, control only returns to it whenmyslot
returns. So by the time the event loop could potentially perform the cleanup requested bydeleteLater
it has already been stopped.If you want to test that
m_obj
is cleaned up correctly you could instead create an additionalQSignalSpy
and connect it to theQObject::destroyed
signal that everyQObject
emits when it gets destroyed.You would, however, need to pass in
m_obj
as a dependency toFoo
either in the constructor or through a setter instead of constructing it inFoo
itself.The test could then look something like this: