diff options
Diffstat (limited to 'tests/auto/quick/qquickstates/tst_qquickstates.cpp')
-rw-r--r-- | tests/auto/quick/qquickstates/tst_qquickstates.cpp | 396 |
1 files changed, 340 insertions, 56 deletions
diff --git a/tests/auto/quick/qquickstates/tst_qquickstates.cpp b/tests/auto/quick/qquickstates/tst_qquickstates.cpp index cbc36ad1b5..3bdbe29d1e 100644 --- a/tests/auto/quick/qquickstates/tst_qquickstates.cpp +++ b/tests/auto/quick/qquickstates/tst_qquickstates.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 <qtest.h> #include <QtQml/qqmlengine.h> #include <QtQml/qqmlcomponent.h> @@ -37,7 +12,8 @@ #include <QtQuick/private/qquickstategroup_p.h> #include <private/qquickitem_p.h> #include <private/qqmlproperty_p.h> -#include "../../shared/util.h" +#include <QtQuickTestUtils/private/qmlutils_p.h> +#include <QtTest/qsignalspy.h> class MyAttached : public QObject { @@ -143,7 +119,12 @@ class tst_qquickstates : public QQmlDataTest { Q_OBJECT public: - tst_qquickstates() {} + tst_qquickstates() : QQmlDataTest(QT_QMLTEST_DATADIR) + { +#ifdef QML_DISABLE_INTERNAL_DEFERRED_PROPERTIES + qputenv("QML_DISABLE_INTERNAL_DEFERRED_PROPERTIES", "1"); +#endif + } private: QByteArray fullDataPath(const QString &path) const; @@ -199,9 +180,18 @@ private slots: void revertListMemoryLeak(); void duplicateStateName(); void trivialWhen(); + void jsValueWhen_data(); + void jsValueWhen(); + void noStateOsciallation(); void parentChangeCorrectReversal(); void revertNullObjectBinding(); void bindableProperties(); + void parentChangeInvolvingBindings(); + void deferredProperties(); + void rewindAnchorChange(); + void rewindAnchorChangeSize(); + void bindingProperlyRemovedWithTransition(); + void doNotCrashOnBroken(); }; void tst_qquickstates::initTestCase() @@ -1053,23 +1043,23 @@ void tst_qquickstates::anchorRewindBug() QQuickItem * column = rect->findChild<QQuickItem*>("column"); QVERIFY(column != nullptr); - QVERIFY(!QQuickItemPrivate::get(column)->heightValid); - QVERIFY(!QQuickItemPrivate::get(column)->widthValid); + QVERIFY(!QQuickItemPrivate::get(column)->heightValid()); + QVERIFY(!QQuickItemPrivate::get(column)->widthValid()); QCOMPARE(column->height(), 200.0); QQuickItemPrivate::get(rect)->setState("reanchored"); // column height and width should stay implicit // and column's implicit resizing should still work - QVERIFY(!QQuickItemPrivate::get(column)->heightValid); - QVERIFY(!QQuickItemPrivate::get(column)->widthValid); + QVERIFY(!QQuickItemPrivate::get(column)->heightValid()); + QVERIFY(!QQuickItemPrivate::get(column)->widthValid()); QTRY_COMPARE(column->height(), 100.0); QQuickItemPrivate::get(rect)->setState(""); // column height and width should stay implicit // and column's implicit resizing should still work - QVERIFY(!QQuickItemPrivate::get(column)->heightValid); - QVERIFY(!QQuickItemPrivate::get(column)->widthValid); + QVERIFY(!QQuickItemPrivate::get(column)->heightValid()); + QVERIFY(!QQuickItemPrivate::get(column)->widthValid()); QTRY_COMPARE(column->height(), 200.0); } @@ -1346,7 +1336,7 @@ void tst_qquickstates::illegalObjectCreation() QQmlComponent component(&engine, testFileUrl("illegalObj.qml")); QList<QQmlError> errors = component.errors(); - QCOMPARE(errors.count(), 1); + QCOMPARE(errors.size(), 1); const QQmlError &error = errors.at(0); QCOMPARE(error.line(), 9); QCOMPARE(error.column(), 23); @@ -1482,7 +1472,7 @@ void tst_qquickstates::editProperties() QQuickRectangle *childRect = rect->findChild<QQuickRectangle*>("rect2"); QVERIFY(childRect != nullptr); QCOMPARE(childRect->width(), qreal(402)); - QVERIFY(QQmlPropertyPrivate::binding(QQmlProperty(childRect, "width"))); + QVERIFY(QQmlAnyBinding::ofProperty(QQmlProperty(childRect, "width"))); QCOMPARE(childRect->height(), qreal(200)); rectPrivate->setState("blue"); @@ -1501,7 +1491,7 @@ void tst_qquickstates::editProperties() rectPrivate->setState(""); - QCOMPARE(propertyChangesBlue->actions().length(), 2); + QCOMPARE(propertyChangesBlue->actions().size(), 2); QVERIFY(propertyChangesBlue->containsValue("width")); QVERIFY(!propertyChangesBlue->containsProperty("x")); QCOMPARE(propertyChangesBlue->value("width").toInt(), 50); @@ -1509,38 +1499,38 @@ void tst_qquickstates::editProperties() propertyChangesBlue->changeValue("width", 60); QCOMPARE(propertyChangesBlue->value("width").toInt(), 60); - QCOMPARE(propertyChangesBlue->actions().length(), 2); + QCOMPARE(propertyChangesBlue->actions().size(), 2); propertyChangesBlue->changeExpression("width", "myRectangle.width / 2"); QVERIFY(!propertyChangesBlue->containsValue("width")); QVERIFY(propertyChangesBlue->containsExpression("width")); QCOMPARE(propertyChangesBlue->value("width").toInt(), 0); - QCOMPARE(propertyChangesBlue->actions().length(), 2); + QCOMPARE(propertyChangesBlue->actions().size(), 2); propertyChangesBlue->changeValue("width", 50); QVERIFY(propertyChangesBlue->containsValue("width")); QVERIFY(!propertyChangesBlue->containsExpression("width")); QCOMPARE(propertyChangesBlue->value("width").toInt(), 50); - QCOMPARE(propertyChangesBlue->actions().length(), 2); + QCOMPARE(propertyChangesBlue->actions().size(), 2); - QVERIFY(QQmlPropertyPrivate::binding(QQmlProperty(childRect, "width"))); + QVERIFY(QQmlAnyBinding::ofProperty(QQmlProperty(childRect, "width"))); rectPrivate->setState("blue"); QCOMPARE(childRect->width(), qreal(50)); QCOMPARE(childRect->height(), qreal(40)); propertyChangesBlue->changeValue("width", 60); QCOMPARE(propertyChangesBlue->value("width").toInt(), 60); - QCOMPARE(propertyChangesBlue->actions().length(), 2); + QCOMPARE(propertyChangesBlue->actions().size(), 2); QCOMPARE(childRect->width(), qreal(60)); - QVERIFY(!QQmlPropertyPrivate::binding(QQmlProperty(childRect, "width"))); + QVERIFY(!QQmlAnyBinding::ofProperty(QQmlProperty(childRect, "width"))); propertyChangesBlue->changeExpression("width", "myRectangle.width / 2"); QVERIFY(!propertyChangesBlue->containsValue("width")); QVERIFY(propertyChangesBlue->containsExpression("width")); QCOMPARE(propertyChangesBlue->value("width").toInt(), 0); - QCOMPARE(propertyChangesBlue->actions().length(), 2); - QVERIFY(QQmlPropertyPrivate::binding(QQmlProperty(childRect, "width"))); + QCOMPARE(propertyChangesBlue->actions().size(), 2); + QVERIFY(QQmlAnyBinding::ofProperty(QQmlProperty(childRect, "width"))); QCOMPARE(childRect->width(), qreal(200)); propertyChangesBlue->changeValue("width", 50); @@ -1548,25 +1538,25 @@ void tst_qquickstates::editProperties() rectPrivate->setState(""); QCOMPARE(childRect->width(), qreal(402)); - QVERIFY(QQmlPropertyPrivate::binding(QQmlProperty(childRect, "width"))); + QVERIFY(QQmlAnyBinding::ofProperty(QQmlProperty(childRect, "width"))); - QCOMPARE(propertyChangesGreen->actions().length(), 2); + QCOMPARE(propertyChangesGreen->actions().size(), 2); rectPrivate->setState("green"); QCOMPARE(childRect->width(), qreal(200)); QCOMPARE(childRect->height(), qreal(100)); - QVERIFY(QQmlPropertyPrivate::binding(QQmlProperty(childRect, "width"))); + QVERIFY(QQmlAnyBinding::ofProperty(QQmlProperty(childRect, "width"))); QVERIFY(greenState->bindingInRevertList(childRect, "width")); - QCOMPARE(propertyChangesGreen->actions().length(), 2); + QCOMPARE(propertyChangesGreen->actions().size(), 2); propertyChangesGreen->removeProperty("height"); - QVERIFY(!QQmlPropertyPrivate::binding(QQmlProperty(childRect, "height"))); + QVERIFY(!QQmlAnyBinding::ofProperty(QQmlProperty(childRect, "height"))); QCOMPARE(childRect->height(), qreal(200)); QVERIFY(greenState->bindingInRevertList(childRect, "width")); QVERIFY(greenState->containsPropertyInRevertList(childRect, "width")); propertyChangesGreen->removeProperty("width"); - QVERIFY(QQmlPropertyPrivate::binding(QQmlProperty(childRect, "width"))); + QVERIFY(QQmlAnyBinding::ofProperty(QQmlProperty(childRect, "width"))); QCOMPARE(childRect->width(), qreal(402)); QVERIFY(!greenState->bindingInRevertList(childRect, "width")); QVERIFY(!greenState->containsPropertyInRevertList(childRect, "width")); @@ -1660,9 +1650,20 @@ void tst_qquickstates::QTBUG_38492() QCOMPARE(item->property("text").toString(), QString("Test")); } +static int getRefCount(const QQmlAnyBinding &binding) +{ + if (binding.isAbstractPropertyBinding()) { + return binding.asAbstractBinding()->ref; + } else { + // this temporarily adds a refcount because we construc a new untypedpropertybinding + // thus -1 + return QPropertyBindingPrivate::get(binding.asUntypedPropertyBinding())->refCount() - 1; + } +} + void tst_qquickstates::revertListMemoryLeak() { - QQmlAbstractBinding::Ptr bindingPtr; + QQmlAnyBinding bindingPtr; { QQmlEngine engine; @@ -1674,12 +1675,12 @@ void tst_qquickstates::revertListMemoryLeak() item->setState("testState"); - QQmlAbstractBinding *binding = state->bindingInRevertList(item.get(), "height").asAbstractBinding(); + auto binding = state->bindingInRevertList(item.get(), "height"); QVERIFY(binding); bindingPtr = binding; - QVERIFY(bindingPtr->ref > 1); + QVERIFY(getRefCount(bindingPtr) > 1); } - QVERIFY(bindingPtr->ref == 1); + QVERIFY(getRefCount(bindingPtr) == 1); } void tst_qquickstates::duplicateStateName() @@ -1702,6 +1703,39 @@ void tst_qquickstates::trivialWhen() QVERIFY(root); } + +void tst_qquickstates::jsValueWhen_data() +{ + QTest::addColumn<QByteArray>("fileName"); + QTest::addRow("jsObject") << QByteArray("jsValueWhen.qml"); + QTest::addRow("qmlObject") << QByteArray("jsValueWhen2.qml"); +} + +void tst_qquickstates::jsValueWhen() +{ + QFETCH(QByteArray, fileName); + QQmlEngine engine; + + QQmlComponent c(&engine, testFileUrl(fileName.constData())); + QScopedPointer<QObject> root(c.create()); + QVERIFY(root); + QVERIFY(root->property("works").toBool()); +} + +void tst_qquickstates::noStateOsciallation() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("noStateOsciallation.qml")); + QScopedPointer<QObject> root {component.create()}; + QVERIFY(root); + // set to 1 on initial transition from "" to "n2" + QCOMPARE(root->property("stateChangeCounter").toInt(), 1); + root->setProperty("number", 1); + // setting number to 1 changes directly from "n2" to "n1" + // without any intermediate transition to "" + QCOMPARE(root->property("stateChangeCounter").toInt(), 2); +} + void tst_qquickstates::parentChangeCorrectReversal() { QQmlEngine engine; @@ -1768,6 +1802,256 @@ void tst_qquickstates::bindableProperties() } } +struct Listener : QQuickItemChangeListener +{ + // We want to get notified about all the states. + constexpr static const QRectF expectations[] = { + QRectF(40, 40, 400, 400), + QRectF(40, 0, 400, 400), + QRectF(0, 0, 400, 400), + QRectF(0, 0, 800, 400), + QRectF(0, 0, 800, 200), + QRectF(0, 0, 400, 200), + QRectF(0, 20, 400, 200), + QRectF(40, 20, 400, 200), + QRectF(84, 42, 400, 200), + QRectF(84, 42, 86, 43), + QRectF(40, 40, 86, 43), + QRectF(40, 40, 400, 400), + QRectF(40, 20, 400, 400), + QRectF(40, 20, 400, 200), + QRectF(20, 20, 400, 200), + QRectF(20, 20, 200, 200), + QRectF(20, 20, 200, 300), + QRectF(20, 20, 300, 300), + QRectF(20, 30, 300, 300), + QRectF(30, 30, 300, 300), + }; + + int position = 0; + bool ok = true; + + void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &rect) override + { + if (rect != expectations[position]) { + qDebug() << position << rect; + ok = false; + } + ++position; + } +}; + +void tst_qquickstates::parentChangeInvolvingBindings() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("parentChangeInvolvingBindings.qml")); + Listener listener; + QScopedPointer<QQuickItem> root { qobject_cast<QQuickItem *>(c.create()) }; + QVERIFY2(root, qPrintable(c.errorString())); + + QObject *child = qmlContext(root.data())->objectForName(QStringLiteral("firstChild")); + QVERIFY(child); + QQuickItem *childItem = qobject_cast<QQuickItem *>(child); + QVERIFY(childItem); + QQuickItemPrivate::get(childItem)->addItemChangeListener(&listener, QQuickItemPrivate::Geometry); + + + QCOMPARE(root->property("childWidth").toInt(), 400); + QCOMPARE(root->property("childX").toInt(), 40); + QCOMPARE(root->property("childRotation").toInt(), 100); + root->setState("reparented"); + + QCOMPARE(root->property("childWidth").toInt(), 800); + QCOMPARE(root->property("childX").toInt(), 0); // x gets zeroed here, from unrelated place. + QCOMPARE(root->property("childRotation").toInt(), 200); + + root->setProperty("myrotation2", 300); + root->setHeight(200); + root->setY(20); + QCOMPARE(root->property("childRotation").toInt(), 300); + QCOMPARE(root->property("childWidth").toInt(), 400); + QCOMPARE(root->property("childX").toInt(), 40); + + QObject *inner = qmlContext(root.data())->objectForName(QStringLiteral("inner")); + QVERIFY(inner); + QQuickItem *innerItem = qobject_cast<QQuickItem *>(inner); + QVERIFY(innerItem); + + QCOMPARE(innerItem->size(), childItem->size()); + + // Does not break bindings and does not survive the state change. + // However, since the binding between x and y stays intact, we don't know + // whether x is set another time from the new y. We pass a pair of numbers that + // matches the binding. + childItem->setPosition(QPointF(84, 42)); + QCOMPARE(root->property("childX").toInt(), 84); + QVERIFY(listener.ok); + childItem->setSize(QSizeF(86, 43)); + QCOMPARE(root->property("childWidth").toInt(), 86); + QVERIFY(listener.ok); + + QCOMPARE(innerItem->size(), childItem->size()); + + QSignalSpy xSpy(childItem, SIGNAL(xChanged())); + QSignalSpy widthSpy(childItem, SIGNAL(widthChanged())); + + root->setState(""); + + QVERIFY(listener.ok); + QCOMPARE(root->property("childRotation").toInt(), 100); + + // First change to 40 via reverse(), then to 20 via binding. + QCOMPARE(xSpy.size(), 2); + + // First change to 400 via reverse(), then to 200 via binding. + QCOMPARE(widthSpy.size(), 2); + + QCOMPARE(root->property("childX").toInt(), 20); + QCOMPARE(root->property("childWidth").toInt(), 200); + + QCOMPARE(innerItem->size(), childItem->size()); + + root->setProperty("myrotation", 50); + root->setHeight(300); + QVERIFY(listener.ok); + root->setY(30); + QVERIFY(listener.ok); + QCOMPARE(root->property("childWidth").toInt(), 300); + QCOMPARE(root->property("childX").toInt(), 30); + QCOMPARE(root->property("childRotation").toInt(), 50); + + QCOMPARE(innerItem->size(), childItem->size()); +} + +void tst_qquickstates::deferredProperties() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("cleanPropertyChange.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QQuickRectangle> root(qobject_cast<QQuickRectangle *>(c.create())); + QVERIFY(root); + + QCOMPARE(root->color(), QColor(Qt::red)); + QCOMPARE(qvariant_cast<QColor>(root->property("extendedColor")), QColor(Qt::cyan)); + QCOMPARE(root->width(), 100.0); + QCOMPARE(root->height(), 100.0); + + QCOMPARE(root->state(), QString()); + root->setState(QStringLiteral("green")); + + QCOMPARE(root->color(), QColor(Qt::yellow)); + QCOMPARE(qvariant_cast<QColor>(root->property("extendedColor")), QColor(Qt::blue)); + QCOMPARE(root->width(), 90.0); + QCOMPARE(root->height(), 90.0); + + QMetaObject::invokeMethod(root.get(), "didSomething"); + const QColor green = qRgb(0x00, 0x80, 0x00); + QCOMPARE(root->color(), green); + QCOMPARE(qvariant_cast<QColor>(root->property("extendedColor")), green); + QCOMPARE(root->width(), 90.0); + QCOMPARE(root->height(), 90.0); + + root->setState(QString()); + + QCOMPARE(root->color(), QColor(Qt::red)); + QCOMPARE(qvariant_cast<QColor>(root->property("extendedColor")), QColor(Qt::cyan)); + QCOMPARE(root->width(), 100.0); + QCOMPARE(root->height(), 100.0); +} + +void tst_qquickstates::rewindAnchorChange() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("anchorRewind.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> root(c.create()); + QVERIFY(root); + + QQmlContext *context = qmlContext(root.data()); + QVERIFY(context); + + QObject *inner = context->objectForName(QStringLiteral("inner")); + QVERIFY(inner); + + QQuickItem *innerRect = qobject_cast<QQuickItem *>(inner); + QVERIFY(innerRect); + + QTRY_COMPARE(innerRect->x(), 0); + QTRY_COMPARE(innerRect->y(), 0); + QTRY_COMPARE(innerRect->width(), 200); + QTRY_COMPARE(innerRect->height(), 200); +} + +void tst_qquickstates::rewindAnchorChangeSize() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("anchorRewindSize.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + std::unique_ptr<QObject> root(c.create()); + QVERIFY(root); + + QQmlContext *context = qmlContext(root.get()); + QVERIFY(context); + + QObject *inner = context->objectForName(QStringLiteral("inner")); + QVERIFY(inner); + + QQuickItem *innerRect = qobject_cast<QQuickItem *>(inner); + QVERIFY(innerRect); + + QCOMPARE(innerRect->x(), 0); + QCOMPARE(innerRect->y(), 0); + QCOMPARE(innerRect->width(), 100); + QCOMPARE(innerRect->height(), 100); + + root->setProperty("changeState", true); + QCOMPARE(innerRect->x(), 0); + QCOMPARE(innerRect->y(), 0); + QCOMPARE(innerRect->width(), 400); + QCOMPARE(innerRect->height(), 400); + + root->setProperty("changeState", false); + QCOMPARE(innerRect->x(), 0); + QCOMPARE(innerRect->y(), 0); + QCOMPARE(innerRect->width(), 100); + QCOMPARE(innerRect->height(), 100); +} + +void tst_qquickstates::bindingProperlyRemovedWithTransition() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("removeBindingWithTransition.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> root(c.create()); + QVERIFY(root); + QQuickItem *item = qobject_cast<QQuickItem *>(root.get()); + QVERIFY(item); + + item->setProperty("toggle", false); + QTRY_COMPARE(item->width(), 300); + + item->setProperty("state1Width", 100); + QCOMPARE(item->width(), 300); + + item->setProperty("toggle", true); + QTRY_COMPARE(item->width(), 100); +} + +void tst_qquickstates::doNotCrashOnBroken() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("broken.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> root(c.create()); + QVERIFY(root); + QQuickItem *item = qobject_cast<QQuickItem *>(root.get()); + QVERIFY(item); + + QQmlListReference states(item, "states"); + QCOMPARE(states.size(), 1); + QCOMPARE(states.at(0), nullptr); +} + QTEST_MAIN(tst_qquickstates) #include "tst_qquickstates.moc" |