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
QSignalSpyandFoolive in the same thread, the signalmySignalis not delivered through the event loop but via a direct connection. Therefore, the event loop stops immediately after the signal is emitted inmyslot. However, sincemyslotwas called by the same event loop, control only returns to it whenmyslotreturns. So by the time the event loop could potentially perform the cleanup requested bydeleteLaterit has already been stopped.If you want to test that
m_objis cleaned up correctly you could instead create an additionalQSignalSpyand connect it to theQObject::destroyedsignal that everyQObjectemits when it gets destroyed.You would, however, need to pass in
m_objas a dependency toFooeither in the constructor or through a setter instead of constructing it inFooitself.The test could then look something like this: