summaryrefslogtreecommitdiffstats
path: root/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/corelib/kernel/qobject/tst_qobject.cpp')
-rw-r--r--tests/auto/corelib/kernel/qobject/tst_qobject.cpp941
1 files changed, 814 insertions, 127 deletions
diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
index a7b47438e0..6c387fde96 100644
--- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
+++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
@@ -1,39 +1,22 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2015 Olivier Goffart <ogoffart@woboq.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2020 Olivier Goffart <ogoffart@woboq.com>
+// Copyright (C) 2021 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+// This test actually wants to practice narrowing conditions, so never define this.
+#ifdef QT_NO_NARROWING_CONVERSIONS_IN_CONNECT
+#undef QT_NO_NARROWING_CONVERSIONS_IN_CONNECT
+#endif
#include <QTest>
+#include <QtTest/private/qpropertytesthelper_p.h>
#include <QStringListModel>
#include <QAbstractEventDispatcher>
#include <QScopedValueRollback>
#include <qcoreapplication.h>
#include <qpointer.h>
+#include <qproperty.h>
#include <qtimer.h>
#include <qregularexpression.h>
#include <qmetaobject.h>
@@ -56,6 +39,8 @@
#include <math.h>
+using namespace Qt::StringLiterals;
+
class tst_QObject : public QObject
{
Q_OBJECT
@@ -75,6 +60,7 @@ private slots:
void connectNotify_connectSlotsByName();
void connectDisconnectNotify_shadowing();
void connectReferenceToIncompleteTypes();
+ void connectAutoQueuedIncomplete();
void emitInDefinedOrder();
void customTypes();
void streamCustomTypes();
@@ -96,10 +82,13 @@ private slots:
void signalBlocking();
void blockingQueuedConnection();
void childEvents();
+ void parentEvents();
void installEventFilter();
+ void installEventFilterOrder();
void deleteSelfInSlot();
void disconnectSelfInSlotAndDeleteAfterEmit();
void dumpObjectInfo();
+ void dumpObjectTree();
void connectToSender();
void qobjectConstCast();
void uniqConnection();
@@ -154,7 +143,6 @@ private slots:
void qmlConnect();
void qmlConnectToQObjectReceiver();
void exceptions();
- void noDeclarativeParentChangedOnDestruction();
void deleteLaterInAboutToBlockHandler();
void mutableFunctor();
void checkArgumentsForNarrowing();
@@ -162,6 +150,10 @@ private slots:
void functorReferencesConnection();
void disconnectDisconnects();
void singleShotConnection();
+ void objectNameBinding();
+ void emitToDestroyedClass();
+ void declarativeData();
+ void asyncCallbackHelper();
};
struct QObjectCreatedOnShutdown
@@ -519,6 +511,12 @@ void tst_QObject::qobject_castTemplate()
QVERIFY(!::qobject_cast<ReceiverObject*>(o.data()));
}
+class DerivedObj : public QObject {
+ Q_OBJECT
+public:
+ using QObject::QObject;
+};
+
void tst_QObject::findChildren()
{
QObject o;
@@ -531,6 +529,10 @@ void tst_QObject::findChildren()
QTimer t1(&o);
QTimer t121(&o12);
QTimer emptyname(&o);
+ QObject oo;
+ QObject o21(&oo);
+ QObject o22(&oo);
+ QObject o23(&oo);
Q_SET_OBJECT_NAME(o);
Q_SET_OBJECT_NAME(o1);
@@ -541,6 +543,13 @@ void tst_QObject::findChildren()
Q_SET_OBJECT_NAME(t1);
Q_SET_OBJECT_NAME(t121);
emptyname.setObjectName("");
+ Q_SET_OBJECT_NAME(oo);
+ const QUtf8StringView utf8_name = u8"utf8 ⁎ obj";
+ o21.setObjectName(utf8_name);
+ const QStringView utf16_name = u"utf16 ⁎ obj";
+ o22.setObjectName(utf16_name);
+ constexpr QLatin1StringView L1_name("L1 ⁎ obj");
+ o23.setObjectName(L1_name);
QObject *op = nullptr;
@@ -571,6 +580,27 @@ void tst_QObject::findChildren()
op = o.findChild<QObject*>("o1");
QCOMPARE(op, &o1);
+ op = oo.findChild<QObject*>(utf8_name);
+ QCOMPARE(op, &o21);
+ op = oo.findChild<QObject*>(utf8_name.chopped(1));
+ QCOMPARE(op, nullptr);
+ const QUtf8StringView utf8_name_with_trailing_data = u8"utf8 ⁎ obj_data";
+ op = oo.findChild<QObject*>(utf8_name_with_trailing_data.chopped(5));
+ QCOMPARE(op, &o21);
+ op = oo.findChild<QObject*>(utf16_name);
+ QCOMPARE(op, &o22);
+ op = oo.findChild<QObject*>(utf16_name.chopped(1));
+ QCOMPARE(op, nullptr);
+ const QStringView utf16_name_with_trailing_data = u"utf16 ⁎ obj_data";
+ op = oo.findChild<QObject*>(utf16_name_with_trailing_data.chopped(5));
+ QCOMPARE(op, &o22);
+ op = oo.findChild<QObject*>(L1_name);
+ QCOMPARE(op, &o23);
+ op = oo.findChild<QObject*>(L1_name.chopped(1));
+ QCOMPARE(op, nullptr);
+ op = oo.findChild<QObject*>((L1_name + "_data"_L1).chopped(5));
+ QCOMPARE(op, &o23);
+
QList<QObject*> l;
QList<QTimer*> tl;
@@ -746,7 +776,20 @@ void tst_QObject::findChildren()
l = o.findChildren<QObject*>(QRegularExpression("^harry$"), Qt::FindDirectChildrenOnly);
QCOMPARE(l.size(), 0);
+ DerivedObj dr1(&o111);
+ DerivedObj dr2(&o111);
+ Q_SET_OBJECT_NAME(dr1);
+ Q_SET_OBJECT_NAME(dr2);
+
// empty and null string check
+ op = o.findChild<QObject*>(Qt::FindDirectChildrenOnly);
+ QCOMPARE(op, &o1);
+ op = o.findChild<QTimer*>(Qt::FindDirectChildrenOnly);
+ QCOMPARE(op, &t1);
+ op = o.findChild<DerivedObj*>(Qt::FindDirectChildrenOnly);
+ QCOMPARE(op, nullptr);
+ op = o.findChild<DerivedObj*>(Qt::FindChildrenRecursively);
+ QCOMPARE(op, &dr1);
op = o.findChild<QObject*>(QString(), Qt::FindDirectChildrenOnly);
QCOMPARE(op, &o1);
op = o.findChild<QObject*>("", Qt::FindDirectChildrenOnly);
@@ -754,6 +797,8 @@ void tst_QObject::findChildren()
op = o.findChild<QObject*>("unnamed", Qt::FindDirectChildrenOnly);
QCOMPARE(op, static_cast<QObject *>(0));
+ l = o.findChildren<QObject*>(Qt::FindDirectChildrenOnly);
+ QCOMPARE(l.size(), 5);
l = o.findChildren<QObject*>(QString(), Qt::FindDirectChildrenOnly);
QCOMPARE(l.size(), 5);
l = o.findChildren<QObject*>("", Qt::FindDirectChildrenOnly);
@@ -905,6 +950,42 @@ void tst_QObject::connectReferenceToIncompleteTypes() {
QVERIFY(connection);
}
+struct Incomplete2;
+class QObjectWithIncomplete2 : public QObject {
+ Q_OBJECT
+
+public:
+ QObjectWithIncomplete2(QObject *parent=nullptr) : QObject(parent) {}
+signals:
+ void signalWithIncomplete(Incomplete2 *ptr);
+public slots:
+ void slotWithIncomplete(Incomplete2 *) { calledSlot = true; }
+ void run() { Q_EMIT signalWithIncomplete(nullptr); }
+public:
+ bool calledSlot = false;
+};
+
+void tst_QObject::connectAutoQueuedIncomplete()
+{
+ auto objectWithIncomplete1 = new QObjectWithIncomplete2();
+ auto objectWithIncomplete2 = new QObjectWithIncomplete2();
+ auto t = new QThread(this);
+ auto cleanup = qScopeGuard([&](){
+ t->quit();
+ QVERIFY(t->wait());
+ delete objectWithIncomplete1;
+ delete objectWithIncomplete2;
+ });
+
+ t->start();
+ objectWithIncomplete2->moveToThread(t);
+
+ connect(objectWithIncomplete2, &QObjectWithIncomplete2::signalWithIncomplete,
+ objectWithIncomplete1, &QObjectWithIncomplete2::slotWithIncomplete);
+ QMetaObject::invokeMethod(objectWithIncomplete2, "run", Qt::QueuedConnection);
+ QTRY_VERIFY(objectWithIncomplete1->calledSlot);
+}
+
static void connectDisconnectNotifyTestSlot() {}
void tst_QObject::connectDisconnectNotifyPMF()
@@ -972,7 +1053,7 @@ void tst_QObject::disconnectNotify_receiverDestroyed()
QVERIFY(QObject::connect((SenderObject *)&s, SIGNAL(signal1()),
(ReceiverObject *)&r, SLOT(slot1())));
}
- QCOMPARE(s.disconnectedSignals.count(), 1);
+ QCOMPARE(s.disconnectedSignals.size(), 1);
QCOMPARE(s.disconnectedSignals.at(0), QMetaMethod::fromSignal(&SenderObject::signal1));
s.disconnectedSignals.clear();
@@ -983,7 +1064,7 @@ void tst_QObject::disconnectNotify_receiverDestroyed()
(ReceiverObject *)&r, SLOT(slot3())));
}
- QCOMPARE(s.disconnectedSignals.count(), 1);
+ QCOMPARE(s.disconnectedSignals.size(), 1);
QCOMPARE(s.disconnectedSignals.at(0), QMetaMethod::fromSignal(&SenderObject::signal3));
s.disconnectedSignals.clear();
@@ -993,7 +1074,7 @@ void tst_QObject::disconnectNotify_receiverDestroyed()
QVERIFY(QObject::connect((SenderObject *)&s, SIGNAL(destroyed()), (ReceiverObject *)&r, SLOT(slot3())));
}
- QCOMPARE(s.disconnectedSignals.count(), 1);
+ QCOMPARE(s.disconnectedSignals.size(), 1);
QCOMPARE(s.disconnectedSignals.at(0), QMetaMethod::fromSignal(&QObject::destroyed));
}
@@ -1008,10 +1089,10 @@ void tst_QObject::disconnectNotify_metaObjConnection()
QVERIFY(c);
QVERIFY(QObject::disconnect(c));
- QCOMPARE(s.disconnectedSignals.count(), 1);
+ QCOMPARE(s.disconnectedSignals.size(), 1);
QCOMPARE(s.disconnectedSignals.at(0), QMetaMethod::fromSignal(&SenderObject::signal1));
- QCOMPARE(s.disconnectedSignals.count(), 1);
+ QCOMPARE(s.disconnectedSignals.size(), 1);
}
}
@@ -1493,8 +1574,7 @@ void tst_QObject::customTypes()
QCOMPARE(checker.received.value(), t1.value());
checker.received = t0;
- int idx = qRegisterMetaType<CustomType>("CustomType");
- QCOMPARE(QMetaType::type("CustomType"), idx);
+ qRegisterMetaType<CustomType>();
checker.disconnect();
connect(&checker, SIGNAL(signal1(CustomType)), &checker, SLOT(slot1(CustomType)),
@@ -1507,11 +1587,6 @@ void tst_QObject::customTypes()
QCoreApplication::processEvents();
QCOMPARE(checker.received.value(), t2.value());
QCOMPARE(instanceCount, 4);
-
- QVERIFY(QMetaType::isRegistered(idx));
- QCOMPARE(qRegisterMetaType<CustomType>("CustomType"), idx);
- QCOMPARE(QMetaType::type("CustomType"), idx);
- QVERIFY(QMetaType::isRegistered(idx));
}
QCOMPARE(instanceCount, 3);
}
@@ -1520,13 +1595,15 @@ void tst_QObject::streamCustomTypes()
{
QByteArray ba;
- int idx = qRegisterMetaType<CustomType>("CustomType");
+ qRegisterMetaType<CustomType>();
+
+ QMetaType metaType = QMetaType::fromType<CustomType>();
{
CustomType t1(1, 2, 3);
QCOMPARE(instanceCount, 1);
QDataStream stream(&ba, (QIODevice::OpenMode)QIODevice::WriteOnly);
- QMetaType::save(stream, idx, &t1);
+ metaType.save(stream, &t1);
}
QCOMPARE(instanceCount, 0);
@@ -1535,7 +1612,7 @@ void tst_QObject::streamCustomTypes()
CustomType t2;
QCOMPARE(instanceCount, 1);
QDataStream stream(&ba, (QIODevice::OpenMode)QIODevice::ReadOnly);
- QMetaType::load(stream, idx, &t2);
+ metaType.load(stream, &t2);
QCOMPARE(instanceCount, 1);
QCOMPARE(t2.i1, 1);
QCOMPARE(t2.i2, 2);
@@ -1794,13 +1871,15 @@ void tst_QObject::moveToThread()
QObject *child = new QObject(object);
QCOMPARE(object->thread(), currentThread);
QCOMPARE(child->thread(), currentThread);
- object->moveToThread(0);
+ QVERIFY(object->moveToThread(nullptr));
QCOMPARE(object->thread(), (QThread *)0);
QCOMPARE(child->thread(), (QThread *)0);
- object->moveToThread(currentThread);
+ QVERIFY(object->moveToThread(currentThread));
QCOMPARE(object->thread(), currentThread);
QCOMPARE(child->thread(), currentThread);
- object->moveToThread(0);
+ QTest::ignoreMessage(QtWarningMsg, "QObject::moveToThread: Cannot move objects with a parent");
+ QVERIFY(!child->moveToThread(nullptr));
+ QVERIFY(object->moveToThread(nullptr));
QCOMPARE(object->thread(), (QThread *)0);
QCOMPARE(child->thread(), (QThread *)0);
// can delete an object with no thread anywhere
@@ -1971,7 +2050,7 @@ void tst_QObject::property()
const int idx = mo->indexOfProperty("variant");
QVERIFY(idx != -1);
- QCOMPARE(QMetaType::Type(mo->property(idx).type()), QMetaType::QVariant);
+ QCOMPARE(mo->property(idx).userType(), QMetaType::QVariant);
QCOMPARE(object.property("variant"), QVariant());
QVariant variant1(42);
QVariant variant2("string");
@@ -1990,7 +2069,7 @@ void tst_QObject::property()
QVERIFY(!property.isEnumType());
QCOMPARE(property.typeName(), "CustomType*");
qRegisterMetaType<CustomType*>();
- QCOMPARE(property.type(), QVariant::UserType);
+ QCOMPARE_GE(property.typeId(), QMetaType::User);
QCOMPARE(property.userType(), qMetaTypeId<CustomType*>());
CustomType *customPointer = nullptr;
@@ -2005,7 +2084,7 @@ void tst_QObject::property()
property = mo->property(mo->indexOfProperty("custom"));
QVERIFY(property.isWritable());
QCOMPARE(property.typeName(), "CustomType*");
- QCOMPARE(property.type(), QVariant::UserType);
+ QCOMPARE_GE(property.typeId(), QMetaType::User);
QCOMPARE(property.userType(), qMetaTypeId<CustomType*>());
QVERIFY(object.setProperty("custom", customVariant));
@@ -2035,13 +2114,13 @@ void tst_QObject::property()
QCOMPARE(object.property("priority").toInt(), 0);
// now it's registered, so it works as expected
- int priorityMetaTypeId = qRegisterMetaType<PropertyObject::Priority>("PropertyObject::Priority");
+ int priorityMetaTypeId = qRegisterMetaType<PropertyObject::Priority>();
QVERIFY(mo->indexOfProperty("priority") != -1);
property = mo->property(mo->indexOfProperty("priority"));
QVERIFY(property.isEnumType());
QCOMPARE(property.typeName(), "PropertyObject::Priority");
- QCOMPARE(property.type(), QVariant::UserType);
+ QCOMPARE_GE(property.typeId(), QMetaType::User);
QCOMPARE(property.userType(), priorityMetaTypeId);
var = object.property("priority");
@@ -2064,7 +2143,7 @@ void tst_QObject::property()
object.setProperty("priority", var);
QCOMPARE(qvariant_cast<PropertyObject::Priority>(object.property("priority")), PropertyObject::High);
- qRegisterMetaType<CustomString>("CustomString");
+ qRegisterMetaType<CustomString>();
QVERIFY(mo->indexOfProperty("customString") != -1);
QCOMPARE(object.property("customString").toString(), QString());
object.setCustomString("String1");
@@ -2137,18 +2216,18 @@ void tst_QObject::metamethod()
QVERIFY(!(m.attributes() & QMetaMethod::Compatibility));
m = mobj->method(mobj->indexOfMethod("invoke1()"));
- QCOMPARE(m.parameterNames().count(), 0);
- QCOMPARE(m.parameterTypes().count(), 0);
+ QCOMPARE(m.parameterNames().size(), 0);
+ QCOMPARE(m.parameterTypes().size(), 0);
m = mobj->method(mobj->indexOfMethod("invoke2(int)"));
- QCOMPARE(m.parameterNames().count(), 1);
- QCOMPARE(m.parameterTypes().count(), 1);
+ QCOMPARE(m.parameterNames().size(), 1);
+ QCOMPARE(m.parameterTypes().size(), 1);
QCOMPARE(m.parameterTypes().at(0), QByteArray("int"));
QVERIFY(m.parameterNames().at(0).isEmpty());
m = mobj->method(mobj->indexOfMethod("invoke3(int,int)"));
- QCOMPARE(m.parameterNames().count(), 2);
- QCOMPARE(m.parameterTypes().count(), 2);
+ QCOMPARE(m.parameterNames().size(), 2);
+ QCOMPARE(m.parameterTypes().size(), 2);
QCOMPARE(m.parameterTypes().at(0), QByteArray("int"));
QCOMPARE(m.parameterNames().at(0), QByteArray("hinz"));
QCOMPARE(m.parameterTypes().at(1), QByteArray("int"));
@@ -2903,7 +2982,7 @@ void tst_QObject::floatProperty()
QVERIFY(idx > 0);
QMetaProperty prop = obj.metaObject()->property(idx);
QVERIFY(prop.isValid());
- QCOMPARE(int(prop.type()), QMetaType::type("float"));
+ QCOMPARE(prop.typeId(), QMetaType::fromType<float>().id());
QVERIFY(!prop.write(&obj, QVariant("Hello")));
QVERIFY(prop.write(&obj, QVariant::fromValue(128.0f)));
QVariant v = prop.read(&obj);
@@ -2918,7 +2997,7 @@ void tst_QObject::qrealProperty()
QVERIFY(idx > 0);
QMetaProperty prop = obj.metaObject()->property(idx);
QVERIFY(prop.isValid());
- QCOMPARE(int(prop.type()), QMetaType::type("qreal"));
+ QCOMPARE(prop.typeId(), QMetaType::fromType<qreal>().id());
QVERIFY(!prop.write(&obj, QVariant("Hello")));
QVERIFY(prop.write(&obj, QVariant::fromValue(128.0f)));
@@ -2963,31 +3042,31 @@ void tst_QObject::dynamicProperties()
// set a dynamic property
QVERIFY(!obj.setProperty("myuserproperty", "Hello"));
- QCOMPARE(obj.changedDynamicProperties.count(), 1);
+ QCOMPARE(obj.changedDynamicProperties.size(), 1);
QCOMPARE(obj.changedDynamicProperties.first(), QByteArray("myuserproperty"));
//check if there is no redundant DynamicPropertyChange events
QVERIFY(!obj.setProperty("myuserproperty", "Hello"));
- QCOMPARE(obj.changedDynamicProperties.count(), 1);
+ QCOMPARE(obj.changedDynamicProperties.size(), 1);
- QCOMPARE(obj.property("myuserproperty").type(), QVariant::String);
+ QCOMPARE(obj.property("myuserproperty").typeId(), QMetaType::QString);
QCOMPARE(obj.property("myuserproperty").toString(), QString("Hello"));
- QCOMPARE(obj.dynamicPropertyNames().count(), 1);
+ QCOMPARE(obj.dynamicPropertyNames().size(), 1);
QCOMPARE(obj.dynamicPropertyNames().first(), QByteArray("myuserproperty"));
// change type of the dynamic property
obj.changedDynamicProperties.clear();
QVERIFY(!obj.setProperty("myuserproperty", QByteArray("Hello")));
- QCOMPARE(obj.changedDynamicProperties.count(), 1);
+ QCOMPARE(obj.changedDynamicProperties.size(), 1);
QCOMPARE(obj.changedDynamicProperties.first(), QByteArray("myuserproperty"));
- QCOMPARE(obj.property("myuserproperty").type(), QVariant::ByteArray);
+ QCOMPARE(obj.property("myuserproperty").typeId(), QMetaType::QByteArray);
QCOMPARE(obj.property("myuserproperty").toString(), QByteArray("Hello"));
// unset the property
obj.changedDynamicProperties.clear();
QVERIFY(!obj.setProperty("myuserproperty", QVariant()));
- QCOMPARE(obj.changedDynamicProperties.count(), 1);
+ QCOMPARE(obj.changedDynamicProperties.size(), 1);
QCOMPARE(obj.changedDynamicProperties.first(), QByteArray("myuserproperty"));
obj.changedDynamicProperties.clear();
@@ -3063,6 +3142,8 @@ void tst_QObject::blockingQueuedConnection()
}
}
+static int s_eventSpyCounter = -1;
+
class EventSpy : public QObject
{
Q_OBJECT
@@ -3082,14 +3163,17 @@ public:
void clear()
{
events.clear();
+ thisCounter = -1;
}
bool eventFilter(QObject *object, QEvent *event) override
{
events.append(qMakePair(object, event->type()));
+ thisCounter = ++s_eventSpyCounter;
return false;
}
+ int thisCounter = -1;
private:
EventList events;
};
@@ -3178,6 +3262,78 @@ void tst_QObject::childEvents()
}
}
+void tst_QObject::parentEvents()
+{
+#ifdef QT_BUILD_INTERNAL
+ EventSpy::EventList expected;
+
+ {
+ // Parent events not enabled
+ QObject parent;
+ QObject child;
+
+ EventSpy spy;
+ child.installEventFilter(&spy);
+
+ QCoreApplication::postEvent(&child, new QEvent(QEvent::Type(QEvent::User + 1)));
+
+ child.setParent(&parent);
+
+ QCoreApplication::postEvent(&child, new QEvent(QEvent::Type(QEvent::User + 2)));
+
+ expected =
+ EventSpy::EventList();
+ QCOMPARE(spy.eventList(), expected);
+ spy.clear();
+
+ QCoreApplication::processEvents();
+
+ expected =
+ EventSpy::EventList()
+ << qMakePair(&child, QEvent::Type(QEvent::User + 1))
+ << qMakePair(&child, QEvent::Type(QEvent::User + 2));
+ QCOMPARE(spy.eventList(), expected);
+ }
+
+ {
+ // Parent events enabled
+ QObject parent;
+ QObject child;
+ auto *childPrivate = QObjectPrivate::get(&child);
+ childPrivate->receiveParentEvents = true;
+
+ EventSpy spy;
+ child.installEventFilter(&spy);
+
+ QCoreApplication::postEvent(&child, new QEvent(QEvent::Type(QEvent::User + 1)));
+
+ child.setParent(&parent);
+ child.setParent(nullptr);
+
+ QCoreApplication::postEvent(&child, new QEvent(QEvent::Type(QEvent::User + 2)));
+
+ expected =
+ EventSpy::EventList()
+ << qMakePair(&child, QEvent::ParentAboutToChange)
+ << qMakePair(&child, QEvent::ParentChange)
+ << qMakePair(&child, QEvent::ParentAboutToChange)
+ << qMakePair(&child, QEvent::ParentChange);
+ QCOMPARE(spy.eventList(), expected);
+ spy.clear();
+
+ QCoreApplication::processEvents();
+
+ expected =
+ EventSpy::EventList()
+ << qMakePair(&child, QEvent::Type(QEvent::User + 1))
+ << qMakePair(&child, QEvent::Type(QEvent::User + 2));
+ QCOMPARE(spy.eventList(), expected);
+ }
+#else
+ QSKIP("Needs QT_BUILD_INTERNAL");
+#endif
+}
+
void tst_QObject::installEventFilter()
{
QEvent event(QEvent::User);
@@ -3219,6 +3375,70 @@ void tst_QObject::installEventFilter()
QVERIFY(spy.eventList().isEmpty());
}
+#define CHECK_FAIL(message) \
+do {\
+ if (QTest::currentTestFailed())\
+ QFAIL("failed one line above on " message);\
+} while (false)
+
+void tst_QObject::installEventFilterOrder()
+{
+ // installEventFilter() adds new objects to d_func()->extraData->eventFilters, which
+ // affects the order of calling each object's eventFilter() when processing the events.
+
+ QObject object;
+ EventSpy spy1, spy2, spy3;
+
+ auto clearSignalSpies = [&] {
+ for (auto *s : {&spy1, &spy2, &spy3})
+ s->clear();
+ s_eventSpyCounter = -1;
+ };
+
+ const EventSpy::EventList expected = { { &object, QEvent::Type(QEvent::User + 1) } };
+
+ // Call Order: from first to last
+ auto checkCallOrder = [&expected](const QList<EventSpy *> &spies) {
+ for (int i = 0; i < spies.size(); ++i) {
+ EventSpy *spy = spies.at(i);
+ QVERIFY2(spy->eventList() == expected,
+ QString("The spy %1 wasn't triggered exactly once.").arg(i).toLatin1());
+ QCOMPARE(spy->thisCounter, i);
+ }
+ };
+
+ // Install event filters and check the order of invocations:
+ // The last installed = the first called.
+ object.installEventFilter(&spy1);
+ object.installEventFilter(&spy2);
+ object.installEventFilter(&spy3);
+ clearSignalSpies();
+ QCoreApplication::postEvent(&object, new QEvent(QEvent::Type(QEvent::User + 1)));
+ QCoreApplication::processEvents();
+ checkCallOrder({ &spy3, &spy2, &spy1 });
+ CHECK_FAIL("checkCallOrder() - 1st round");
+
+ // Install event filter for `spy1` again, which reorders spy1 in `eventFilters`
+ // (the list doesn't have duplicates).
+ object.installEventFilter(&spy1);
+ clearSignalSpies();
+ QCoreApplication::postEvent(&object, new QEvent(QEvent::Type(QEvent::User + 1)));
+ QCoreApplication::processEvents();
+ checkCallOrder({ &spy1, &spy3, &spy2 });
+ CHECK_FAIL("checkCallOrder() - 2nd round");
+
+ // Remove event filter for `spy3`, ensure it's not called anymore and the
+ // existing filters order is preserved.
+ object.removeEventFilter(&spy3);
+ clearSignalSpies();
+ QCoreApplication::postEvent(&object, new QEvent(QEvent::Type(QEvent::User + 1)));
+ QCoreApplication::processEvents();
+ checkCallOrder({ &spy1, &spy2 });
+ CHECK_FAIL("checkCallOrder() - 3rd round");
+ QVERIFY(spy3.eventList().isEmpty());
+ QCOMPARE(spy3.thisCounter, -1);
+}
+
class EmitThread : public QThread
{ Q_OBJECT
public:
@@ -3402,6 +3622,32 @@ void tst_QObject::dumpObjectInfo()
a.dumpObjectInfo(); // should not crash
}
+void tst_QObject::dumpObjectTree()
+{
+ QObject a;
+ Q_SET_OBJECT_NAME(a);
+
+ QTimer b(&a);
+ Q_SET_OBJECT_NAME(b);
+
+ QObject c(&b);
+ Q_SET_OBJECT_NAME(c);
+
+ QFile f(&a);
+ Q_SET_OBJECT_NAME(f);
+
+ const char * const output[] = {
+ "QObject::a ",
+ " QTimer::b ",
+ " QObject::c ",
+ " QFile::f ",
+ };
+ for (const char *line : output)
+ QTest::ignoreMessage(QtDebugMsg, line);
+
+ a.dumpObjectTree();
+}
+
class ConnectToSender : public QObject
{ Q_OBJECT
public slots:
@@ -3579,8 +3825,6 @@ void tst_QObject::interfaceIid()
QByteArray(Bleh_iid));
QCOMPARE(QByteArray(qobject_interface_iid<Foo::Bar *>()),
QByteArray("com.qtest.foobar"));
- QCOMPARE(QByteArray(qobject_interface_iid<FooObject *>()),
- QByteArray());
}
void tst_QObject::deleteQObjectWhenDeletingEvent()
@@ -4497,6 +4741,17 @@ void tst_QObject::pointerConnect()
con = connect(&r1, &ReceiverObject::slot4 , &s, &SenderObject::signal4);
QVERIFY(!con);
QVERIFY(!QObject::disconnect(con));
+
+ //connect an arbitrary PMF to a slot
+ QTest::ignoreMessage(QtWarningMsg, "QObject::connect: signal not found in ReceiverObject");
+ con = connect(&r1, &ReceiverObject::reset, &r1, &ReceiverObject::slot1);
+ QVERIFY(!con);
+ QVERIFY(!QObject::disconnect(con));
+
+ QTest::ignoreMessage(QtWarningMsg, "QObject::connect: signal not found in ReceiverObject");
+ con = connect(&r1, &ReceiverObject::reset, &r1, [](){});
+ QVERIFY(!con);
+ QVERIFY(!QObject::disconnect(con));
}
void tst_QObject::pointerDisconnect()
@@ -4679,8 +4934,7 @@ void tst_QObject::customTypesPointer()
checker.disconnect();
- int idx = qRegisterMetaType<CustomType>("CustomType");
- QCOMPARE(QMetaType::type("CustomType"), idx);
+ qRegisterMetaType<CustomType>();
connect(&checker, &QCustomTypeChecker::signal1, &checker, &QCustomTypeChecker::slot1,
Qt::QueuedConnection);
@@ -4693,11 +4947,6 @@ void tst_QObject::customTypesPointer()
QCOMPARE(checker.received.value(), t2.value());
QCOMPARE(instanceCount, 4);
- QVERIFY(QMetaType::isRegistered(idx));
- QCOMPARE(qRegisterMetaType<CustomType>("CustomType"), idx);
- QCOMPARE(QMetaType::type("CustomType"), idx);
- QVERIFY(QMetaType::isRegistered(idx));
-
// Test auto registered type (QList<CustomType>)
QList<CustomType> list;
QCOMPARE(instanceCount, 4);
@@ -5218,7 +5467,7 @@ namespace ManyArgumentNamespace {
}
};
- struct Funct6 {
+ struct Funct6 final {
void operator()(const QString &a, const QString &b, const QString &c, const QString&d, const QString&e, const QString&f) {
MANYARGUMENT_COMPARE(a); MANYARGUMENT_COMPARE(b); MANYARGUMENT_COMPARE(c);
MANYARGUMENT_COMPARE(d); MANYARGUMENT_COMPARE(e); MANYARGUMENT_COMPARE(f);
@@ -5932,10 +6181,10 @@ class ConnectToPrivateSlotPrivate;
class ConnectToPrivateSlot :public QObject {
Q_OBJECT
+ Q_DECLARE_PRIVATE(ConnectToPrivateSlot)
public:
ConnectToPrivateSlot();
void test(SenderObject *obj1) ;
- Q_DECLARE_PRIVATE(ConnectToPrivateSlot)
};
class ConnectToPrivateSlotPrivate : public QObjectPrivate {
@@ -5952,6 +6201,8 @@ public:
receivedCount++;
receivedValue = v;
};
+
+ void testFromPrivate(SenderObject *obj);
};
ConnectToPrivateSlot::ConnectToPrivateSlot(): QObject(*new ConnectToPrivateSlotPrivate) {}
@@ -5978,6 +6229,14 @@ void ConnectToPrivateSlot::test(SenderObject* obj1) {
QVERIFY(!QObjectPrivate::disconnect(obj1, &SenderObject::signal2, d, &ConnectToPrivateSlotPrivate::thisIsAPrivateSlot));
}
+// Compile test to verify that we can use QObjectPrivate::connect in
+// the code of the private class, even if Q_DECLARE_PUBLIC is used in the
+// private section of the private class.
+void ConnectToPrivateSlotPrivate::testFromPrivate(SenderObject *obj)
+{
+ QVERIFY(QObjectPrivate::connect(obj, &SenderObject::signal1, this, &ConnectToPrivateSlotPrivate::thisIsAPrivateSlot));
+}
+
void tst_QObject::connectPrivateSlots()
{
SenderObject sender;
@@ -6014,6 +6273,7 @@ void tst_QObject::connectFunctorArgDifference()
connect(&timer, &QTimer::timeout, [=](){});
connect(&timer, &QTimer::objectNameChanged, [=](const QString &){});
+ connect(&timer, &QTimer::objectNameChanged, this, [](){});
connect(qApp, &QCoreApplication::aboutToQuit, [=](){});
connect(&timer, &QTimer::objectNameChanged, [=](){});
@@ -6238,13 +6498,49 @@ void tst_QObject::connectFunctorWithContextUnique()
SenderObject sender;
ReceiverObject receiver;
- QObject::connect(&sender, &SenderObject::signal1, &receiver, &ReceiverObject::slot1);
+ QVERIFY(QObject::connect(&sender, &SenderObject::signal1, &receiver, &ReceiverObject::slot1));
receiver.count_slot1 = 0;
- QObject::connect(&sender, &SenderObject::signal1, &receiver, SlotFunctor(), Qt::UniqueConnection);
+ QVERIFY(QObject::connect(&sender, &SenderObject::signal2, &receiver, &ReceiverObject::slot2));
+ receiver.count_slot2 = 0;
+
+ const auto oredType = Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection);
+
+ // Will assert in debug builds, so only test in release builds
+#if defined(QT_NO_DEBUG) && !defined(QT_FORCE_ASSERTS)
+ auto ignoreMsg = [] {
+ QTest::ignoreMessage(QtWarningMsg,
+ "QObject::connect(SenderObject, ReceiverObject): unique connections "
+ "require a pointer to member function of a QObject subclass");
+ };
+
+ ignoreMsg();
+ QVERIFY(!QObject::connect(&sender, &SenderObject::signal1, &receiver, [&](){ receiver.slot1(); }, Qt::UniqueConnection));
+
+ ignoreMsg();
+ QVERIFY(!QObject::connect(
+ &sender, &SenderObject::signal2, &receiver, [&]() { receiver.slot2(); }, oredType));
+#endif
sender.emitSignal1();
QCOMPARE(receiver.count_slot1, 1);
+
+ sender.emitSignal2();
+ QCOMPARE(receiver.count_slot2, 1);
+
+ // Check connecting to PMF doesn't hit the assert
+
+ QVERIFY(QObject::connect(&sender, &SenderObject::signal3, &receiver, &ReceiverObject::slot3,
+ Qt::UniqueConnection));
+ receiver.count_slot3 = 0;
+ sender.emitSignal3();
+ QCOMPARE(receiver.count_slot3, 1);
+
+ QVERIFY(QObject::connect(&sender, &SenderObject::signal4, &receiver, &ReceiverObject::slot4,
+ oredType));
+ receiver.count_slot4 = 0;
+ sender.emitSignal4();
+ QCOMPARE(receiver.count_slot4, 1);
}
class MyFunctor
@@ -6751,7 +7047,11 @@ struct QmlReceiver : public QtPrivate::QSlotObjectBase
, magic(0)
{}
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
static void impl(int which, QSlotObjectBase *this_, QObject *, void **metaArgs, bool *ret)
+#else
+ static void impl(QSlotObjectBase *this_, QObject *, void **metaArgs, int which, bool *ret)
+#endif
{
switch (which) {
case Destroy: delete static_cast<QmlReceiver*>(this_); return;
@@ -6993,42 +7293,6 @@ void tst_QObject::exceptions()
#endif
}
-#ifdef QT_BUILD_INTERNAL
-static bool parentChangeCalled = false;
-
-static void testParentChanged(QAbstractDeclarativeData *, QObject *, QObject *)
-{
- parentChangeCalled = true;
-}
-#endif
-
-void tst_QObject::noDeclarativeParentChangedOnDestruction()
-{
-#ifdef QT_BUILD_INTERNAL
- typedef void (*ParentChangedCallback)(QAbstractDeclarativeData *, QObject *, QObject *);
- QScopedValueRollback<ParentChangedCallback> rollback(QAbstractDeclarativeData::parentChanged);
- QAbstractDeclarativeData::parentChanged = testParentChanged;
-
- QObject *parent = new QObject;
- QObject *child = new QObject;
-
- QAbstractDeclarativeData dummy;
- QObjectPrivate::get(child)->declarativeData = &dummy;
-
- parentChangeCalled = false;
- child->setParent(parent);
-
- QVERIFY(parentChangeCalled);
- parentChangeCalled = false;
-
- delete child;
- QVERIFY(!parentChangeCalled);
-
- delete parent;
-#else
- QSKIP("Needs QT_BUILD_INTERNAL");
-#endif
-}
struct MutableFunctor {
int count;
@@ -7050,6 +7314,12 @@ void tst_QObject::mutableFunctor()
void tst_QObject::checkArgumentsForNarrowing()
{
+ // Clang and ICC masquerade as GCC, so introduce a more strict define
+ // for exactly GCC (to exclude/include it from some tests).
+#if defined(Q_CC_GNU) && !defined(Q_CC_CLANG)
+#define Q_CC_EXACTLY_GCC Q_CC_GNU
+#endif
+
enum UnscopedEnum { UnscopedEnumV1 = INT_MAX, UnscopedEnumV2 };
enum SignedUnscopedEnum { SignedUnscopedEnumV1 = INT_MIN, SignedUnscopedEnumV2 = INT_MAX };
@@ -7067,7 +7337,7 @@ void tst_QObject::checkArgumentsForNarrowing()
// GCC < 9 does not consider floating point to bool to be narrowing,
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65043
-#if !defined(Q_CC_GNU) || Q_CC_GNU >= 900
+#if !defined(Q_CC_EXACTLY_GCC) || Q_CC_EXACTLY_GCC >= 900
NARROWS(float, bool);
NARROWS(double, bool);
NARROWS(long double, bool);
@@ -7100,10 +7370,10 @@ void tst_QObject::checkArgumentsForNarrowing()
FITS(float, double);
FITS(float, long double);
- // GCC thinks this is narrowing only on architectures where
+ // GCC < 11 thinks this is narrowing only on architectures where
// sizeof(long double) > sizeof(double)
- // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92856
-#if defined(Q_CC_GNU)
+ // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94590
+#if defined(Q_CC_EXACTLY_GCC) && (Q_CC_EXACTLY_GCC < 1100)
NARROWS_IF(long double, double, sizeof(long double) > sizeof(double));
#else
NARROWS(long double, double);
@@ -7253,11 +7523,14 @@ void tst_QObject::checkArgumentsForNarrowing()
// integral to integral with different signedness. smaller ones tested above
NARROWS(signed char, unsigned char);
+
+ // Issue is reported to Green Hills, 2021-09-14.
+#if !defined(Q_CC_GHS)
NARROWS(signed char, unsigned short);
NARROWS(signed char, unsigned int);
NARROWS(signed char, unsigned long);
NARROWS(signed char, unsigned long long);
-
+#endif // Q_CC_GHS
NARROWS(unsigned char, signed char);
FITS(unsigned char, short);
FITS(unsigned char, int);
@@ -7265,19 +7538,23 @@ void tst_QObject::checkArgumentsForNarrowing()
FITS(unsigned char, long long);
NARROWS(short, unsigned short);
+#if !defined(Q_CC_GHS)
NARROWS(short, unsigned int);
NARROWS(short, unsigned long);
- NARROWS(short, unsigned long long);
+ NARROWS(short, unsigned long long);
+#endif // Q_CC_GHS
NARROWS(unsigned short, short);
+
FITS(unsigned short, int);
FITS(unsigned short, long);
FITS(unsigned short, long long);
NARROWS(int, unsigned int);
+#if !defined(Q_CC_GHS)
NARROWS(int, unsigned long);
NARROWS(int, unsigned long long);
-
+#endif // Q_CC_GHS
NARROWS(unsigned int, int);
NARROWS_IF(unsigned int, long, (sizeof(int) >= sizeof(long)));
FITS(unsigned int, long long);
@@ -7335,18 +7612,22 @@ void tst_QObject::checkArgumentsForNarrowing()
/* implicit */ operator double() const { return 42.0; }
};
+#if !defined(Q_CC_GHS)
NARROWS(ConvertingToDouble, char);
NARROWS(ConvertingToDouble, short);
NARROWS(ConvertingToDouble, int);
NARROWS(ConvertingToDouble, long);
NARROWS(ConvertingToDouble, long long);
NARROWS(ConvertingToDouble, float);
+#endif // Q_CC_GHS
FITS(ConvertingToDouble, double);
FITS(ConvertingToDouble, long double);
- // no compiler still implements this properly.
-#if 0
+ // GCC, GHS and clang don't implement this properly yet:
+ // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99625
+ // https://bugs.llvm.org/show_bug.cgi?id=49676
+#if defined(Q_CC_MSVC) && !defined(Q_CC_CLANG) // at least since VS2017
struct ConstructibleFromInt {
/* implicit */ ConstructibleFromInt(int) {}
};
@@ -7365,7 +7646,9 @@ void tst_QObject::checkArgumentsForNarrowing()
class ForwardDeclared;
FITS(ForwardDeclared, ForwardDeclared);
-#if 0 // waiting for official compiler releases that implement P1957...
+#if (defined(Q_CC_EXACTLY_GCC) && Q_CC_EXACTLY_GCC >= 1100) \
+ || (defined(Q_CC_CLANG) && Q_CC_CLANG >= 1100) \
+ || defined(Q_CC_MSVC) // at least since VS2017
{
// wg21.link/P1957
NARROWS(char*, bool);
@@ -7377,6 +7660,10 @@ void tst_QObject::checkArgumentsForNarrowing()
#undef FITS_IF
#undef NARROWS
#undef FITS
+
+#ifdef Q_CC_EXACTLY_GCC
+#undef Q_CC_EXACTLY_GCC
+#endif
}
void tst_QObject::nullReceiver()
@@ -8138,9 +8425,409 @@ void tst_QObject::singleShotConnection()
}
}
+void tst_QObject::objectNameBinding()
+{
+ QObject obj;
+ QTestPrivate::testReadWritePropertyBasics<QObject, QString>(obj, "test1", "test2",
+ "objectName");
+}
+
+namespace EmitToDestroyedClass {
+static int assertionCallCount = 0;
+static int wouldHaveAssertedCount = 0;
+struct WouldAssert : std::exception {};
+class Base : public QObject
+{
+ Q_OBJECT
+public:
+ ~Base()
+ {
+ try {
+ emit theSignal();
+ } catch (const WouldAssert &) {
+ ++wouldHaveAssertedCount;
+ }
+ }
+
+signals:
+ void theSignal();
+};
+
+class Derived : public Base
+{
+ Q_OBJECT
+public:
+ ~Derived() { }
+
+public slots:
+ void doNothing() {}
+};
+} // namespace EmitToDestroyedClass
+
+QT_BEGIN_NAMESPACE
+namespace QtPrivate {
+template<> void assertObjectType<EmitToDestroyedClass::Derived>(QObject *o)
+{
+ // override the assertion so we don't assert and so something does happen
+ // when assertions are disabled. By throwing, we also prevent the UB from
+ // happening.
+ using namespace EmitToDestroyedClass;
+ ++assertionCallCount;
+ if (!qobject_cast<Derived *>(o))
+ throw WouldAssert();
+}
+}
+QT_END_NAMESPACE
+
+void tst_QObject::emitToDestroyedClass()
+{
+ using namespace EmitToDestroyedClass;
+ std::unique_ptr ptr = std::make_unique<Derived>();
+ QObject::connect(ptr.get(), &Base::theSignal, ptr.get(), &Derived::doNothing);
+ QCOMPARE(assertionCallCount, 0);
+ QCOMPARE(wouldHaveAssertedCount, 0);
+
+ // confirm our replacement function did get called
+ emit ptr->theSignal();
+ QCOMPARE(assertionCallCount, 1);
+ QCOMPARE(wouldHaveAssertedCount, 0);
+
+ ptr.reset();
+ QCOMPARE(assertionCallCount, 2);
+ QCOMPARE(wouldHaveAssertedCount, 1);
+}
+
// Test for QtPrivate::HasQ_OBJECT_Macro
static_assert(QtPrivate::HasQ_OBJECT_Macro<tst_QObject>::Value);
static_assert(!QtPrivate::HasQ_OBJECT_Macro<SiblingDeleter>::Value);
+Q_DECLARE_SMART_POINTER_METATYPE(std::shared_ptr)
+Q_DECLARE_SMART_POINTER_METATYPE(std::unique_ptr)
+
+
+// QTBUG-103741: OK to use smart pointers to const QObject in signals/slots
+class SenderWithSharedPointerConstQObject : public QObject
+{
+ Q_OBJECT
+
+signals:
+ void aSignal1(const QSharedPointer<const QObject> &);
+ void aSignal2(const QWeakPointer<const QObject> &);
+ void aSignal3(const QPointer<const QObject> &);
+ void aSignal4(const std::shared_ptr<const QObject> &);
+ void aSignal5(const std::unique_ptr<const QObject> &);
+};
+
+#ifdef QT_BUILD_INTERNAL
+/*
+ Since QObjectPrivate stores the declarativeData pointer in a union with the pointer
+ to the currently destroyed child, calls to the QtDeclarative handlers need to be
+ correctly guarded. QTBUG-105286
+*/
+namespace QtDeclarative {
+static QAbstractDeclarativeData *theData;
+
+static void destroyed(QAbstractDeclarativeData *data, QObject *)
+{
+ QCOMPARE(data, theData);
+}
+static void signalEmitted(QAbstractDeclarativeData *data, QObject *, int, void **)
+{
+ QCOMPARE(data, theData);
+}
+// we can't use QCOMPARE in the next two functions, as they don't return void
+static int receivers(QAbstractDeclarativeData *data, const QObject *, int)
+{
+ QTest::qCompare(data, theData, "data", "theData", __FILE__, __LINE__);
+ return 0;
+}
+static bool isSignalConnected(QAbstractDeclarativeData *data, const QObject *, int)
+{
+ QTest::qCompare(data, theData, "data", "theData", __FILE__, __LINE__);
+ return true;
+}
+
+class Object : public QObject
+{
+ Q_OBJECT
+public:
+ using QObject::QObject;
+ ~Object()
+ {
+ if (Object *p = static_cast<Object *>(parent()))
+ p->emitSignal();
+ }
+
+ void emitSignal()
+ {
+ emit theSignal();
+ }
+
+signals:
+ void theSignal();
+};
+
+}
+#endif
+
+void tst_QObject::declarativeData()
+{
+#ifdef QT_BUILD_INTERNAL
+ QScopedValueRollback destroyed(QAbstractDeclarativeData::destroyed,
+ QtDeclarative::destroyed);
+ QScopedValueRollback signalEmitted(QAbstractDeclarativeData::signalEmitted,
+ QtDeclarative::signalEmitted);
+ QScopedValueRollback receivers(QAbstractDeclarativeData::receivers,
+ QtDeclarative::receivers);
+ QScopedValueRollback isSignalConnected(QAbstractDeclarativeData::isSignalConnected,
+ QtDeclarative::isSignalConnected);
+
+ QtDeclarative::Object p;
+ QObjectPrivate *priv = QObjectPrivate::get(&p);
+ priv->declarativeData = QtDeclarative::theData = new QAbstractDeclarativeData;
+
+ connect(&p, &QtDeclarative::Object::theSignal, &p, []{
+ });
+
+ QtDeclarative::Object *child = new QtDeclarative::Object;
+ child->setParent(&p);
+#endif
+}
+
+/*
+ Compile-time test for the helpers in qobjectdefs_impl.h.
+*/
+class AsyncCaller : public QObject
+{
+ Q_OBJECT
+public:
+ ~AsyncCaller()
+ {
+ if (slotObject)
+ slotObject->destroyIfLastRef();
+ }
+ void callback0() {}
+ void callback1(const QString &) {}
+ void callbackInt(int) {}
+ int returnInt() const { return 0; }
+
+ static int staticCallback0() { return 0; }
+ static void staticCallback1(const QString &) {}
+
+ using Prototype0 = int(*)();
+ using Prototype1 = void(*)(QString);
+
+ template<typename Functor>
+ bool callMe0(const typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType *, Functor &&func)
+ {
+ if (slotObject) {
+ slotObject->destroyIfLastRef();
+ slotObject = nullptr;
+ }
+ QtPrivate::AssertCompatibleFunctions<Prototype0, Functor>();
+ slotObject = QtPrivate::makeCallableObject<Prototype0>(std::forward<Functor>(func));
+ return true;
+ }
+
+ template<typename Functor>
+ bool callMe0(Functor &&func)
+ {
+ return callMe0(nullptr, std::forward<Functor>(func));
+ }
+
+ template<typename Functor>
+ bool callMe1(const typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType *, Functor &&func)
+ {
+ if (slotObject) {
+ slotObject->destroyIfLastRef();
+ slotObject = nullptr;
+ }
+ QtPrivate::AssertCompatibleFunctions<Prototype1, Functor>();
+ slotObject = QtPrivate::makeCallableObject<Prototype1>(std::forward<Functor>(func));
+ return true;
+ }
+
+ template<typename Functor>
+ bool callMe1(Functor &&func)
+ {
+ return callMe1(nullptr, std::forward<Functor>(func));
+ }
+
+ QtPrivate::QSlotObjectBase *slotObject = nullptr;
+};
+
+static void freeFunction0() {}
+static void freeFunction1(QString) {}
+static void freeFunctionVariant(QVariant) {}
+
+template<typename Prototype, typename Functor>
+inline constexpr bool compiles(Functor &&) {
+ return QtPrivate::AreFunctionsCompatible<Prototype, Functor>::value;
+}
+
+void tst_QObject::asyncCallbackHelper()
+{
+ int result = 0;
+ QString arg1 = "Parameter";
+ void *argv[] = { &result, &arg1 };
+
+ auto lambda0 = []{};
+ auto lambda1 = [](const QString &) {};
+ auto lambda2 = [](const QString &, int) {};
+ const auto constLambda = [](const QString &) {};
+ auto moveOnlyLambda = [u = std::unique_ptr<int>()]{};
+ auto moveOnlyLambda1 = [u = std::unique_ptr<int>()](const QString &){};
+
+ SlotFunctor functor0;
+ SlotFunctorString functor1;
+
+ // no parameters provided or needed
+ static_assert(compiles<AsyncCaller::Prototype0>(&AsyncCaller::callback0));
+ static_assert(compiles<AsyncCaller::Prototype0>(&AsyncCaller::staticCallback0));
+ static_assert(compiles<AsyncCaller::Prototype0>(lambda0));
+ static_assert(compiles<AsyncCaller::Prototype0>(std::move(moveOnlyLambda)));
+ static_assert(compiles<AsyncCaller::Prototype0>(freeFunction0));
+ static_assert(compiles<AsyncCaller::Prototype0>(functor0));
+
+ // more parameters than needed
+ static_assert(compiles<AsyncCaller::Prototype1>(&AsyncCaller::callback0));
+ static_assert(compiles<AsyncCaller::Prototype1>(&AsyncCaller::staticCallback0));
+ static_assert(compiles<AsyncCaller::Prototype1>(lambda0));
+ static_assert(compiles<AsyncCaller::Prototype1>(freeFunction0));
+ static_assert(compiles<AsyncCaller::Prototype1>(functor0));
+
+ // matching parameter
+ static_assert(compiles<AsyncCaller::Prototype1>(&AsyncCaller::callback1));
+ static_assert(compiles<AsyncCaller::Prototype1>(&AsyncCaller::staticCallback1));
+ static_assert(compiles<AsyncCaller::Prototype1>(lambda1));
+ static_assert(compiles<AsyncCaller::Prototype1>(std::move(moveOnlyLambda1)));
+ static_assert(compiles<AsyncCaller::Prototype1>(constLambda));
+ static_assert(compiles<AsyncCaller::Prototype1>(freeFunction1));
+ static_assert(compiles<AsyncCaller::Prototype1>(functor1));
+
+ // not enough parameters
+ static_assert(!compiles<AsyncCaller::Prototype0>(&AsyncCaller::callback1));
+ static_assert(!compiles<AsyncCaller::Prototype0>(&AsyncCaller::staticCallback1));
+ static_assert(!compiles<AsyncCaller::Prototype0>(lambda1));
+ static_assert(!compiles<AsyncCaller::Prototype0>(constLambda));
+ static_assert(!compiles<AsyncCaller::Prototype0>(lambda2));
+ static_assert(!compiles<AsyncCaller::Prototype0>(freeFunction1));
+ static_assert(!compiles<AsyncCaller::Prototype0>(functor1));
+
+ // wrong parameter type
+ static_assert(!compiles<AsyncCaller::Prototype1>(&AsyncCaller::callbackInt));
+
+ // old-style slot name
+ static_assert(!compiles<AsyncCaller::Prototype0>("callback1"));
+
+ // slot with return value is ok, we just don't pass
+ // the return value through to anything.
+ static_assert(compiles<AsyncCaller::Prototype0>(&AsyncCaller::returnInt));
+
+ static_assert(compiles<AsyncCaller::Prototype1>(freeFunctionVariant));
+
+ std::function<int()> stdFunction0(&AsyncCaller::staticCallback0);
+ std::function<void(QString)> stdFunction1(&AsyncCaller::staticCallback1);
+ static_assert(compiles<AsyncCaller::Prototype0>(stdFunction0));
+ static_assert(compiles<AsyncCaller::Prototype1>(stdFunction1));
+
+ AsyncCaller caller;
+ // with context
+ QVERIFY(caller.callMe0(&caller, &AsyncCaller::callback0));
+ QVERIFY(caller.callMe0(&caller, &AsyncCaller::returnInt));
+ QVERIFY(caller.callMe0(&caller, &AsyncCaller::staticCallback0));
+ QVERIFY(caller.callMe0(&caller, lambda0));
+ QVERIFY(caller.callMe0(&caller, freeFunction0));
+ QVERIFY(caller.callMe0(&caller, std::move(moveOnlyLambda)));
+ QVERIFY(caller.callMe0(&caller, stdFunction0));
+
+ QVERIFY(caller.callMe1(&caller, &AsyncCaller::callback1));
+ QVERIFY(caller.callMe1(&caller, &AsyncCaller::staticCallback1));
+ QVERIFY(caller.callMe1(&caller, lambda1));
+ QVERIFY(caller.callMe1(&caller, freeFunction1));
+ QVERIFY(caller.callMe1(&caller, constLambda));
+ QVERIFY(caller.callMe1(&caller, stdFunction1));
+
+ // without context
+ QVERIFY(caller.callMe0(&AsyncCaller::staticCallback0));
+ QVERIFY(caller.callMe0(lambda0));
+ QVERIFY(caller.callMe0(freeFunction0));
+ QVERIFY(caller.callMe0(stdFunction0));
+
+ QVERIFY(caller.callMe1(&AsyncCaller::staticCallback1));
+ QVERIFY(caller.callMe1(lambda1));
+ QVERIFY(caller.callMe1(constLambda));
+ QVERIFY(caller.callMe1(std::move(moveOnlyLambda1)));
+ QVERIFY(caller.callMe1(freeFunction1));
+ QVERIFY(caller.callMe1(stdFunction1));
+
+ static const char *expectedPayload = "Hello World!";
+ {
+ struct MoveOnlyFunctor {
+ MoveOnlyFunctor() = default;
+ MoveOnlyFunctor(MoveOnlyFunctor &&) = default;
+ MoveOnlyFunctor(const MoveOnlyFunctor &) = delete;
+ ~MoveOnlyFunctor() = default;
+
+ int operator()() const {
+ qDebug().noquote() << payload;
+ return int(payload.length());
+ }
+ QString payload = expectedPayload;
+ } moveOnlyFunctor;
+ QVERIFY(caller.callMe0(std::move(moveOnlyFunctor)));
+ }
+ QTest::ignoreMessage(QtDebugMsg, expectedPayload);
+ caller.slotObject->call(nullptr, argv);
+ QCOMPARE(result, QLatin1String(expectedPayload).length());
+
+ // mutable lambda; same behavior as mutableFunctor - we copy the functor
+ // in the QCallableObject, so the original is not modified
+ int status = 0;
+ auto mutableLambda1 = [&status, calls = 0]() mutable { status = ++calls; };
+
+ mutableLambda1();
+ QCOMPARE(status, 1);
+ QVERIFY(caller.callMe0(mutableLambda1)); // this copies the lambda with count == 1
+ caller.slotObject->call(nullptr, argv); // this doesn't change mutableLambda1, but the copy
+ QCOMPARE(status, 2);
+ mutableLambda1();
+ QCOMPARE(status, 2); // and we are still at two
+
+ auto mutableLambda2 = [calls = 0]() mutable { return ++calls; };
+ QCOMPARE(mutableLambda2(), 1);
+ QVERIFY(caller.callMe0(mutableLambda2)); // this copies the lambda
+ caller.slotObject->call(nullptr, argv); // this call doesn't change mutableLambda2
+ QCOMPARE(mutableLambda2(), 2); // so we are still at 2
+
+ {
+ int called = -1;
+ struct MutableFunctor {
+ void operator()() { called = 0; }
+ int &called;
+ };
+ struct ConstFunctor
+ {
+ void operator()() const { called = 1; }
+ int &called;
+ };
+
+ MutableFunctor mf{called};
+ QMetaObject::invokeMethod(this, mf);
+ QCOMPARE(called, 0);
+ ConstFunctor cf{called};
+ QMetaObject::invokeMethod(this, cf);
+ QCOMPARE(called, 1);
+ QMetaObject::invokeMethod(this, [&called, u = std::unique_ptr<int>()]{ called = 2; });
+ QCOMPARE(called, 2);
+ QMetaObject::invokeMethod(this, [&called, count = 0]() mutable {
+ if (!count)
+ called = 3;
+ ++count;
+ });
+ QCOMPARE(called, 3);
+ }
+}
+
QTEST_MAIN(tst_QObject)
#include "tst_qobject.moc"