aboutsummaryrefslogtreecommitdiffstats
path: root/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/quick/qquickanimations/tst_qquickanimations.cpp')
-rw-r--r--tests/auto/quick/qquickanimations/tst_qquickanimations.cpp435
1 files changed, 366 insertions, 69 deletions
diff --git a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
index 53216da5b7..25c8559ed3 100644
--- a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
+++ b/tests/auto/quick/qquickanimations/tst_qquickanimations.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 <QtTest/QtTest>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
@@ -41,18 +16,19 @@
#include <QtQuick/private/qquickpathinterpolator_p.h>
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qquicklistview_p.h>
+#include <QtQuick/private/qquickframeanimation_p.h>
#include <QEasingCurve>
#include <limits.h>
#include <math.h>
-#include "../../shared/util.h"
+#include <QtQuickTestUtils/private/qmlutils_p.h>
class tst_qquickanimations : public QQmlDataTest
{
Q_OBJECT
public:
- tst_qquickanimations() {}
+ tst_qquickanimations() : QQmlDataTest(QT_QMLTEST_DATADIR) {}
private slots:
void initTestCase() override
@@ -116,6 +92,15 @@ private slots:
void fastFlickingBug();
void opacityAnimationFromZero();
void alwaysRunToEndInSequentialAnimationBug();
+ void cleanupWhenRenderThreadStops();
+ void changePropertiesDuringAnimation_data();
+ void changePropertiesDuringAnimation();
+ void infiniteLoopsWithoutFrom();
+ void frameAnimation1();
+ void frameAnimation2();
+ void restartAnimationGroupWhenDirty();
+ void restartNestedAnimationGroupWhenDirty();
+ void targetsDeletedNotRemoved();
};
#define QTIMED_COMPARE(lhs, rhs) do { \
@@ -139,8 +124,8 @@ void tst_qquickanimations::simpleProperty()
QCOMPARE(animation.target(), &rect);
QCOMPARE(animation.property(), QLatin1String("x"));
QCOMPARE(animation.to().toReal(), 200.0);
- QCOMPARE(fromChangedSpy.count(), 0);
- QCOMPARE(toChangedSpy.count(), 1);
+ QCOMPARE(fromChangedSpy.size(), 0);
+ QCOMPARE(toChangedSpy.size(), 1);
animation.start();
QVERIFY(animation.isRunning());
QTest::qWait(animation.duration());
@@ -155,8 +140,8 @@ void tst_qquickanimations::simpleProperty()
QCOMPARE(animation.currentTime(), 125);
QCOMPARE(rect.x(),100.0);
animation.setFrom(100);
- QCOMPARE(fromChangedSpy.count(), 1);
- QCOMPARE(toChangedSpy.count(), 1);
+ QCOMPARE(fromChangedSpy.size(), 1);
+ QCOMPARE(toChangedSpy.size(), 1);
}
void tst_qquickanimations::simpleNumber()
@@ -171,8 +156,8 @@ void tst_qquickanimations::simpleNumber()
QCOMPARE(animation.target(), &rect);
QCOMPARE(animation.property(), QLatin1String("x"));
QCOMPARE(animation.to(), qreal(200));
- QCOMPARE(fromChangedSpy.count(), 0);
- QCOMPARE(toChangedSpy.count(), 1);
+ QCOMPARE(fromChangedSpy.size(), 0);
+ QCOMPARE(toChangedSpy.size(), 1);
animation.start();
QVERIFY(animation.isRunning());
QTest::qWait(animation.duration());
@@ -187,8 +172,8 @@ void tst_qquickanimations::simpleNumber()
QCOMPARE(animation.currentTime(), 125);
QCOMPARE(rect.x(), qreal(100));
animation.setFrom(100);
- QCOMPARE(fromChangedSpy.count(), 1);
- QCOMPARE(toChangedSpy.count(), 1);
+ QCOMPARE(fromChangedSpy.size(), 1);
+ QCOMPARE(toChangedSpy.size(), 1);
}
void tst_qquickanimations::simpleColor()
@@ -203,8 +188,8 @@ void tst_qquickanimations::simpleColor()
QCOMPARE(animation.target(), &rect);
QCOMPARE(animation.property(), QLatin1String("color"));
QCOMPARE(animation.to(), QColor("red"));
- QCOMPARE(fromChangedSpy.count(), 0);
- QCOMPARE(toChangedSpy.count(), 1);
+ QCOMPARE(fromChangedSpy.size(), 0);
+ QCOMPARE(toChangedSpy.size(), 1);
animation.start();
QVERIFY(animation.isRunning());
QTest::qWait(animation.duration());
@@ -217,18 +202,18 @@ void tst_qquickanimations::simpleColor()
QVERIFY(animation.isPaused());
animation.setCurrentTime(125);
QCOMPARE(animation.currentTime(), 125);
- QCOMPARE(rect.color(), QColor::fromRgbF(0.498039, 0, 0.498039, 1));
+ QCOMPARE(rect.color(), QColor::fromRgbF(0.498039f, 0, 0.498039f, 1));
rect.setColor(QColor("green"));
animation.setFrom(QColor("blue"));
QCOMPARE(animation.from(), QColor("blue"));
- QCOMPARE(fromChangedSpy.count(), 1);
- QCOMPARE(toChangedSpy.count(), 1);
+ QCOMPARE(fromChangedSpy.size(), 1);
+ QCOMPARE(toChangedSpy.size(), 1);
animation.restart();
QCOMPARE(rect.color(), QColor("blue"));
QVERIFY(animation.isRunning());
animation.setCurrentTime(125);
- QCOMPARE(rect.color(), QColor::fromRgbF(0.498039, 0, 0.498039, 1));
+ QCOMPARE(rect.color(), QColor::fromRgbF(0.498039f, 0, 0.498039f, 1));
}
void tst_qquickanimations::simpleRotation()
@@ -244,8 +229,8 @@ void tst_qquickanimations::simpleRotation()
QCOMPARE(animation.property(), QLatin1String("rotation"));
QCOMPARE(animation.to(), qreal(270));
QCOMPARE(animation.direction(), QQuickRotationAnimation::Numerical);
- QCOMPARE(fromChangedSpy.count(), 0);
- QCOMPARE(toChangedSpy.count(), 1);
+ QCOMPARE(fromChangedSpy.size(), 0);
+ QCOMPARE(toChangedSpy.size(), 1);
animation.start();
QVERIFY(animation.isRunning());
QTest::qWait(animation.duration());
@@ -260,8 +245,8 @@ void tst_qquickanimations::simpleRotation()
QCOMPARE(animation.currentTime(), 125);
QCOMPARE(rect.rotation(), qreal(135));
animation.setFrom(90);
- QCOMPARE(fromChangedSpy.count(), 1);
- QCOMPARE(toChangedSpy.count(), 1);
+ QCOMPARE(fromChangedSpy.size(), 1);
+ QCOMPARE(toChangedSpy.size(), 1);
}
void tst_qquickanimations::simplePath()
@@ -685,11 +670,11 @@ void tst_qquickanimations::resume()
QSignalSpy spy(&animation, SIGNAL(pausedChanged(bool)));
animation.pause();
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QVERIFY(animation.isPaused());
animation.stop();
QVERIFY(!animation.isPaused());
- QCOMPARE(spy.count(), 2);
+ QCOMPARE(spy.size(), 2);
// Load QtQuick to ensure that QQuickPropertyAnimation is registered as PropertyAnimation
{
@@ -701,12 +686,12 @@ void tst_qquickanimations::resume()
QByteArray message = "<Unknown File>: QML PropertyAnimation: setPaused() cannot be used when animation isn't running.";
QTest::ignoreMessage(QtWarningMsg, message);
animation.pause();
- QCOMPARE(spy.count(), 2);
+ QCOMPARE(spy.size(), 2);
QVERIFY(!animation.isPaused());
animation.resume();
QVERIFY(!animation.isPaused());
QVERIFY(!animation.isRunning());
- QCOMPARE(spy.count(), 2);
+ QCOMPARE(spy.size(), 2);
}
void tst_qquickanimations::dotProperty()
@@ -745,7 +730,7 @@ void tst_qquickanimations::badTypes()
QScopedPointer<QObject> obj(c.create());
QVERIFY(obj.isNull());
- QCOMPARE(c.errors().count(), 1);
+ QCOMPARE(c.errors().size(), 1);
QCOMPARE(c.errors().at(0).description(), QLatin1String("Invalid property assignment: number expected"));
}
@@ -757,7 +742,7 @@ void tst_qquickanimations::badTypes()
QScopedPointer<QObject> obj(c.create());
QVERIFY(obj.isNull());
- QCOMPARE(c.errors().count(), 1);
+ QCOMPARE(c.errors().size(), 1);
QCOMPARE(c.errors().at(0).description(), QLatin1String("Invalid property assignment: color expected"));
}
@@ -1065,12 +1050,12 @@ void tst_qquickanimations::disabledTransition()
QSignalSpy runningSpy(trans, SIGNAL(runningChanged()));
QQuickItemPrivate::get(rect)->setState("");
QCOMPARE(myRect->x(),qreal(200));
- QCOMPARE(runningSpy.count(), 1); //stopped -> running
+ QCOMPARE(runningSpy.size(), 1); //stopped -> running
QVERIFY(trans->running());
QTest::qWait(300);
QTIMED_COMPARE(myRect->x(),qreal(100));
QVERIFY(!trans->running());
- QCOMPARE(runningSpy.count(), 2); //running -> stopped
+ QCOMPARE(runningSpy.size(), 2); //running -> stopped
}
void tst_qquickanimations::invalidDuration()
@@ -1244,7 +1229,7 @@ void tst_qquickanimations::easingProperties()
QVERIFY(animObject != nullptr);
QCOMPARE(animObject->easing().type(), QEasingCurve::BezierSpline);
QVector<QPointF> points = animObject->easing().toCubicSpline();
- QCOMPARE(points.count(), 3);
+ QCOMPARE(points.size(), 3);
QCOMPARE(points.at(0), QPointF(0.5, 0.2));
QCOMPARE(points.at(1), QPointF(0.13, 0.65));
QCOMPARE(points.at(2), QPointF(1.0, 1.0));
@@ -1373,7 +1358,7 @@ void tst_qquickanimations::signalOrder()
colorAnimation->setDuration(duration);
animation->start();
- QTRY_VERIFY(finishedSpy.count());
+ QTRY_VERIFY(finishedSpy.size());
QCOMPARE(actualSignalOrder, expectedSignalOrder);
}
@@ -1696,7 +1681,7 @@ void tst_qquickanimations::unsetAnimatorProxyJobWindow()
item.setParentItem(&dummy);
QSignalSpy spy(&window, SIGNAL(sceneGraphInitialized()));
window.show();
- if (spy.count() < 1)
+ if (spy.size() < 1)
spy.wait();
QCOMPARE(proxy.job().data(), job);
}
@@ -1723,8 +1708,8 @@ void tst_qquickanimations::finished()
QVERIFY(finishedSpy.isValid());
QVERIFY(simpleTopLevelAnimation->setProperty("running", QVariant(true)));
- QTRY_COMPARE(stoppedSpy.count(), 1);
- QCOMPARE(finishedSpy.count(), 1);
+ QTRY_COMPARE(stoppedSpy.size(), 1);
+ QCOMPARE(finishedSpy.size(), 1);
// Test that the signal is properly revisioned and hence accessible from QML.
QCOMPARE(root->property("finishedUsableInQml").toBool(), true);
@@ -1751,9 +1736,9 @@ void tst_qquickanimations::finished()
QObject *transitionRect = root->property("transitionRect").value<QObject*>();
QVERIFY(transitionRect);
QVERIFY(transitionRect->setProperty("state", QVariant(QLatin1String("go"))));
- QTRY_COMPARE(runningChangedSpy.count(), 1);
- QCOMPARE(stoppedSpy.count(), 0);
- QCOMPARE(finishedSpy.count(), 0);
+ QTRY_COMPARE(runningChangedSpy.size(), 1);
+ QCOMPARE(stoppedSpy.size(), 0);
+ QCOMPARE(finishedSpy.size(), 0);
}
// Test that finished() is not emitted for animations within a Behavior.
@@ -1770,8 +1755,8 @@ void tst_qquickanimations::finished()
QVERIFY(root->setProperty("bar", QVariant(1.0)));
QTRY_COMPARE(root->property("bar").toReal(), 1.0);
- QCOMPARE(stoppedSpy.count(), 0);
- QCOMPARE(finishedSpy.count(), 0);
+ QCOMPARE(stoppedSpy.size(), 0);
+ QCOMPARE(finishedSpy.size(), 0);
}
}
@@ -1862,9 +1847,8 @@ void tst_qquickanimations::fastFlickingBug()
void tst_qquickanimations::opacityAnimationFromZero()
{
- if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
- || (QGuiApplication::platformName() == QLatin1String("minimal")))
- QSKIP("Skipping due to grabWindow not functional on offscreen/minimimal platforms");
+ if (QGuiApplication::platformName() == QLatin1String("minimal"))
+ QSKIP("Skipping due to grabWindow not functional on minimal platforms");
// not easy to verify this in threaded render loop
// since it's difficult to capture the first frame when scene graph
@@ -1898,7 +1882,7 @@ void tst_qquickanimations::opacityAnimationFromZero()
void tst_qquickanimations::alwaysRunToEndInSequentialAnimationBug()
{
- QQuickView view(QUrl::fromLocalFile("data/alwaysRunToEndInSequentialAnimationBug.qml"));
+ QQuickView view(testFileUrl("alwaysRunToEndInSequentialAnimationBug.qml"));
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
@@ -1998,6 +1982,319 @@ void tst_qquickanimations::alwaysRunToEndInSequentialAnimationBug()
QCOMPARE(whiteRect->property("opacity").value<qreal>(),1.0);
}
+void tst_qquickanimations::cleanupWhenRenderThreadStops()
+{
+ QQuickView view(testFileUrl("cleanupWhenRenderThreadStops.qml"));
+ view.show();
+ view.setPersistentGraphics(false);
+ view.setPersistentSceneGraph(false);
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+ QTest::qWait(50);
+ view.hide();
+ view.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+}
+
+// This will be called each frame and should return true for the test to pass.
+typedef std::function<bool(QQuickItem *, QString &)> PropertyValidatorFunc;
+Q_DECLARE_METATYPE(PropertyValidatorFunc)
+
+void tst_qquickanimations::changePropertiesDuringAnimation_data()
+{
+ QTest::addColumn<int>("loops");
+ QTest::addColumn<QString>("propertyName");
+ QTest::addColumn<qreal>("newValue");
+ QTest::addColumn<PropertyValidatorFunc>("propertyValidatorFunc");
+
+ // Use a value large enough to ensure that the animation is running for the duration of
+ // the test. We test both infinite and non-infinite loop counts.
+ const int largeLoopCount = 100;
+
+ const auto fromValidator = PropertyValidatorFunc([](QQuickItem *rect, QString &failureMessage){
+ if (rect->x() >= 0)
+ return true;
+ QDebug(&failureMessage) << "Expected x of rect to never go below new \"from\" value of 0, but it's" << rect->x();
+ return false;
+ });
+ QTest::newRow("from") << largeLoopCount << "from" << 0.0 << fromValidator;
+ QTest::newRow("from,infinite") << int(QQuickAbstractAnimation::Infinite) << "from" << 0.0 << fromValidator;
+
+ const auto toValidator = PropertyValidatorFunc([](QQuickItem *rect, QString &failureMessage){
+ if (rect->x() <= 100)
+ return true;
+ QDebug(&failureMessage) << "Expected x of rect to never go above new \"to\" value of 100, but it's" << rect->x();
+ return false;
+ });
+ QTest::newRow("to") << largeLoopCount << "to" << 100.0 << toValidator;
+ QTest::newRow("to,infinite") << int(QQuickAbstractAnimation::Infinite) << "to" << 100.0 << toValidator;
+
+ // Duration and easing.type would be difficult/flaky to test in CI so they're left out here.
+}
+
+// Tests that changing a NumberAnimation's properties while it's running will result
+// in those changes being picked up on the next loop. This is new behavior introduced
+// in Qt 6.4.
+void tst_qquickanimations::changePropertiesDuringAnimation()
+{
+ QFETCH(int, loops);
+ QFETCH(QString, propertyName);
+ QFETCH(qreal, newValue);
+ QFETCH(PropertyValidatorFunc, propertyValidatorFunc);
+
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("changePropertiesDuringAnimation.qml"));
+ QScopedPointer<QQuickItem> rootItem(qobject_cast<QQuickItem*>(component.createWithInitialProperties({{ "loops", loops }})));
+ QVERIFY2(!component.isError(), qPrintable(component.errorString()));
+
+ auto numberAnimation = rootItem->property("numberAnimation").value<QQuickNumberAnimation*>();
+ QVERIFY(numberAnimation);
+ QCOMPARE(numberAnimation->from(), -100);
+ QCOMPARE(numberAnimation->to(), rootItem->width());
+
+ // Start the animation.
+ numberAnimation->start();
+ QVERIFY(numberAnimation->isRunning());
+
+ int loopCountBeforeModification = 0;
+ // Ensure that it's past the first loop so that we can check that it resumes
+ // from that loop after "restarting".
+ QTRY_VERIFY(numberAnimation->qtAnimation()->currentLoop() >= 1);
+ loopCountBeforeModification = numberAnimation->qtAnimation()->currentLoop();
+
+ QSignalSpy startedSpy(numberAnimation, SIGNAL(started()));
+ QVERIFY(startedSpy.isValid());
+ QSignalSpy stoppedSpy(numberAnimation, SIGNAL(stopped()));
+ QVERIFY(stoppedSpy.isValid());
+
+ // Modify the property.
+ // QQuickPropertyAnimation has a setProperty function of its own, and we don't want to call it, hence the cast.
+ QVERIFY(static_cast<QObject*>(numberAnimation)->setProperty(propertyName.toLatin1().constData(), QVariant(newValue)));
+
+ // Make sure we've reached the end of the animation.
+ auto rect = rootItem->property("rect").value<QQuickItem*>();
+ QVERIFY(rect);
+ // Ensure that we've passed the loop on which we modified the property, while also checking
+ // that currentLoop never gets reset to 0. We can't just use QTRY_VERIFY
+ // for this, because it could start at 0 and then pass loopCountBeforeModification;
+ // we need to ensure that it never goes below loopCountBeforeModification.
+ while (numberAnimation->qtAnimation()->currentLoop() < loopCountBeforeModification + 1) {
+ QVERIFY2(numberAnimation->qtAnimation()->currentLoop() >= loopCountBeforeModification,
+ qPrintable(QString::fromLatin1("Expected currentLoop to be larger than %1, but it's %2")
+ .arg(loopCountBeforeModification).arg(numberAnimation->qtAnimation()->currentLoop())));
+ QTest::qWait(0);
+ }
+
+ // Now that we know the modification should have been taken into account,
+ // check that the animated property never gets set to a value that we wouldn't expect after the change.
+ const int previousLoop = numberAnimation->qtAnimation()->currentLoop();
+ QString failureMessage;
+ while (numberAnimation->qtAnimation()->currentLoop() < previousLoop + 1) {
+ if (!propertyValidatorFunc(rect, failureMessage))
+ QFAIL(qPrintable(failureMessage));
+ QTest::qWait(0);
+ }
+
+ // The started and stopped signals should not be emitted when adapting to changes
+ // mid-animation.
+ if (loops != QQuickAbstractAnimation::Infinite)
+ QVERIFY(numberAnimation->qtAnimation()->currentLoop() < numberAnimation->loops());
+ QCOMPARE(startedSpy.size(), 0);
+ QCOMPARE(stoppedSpy.size(), 0);
+}
+
+void tst_qquickanimations::infiniteLoopsWithoutFrom()
+{
+ // This test checks QTBUG-84375
+ QQuickView view(testFileUrl("infiniteAnimationWithoutFrom.qml"));
+ view.show();
+
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+ QVERIFY(view.rootObject());
+
+ QObject *root = view.rootObject();
+ QQuickAbstractAnimation *animation = root->findChild<QQuickAbstractAnimation *>("anim");
+ QVERIFY(animation);
+ QQuickRectangle *rectangle = root->findChild<QQuickRectangle *>("rect");
+ QVERIFY(rectangle);
+
+ qreal prevRotation = rectangle->rotation();
+ int numsCrossedZero = 0;
+ connect(rectangle, &QQuickRectangle::rotationChanged, this, [&]() {
+ const auto rotation = rectangle->rotation();
+ // We take a large range of (180; 360] here, because the animation in
+ // the test runs for a short time, and so there can be huge gaps between
+ // rotation values.
+ const bool prevRotationOldLoop = prevRotation > 180.0 && prevRotation <= 360.0;
+ const bool currRotationNewLoop = rotation >= 0.0 && rotation <= 180.0;
+ if (prevRotationOldLoop && currRotationNewLoop)
+ numsCrossedZero++;
+ prevRotation = rotation;
+ });
+
+ // The logic in lamdba function above requires at least two positions in
+ // a rotation animation - one in [0; 180] range, and another in (180; 360]
+ // range
+ animation->start();
+ animation->pause();
+ QCOMPARE(numsCrossedZero, 0);
+ animation->setCurrentTime(40);
+ animation->setCurrentTime(90);
+ QCOMPARE(numsCrossedZero, 0);
+ animation->setCurrentTime(140);
+ animation->setCurrentTime(190);
+ QCOMPARE(numsCrossedZero, 1);
+ animation->setCurrentTime(240);
+ animation->setCurrentTime(290);
+ QCOMPARE(numsCrossedZero, 2);
+
+ animation->stop();
+}
+
+void tst_qquickanimations::frameAnimation1()
+{
+ QQuickFrameAnimation frameAnimation;
+ QVERIFY(!frameAnimation.isRunning());
+ QVERIFY(!frameAnimation.isPaused());
+ QCOMPARE(frameAnimation.currentFrame(), 0);
+ QCOMPARE(frameAnimation.frameTime(), 0);
+ QCOMPARE(frameAnimation.smoothFrameTime(), 0);
+ QCOMPARE(frameAnimation.elapsedTime(), 0);
+
+ frameAnimation.start();
+ QVERIFY(frameAnimation.isRunning());
+ QVERIFY(!frameAnimation.isPaused());
+ frameAnimation.pause();
+ QVERIFY(frameAnimation.isRunning());
+ QVERIFY(frameAnimation.isPaused());
+ frameAnimation.resume();
+ QVERIFY(frameAnimation.isRunning());
+ QVERIFY(!frameAnimation.isPaused());
+ frameAnimation.stop();
+ QVERIFY(!frameAnimation.isRunning());
+ QVERIFY(!frameAnimation.isPaused());
+ frameAnimation.restart();
+ QVERIFY(frameAnimation.isRunning());
+ QVERIFY(!frameAnimation.isPaused());
+}
+
+void tst_qquickanimations::frameAnimation2()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("frameAnimation.qml"));
+ QScopedPointer<QObject> obj(c.create());
+ auto *root = qobject_cast<QQuickRectangle*>(obj.data());
+ QVERIFY(root);
+
+ QQuickFrameAnimation *frameAnimation = root->findChild<QQuickFrameAnimation*>();
+ QVERIFY(frameAnimation);
+ QSignalSpy spy(frameAnimation, SIGNAL(triggered()));
+
+ // Start the animation and wait at least 1 frame
+ frameAnimation->start();
+ QVERIFY(frameAnimation->isRunning());
+ QVERIFY(spy.wait(500));
+ QVERIFY(frameAnimation->currentFrame() > 0);
+ QVERIFY(frameAnimation->frameTime() > 0);
+ QVERIFY(frameAnimation->smoothFrameTime() > 0);
+ QVERIFY(frameAnimation->elapsedTime() > 0);
+
+ // Stopping and reseting should return currentFrame back to 0
+ frameAnimation->stop();
+ frameAnimation->reset();
+ QCOMPARE(frameAnimation->currentFrame(), 0);
+
+ // Start and wait so the animation runs into frame 3 and pauses
+ frameAnimation->start();
+ QTRY_VERIFY(frameAnimation->isPaused());
+ QVERIFY(frameAnimation->currentFrame() >= 3);
+
+ // Then resume the animation
+ frameAnimation->resume();
+ QVERIFY(!frameAnimation->isPaused());
+ QVERIFY(spy.wait(500));
+ QVERIFY(frameAnimation->currentFrame() > 3);
+}
+
+//QTBUG-110589
+void tst_qquickanimations::restartAnimationGroupWhenDirty()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("restartAnimationGroupWhenDirty.qml"));
+ QScopedPointer<QObject> obj(c.create());
+ auto *root = qobject_cast<QQuickRectangle*>(obj.data());
+ QVERIFY2(root, qPrintable(c.errorString()));
+
+ QQuickSequentialAnimation *seqAnim0 = root->findChild<QQuickSequentialAnimation*>("seqAnim0");
+ QVERIFY(seqAnim0);
+ QQuickRectangle *target0 = root->findChild<QQuickRectangle*>("target0");
+ QVERIFY(target0);
+ QQuickParallelAnimation *parAnim0 = root->findChild<QQuickParallelAnimation*>("parAnim0");
+ QVERIFY(parAnim0);
+ QQuickRectangle *target1 = root->findChild<QQuickRectangle*>("target1");
+ QVERIFY(target1);
+
+ QTRY_VERIFY(seqAnim0->isPaused());
+ QTRY_VERIFY(parAnim0->isPaused());
+ QTRY_VERIFY(target0->x() > 140);
+ QTRY_VERIFY(target1->x() > 140);
+ seqAnim0->resume();
+ parAnim0->resume();
+ QTRY_VERIFY(target0->property("onFinishedCalled").value<bool>());
+ QTRY_VERIFY(target1->property("onFinishedCalled").value<bool>());
+ QTRY_COMPARE(target0->x(), 140);
+ QTRY_COMPARE(target1->x(), 140);
+}
+
+//QTBUG-95840
+void tst_qquickanimations::restartNestedAnimationGroupWhenDirty()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("restartNestedAnimationGroupWhenDirty.qml"));
+ QScopedPointer<QObject> obj(c.create());
+ auto *root = qobject_cast<QQuickRectangle*>(obj.data());
+ QVERIFY2(root, qPrintable(c.errorString()));
+
+ QQuickSequentialAnimation *seqAnim0 = root->findChild<QQuickSequentialAnimation*>("seqAnim0");
+ QVERIFY(seqAnim0);
+ QQuickRectangle *target0 = root->findChild<QQuickRectangle*>("target0");
+ QVERIFY(target0);
+ QQuickParallelAnimation *parAnim0 = root->findChild<QQuickParallelAnimation*>("parAnim0");
+ QVERIFY(parAnim0);
+ QQuickRectangle *target1 = root->findChild<QQuickRectangle*>("target1");
+ QVERIFY(target1);
+
+ QTRY_VERIFY(seqAnim0->isPaused());
+ QTRY_VERIFY(parAnim0->isPaused());
+ QTRY_VERIFY(target0->x() > 140);
+ QTRY_VERIFY(target1->x() > 140);
+ seqAnim0->resume();
+ parAnim0->resume();
+ QTRY_VERIFY(target0->property("onFinishedCalled").value<bool>());
+ QTRY_VERIFY(target1->property("onFinishedCalled").value<bool>());
+ QTRY_COMPARE(target0->x(), 140);
+ QTRY_COMPARE(target1->x(), 140);
+}
+
+void tst_qquickanimations::targetsDeletedNotRemoved()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("targetsDeletedWithoutRemoval.qml"));
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY2(obj.get(), qPrintable(component.errorString()));
+ {
+ QQmlListReference ref(obj.get(), "targets");
+ QVERIFY(ref.isValid());
+ QCOMPARE(ref.size(), 1);
+ QTRY_COMPARE(ref.at(0), nullptr);
+ }
+ {
+ QQmlListReference ref(obj.get(), "animTargets");
+ QVERIFY(ref.isValid());
+ QCOMPARE(ref.size(), 1);
+ QCOMPARE(ref.at(0), nullptr);
+ }
+}
+
QTEST_MAIN(tst_qquickanimations)
#include "tst_qquickanimations.moc"