aboutsummaryrefslogtreecommitdiffstats
path: root/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp')
-rw-r--r--tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp295
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"