QObject :: deleteLater не вызывается должным образом в моем Qt Test

Я тестирую общую библиотеку, которая содержит внутренний вызов deleteLater. Внутри библиотеки нет цикла событий, поэтому для приложения требуется, чтобы цикл событий был запущен, чтобы вся память была должным образом освобождена.

Но в тесте объект dtor вызывается не так, как ожидалось.

Например :

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");
}

Класс Foo выглядит так:

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);
  }
};

Я добавил отладочную печать в dtor m_obj, и она не вызывается при выполнении test1.

Однако, если я выполняю тест дважды (добавляя слот test2, который является копией test1), он вызывается один раз.

Насколько я понимаю, когда сигнал испускается, он останавливает цикл шпионских событий, а затем deleteLater никогда не вызывается. И после этого в test2 запускается второй цикл событий, он обрабатывает отложенное удаление из предыдущего test1.

Это правильно? Спасибо.


person Fryz    schedule 19.09.2018    source источник


Ответы (1)


Да, ты прав. Поскольку QSignalSpy и Foo живут в одном потоке, сигнал mySignal доставляется не через цикл событий, а через прямое соединение. Следовательно, цикл обработки событий останавливается сразу же после того, как сигнал испускается в myslot. Однако, поскольку myslot был вызван тем же циклом событий, управление возвращается к нему только тогда, когда возвращается myslot. Таким образом, к тому времени, когда цикл событий потенциально мог выполнить очистку, запрошенную deleteLater, он уже был остановлен.

Если вы хотите проверить, что m_obj очищается правильно, вы можете вместо этого создать дополнительный QSignalSpy и подключить его к сигналу QObject::destroyed, который излучает каждый QObject, когда он уничтожается.

Однако вам нужно будет передать m_obj как зависимость от Foo либо в конструкторе, либо через установщик вместо того, чтобы создавать его в самом Foo.

Тогда тест мог бы выглядеть примерно так:

void test1()
{
    auto obj = new Obj{}; // will be assigned to `Foo::m_obj`
    Foo foo{obj};

    QSignalSpy deletion_spy(obj, &QObject::destroyed);
    QSignalSpy mysignal_spy(&Foo, &Foo::mySignal);

    QVERIFY(deletion_spy.wait(10000));
    QCOMPARE(deletion_spy.count(), 1); // verify that `obj` got deleted

    QCOMPARE(mysignal_spy.count(), 1);
    QList<QVariant> sig = spy.takeFirst();
    Foo::Status status = qvariant_cast<Foo::Status>(sig.at(0));

    QVERIFY2(status == Foo:Ok, "Failed");
}
person Corristo    schedule 19.09.2018