diff options
Diffstat (limited to 'tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp')
-rw-r--r-- | tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp | 295 |
1 files changed, 253 insertions, 42 deletions
diff --git a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp index bc2be554a5..494d765798 100644 --- a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp +++ b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp @@ -1,36 +1,12 @@ -/**************************************************************************** -** -** 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 <qtest.h> #include <QtQml/qqmlengine.h> #include <QtQml/qqmlcomponent.h> -#include <private/qqmlbind_p.h> +#include <QtQml/private/qqmlbind_p.h> +#include <QtQml/private/qqmlcomponentattached_p.h> #include <QtQuick/private/qquickrectangle_p.h> -#include "../../shared/util.h" +#include <QtQuickTestUtils/private/qmlutils_p.h> #include "WithBindableProperties.h" class tst_qqmlbinding : public QQmlDataTest @@ -59,13 +35,23 @@ private slots: void bindToQmlComponent(); void bindingDoesNoWeirdConversion(); void bindNaNToInt(); + void intOverflow(); + void generalizedGroupedProperties(); + void localSignalHandler(); + void whenEvaluatedEarlyEnough(); + void propertiesAttachedToBindingItself(); + void toggleEnableProperlyRemembersValues(); private: QQmlEngine engine; }; tst_qqmlbinding::tst_qqmlbinding() + : QQmlDataTest(QT_QMLTEST_DATADIR) { +#ifdef QML_DISABLE_INTERNAL_DEFERRED_PROPERTIES + qputenv("QML_DISABLE_INTERNAL_DEFERRED_PROPERTIES", "1"); +#endif } void tst_qqmlbinding::binding() @@ -168,8 +154,11 @@ void tst_qqmlbinding::restoreBindingValue() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("restoreBinding2.qml")); - QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(c.create())); - QVERIFY(!rect.isNull()); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(o.data()); + QVERIFY(rect); auto myItem = qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("myItem")); QVERIFY(myItem != nullptr); @@ -192,8 +181,11 @@ void tst_qqmlbinding::restoreBindingVarValue() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("restoreBinding3.qml")); - QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(c.create())); - QVERIFY(!rect.isNull()); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(o.data()); + QVERIFY(rect); auto myItem = qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("myItem")); QVERIFY(myItem != nullptr); @@ -216,8 +208,11 @@ void tst_qqmlbinding::restoreBindingJSValue() { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("restoreBinding4.qml")); - QScopedPointer<QQuickRectangle> rect(qobject_cast<QQuickRectangle*>(c.create())); - QVERIFY(!rect.isNull()); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(o.data()); + QVERIFY(rect); auto myItem = qobject_cast<QQuickRectangle*>(rect->findChild<QQuickRectangle*>("myItem")); QVERIFY(myItem != nullptr); @@ -261,8 +256,8 @@ void tst_qqmlbinding::restoreBindingWithLoop() QCOMPARE(myItem->x(), qreal(88)); //original binding restored - QString warning = c.url().toString() + QLatin1String(":9:5: QML Rectangle: Binding loop detected for property \"x\""); - QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); + QString warning = c.url().toString() + QLatin1String(R"(:\d+:\d+: QML Rectangle: Binding loop detected for property "x")"); + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(warning)); rect->setProperty("activateBinding", false); QCOMPARE(myItem->x(), qreal(88 + 100)); //if loop handling changes this could be 90 + 100 @@ -330,7 +325,7 @@ void tst_qqmlbinding::warningOnUnknownProperty() QScopedPointer<QQuickItem> item { qobject_cast<QQuickItem *>(c.create()) }; QVERIFY(item); - QCOMPARE(messageHandler.messages().count(), 1); + QCOMPARE(messageHandler.messages().size(), 1); const QString expectedMessage = c.url().toString() + QLatin1String(":6:5: QML Binding: Property 'unknown' does not exist on Item."); QCOMPARE(messageHandler.messages().first(), expectedMessage); @@ -345,7 +340,7 @@ void tst_qqmlbinding::warningOnReadOnlyProperty() QScopedPointer<QQuickItem> item { qobject_cast<QQuickItem *>(c.create()) }; QVERIFY(item); - QCOMPARE(messageHandler.messages().count(), 1); + QCOMPARE(messageHandler.messages().size(), 1); const QString expectedMessage = c.url().toString() + QLatin1String(":8:5: QML Binding: Property 'name' on Item is read-only."); QCOMPARE(messageHandler.messages().first(), expectedMessage); @@ -360,7 +355,7 @@ void tst_qqmlbinding::disabledOnUnknownProperty() QScopedPointer<QQuickItem> item { qobject_cast<QQuickItem *>(c.create()) }; QVERIFY(item); - QCOMPARE(messageHandler.messages().count(), 0); + QCOMPARE(messageHandler.messages().size(), 0); } void tst_qqmlbinding::disabledOnReadonlyProperty() @@ -371,7 +366,7 @@ void tst_qqmlbinding::disabledOnReadonlyProperty() QQmlComponent c(&engine, testFileUrl("disabledReadonly.qml")); QScopedPointer<QQuickItem> item { qobject_cast<QQuickItem *>(c.create()) }; QVERIFY(item); - QCOMPARE(messageHandler.messages().count(), 0); + QCOMPARE(messageHandler.messages().size(), 0); } void tst_qqmlbinding::delayed() @@ -381,15 +376,76 @@ void tst_qqmlbinding::delayed() QScopedPointer<QQuickItem> item {qobject_cast<QQuickItem*>(c.create())}; QVERIFY(item != nullptr); + + // objectName is not deferred + QCOMPARE(item->objectName(), QStringLiteral("c: 10")); + + // constants are never delayed + QCOMPARE(item->x(), 10.0); + QCOMPARE(item->y(), 20.0); + // update on creation QCOMPARE(item->property("changeCount").toInt(), 1); + QCOMPARE(item->property("changeCount2").toInt(), 1); QMetaObject::invokeMethod(item.get(), "updateText"); // doesn't update immediately QCOMPARE(item->property("changeCount").toInt(), 1); + QCOMPARE(item->property("changeCount2").toInt(), 1); // only updates once (non-delayed would update twice) QTRY_COMPARE(item->property("changeCount").toInt(), 2); + QTRY_COMPARE(item->property("changeCount2").toInt(), 2); + + item->setProperty("delayed", QVariant::fromValue<bool>(false)); + QCOMPARE(item->property("changeCount"), 2); + QCOMPARE(item->property("changeCount2"), 2); + + QMetaObject::invokeMethod(item.get(), "resetText"); + QCOMPARE(item->property("changeCount"), 4); + QCOMPARE(item->property("changeCount2"), 4); + + QMetaObject::invokeMethod(item.get(), "updateText"); + QCOMPARE(item->property("changeCount"), 6); + QCOMPARE(item->property("changeCount2"), 6); + + item->setProperty("delayed", QVariant::fromValue<bool>(true)); + QCOMPARE(item->property("changeCount"), 6); + QCOMPARE(item->property("changeCount2"), 6); + + QMetaObject::invokeMethod(item.get(), "resetText"); + QCOMPARE(item->property("changeCount"), 6); + QCOMPARE(item->property("changeCount2"), 6); + + QMetaObject::invokeMethod(item.get(), "updateText"); + QCOMPARE(item->property("changeCount"), 6); + QCOMPARE(item->property("changeCount2"), 6); + + item->setProperty("delayed", QVariant::fromValue<bool>(false)); + // Intermediate change is ignored + QCOMPARE(item->property("changeCount"), 6); + QCOMPARE(item->property("changeCount2"), 6); + + item->setProperty("delayed", QVariant::fromValue<bool>(true)); + QCOMPARE(item->property("changeCount"), 6); + QCOMPARE(item->property("changeCount2"), 6); + + QMetaObject::invokeMethod(item.get(), "resetText"); + QCOMPARE(item->property("changeCount"), 6); + QCOMPARE(item->property("changeCount2"), 6); + + // only updates once (non-delayed would update twice) + QTRY_COMPARE(item->property("changeCount").toInt(), 7); + QTRY_COMPARE(item->property("changeCount2").toInt(), 7); + + QMetaObject::invokeMethod(item.get(), "updateText"); + // doesn't update immediately + QCOMPARE(item->property("changeCount").toInt(), 7); + QCOMPARE(item->property("changeCount2").toInt(), 7); + + // only updates once (non-delayed would update twice) + QTRY_COMPARE(item->property("changeCount").toInt(), 8); + QTRY_COMPARE(item->property("changeCount2").toInt(), 8); } void tst_qqmlbinding::bindingOverwriting() @@ -401,9 +457,15 @@ void tst_qqmlbinding::bindingOverwriting() QQmlComponent c(&engine, testFileUrl("bindingOverwriting.qml")); QScopedPointer<QQuickItem> item {qobject_cast<QQuickItem*>(c.create())}; QVERIFY(item); + QCOMPARE(messageHandler.messages().size(), 2); + + QQmlComponent c2(&engine, testFileUrl("bindingOverwriting2.qml")); + QScopedPointer<QObject> o(c2.create()); + QVERIFY(o); + QTRY_COMPARE(o->property("i").toInt(), 123); + QCOMPARE(messageHandler.messages().size(), 3); QLoggingCategory::setFilterRules(QString()); - QCOMPARE(messageHandler.messages().count(), 2); } void tst_qqmlbinding::bindToQmlComponent() @@ -439,6 +501,155 @@ void tst_qqmlbinding::bindNaNToInt() QVERIFY(item != nullptr); QCOMPARE(item->property("val").toInt(), 0); } + +void tst_qqmlbinding::intOverflow() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("intOverflow.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> obj(c.create()); + QVERIFY(!obj.isNull()); + QCOMPARE(obj->property("b"), 5); + QCOMPARE(obj->property("a").toDouble(), 1.09951162778e+12); +} + +void tst_qqmlbinding::generalizedGroupedProperties() +{ + QQmlEngine engine; + const QUrl url = testFileUrl("generalizedGroupedProperty.qml"); + QQmlComponent c(&engine, url); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + + QTest::ignoreMessage( + QtWarningMsg, + qPrintable(QStringLiteral( + "%1:8:29: QML Binding: Unknown name \"root.objectNameChanged\". " + "The binding is ignored.").arg(url.toString()))); + QScopedPointer<QObject> root(c.create()); + QVERIFY(!root.isNull()); + + QCOMPARE(root->objectName(), QStringLiteral("barrrrr ...")); + QCOMPARE(root->property("i").toInt(), 2); + + QQmlComponentAttached *rootAttached = qobject_cast<QQmlComponentAttached *>( + qmlAttachedPropertiesObject<QQmlComponent>(root.data())); + QVERIFY(rootAttached); + QCOMPARE(rootAttached->objectName(), QStringLiteral("foo")); + + QQmlBind *child = qvariant_cast<QQmlBind *>(root->property("child")); + QVERIFY(child); + QCOMPARE(child->objectName(), QStringLiteral("barrrrr")); + QQmlComponentAttached *childAttached = qobject_cast<QQmlComponentAttached *>( + qmlAttachedPropertiesObject<QQmlComponent>(child)); + QVERIFY(childAttached); + QCOMPARE(childAttached->objectName(), QString()); + QCOMPARE(child->when(), true); + child->setWhen(false); + + QCOMPARE(root->objectName(), QStringLiteral("foo")); + QCOMPARE(root->property("i").toInt(), 112); + QCOMPARE(rootAttached->objectName(), QString()); + + QQmlBind *meanChild = qvariant_cast<QQmlBind *>(root->property("meanChild")); + QVERIFY(meanChild); + QCOMPARE(meanChild->when(), false); + + // This one is immediate + QCOMPARE(qvariant_cast<QString>(meanChild->QObject::property("extra")), + QStringLiteral("foo extra")); + + meanChild->setWhen(true); + QCOMPARE(qvariant_cast<QString>(meanChild->QObject::property("extra")), + QStringLiteral("foo extra")); + + QCOMPARE(root->objectName(), QStringLiteral("foo")); + QCOMPARE(root->property("i").toInt(), 3); + QCOMPARE(child->objectName(), QStringLiteral("bar")); + QCOMPARE(childAttached->objectName(), QStringLiteral("bar")); + + child->setWhen(true); + QCOMPARE(child->objectName(), QStringLiteral("bar")); + QCOMPARE(root->objectName(), QStringLiteral("bar ...")); + QCOMPARE(rootAttached->objectName(), QStringLiteral("foo")); + QCOMPARE(root->property("i").toInt(), 2); + + meanChild->setWhen(false); + // root->property("i") is now unspecified. Too bad. + // In fact we restore the binding from before meanChild was activated, but that's + // not what the user would expect here. We currently don't see that the value has + // been meddled with. + // TODO: Fix this. It's not related to generalized grouped properties, though. + QCOMPARE(root->objectName(), QStringLiteral("barrrrr ...")); + QCOMPARE(rootAttached->objectName(), QStringLiteral("foo")); + QCOMPARE(child->objectName(), QStringLiteral("barrrrr")); + QCOMPARE(childAttached->objectName(), QString()); + + child->setWhen(false); + QCOMPARE(root->objectName(), QStringLiteral("foo")); + // root->property("i").toInt() is still unspecified. + QCOMPARE(rootAttached->objectName(), QString()); +} + +void tst_qqmlbinding::localSignalHandler() +{ + QQmlEngine e; + QQmlComponent c(&e, testFileUrl("bindingWithHandler.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + o->setProperty("input", QStringLiteral("abc")); + QCOMPARE(o->property("output").toString(), QStringLiteral("abc")); +} + +void tst_qqmlbinding::whenEvaluatedEarlyEnough() +{ + QQmlEngine e; + QQmlComponent c(&e, testFileUrl("whenEvaluatedEarlyEnough.qml")); + QTest::failOnWarning(QRegularExpression(".*")); + std::unique_ptr<QObject> root { c.create() }; + root->setProperty("toggle", false); // should not cause warnings + // until "when" is actually true + QTest::ignoreMessage(QtMsgType::QtWarningMsg, + QRegularExpression(".*QML Binding: Property 'i' does not exist on Item.*")); + root->setProperty("forceEnable", true); +} + +void tst_qqmlbinding::propertiesAttachedToBindingItself() +{ + QQmlEngine e; + QQmlComponent c(&e, testFileUrl("propertiesAttachedToBindingItself.qml")); + QTest::failOnWarning(QRegularExpression(".*")); + std::unique_ptr<QObject> root { c.create() }; + QVERIFY2(root, qPrintable(c.errorString())); + // 0 => everything broken; 1 => normal attached properties broken; + // 2 => Component.onCompleted broken, 3 => everything works + QTRY_COMPARE(root->property("check").toInt(), 3); +} + +void tst_qqmlbinding::toggleEnableProperlyRemembersValues() +{ + QQmlEngine e; + QQmlComponent c(&e, testFileUrl("toggleEnableProperlyRemembersValues.qml")); + std::unique_ptr<QObject> root { c.create() }; + QVERIFY2(root, qPrintable(c.errorString())); + for (int i = 0; i < 3; ++i) { + { + QJSManagedValue arr(root->property("arr"), &e); + QJSManagedValue func(root->property("func"), &e); + QCOMPARE(arr.property("length").toInt(), 2); + QCOMPARE(func.call().toInt(), 1); + } + root->setProperty("enabled", true); + { + QJSManagedValue arr(root->property("arr"), &e); + QJSManagedValue func(root->property("func"), &e); + QCOMPARE(arr.property("length").toInt(), 3); + QCOMPARE(func.call().toInt(), 2); + } + root->setProperty("enabled", false); + } +} + QTEST_MAIN(tst_qqmlbinding) #include "tst_qqmlbinding.moc" |