diff options
Diffstat (limited to 'tests/auto/corelib/kernel/qobject')
5 files changed, 831 insertions, 187 deletions
diff --git a/tests/auto/corelib/kernel/qobject/CMakeLists.txt b/tests/auto/corelib/kernel/qobject/CMakeLists.txt index 7ff42f7212..46b8e2d238 100644 --- a/tests/auto/corelib/kernel/qobject/CMakeLists.txt +++ b/tests/auto/corelib/kernel/qobject/CMakeLists.txt @@ -1,20 +1,26 @@ -# Generated from qobject.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qobject Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qobject LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qobject SOURCES tst_qobject.cpp - DEFINES - QT_DISABLE_DEPRECATED_BEFORE=0 - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Network + Qt::TestPrivate ) ## Scopes: ##################################################################### add_subdirectory(signalbug) -add_dependencies(tst_qobject signalbug_helper) # special case +add_dependencies(tst_qobject signalbug_helper) diff --git a/tests/auto/corelib/kernel/qobject/signalbug/CMakeLists.txt b/tests/auto/corelib/kernel/qobject/signalbug/CMakeLists.txt index b479ada78e..aefa1554b6 100644 --- a/tests/auto/corelib/kernel/qobject/signalbug/CMakeLists.txt +++ b/tests/auto/corelib/kernel/qobject/signalbug/CMakeLists.txt @@ -1,4 +1,5 @@ -# Generated from signalbug.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## signalbug_helper Binary: diff --git a/tests/auto/corelib/kernel/qobject/signalbug/signalbug.cpp b/tests/auto/corelib/kernel/qobject/signalbug/signalbug.cpp index 57fb1eb836..bb8d7984f4 100644 --- a/tests/auto/corelib/kernel/qobject/signalbug/signalbug.cpp +++ b/tests/auto/corelib/kernel/qobject/signalbug/signalbug.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** 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) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "signalbug.h" diff --git a/tests/auto/corelib/kernel/qobject/signalbug/signalbug.h b/tests/auto/corelib/kernel/qobject/signalbug/signalbug.h index e6e40c35d9..ac124c2a1c 100644 --- a/tests/auto/corelib/kernel/qobject/signalbug/signalbug.h +++ b/tests/auto/corelib/kernel/qobject/signalbug/signalbug.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** 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) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef SIGNAL_BUG_H #define SIGNAL_BUG_H 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" |