diff options
Diffstat (limited to 'tests/auto/corelib/kernel/qobject/tst_qobject.cpp')
-rw-r--r-- | tests/auto/corelib/kernel/qobject/tst_qobject.cpp | 202 |
1 files changed, 177 insertions, 25 deletions
diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp index 31268c5cf3..e0394a5d25 100644 --- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp +++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp @@ -154,6 +154,8 @@ private slots: void mutableFunctor(); void checkArgumentsForNarrowing(); void nullReceiver(); + void functorReferencesConnection(); + void disconnectDisconnects(); }; struct QObjectCreatedOnShutdown @@ -3409,12 +3411,11 @@ void tst_QObject::disconnectSelfInSlotAndDeleteAfterEmit() void tst_QObject::dumpObjectInfo() { QObject a, b; - QObject::connect(&a, SIGNAL(destroyed(QObject*)), &b, SLOT(deleteLater())); - a.disconnect(&b); + QObject::connect(&a, &QObject::destroyed, &b, &QObject::deleteLater); QTest::ignoreMessage(QtDebugMsg, "OBJECT QObject::unnamed"); QTest::ignoreMessage(QtDebugMsg, " SIGNALS OUT"); QTest::ignoreMessage(QtDebugMsg, " signal: destroyed(QObject*)"); - QTest::ignoreMessage(QtDebugMsg, " <Disconnected receiver>"); + QTest::ignoreMessage(QtDebugMsg, " <functor or function pointer>"); QTest::ignoreMessage(QtDebugMsg, " SIGNALS IN"); QTest::ignoreMessage(QtDebugMsg, " <None>"); a.dumpObjectInfo(); // should not crash @@ -4793,13 +4794,13 @@ class LotsOfSignalsAndSlots: public QObject public slots: void slot_v() {} - void slot_v_noexcept() Q_DECL_NOTHROW {} + void slot_v_noexcept() noexcept {} void slot_vi(int) {} - void slot_vi_noexcept() Q_DECL_NOTHROW {} + void slot_vi_noexcept() noexcept {} void slot_vii(int, int) {} void slot_viii(int, int, int) {} int slot_i() { return 0; } - int slot_i_noexcept() Q_DECL_NOTHROW { return 0; } + int slot_i_noexcept() noexcept { return 0; } int slot_ii(int) { return 0; } int slot_iii(int, int) { return 0; } int slot_iiii(int, int, int) { return 0; } @@ -4813,18 +4814,18 @@ class LotsOfSignalsAndSlots: public QObject void slot_vPFvvE(fptr) {} void const_slot_v() const {}; - void const_slot_v_noexcept() const Q_DECL_NOTHROW {} + void const_slot_v_noexcept() const noexcept {} void const_slot_vi(int) const {}; - void const_slot_vi_noexcept(int) const Q_DECL_NOTHROW {} + void const_slot_vi_noexcept(int) const noexcept {} static void static_slot_v() {} - static void static_slot_v_noexcept() Q_DECL_NOTHROW {} + static void static_slot_v_noexcept() noexcept {} static void static_slot_vi(int) {} - static void static_slot_vi_noexcept(int) Q_DECL_NOTHROW {} + static void static_slot_vi_noexcept(int) noexcept {} static void static_slot_vii(int, int) {} static void static_slot_viii(int, int, int) {} static int static_slot_i() { return 0; } - static int static_slot_i_noexcept() Q_DECL_NOTHROW { return 0; } + static int static_slot_i_noexcept() noexcept { return 0; } static int static_slot_ii(int) { return 0; } static int static_slot_iii(int, int) { return 0; } static int static_slot_iiii(int, int, int) { return 0; } @@ -4987,11 +4988,11 @@ void tst_QObject::connectCxx0xTypeMatching() } -void receiverFunction_noexcept() Q_DECL_NOTHROW {} -struct Functor_noexcept { void operator()() Q_DECL_NOTHROW {} }; +void receiverFunction_noexcept() noexcept {} +struct Functor_noexcept { void operator()() noexcept {} }; void tst_QObject::connectCxx17Noexcept() { - // this is about connecting signals to slots with the Q_DECL_NOTHROW qualifier + // this is about connecting signals to slots with the noexcept qualifier // as semantics changed due to http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0012r1.html typedef LotsOfSignalsAndSlots Foo; Foo obj; @@ -6028,7 +6029,6 @@ void tst_QObject::connectFunctorArgDifference() QStringListModel model; connect(&model, &QStringListModel::rowsInserted, SlotFunctor()); -#if defined(Q_COMPILER_LAMBDA) connect(&timer, &QTimer::timeout, [=](){}); connect(&timer, &QTimer::objectNameChanged, [=](const QString &){}); connect(qApp, &QCoreApplication::aboutToQuit, [=](){}); @@ -6036,7 +6036,6 @@ void tst_QObject::connectFunctorArgDifference() connect(&timer, &QTimer::objectNameChanged, [=](){}); connect(&model, &QStringListModel::rowsInserted, [=](){}); connect(&model, &QStringListModel::rowsInserted, [=](const QModelIndex &){}); -#endif QVERIFY(true); } @@ -6074,7 +6073,6 @@ void tst_QObject::connectFunctorQueued() e.exec(); QCOMPARE(status, 2); -#if defined(Q_COMPILER_LAMBDA) status = 1; connect(&obj, &SenderObject::signal1, this, [&status] { status = 2; }, Qt::QueuedConnection); @@ -6082,7 +6080,6 @@ void tst_QObject::connectFunctorQueued() QCOMPARE(status, 1); e.exec(); QCOMPARE(status, 2); -#endif } void tst_QObject::connectFunctorWithContext() @@ -6116,7 +6113,6 @@ void tst_QObject::connectFunctorWithContext() e.exec(); QCOMPARE(status, 2); -#if defined(Q_COMPILER_LAMBDA) status = 1; connect(&obj, &SenderObject::signal1, this, [this, &status, &obj] { status = 2; QCOMPARE(sender(), &obj); }, Qt::QueuedConnection); @@ -6124,7 +6120,6 @@ void tst_QObject::connectFunctorWithContext() QCOMPARE(status, 1); e.exec(); QCOMPARE(status, 2); -#endif // Free context->deleteLater(); @@ -6434,7 +6429,7 @@ void connectFunctorOverload_impl(Signal signal, int expOverload, QList<QVariant> void tst_QObject::connectFunctorOverloads() { -#if defined (Q_COMPILER_DECLTYPE) && defined (Q_COMPILER_VARIADIC_TEMPLATES) +#if defined (Q_COMPILER_VARIADIC_TEMPLATES) connectFunctorOverload_impl<ComplexFunctor>(&FunctorArgDifferenceObject::signal_ii, 1, (QList<QVariant>() << 1 << 2)); connectFunctorOverload_impl<ComplexFunctor>(&FunctorArgDifferenceObject::signal_iiS, 1, @@ -6608,7 +6603,6 @@ void tst_QObject::disconnectDoesNotLeakFunctor() } QCOMPARE(countedStructObjectsCount, 0); { -#if defined(Q_COMPILER_LAMBDA) CountedStruct s; QCOMPARE(countedStructObjectsCount, 1); QTimer timer; @@ -6618,7 +6612,6 @@ void tst_QObject::disconnectDoesNotLeakFunctor() QCOMPARE(countedStructObjectsCount, 2); QVERIFY(QObject::disconnect(c)); QCOMPARE(countedStructObjectsCount, 1); -#endif // Q_COMPILER_LAMBDA } QCOMPARE(countedStructObjectsCount, 0); } @@ -6666,7 +6659,6 @@ void tst_QObject::contextDoesNotLeakFunctor() } QCOMPARE(countedStructObjectsCount, 0); { -#if defined(Q_COMPILER_LAMBDA) CountedStruct s; QEventLoop e; ContextObject *context = new ContextObject; @@ -6679,7 +6671,6 @@ void tst_QObject::contextDoesNotLeakFunctor() context->deleteLater(); e.exec(); QCOMPARE(countedStructObjectsCount, 1); -#endif // Q_COMPILER_LAMBDA } QCOMPARE(countedStructObjectsCount, 0); } @@ -7487,6 +7478,167 @@ void tst_QObject::nullReceiver() QVERIFY(!connect(&o, SIGNAL(destroyed()), nullObj, SLOT(deleteLater()))); } +void tst_QObject::functorReferencesConnection() +{ + countedStructObjectsCount = 0; + QMetaObject::Connection globalCon; + { + GetSenderObject obj; + CountedStruct counted(&obj); + QCOMPARE(countedStructObjectsCount, 1); + auto c = QSharedPointer<QMetaObject::Connection>::create(); + int slotCalled = 0; + *c = connect(&obj, &GetSenderObject::aSignal, &obj, [&slotCalled, c, counted] { + QObject::disconnect(*c); + slotCalled++; + }); + globalCon = *c; // keep a handle to the connection somewhere; + QVERIFY(globalCon); + QCOMPARE(countedStructObjectsCount, 2); + obj.triggerSignal(); + QCOMPARE(slotCalled, 1); + QCOMPARE(countedStructObjectsCount, 1); + QVERIFY(!globalCon); + obj.triggerSignal(); + QCOMPARE(slotCalled, 1); + QCOMPARE(countedStructObjectsCount, 1); + } + QCOMPARE(countedStructObjectsCount, 0); + + { + GetSenderObject obj; + CountedStruct counted(&obj); + QCOMPARE(countedStructObjectsCount, 1); + auto *rec = new QObject; + int slotCalled = 0; + globalCon = connect(&obj, &GetSenderObject::aSignal, rec, [&slotCalled, rec, counted] { + delete rec; + slotCalled++; + }); + QCOMPARE(countedStructObjectsCount, 2); + obj.triggerSignal(); + QCOMPARE(slotCalled, 1); + QCOMPARE(countedStructObjectsCount, 1); + QVERIFY(!globalCon); + obj.triggerSignal(); + QCOMPARE(slotCalled, 1); + QCOMPARE(countedStructObjectsCount, 1); + } + QCOMPARE(countedStructObjectsCount, 0); + { + int slotCalled = 0; + QEventLoop eventLoop; + { + // Sender will be destroyed when the labda goes out of scope lambda, so it will exit the event loop + auto sender = QSharedPointer<GetSenderObject>::create(); + connect(sender.data(), &QObject::destroyed, &eventLoop, &QEventLoop::quit, Qt::QueuedConnection); + globalCon = connect(sender.data(), &GetSenderObject::aSignal, this, [&slotCalled, sender, &globalCon, this] { + ++slotCalled; + // This signal will be connected, but should never be called as the sender will be destroyed before + auto c2 = connect(sender.data(), &GetSenderObject::aSignal, [] { QFAIL("Should not be called"); }); + QVERIFY(c2); + QVERIFY(QObject::disconnect(sender.data(), nullptr, this, nullptr)); + QVERIFY(!globalCon); // this connection has been disconnected + QVERIFY(c2); // sender should not have been deleted yet, only after the emission is done + }); + QMetaObject::invokeMethod(sender.data(), &GetSenderObject::triggerSignal, Qt::QueuedConnection); + QMetaObject::invokeMethod(sender.data(), &GetSenderObject::triggerSignal, Qt::QueuedConnection); + QMetaObject::invokeMethod(sender.data(), &GetSenderObject::triggerSignal, Qt::QueuedConnection); + } + eventLoop.exec(); + QCOMPARE(slotCalled, 1); + } + + { + GetSenderObject obj; + CountedStruct counted(&obj); + QCOMPARE(countedStructObjectsCount, 1); + auto c1 = QSharedPointer<QMetaObject::Connection>::create(); + auto c2 = QSharedPointer<QMetaObject::Connection>::create(); + int slot1Called = 0; + int slot3Called = 0; + *c1 = connect(&obj, &GetSenderObject::aSignal, &obj, [&slot1Called, &slot3Called, &obj, c1, c2, counted] { + auto c3 = connect(&obj, &GetSenderObject::aSignal, [counted, &slot3Called] { + slot3Called++; + }); + // top-level + the one in the 3 others lambdas + QCOMPARE(countedStructObjectsCount, 4); + QObject::disconnect(*c2); + slot1Called++; + }); + connect(&obj, &GetSenderObject::aSignal, [] {}); // just a dummy signal to fill the connection list + *c2 = connect(&obj, &GetSenderObject::aSignal, [counted, c2] { QFAIL("should not be called"); }); + QVERIFY(c1 && c2); + QCOMPARE(countedStructObjectsCount, 3); // top-level + c1 + c2 + obj.triggerSignal(); + QCOMPARE(slot1Called, 1); + QCOMPARE(slot3Called, 0); + QCOMPARE(countedStructObjectsCount, 3); // top-level + c1 + c3 + QObject::disconnect(*c1); + QCOMPARE(countedStructObjectsCount, 2); // top-level + c3 + obj.triggerSignal(); + QCOMPARE(slot1Called, 1); + QCOMPARE(slot3Called, 1); + } + { + struct DestroyEmit { + Q_DISABLE_COPY(DestroyEmit); + explicit DestroyEmit(SenderObject *obj) : obj(obj) {} + SenderObject *obj; + ~DestroyEmit() { + obj->emitSignal1(); + } + }; + SenderObject obj; + int slot1Called = 0; + int slot2Called = 0; + int slot3Called = 0; + auto c1 = QSharedPointer<QMetaObject::Connection>::create(); + auto de = QSharedPointer<DestroyEmit>::create(&obj); + *c1 = connect(&obj, &SenderObject::signal1, [&slot1Called, &slot3Called, de, c1, &obj] { + connect(&obj, &SenderObject::signal1, [&slot3Called] { slot3Called++; }); + slot1Called++; + QObject::disconnect(*c1); + }); + de.clear(); + connect(&obj, &SenderObject::signal1, [&slot2Called] { slot2Called++; }); + obj.emitSignal1(); + QCOMPARE(slot1Called, 1); + QCOMPARE(slot2Called, 2); // because also called from ~DestroyEmit + QCOMPARE(slot3Called, 1); + } +} + +void tst_QObject::disconnectDisconnects() +{ + // Test what happens if the destructor of an functor slot also disconnects more slot; + + SenderObject s1; + QScopedPointer<QObject> receiver(new QObject); + + auto s2 = QSharedPointer<SenderObject>::create(); + QPointer<QObject> s2_tracker = s2.data(); + int count = 0; + connect(&s1, &SenderObject::signal1, [&count] { count++; }); // α + connect(&s1, &SenderObject::signal1, receiver.data(), [s2] { QFAIL("!!"); }); // β + connect(s2.data(), &SenderObject::signal1, receiver.data(), [] { QFAIL("!!"); }); + connect(&s1, &SenderObject::signal2, receiver.data(), [] { QFAIL("!!"); }); + connect(s2.data(), &SenderObject::signal2, receiver.data(), [] { QFAIL("!!"); }); + connect(&s1, &SenderObject::signal1, [&count] { count++; }); // γ + connect(&s1, &SenderObject::signal2, [&count] { count++; }); // δ + s2.clear(); + + QVERIFY(s2_tracker); + receiver + .reset(); // this will delete the receiver which must also delete s2 as β is disconnected + QVERIFY(!s2_tracker); + // test that the data structures are still in order + s1.emitSignal1(); + QCOMPARE(count, 2); // α + γ + s1.emitSignal2(); + QCOMPARE(count, 3); // + δ +} + // Test for QtPrivate::HasQ_OBJECT_Macro Q_STATIC_ASSERT(QtPrivate::HasQ_OBJECT_Macro<tst_QObject>::Value); Q_STATIC_ASSERT(!QtPrivate::HasQ_OBJECT_Macro<SiblingDeleter>::Value); |