diff options
Diffstat (limited to 'tests/auto/declarative/animation/qsequentialanimationgroupjob')
2 files changed, 1622 insertions, 0 deletions
diff --git a/tests/auto/declarative/animation/qsequentialanimationgroupjob/qsequentialanimationgroupjob.pro b/tests/auto/declarative/animation/qsequentialanimationgroupjob/qsequentialanimationgroupjob.pro new file mode 100644 index 0000000000..914fc3ca62 --- /dev/null +++ b/tests/auto/declarative/animation/qsequentialanimationgroupjob/qsequentialanimationgroupjob.pro @@ -0,0 +1,5 @@ +CONFIG += testcase parallel_test +macx:CONFIG -= app_bundle +TARGET = tst_qsequentialanimationgroupjob +QT = core-private declarative-private testlib +SOURCES = tst_qsequentialanimationgroupjob.cpp diff --git a/tests/auto/declarative/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp b/tests/auto/declarative/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp new file mode 100644 index 0000000000..895e4f849e --- /dev/null +++ b/tests/auto/declarative/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp @@ -0,0 +1,1617 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <QtDeclarative/private/qsequentialanimationgroupjob_p.h> +#include <QtDeclarative/private/qparallelanimationgroupjob_p.h> +#include <QtDeclarative/private/qpauseanimationjob_p.h> + +Q_DECLARE_METATYPE(QAbstractAnimationJob::State) +Q_DECLARE_METATYPE(QAbstractAnimationJob*) + +class tst_QSequentialAnimationGroupJob : public QObject +{ + Q_OBJECT +public Q_SLOTS: + void initTestCase(); + +private slots: + void construction(); + void setCurrentTime(); + void setCurrentTimeWithUncontrolledAnimation(); + void seekingForwards(); + void seekingBackwards(); + void pauseAndResume(); + void restart(); + void looping(); + void startDelay(); + void clearGroup(); + void groupWithZeroDurationAnimations(); + void propagateGroupUpdateToChildren(); + void updateChildrenWithRunningGroup(); + void deleteChildrenWithRunningGroup(); + void startChildrenWithStoppedGroup(); + void stopGroupWithRunningChild(); + void startGroupWithRunningChild(); + void zeroDurationAnimation(); + void stopUncontrolledAnimations(); + void finishWithUncontrolledAnimation(); + void addRemoveAnimation(); + void currentAnimation(); + void currentAnimationWithZeroDuration(); + void insertAnimation(); + void clear(); + void pauseResume(); +}; + +void tst_QSequentialAnimationGroupJob::initTestCase() +{ + qRegisterMetaType<QAbstractAnimationJob::State>("QAbstractAnimationJob::State"); + qRegisterMetaType<QAbstractAnimationJob*>("QAbstractAnimationJob*"); +} + +void tst_QSequentialAnimationGroupJob::construction() +{ + QSequentialAnimationGroupJob animationgroup; +} + +class TestAnimation : public QAbstractAnimationJob +{ +public: + TestAnimation(int duration = 250) : m_duration(duration) {} + int duration() const { return m_duration; } + +private: + int m_duration; +}; + +class TestValueAnimation : public TestAnimation +{ +public: + TestValueAnimation(int duration = 250) + : TestAnimation(duration), start(0), end(0), value(0) {} + + void updateCurrentTime(int msecs) + { + if (msecs >= duration()) + value = end; + else + value = start + (end - start) * (qreal(msecs) / duration()); + } + + qreal start, end; + qreal value; +}; + +class UncontrolledAnimation : public QObject, public QAbstractAnimationJob +{ + Q_OBJECT +public: + int duration() const { return -1; /* not time driven */ } + +protected: + void updateCurrentTime(int currentTime) + { + if (currentTime >= 250) + stop(); + } +}; + +class StateChangeListener: public QAnimation2ChangeListener +{ +public: + virtual void animationStateChanged(QAbstractAnimationJob *, QAbstractAnimationJob::State newState, QAbstractAnimationJob::State) + { + states << newState; + } + + void clear() { states.clear(); } + int count() const { return states.count(); } + + QList<QAbstractAnimationJob::State> states; +}; + +class FinishedListener: public QAnimation2ChangeListener +{ +public: + FinishedListener() : m_count(0) {} + + virtual void animationFinished(QAbstractAnimationJob *) { ++m_count; } + void clear() { m_count = 0; } + int count() { return m_count; } + +private: + int m_count; +}; + +void tst_QSequentialAnimationGroupJob::setCurrentTime() +{ + // sequence operating on same object/property + QAnimationGroupJob *sequence = new QSequentialAnimationGroupJob(); + TestAnimation *a1_s_o1 = new TestAnimation; + TestAnimation *a2_s_o1 = new TestAnimation; + TestAnimation *a3_s_o1 = new TestAnimation; + a2_s_o1->setLoopCount(3); + sequence->appendAnimation(a1_s_o1); + sequence->appendAnimation(a2_s_o1); + sequence->appendAnimation(a3_s_o1); + + // sequence operating on different object/properties + QAnimationGroupJob *sequence2 = new QSequentialAnimationGroupJob(); + TestAnimation *a1_s_o2 = new TestAnimation; + TestAnimation *a1_s_o3 = new TestAnimation; + sequence2->appendAnimation(a1_s_o2); + sequence2->appendAnimation(a1_s_o3); + + QSequentialAnimationGroupJob group; + group.appendAnimation(sequence); + group.appendAnimation(sequence2); + + // Current time = 1 + group.setCurrentTime(1); + QCOMPARE(group.state(), QAnimationGroupJob::Stopped); + QCOMPARE(sequence->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a1_s_o1->state(), QAnimationGroupJob::Stopped); + QCOMPARE(sequence2->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a1_s_o2->state(), QAnimationGroupJob::Stopped); + + QCOMPARE(group.currentLoopTime(), 1); + QCOMPARE(sequence->currentLoopTime(), 1); + QCOMPARE(a1_s_o1->currentLoopTime(), 1); + QCOMPARE(a2_s_o1->currentLoopTime(), 0); + QCOMPARE(a3_s_o1->currentLoopTime(), 0); + QCOMPARE(a1_s_o2->currentLoopTime(), 0); + QCOMPARE(a1_s_o3->currentLoopTime(), 0); + + // Current time = 250 + group.setCurrentTime(250); + QCOMPARE(group.currentLoopTime(), 250); + QCOMPARE(sequence->currentLoopTime(), 250); + QCOMPARE(a1_s_o1->currentLoopTime(), 250); + QCOMPARE(a2_s_o1->currentLoopTime(), 0); + QCOMPARE(a3_s_o1->currentLoopTime(), 0); + QCOMPARE(a1_s_o2->currentLoopTime(), 0); + QCOMPARE(a1_s_o3->currentLoopTime(), 0); + + // Current time = 251 + group.setCurrentTime(251); + QCOMPARE(group.currentLoopTime(), 251); + QCOMPARE(sequence->currentLoopTime(), 251); + QCOMPARE(a1_s_o1->currentLoopTime(), 250); + QCOMPARE(a2_s_o1->currentLoopTime(), 1); + QCOMPARE(a2_s_o1->currentLoop(), 0); + QCOMPARE(a3_s_o1->currentLoopTime(), 0); + QCOMPARE(sequence2->currentLoopTime(), 0); + QCOMPARE(a1_s_o2->currentLoopTime(), 0); + QCOMPARE(a1_s_o3->currentLoopTime(), 0); + + // Current time = 750 + group.setCurrentTime(750); + QCOMPARE(group.currentLoopTime(), 750); + QCOMPARE(sequence->currentLoopTime(), 750); + QCOMPARE(a1_s_o1->currentLoopTime(), 250); + QCOMPARE(a2_s_o1->currentLoopTime(), 0); + QCOMPARE(a2_s_o1->currentLoop(), 2); + QCOMPARE(a3_s_o1->currentLoopTime(), 0); + QCOMPARE(sequence2->currentLoopTime(), 0); + QCOMPARE(a1_s_o2->currentLoopTime(), 0); + QCOMPARE(a1_s_o3->currentLoopTime(), 0); + + // Current time = 1000 + group.setCurrentTime(1000); + QCOMPARE(group.currentLoopTime(), 1000); + QCOMPARE(sequence->currentLoopTime(), 1000); + QCOMPARE(a1_s_o1->currentLoopTime(), 250); + QCOMPARE(a2_s_o1->currentLoopTime(), 250); + QCOMPARE(a2_s_o1->currentLoop(), 2); + QCOMPARE(a3_s_o1->currentLoopTime(), 0); + QCOMPARE(sequence2->currentLoopTime(), 0); + QCOMPARE(a1_s_o2->currentLoopTime(), 0); + QCOMPARE(a1_s_o3->currentLoopTime(), 0); + + // Current time = 1010 + group.setCurrentTime(1010); + QCOMPARE(group.currentLoopTime(), 1010); + QCOMPARE(sequence->currentLoopTime(), 1010); + QCOMPARE(a1_s_o1->currentLoopTime(), 250); + QCOMPARE(a2_s_o1->currentLoopTime(), 250); + QCOMPARE(a2_s_o1->currentLoop(), 2); + QCOMPARE(a3_s_o1->currentLoopTime(), 10); + QCOMPARE(sequence2->currentLoopTime(), 0); + QCOMPARE(a1_s_o2->currentLoopTime(), 0); + QCOMPARE(a1_s_o3->currentLoopTime(), 0); + + // Current time = 1250 + group.setCurrentTime(1250); + QCOMPARE(group.currentLoopTime(), 1250); + QCOMPARE(sequence->currentLoopTime(), 1250); + QCOMPARE(a1_s_o1->currentLoopTime(), 250); + QCOMPARE(a2_s_o1->currentLoopTime(), 250); + QCOMPARE(a2_s_o1->currentLoop(), 2); + QCOMPARE(a3_s_o1->currentLoopTime(), 250); + QCOMPARE(sequence2->currentLoopTime(), 0); + QCOMPARE(a1_s_o2->currentLoopTime(), 0); + QCOMPARE(a1_s_o3->currentLoopTime(), 0); + + // Current time = 1500 + group.setCurrentTime(1500); + QCOMPARE(group.currentLoopTime(), 1500); + QCOMPARE(sequence->currentLoopTime(), 1250); + QCOMPARE(a1_s_o1->currentLoopTime(), 250); + QCOMPARE(a2_s_o1->currentLoopTime(), 250); + QCOMPARE(a2_s_o1->currentLoop(), 2); + QCOMPARE(a3_s_o1->currentLoopTime(), 250); + QCOMPARE(sequence2->currentLoopTime(), 250); + QCOMPARE(a1_s_o2->currentLoopTime(), 250); + QCOMPARE(a1_s_o3->currentLoopTime(), 0); + + // Current time = 1750 + group.setCurrentTime(1750); + QCOMPARE(group.currentLoopTime(), 1750); + QCOMPARE(sequence->currentLoopTime(), 1250); + QCOMPARE(a1_s_o1->currentLoopTime(), 250); + QCOMPARE(a2_s_o1->currentLoopTime(), 250); + QCOMPARE(a2_s_o1->currentLoop(), 2); + QCOMPARE(a3_s_o1->currentLoopTime(), 250); + QCOMPARE(sequence2->currentLoopTime(), 500); + QCOMPARE(a1_s_o2->currentLoopTime(), 250); + QCOMPARE(a1_s_o3->currentLoopTime(), 250); + + // Current time = 2000 + group.setCurrentTime(2000); + QCOMPARE(group.currentLoopTime(), 1750); + QCOMPARE(sequence->currentLoopTime(), 1250); + QCOMPARE(a1_s_o1->currentLoopTime(), 250); + QCOMPARE(a2_s_o1->currentLoopTime(), 250); + QCOMPARE(a2_s_o1->currentLoop(), 2); + QCOMPARE(a3_s_o1->currentLoopTime(), 250); + QCOMPARE(sequence2->currentLoopTime(), 500); + QCOMPARE(a1_s_o2->currentLoopTime(), 250); + QCOMPARE(a1_s_o3->currentLoopTime(), 250); +} + +void tst_QSequentialAnimationGroupJob::setCurrentTimeWithUncontrolledAnimation() +{ + // sequence operating on different object/properties + QAnimationGroupJob *sequence = new QSequentialAnimationGroupJob(); + TestAnimation *a1_s_o1 = new TestAnimation; + TestAnimation *a1_s_o2 = new TestAnimation; + sequence->appendAnimation(a1_s_o1); + sequence->appendAnimation(a1_s_o2); + + UncontrolledAnimation *notTimeDriven = new UncontrolledAnimation; + QCOMPARE(notTimeDriven->totalDuration(), -1); + + TestAnimation *loopsForever = new TestAnimation; + loopsForever->setLoopCount(-1); + QCOMPARE(loopsForever->totalDuration(), -1); + + QSequentialAnimationGroupJob group; + group.appendAnimation(sequence); + group.appendAnimation(notTimeDriven); + group.appendAnimation(loopsForever); + group.start(); + group.pause(); // this allows the group to listen for the finish signal of its children + + // Current time = 1 + group.setCurrentTime(1); + QCOMPARE(group.state(), QAnimationGroupJob::Paused); + QCOMPARE(sequence->state(), QAnimationGroupJob::Paused); + QCOMPARE(a1_s_o1->state(), QAnimationGroupJob::Paused); + QCOMPARE(a1_s_o2->state(), QAnimationGroupJob::Stopped); + QCOMPARE(notTimeDriven->state(), QAnimationGroupJob::Stopped); + QCOMPARE(loopsForever->state(), QAnimationGroupJob::Stopped); + + QCOMPARE(group.currentLoopTime(), 1); + QCOMPARE(sequence->currentLoopTime(), 1); + QCOMPARE(a1_s_o1->currentLoopTime(), 1); + QCOMPARE(a1_s_o2->currentLoopTime(), 0); + QCOMPARE(notTimeDriven->currentLoopTime(), 0); + QCOMPARE(loopsForever->currentLoopTime(), 0); + + // Current time = 250 + group.setCurrentTime(250); + QCOMPARE(group.currentLoopTime(), 250); + QCOMPARE(sequence->currentLoopTime(), 250); + QCOMPARE(a1_s_o1->currentLoopTime(), 250); + QCOMPARE(a1_s_o2->currentLoopTime(), 0); + QCOMPARE(notTimeDriven->currentLoopTime(), 0); + QCOMPARE(loopsForever->currentLoopTime(), 0); + + // Current time = 500 + group.setCurrentTime(500); + QCOMPARE(group.currentLoopTime(), 500); + QCOMPARE(sequence->currentLoopTime(), 500); + QCOMPARE(a1_s_o1->currentLoopTime(), 250); + QCOMPARE(a1_s_o2->currentLoopTime(), 250); + QCOMPARE(notTimeDriven->currentLoopTime(), 0); + QCOMPARE(loopsForever->currentLoopTime(), 0); + QCOMPARE(group.currentAnimation(), static_cast<QAbstractAnimationJob *>(notTimeDriven)); + + // Current time = 505 + group.setCurrentTime(505); + QCOMPARE(group.currentLoopTime(), 505); + QCOMPARE(sequence->currentLoopTime(), 500); + QCOMPARE(a1_s_o1->currentLoopTime(), 250); + QCOMPARE(a1_s_o2->currentLoopTime(), 250); + QCOMPARE(notTimeDriven->currentLoopTime(), 5); + QCOMPARE(loopsForever->currentLoopTime(), 0); + QCOMPARE(group.currentAnimation(), static_cast<QAbstractAnimationJob *>(notTimeDriven)); + QCOMPARE(sequence->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a1_s_o1->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a1_s_o2->state(), QAnimationGroupJob::Stopped); + QCOMPARE(notTimeDriven->state(), QAnimationGroupJob::Paused); + QCOMPARE(loopsForever->state(), QAnimationGroupJob::Stopped); + + // Current time = 750 (end of notTimeDriven animation) + group.setCurrentTime(750); + QCOMPARE(group.currentLoopTime(), 750); + QCOMPARE(sequence->currentLoopTime(), 500); + QCOMPARE(a1_s_o1->currentLoopTime(), 250); + QCOMPARE(a1_s_o2->currentLoopTime(), 250); + QCOMPARE(notTimeDriven->currentLoopTime(), 250); + QCOMPARE(loopsForever->currentLoopTime(), 0); + QCOMPARE(group.currentAnimation(), loopsForever); + QCOMPARE(sequence->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a1_s_o1->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a1_s_o2->state(), QAnimationGroupJob::Stopped); + QCOMPARE(notTimeDriven->state(), QAnimationGroupJob::Stopped); + QCOMPARE(loopsForever->state(), QAnimationGroupJob::Paused); + + // Current time = 800 (as notTimeDriven was finished at 750, loopsforever should still run) + group.setCurrentTime(800); + QCOMPARE(group.currentLoopTime(), 800); + QCOMPARE(group.currentAnimation(), loopsForever); + QCOMPARE(sequence->currentLoopTime(), 500); + QCOMPARE(a1_s_o1->currentLoopTime(), 250); + QCOMPARE(a1_s_o2->currentLoopTime(), 250); + QCOMPARE(notTimeDriven->currentLoopTime(), 250); + QCOMPARE(loopsForever->currentLoopTime(), 50); + + loopsForever->stop(); // this should stop the group + + QCOMPARE(group.state(), QAnimationGroupJob::Stopped); + QCOMPARE(sequence->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a1_s_o1->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a1_s_o2->state(), QAnimationGroupJob::Stopped); + QCOMPARE(notTimeDriven->state(), QAnimationGroupJob::Stopped); + QCOMPARE(loopsForever->state(), QAnimationGroupJob::Stopped); +} + +void tst_QSequentialAnimationGroupJob::seekingForwards() +{ + + // sequence operating on same object/property + QAnimationGroupJob *sequence = new QSequentialAnimationGroupJob; + TestAnimation *a1_s_o1 = new TestAnimation; + TestAnimation *a2_s_o1 = new TestAnimation; + TestAnimation *a3_s_o1 = new TestAnimation; + a2_s_o1->setLoopCount(3); + sequence->appendAnimation(a1_s_o1); + sequence->appendAnimation(a2_s_o1); + sequence->appendAnimation(a3_s_o1); + + // sequence operating on different object/properties + QAnimationGroupJob *sequence2 = new QSequentialAnimationGroupJob; + TestAnimation *a1_s_o2 = new TestAnimation; + TestAnimation *a1_s_o3 = new TestAnimation; + sequence2->appendAnimation(a1_s_o2); + sequence2->appendAnimation(a1_s_o3); + + QSequentialAnimationGroupJob group; + group.appendAnimation(sequence); + group.appendAnimation(sequence2); + + // Current time = 1 + group.setCurrentTime(1); + QCOMPARE(group.state(), QAnimationGroupJob::Stopped); + QCOMPARE(sequence->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a1_s_o1->state(), QAnimationGroupJob::Stopped); + QCOMPARE(sequence2->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a1_s_o2->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a1_s_o3->state(), QAnimationGroupJob::Stopped); + + QCOMPARE(group.currentLoopTime(), 1); + QCOMPARE(sequence->currentLoopTime(), 1); + QCOMPARE(a1_s_o1->currentLoopTime(), 1); + QCOMPARE(a2_s_o1->currentLoopTime(), 0); + QCOMPARE(a3_s_o1->currentLoopTime(), 0); + QCOMPARE(sequence2->currentLoopTime(), 0); + QCOMPARE(a1_s_o2->currentLoopTime(), 0); + QCOMPARE(a1_s_o3->currentLoopTime(), 0); + + // Current time = 1500 + group.setCurrentTime(1500); + QCOMPARE(group.currentLoopTime(), 1500); + QCOMPARE(sequence->currentLoopTime(), 1250); + QCOMPARE(a1_s_o1->currentLoopTime(), 250); + QCOMPARE(a2_s_o1->currentLoopTime(), 250); + QCOMPARE(a2_s_o1->currentLoop(), 2); + QCOMPARE(a3_s_o1->currentLoopTime(), 250); + QCOMPARE(sequence2->currentLoopTime(), 250); + QCOMPARE(a1_s_o2->currentLoopTime(), 250); + QCOMPARE(a1_s_o3->currentLoopTime(), 0); + + // this will restart the group + group.start(); + group.pause(); + QCOMPARE(group.state(), QAnimationGroupJob::Paused); + QCOMPARE(sequence->state(), QAnimationGroupJob::Paused); + QCOMPARE(a1_s_o1->state(), QAnimationGroupJob::Paused); + QCOMPARE(sequence2->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a1_s_o2->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a1_s_o3->state(), QAnimationGroupJob::Stopped); + + // Current time = 1750 + group.setCurrentTime(1750); + QCOMPARE(group.currentLoopTime(), 1750); + QCOMPARE(sequence->currentLoopTime(), 1250); + QCOMPARE(a1_s_o1->currentLoopTime(), 250); + QCOMPARE(a2_s_o1->currentLoopTime(), 250); + QCOMPARE(a2_s_o1->currentLoop(), 2); + QCOMPARE(a3_s_o1->currentLoopTime(), 250); + QCOMPARE(sequence2->currentLoopTime(), 500); + QCOMPARE(a1_s_o2->currentLoopTime(), 250); + QCOMPARE(a1_s_o3->currentLoopTime(), 250); +} + +void tst_QSequentialAnimationGroupJob::seekingBackwards() +{ + // sequence operating on same object/property + QAnimationGroupJob *sequence = new QSequentialAnimationGroupJob(); + TestAnimation *a1_s_o1 = new TestAnimation; + TestAnimation *a2_s_o1 = new TestAnimation; + TestAnimation *a3_s_o1 = new TestAnimation; + a2_s_o1->setLoopCount(3); + sequence->appendAnimation(a1_s_o1); + sequence->appendAnimation(a2_s_o1); + sequence->appendAnimation(a3_s_o1); + + // sequence operating on different object/properties + QAnimationGroupJob *sequence2 = new QSequentialAnimationGroupJob(); + TestAnimation *a1_s_o2 = new TestAnimation; + TestAnimation *a1_s_o3 = new TestAnimation; + sequence2->appendAnimation(a1_s_o2); + sequence2->appendAnimation(a1_s_o3); + + QSequentialAnimationGroupJob group; + group.appendAnimation(sequence); + group.appendAnimation(sequence2); + + group.start(); + + // Current time = 1600 + group.setCurrentTime(1600); + QCOMPARE(group.currentLoopTime(), 1600); + QCOMPARE(sequence->currentLoopTime(), 1250); + QCOMPARE(a1_s_o1->currentLoopTime(), 250); + QCOMPARE(a2_s_o1->currentLoopTime(), 250); + QCOMPARE(a2_s_o1->currentLoop(), 2); + QCOMPARE(a3_s_o1->currentLoopTime(), 250); + QCOMPARE(sequence2->currentLoopTime(), 350); + QCOMPARE(a1_s_o2->currentLoopTime(), 250); + QCOMPARE(a1_s_o3->currentLoopTime(), 100); + + QCOMPARE(group.state(), QAnimationGroupJob::Running); + QCOMPARE(sequence->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a1_s_o1->state(), QAnimationGroupJob::Stopped); + QCOMPARE(sequence2->state(), QAnimationGroupJob::Running); + QCOMPARE(a1_s_o2->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a1_s_o3->state(), QAnimationGroupJob::Running); + + // Seeking backwards, current time = 1 + group.setCurrentTime(1); + QCOMPARE(group.currentLoopTime(), 1); + QCOMPARE(sequence->currentLoopTime(), 1); + QCOMPARE(a1_s_o1->currentLoopTime(), 1); + + QEXPECT_FAIL("", "rewinding in nested groups is considered as a restart from the children," + "hence they don't reset from their current animation", Continue); + QCOMPARE(a2_s_o1->currentLoopTime(), 0); + QEXPECT_FAIL("", "rewinding in nested groups is considered as a restart from the children," + "hence they don't reset from their current animation", Continue); + QCOMPARE(a2_s_o1->currentLoop(), 0); + QEXPECT_FAIL("", "rewinding in nested groups is considered as a restart from the children," + "hence they don't reset from their current animation", Continue); + QCOMPARE(a3_s_o1->currentLoopTime(), 0); + QCOMPARE(sequence2->currentLoopTime(), 0); + QCOMPARE(a1_s_o2->currentLoopTime(), 0); + QCOMPARE(a1_s_o3->currentLoopTime(), 0); + + QCOMPARE(group.state(), QAnimationGroupJob::Running); + QCOMPARE(sequence->state(), QAnimationGroupJob::Running); + QCOMPARE(a1_s_o1->state(), QAnimationGroupJob::Running); + QCOMPARE(sequence2->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a1_s_o2->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a1_s_o3->state(), QAnimationGroupJob::Stopped); + + // Current time = 2000 + group.setCurrentTime(2000); + QCOMPARE(group.currentLoopTime(), 1750); + QCOMPARE(sequence->currentLoopTime(), 1250); + QCOMPARE(a1_s_o1->currentLoopTime(), 250); + QCOMPARE(a2_s_o1->currentLoopTime(), 250); + QCOMPARE(a2_s_o1->currentLoop(), 2); + QCOMPARE(a3_s_o1->currentLoopTime(), 250); + QCOMPARE(sequence2->currentLoopTime(), 500); + QCOMPARE(a1_s_o2->currentLoopTime(), 250); + QCOMPARE(a1_s_o3->currentLoopTime(), 250); + + QCOMPARE(group.state(), QAnimationGroupJob::Stopped); + QCOMPARE(sequence->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a1_s_o1->state(), QAnimationGroupJob::Stopped); + QCOMPARE(sequence2->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a1_s_o2->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a1_s_o3->state(), QAnimationGroupJob::Stopped); +} + +typedef QList<QAbstractAnimationJob::State> StateList; + +static bool compareStates(const StateChangeListener& spy, const StateList &expectedStates) +{ + bool equals = true; + for (int i = 0; i < qMax(expectedStates.count(), spy.count()); ++i) { + if (i >= spy.count() || i >= expectedStates.count()) { + equals = false; + break; + } + QAbstractAnimationJob::State st = expectedStates.at(i); + QAbstractAnimationJob::State actual = spy.states.at(i); + if (equals && actual != st) { + equals = false; + break; + } + } + if (!equals) { + const char *stateStrings[] = {"Stopped", "Paused", "Running"}; + QString e,a; + for (int i = 0; i < qMax(expectedStates.count(), spy.count()); ++i) { + if (i < expectedStates.count()) { + int exp = int(expectedStates.at(i)); + if (!e.isEmpty()) + e += QLatin1String(", "); + e += QLatin1String(stateStrings[exp]); + } + if (i < spy.count()) { + QAbstractAnimationJob::State actual = spy.states.at(i); + if (!a.isEmpty()) + a += QLatin1String(", "); + if (int(actual) >= 0 && int(actual) <= 2) { + a += QLatin1String(stateStrings[int(actual)]); + } else { + a += QLatin1String("NaN"); + } + } + + } + qDebug("\n" + "expected (count == %d): %s\n" + "actual (count == %d): %s\n", expectedStates.count(), qPrintable(e), spy.count(), qPrintable(a)); + } + return equals; +} + +void tst_QSequentialAnimationGroupJob::pauseAndResume() +{ + // sequence operating on same object/property + QAnimationGroupJob *sequence = new QSequentialAnimationGroupJob(); + TestAnimation *a1_s_o1 = new TestAnimation; + TestAnimation *a2_s_o1 = new TestAnimation; + TestAnimation *a3_s_o1 = new TestAnimation; + a2_s_o1->setLoopCount(2); + sequence->appendAnimation(a1_s_o1); + sequence->appendAnimation(a2_s_o1); + sequence->appendAnimation(a3_s_o1); + sequence->setLoopCount(2); + + StateChangeListener a1StateChangedSpy; + a1_s_o1->addAnimationChangeListener(&a1StateChangedSpy, QAbstractAnimationJob::StateChange); + StateChangeListener seqStateChangedSpy; + sequence->addAnimationChangeListener(&seqStateChangedSpy, QAbstractAnimationJob::StateChange); + + QSequentialAnimationGroupJob group; + group.appendAnimation(sequence); + + group.start(); + group.pause(); + + // Current time = 1751 + group.setCurrentTime(1751); + QCOMPARE(group.currentLoopTime(), 1751); + QCOMPARE(sequence->currentLoopTime(), 751); + QCOMPARE(sequence->currentLoop(), 1); + QCOMPARE(a1_s_o1->currentLoopTime(), 250); + QCOMPARE(a2_s_o1->currentLoopTime(), 250); + QCOMPARE(a2_s_o1->currentLoop(), 1); + QCOMPARE(a3_s_o1->currentLoop(), 0); + QCOMPARE(a3_s_o1->currentLoopTime(), 1); + + QCOMPARE(group.state(), QAnimationGroupJob::Paused); + QCOMPARE(sequence->state(), QAnimationGroupJob::Paused); + QCOMPARE(a1_s_o1->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a2_s_o1->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a3_s_o1->state(), QAnimationGroupJob::Paused); + + QCOMPARE(a1StateChangedSpy.count(), 5); // Running,Paused,Stopped,Running,Stopped + QCOMPARE(seqStateChangedSpy.count(), 2); // Running,Paused + + QVERIFY(compareStates(a1StateChangedSpy, (StateList() << QAbstractAnimationJob::Running + << QAbstractAnimationJob::Paused + << QAbstractAnimationJob::Stopped + << QAbstractAnimationJob::Running + << QAbstractAnimationJob::Stopped))); + + //### is this the same test as compareStates test above? + QCOMPARE(a1StateChangedSpy.states.at(0), QAnimationGroupJob::Running); + QCOMPARE(a1StateChangedSpy.states.at(1), QAnimationGroupJob::Paused); + QCOMPARE(a1StateChangedSpy.states.at(2), QAnimationGroupJob::Stopped); + QCOMPARE(a1StateChangedSpy.states.at(3), QAnimationGroupJob::Running); + QCOMPARE(a1StateChangedSpy.states.at(4), QAnimationGroupJob::Stopped); + + QCOMPARE(seqStateChangedSpy.states.at(0), QAnimationGroupJob::Running); + QCOMPARE(seqStateChangedSpy.states.at(1), QAnimationGroupJob::Paused); + + group.resume(); + + QCOMPARE(group.state(), QAnimationGroupJob::Running); + QCOMPARE(sequence->state(), QAnimationGroupJob::Running); + QCOMPARE(a1_s_o1->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a2_s_o1->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a3_s_o1->state(), QAnimationGroupJob::Running); + + QVERIFY(group.currentLoopTime() >= 1751); + QVERIFY(sequence->currentLoopTime() >= 751); + QCOMPARE(sequence->currentLoop(), 1); + QCOMPARE(a1_s_o1->currentLoopTime(), 250); + QCOMPARE(a2_s_o1->currentLoopTime(), 250); + QCOMPARE(a2_s_o1->currentLoop(), 1); + QCOMPARE(a3_s_o1->currentLoop(), 0); + QVERIFY(a3_s_o1->currentLoopTime() >= 1); + + QCOMPARE(seqStateChangedSpy.count(), 3); // Running,Paused,Running + QCOMPARE(seqStateChangedSpy.states.at(2), QAnimationGroupJob::Running); + + group.pause(); + + QCOMPARE(group.state(), QAnimationGroupJob::Paused); + QCOMPARE(sequence->state(), QAnimationGroupJob::Paused); + QCOMPARE(a1_s_o1->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a2_s_o1->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a3_s_o1->state(), QAnimationGroupJob::Paused); + + QVERIFY(group.currentLoopTime() >= 1751); + QVERIFY(sequence->currentLoopTime() >= 751); + QCOMPARE(sequence->currentLoop(), 1); + QCOMPARE(a1_s_o1->currentLoopTime(), 250); + QCOMPARE(a2_s_o1->currentLoopTime(), 250); + QCOMPARE(a2_s_o1->currentLoop(), 1); + QCOMPARE(a3_s_o1->currentLoop(), 0); + QVERIFY(a3_s_o1->currentLoopTime() >= 1); + + QCOMPARE(seqStateChangedSpy.count(), 4); // Running,Paused,Running,Paused + QCOMPARE(seqStateChangedSpy.states.at(3), QAnimationGroupJob::Paused); + + group.stop(); + + QCOMPARE(seqStateChangedSpy.count(), 5); // Running,Paused,Running,Paused,Stopped + QCOMPARE(seqStateChangedSpy.states.at(4), QAnimationGroupJob::Stopped); +} + +void tst_QSequentialAnimationGroupJob::restart() +{ + // originally was sequence operating on same object/property + QAnimationGroupJob *sequence = new QSequentialAnimationGroupJob(); + //### no equivilant signal + //QSignalSpy seqCurrentAnimChangedSpy(sequence, SIGNAL(currentAnimationChanged(QAbstractAnimationJob*))); + + StateChangeListener seqStateChangedSpy; + sequence->addAnimationChangeListener(&seqStateChangedSpy, QAbstractAnimationJob::StateChange); + + TestAnimation *anims[3]; + StateChangeListener *animsStateChanged[3]; + + for (int i = 0; i < 3; i++) { + anims[i] = new TestAnimation(100); + animsStateChanged[i] = new StateChangeListener; + anims[i]->addAnimationChangeListener(animsStateChanged[i], QAbstractAnimationJob::StateChange); + } + + anims[1]->setLoopCount(2); + sequence->appendAnimation(anims[0]); + sequence->appendAnimation(anims[1]); + sequence->appendAnimation(anims[2]); + sequence->setLoopCount(2); + + QSequentialAnimationGroupJob group; + group.appendAnimation(sequence); + + group.start(); + + QTest::qWait(500); + + QCOMPARE(group.state(), QAnimationGroupJob::Running); + + QTest::qWait(300); + QTRY_COMPARE(group.state(), QAnimationGroupJob::Stopped); + + for (int i = 0; i < 3; i++) { + QCOMPARE(animsStateChanged[i]->count(), 4); + QCOMPARE(animsStateChanged[i]->states.at(0), QAnimationGroupJob::Running); + QCOMPARE(animsStateChanged[i]->states.at(1), QAnimationGroupJob::Stopped); + QCOMPARE(animsStateChanged[i]->states.at(2), QAnimationGroupJob::Running); + QCOMPARE(animsStateChanged[i]->states.at(3), QAnimationGroupJob::Stopped); + } + + QCOMPARE(seqStateChangedSpy.count(), 2); + QCOMPARE(seqStateChangedSpy.states.at(0), QAnimationGroupJob::Running); + QCOMPARE(seqStateChangedSpy.states.at(1), QAnimationGroupJob::Stopped); + + //QCOMPARE(seqCurrentAnimChangedSpy.count(), 6); + //for(int i=0; i<seqCurrentAnimChangedSpy.count(); i++) + // QCOMPARE(static_cast<QAbstractAnimationJob*>(anims[i%3]), qVariantValue<QAbstractAnimationJob*>(seqCurrentAnimChangedSpy.at(i).at(0))); + + group.start(); + + QCOMPARE(animsStateChanged[0]->count(), 5); + QCOMPARE(animsStateChanged[1]->count(), 4); + QCOMPARE(animsStateChanged[2]->count(), 4); + QCOMPARE(seqStateChangedSpy.count(), 3); +} + +void tst_QSequentialAnimationGroupJob::looping() +{ + // originally was sequence operating on same object/property + QSequentialAnimationGroupJob *sequence = new QSequentialAnimationGroupJob(); + QAbstractAnimationJob *a1_s_o1 = new TestAnimation; + QAbstractAnimationJob *a2_s_o1 = new TestAnimation; + QAbstractAnimationJob *a3_s_o1 = new TestAnimation; + + StateChangeListener a1Spy; + a1_s_o1->addAnimationChangeListener(&a1Spy, QAbstractAnimationJob::StateChange); + StateChangeListener a2Spy; + a2_s_o1->addAnimationChangeListener(&a2Spy, QAbstractAnimationJob::StateChange); + StateChangeListener a3Spy; + a3_s_o1->addAnimationChangeListener(&a3Spy, QAbstractAnimationJob::StateChange); + StateChangeListener seqSpy; + sequence->addAnimationChangeListener(&seqSpy, QAbstractAnimationJob::StateChange); + + a2_s_o1->setLoopCount(2); + sequence->appendAnimation(a1_s_o1); + sequence->appendAnimation(a2_s_o1); + sequence->appendAnimation(a3_s_o1); + sequence->setLoopCount(2); + + QSequentialAnimationGroupJob group; + StateChangeListener groupSpy; + group.addAnimationChangeListener(&groupSpy, QAbstractAnimationJob::StateChange); + + group.appendAnimation(sequence); + group.setLoopCount(2); + + group.start(); + group.pause(); + + // Current time = 1750 + group.setCurrentTime(1750); + QCOMPARE(group.currentLoopTime(), 1750); + QCOMPARE(sequence->currentLoopTime(), 750); + QCOMPARE(sequence->currentLoop(), 1); + QCOMPARE(a1_s_o1->currentLoopTime(), 250); + QCOMPARE(a2_s_o1->currentLoopTime(), 250); + QCOMPARE(a2_s_o1->currentLoop(), 1); + // this animation is at the beginning because it is the current one inside sequence + QCOMPARE(a3_s_o1->currentLoop(), 0); + QCOMPARE(a3_s_o1->currentLoopTime(), 0); + QCOMPARE(sequence->currentAnimation(), a3_s_o1); + + QCOMPARE(group.state(), QAnimationGroupJob::Paused); + QCOMPARE(sequence->state(), QAnimationGroupJob::Paused); + QCOMPARE(a1_s_o1->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a2_s_o1->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a3_s_o1->state(), QAnimationGroupJob::Paused); + + QCOMPARE(a1Spy.count(), 5); // Running,Paused,Stopped,Running,Stopped + QVERIFY(compareStates(a1Spy, (StateList() << QAbstractAnimationJob::Running + << QAbstractAnimationJob::Paused + << QAbstractAnimationJob::Stopped + << QAbstractAnimationJob::Running + << QAbstractAnimationJob::Stopped))); + + QCOMPARE(a2Spy.count(), 4); // Running,Stopped,Running,Stopped + QVERIFY(compareStates(a3Spy, (StateList() << QAbstractAnimationJob::Running + << QAbstractAnimationJob::Stopped + << QAbstractAnimationJob::Running + << QAbstractAnimationJob::Paused))); + + QCOMPARE(seqSpy.count(), 2); // Running,Paused + QCOMPARE(groupSpy.count(), 2); // Running,Paused + + // Looping, current time = duration + 1 + group.setCurrentTime(group.duration() + 1); + QCOMPARE(group.currentLoopTime(), 1); + QCOMPARE(group.currentLoop(), 1); + QCOMPARE(sequence->currentLoopTime(), 1); + QCOMPARE(sequence->currentLoop(), 0); + QCOMPARE(a1_s_o1->currentLoopTime(), 1); + QCOMPARE(a2_s_o1->currentLoopTime(), 250); + QCOMPARE(a2_s_o1->currentLoop(), 1); + // this animation is at the end because it was run on the previous loop + QCOMPARE(a3_s_o1->currentLoop(), 0); + QCOMPARE(a3_s_o1->currentLoopTime(), 250); + + QCOMPARE(group.state(), QAnimationGroupJob::Paused); + QCOMPARE(sequence->state(), QAnimationGroupJob::Paused); + QCOMPARE(a1_s_o1->state(), QAnimationGroupJob::Paused); + QCOMPARE(a2_s_o1->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a3_s_o1->state(), QAnimationGroupJob::Stopped); + + QCOMPARE(a1Spy.count(), 7); // Running,Paused,Stopped,Running,Stopped,Running,Stopped + QCOMPARE(a2Spy.count(), 4); // Running, Stopped, Running, Stopped + QVERIFY(compareStates(a3Spy, (StateList() << QAbstractAnimationJob::Running + << QAbstractAnimationJob::Stopped + << QAbstractAnimationJob::Running + << QAbstractAnimationJob::Paused + << QAbstractAnimationJob::Stopped))); + QVERIFY(compareStates(seqSpy, (StateList() << QAbstractAnimationJob::Running + << QAbstractAnimationJob::Paused + << QAbstractAnimationJob::Stopped + << QAbstractAnimationJob::Running + << QAbstractAnimationJob::Paused))); + QCOMPARE(groupSpy.count(), 2); + + //cleanup + a1_s_o1->removeAnimationChangeListener(&a1Spy, QAbstractAnimationJob::StateChange); + a2_s_o1->removeAnimationChangeListener(&a2Spy, QAbstractAnimationJob::StateChange); + a3_s_o1->removeAnimationChangeListener(&a3Spy, QAbstractAnimationJob::StateChange); + sequence->removeAnimationChangeListener(&seqSpy, QAbstractAnimationJob::StateChange); + group.removeAnimationChangeListener(&groupSpy, QAbstractAnimationJob::StateChange); +} + +void tst_QSequentialAnimationGroupJob::startDelay() +{ + QSequentialAnimationGroupJob group; + group.appendAnimation(new QPauseAnimationJob(250)); + group.appendAnimation(new QPauseAnimationJob(125)); + QCOMPARE(group.totalDuration(), 375); + + group.start(); + QCOMPARE(group.state(), QAnimationGroupJob::Running); + + QTest::qWait(500); + + QTRY_COMPARE(group.state(), QAnimationGroupJob::Stopped); + QVERIFY(group.currentLoopTime() == 375); +} + +void tst_QSequentialAnimationGroupJob::clearGroup() +{ + QSequentialAnimationGroupJob group; + + static const int animationCount = 20; + + for (int i = 0; i < animationCount/2; ++i) { + QSequentialAnimationGroupJob *subGroup = new QSequentialAnimationGroupJob; + group.appendAnimation(subGroup); + group.appendAnimation(new QPauseAnimationJob(100)); + subGroup->appendAnimation(new QPauseAnimationJob(10)); + } + + int count = 0; + for (QAbstractAnimationJob *anim = group.firstChild(); anim; anim = anim->nextSibling()) + ++count; + QCOMPARE(count, animationCount); + + group.clear(); + + QVERIFY(!group.firstChild() && !group.lastChild()); + QCOMPARE(group.currentLoopTime(), 0); +} + +void tst_QSequentialAnimationGroupJob::groupWithZeroDurationAnimations() +{ + QSequentialAnimationGroupJob group; + + TestValueAnimation *a1 = new TestValueAnimation(0); + a1->start = 42; + a1->end = 43; + group.appendAnimation(a1); + + //this should just run fine and change nothing + group.setCurrentTime(0); + QCOMPARE(group.currentAnimation(), static_cast<QAbstractAnimationJob*>(a1)); + + TestValueAnimation *a2 = new TestValueAnimation(500); + a2->start = 13; + a2->end = 31; + group.appendAnimation(a2); + + TestValueAnimation *a3 = new TestValueAnimation(0); + a3->start = 43; + a3->end = 44; + group.appendAnimation(a3); + + TestValueAnimation *a4 = new TestValueAnimation(250); + a4->start = 13; + a4->end = 75; + group.appendAnimation(a4); + + TestValueAnimation *a5 = new TestValueAnimation(0); + a5->start = 42; + a5->end = 12; + group.appendAnimation(a5); + + QCOMPARE((int)a1->value, 43); //### is this actually the behavior we want? + QCOMPARE((int)a2->value, 0); + QCOMPARE((int)a3->value, 0); + QCOMPARE((int)a4->value, 0); + QCOMPARE((int)a5->value, 0); + + group.start(); + + QCOMPARE((int)a1->value, 43); //### is this actually the behavior we want? + QCOMPARE((int)a2->value, 13); + QCOMPARE((int)a3->value, 0); + QCOMPARE((int)a4->value, 0); + QCOMPARE((int)a5->value, 0); + + QTest::qWait(100); + + QCOMPARE((int)a1->value, 43); + QVERIFY(a2->value > 13 && a2->value < 31); + QCOMPARE((int)a3->value, 0); + QCOMPARE((int)a4->value, 0); + QCOMPARE((int)a5->value, 0); + + QTest::qWait(500); + + QTRY_COMPARE((int)a3->value, 44); + QCOMPARE((int)a1->value, 43); + QCOMPARE((int)a2->value, 31); + //QCOMPARE((int)a4->value, 36); + QCOMPARE((int)a5->value, 0); + QCOMPARE(a1->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a2->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a3->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a4->state(), QAnimationGroupJob::Running); + QCOMPARE(a5->state(), QAnimationGroupJob::Stopped); + QCOMPARE(group.state(), QAnimationGroupJob::Running); + QTest::qWait(500); + + QTRY_COMPARE(group.state(), QAnimationGroupJob::Stopped); + QCOMPARE((int)a1->value, 43); + QCOMPARE((int)a2->value, 31); + QCOMPARE((int)a3->value, 44); + QCOMPARE((int)a4->value, 75); + QCOMPARE((int)a5->value, 12); + QCOMPARE(a1->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a2->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a3->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a4->state(), QAnimationGroupJob::Stopped); + QCOMPARE(a5->state(), QAnimationGroupJob::Stopped); +} + +void tst_QSequentialAnimationGroupJob::propagateGroupUpdateToChildren() +{ + // this test verifies if group state changes are updating its children correctly + QSequentialAnimationGroupJob group; + + TestAnimation anim1(100); + TestAnimation anim2(200); + + QCOMPARE(group.state(), QAnimationGroupJob::Stopped); + QCOMPARE(anim1.state(), QAnimationGroupJob::Stopped); + QCOMPARE(anim2.state(), QAnimationGroupJob::Stopped); + + group.appendAnimation(&anim1); + group.appendAnimation(&anim2); + + group.start(); + + QCOMPARE(group.state(), QAnimationGroupJob::Running); + QCOMPARE(anim1.state(), QAnimationGroupJob::Running); + QCOMPARE(anim2.state(), QAnimationGroupJob::Stopped); + + group.pause(); + + QCOMPARE(group.state(), QAnimationGroupJob::Paused); + QCOMPARE(anim1.state(), QAnimationGroupJob::Paused); + QCOMPARE(anim2.state(), QAnimationGroupJob::Stopped); + + group.stop(); + + QCOMPARE(group.state(), QAnimationGroupJob::Stopped); + QCOMPARE(anim1.state(), QAnimationGroupJob::Stopped); + QCOMPARE(anim2.state(), QAnimationGroupJob::Stopped); +} + +void tst_QSequentialAnimationGroupJob::updateChildrenWithRunningGroup() +{ + // assert that its possible to modify a child's state directly while their group is running + QSequentialAnimationGroupJob group; + + TestAnimation anim(200); + + StateChangeListener groupStateChangedSpy; + group.addAnimationChangeListener(&groupStateChangedSpy, QAbstractAnimationJob::StateChange); + StateChangeListener childStateChangedSpy; + anim.addAnimationChangeListener(&childStateChangedSpy, QAbstractAnimationJob::StateChange); + + QCOMPARE(groupStateChangedSpy.count(), 0); + QCOMPARE(childStateChangedSpy.count(), 0); + QCOMPARE(group.state(), QAnimationGroupJob::Stopped); + QCOMPARE(anim.state(), QAnimationGroupJob::Stopped); + + group.appendAnimation(&anim); + + group.start(); + + QCOMPARE(group.state(), QAnimationGroupJob::Running); + QCOMPARE(anim.state(), QAnimationGroupJob::Running); + + QCOMPARE(groupStateChangedSpy.count(), 1); + QCOMPARE(childStateChangedSpy.count(), 1); + + QCOMPARE(groupStateChangedSpy.states.at(0), QAnimationGroupJob::Running); + QCOMPARE(childStateChangedSpy.states.at(0), QAnimationGroupJob::Running); + + // starting directly a running child will not have any effect + anim.start(); + + QCOMPARE(groupStateChangedSpy.count(), 1); + QCOMPARE(childStateChangedSpy.count(), 1); + + anim.pause(); + + QCOMPARE(group.state(), QAnimationGroupJob::Running); + QCOMPARE(anim.state(), QAnimationGroupJob::Paused); + + // in the animation stops directly, the group will still be running + anim.stop(); + + QCOMPARE(group.state(), QAnimationGroupJob::Running); + QCOMPARE(anim.state(), QAnimationGroupJob::Stopped); + + //cleanup + group.removeAnimationChangeListener(&groupStateChangedSpy, QAbstractAnimationJob::StateChange); + anim.removeAnimationChangeListener(&childStateChangedSpy, QAbstractAnimationJob::StateChange); +} + +void tst_QSequentialAnimationGroupJob::deleteChildrenWithRunningGroup() +{ + // test if children can be activated when their group is stopped + QSequentialAnimationGroupJob group; + + TestAnimation *anim1 = new TestAnimation(200); + group.appendAnimation(anim1); + + QCOMPARE(group.duration(), anim1->duration()); + + group.start(); + QCOMPARE(group.state(), QAnimationGroupJob::Running); + QCOMPARE(anim1->state(), QAnimationGroupJob::Running); + + QTest::qWait(100); + QTRY_VERIFY(group.currentLoopTime() > 0); + + delete anim1; + QVERIFY(!group.firstChild()); + QCOMPARE(group.duration(), 0); + QCOMPARE(group.state(), QAnimationGroupJob::Stopped); + QCOMPARE(group.currentLoopTime(), 0); //that's the invariant +} + +void tst_QSequentialAnimationGroupJob::startChildrenWithStoppedGroup() +{ + // test if children can be activated when their group is stopped + QSequentialAnimationGroupJob group; + + TestAnimation anim1(200); + TestAnimation anim2(200); + + QCOMPARE(group.state(), QAnimationGroupJob::Stopped); + QCOMPARE(anim1.state(), QAnimationGroupJob::Stopped); + QCOMPARE(anim2.state(), QAnimationGroupJob::Stopped); + + group.appendAnimation(&anim1); + group.appendAnimation(&anim2); + + group.stop(); + + QCOMPARE(group.state(), QAnimationGroupJob::Stopped); + QCOMPARE(anim1.state(), QAnimationGroupJob::Stopped); + QCOMPARE(anim2.state(), QAnimationGroupJob::Stopped); + + anim1.start(); + anim2.start(); + anim2.pause(); + + QCOMPARE(group.state(), QAnimationGroupJob::Stopped); + QCOMPARE(anim1.state(), QAnimationGroupJob::Running); + QCOMPARE(anim2.state(), QAnimationGroupJob::Paused); +} + +void tst_QSequentialAnimationGroupJob::stopGroupWithRunningChild() +{ + // children that started independently will not be affected by a group stop + QSequentialAnimationGroupJob group; + + TestAnimation anim1(200); + TestAnimation anim2(200); + + QCOMPARE(group.state(), QAnimationGroupJob::Stopped); + QCOMPARE(anim1.state(), QAnimationGroupJob::Stopped); + QCOMPARE(anim2.state(), QAnimationGroupJob::Stopped); + + group.appendAnimation(&anim1); + group.appendAnimation(&anim2); + + anim1.start(); + anim2.start(); + anim2.pause(); + + QCOMPARE(group.state(), QAnimationGroupJob::Stopped); + QCOMPARE(anim1.state(), QAnimationGroupJob::Running); + QCOMPARE(anim2.state(), QAnimationGroupJob::Paused); + + group.stop(); + + QCOMPARE(group.state(), QAnimationGroupJob::Stopped); + QCOMPARE(anim1.state(), QAnimationGroupJob::Running); + QCOMPARE(anim2.state(), QAnimationGroupJob::Paused); + + anim1.stop(); + anim2.stop(); + + QCOMPARE(group.state(), QAnimationGroupJob::Stopped); + QCOMPARE(anim1.state(), QAnimationGroupJob::Stopped); + QCOMPARE(anim2.state(), QAnimationGroupJob::Stopped); +} + +void tst_QSequentialAnimationGroupJob::startGroupWithRunningChild() +{ + // as the group has precedence over its children, starting a group will restart all the children + QSequentialAnimationGroupJob group; + + TestAnimation *anim1 = new TestAnimation(200); + TestAnimation *anim2 = new TestAnimation(200); + + StateChangeListener stateChangedSpy1; + anim1->addAnimationChangeListener(&stateChangedSpy1, QAbstractAnimationJob::StateChange); + StateChangeListener stateChangedSpy2; + anim2->addAnimationChangeListener(&stateChangedSpy2, QAbstractAnimationJob::StateChange); + + QCOMPARE(stateChangedSpy1.count(), 0); + QCOMPARE(stateChangedSpy2.count(), 0); + QCOMPARE(group.state(), QAnimationGroupJob::Stopped); + QCOMPARE(anim1->state(), QAnimationGroupJob::Stopped); + QCOMPARE(anim2->state(), QAnimationGroupJob::Stopped); + + group.appendAnimation(anim1); + group.appendAnimation(anim2); + + anim1->start(); + anim2->start(); + anim2->pause(); + + QVERIFY(compareStates(stateChangedSpy1, (StateList() << QAbstractAnimationJob::Running))); + + QVERIFY(compareStates(stateChangedSpy2, (StateList() << QAbstractAnimationJob::Running + << QAbstractAnimationJob::Paused))); + + QCOMPARE(group.state(), QAnimationGroupJob::Stopped); + QCOMPARE(anim1->state(), QAnimationGroupJob::Running); + QCOMPARE(anim2->state(), QAnimationGroupJob::Paused); + + group.start(); + + QVERIFY(compareStates(stateChangedSpy1, (StateList() << QAbstractAnimationJob::Running + << QAbstractAnimationJob::Stopped + << QAbstractAnimationJob::Running))); + QVERIFY(compareStates(stateChangedSpy2, (StateList() << QAbstractAnimationJob::Running + << QAbstractAnimationJob::Paused))); + + QCOMPARE(group.state(), QAnimationGroupJob::Running); + QCOMPARE(anim1->state(), QAnimationGroupJob::Running); + QCOMPARE(anim2->state(), QAnimationGroupJob::Paused); + + QTest::qWait(300); + + QCOMPARE(group.state(), QAnimationGroupJob::Running); + QCOMPARE(anim1->state(), QAnimationGroupJob::Stopped); + QCOMPARE(anim2->state(), QAnimationGroupJob::Running); + + QCOMPARE(stateChangedSpy2.count(), 4); + QCOMPARE(stateChangedSpy2.states.at(2), QAnimationGroupJob::Stopped); + QCOMPARE(stateChangedSpy2.states.at(3), QAnimationGroupJob::Running); + + group.stop(); + + QCOMPARE(group.state(), QAnimationGroupJob::Stopped); + QCOMPARE(anim1->state(), QAnimationGroupJob::Stopped); + QCOMPARE(anim2->state(), QAnimationGroupJob::Stopped); + + anim1->removeAnimationChangeListener(&stateChangedSpy1, QAbstractAnimationJob::StateChange); + anim2->removeAnimationChangeListener(&stateChangedSpy2, QAbstractAnimationJob::StateChange); +} + +void tst_QSequentialAnimationGroupJob::zeroDurationAnimation() +{ + QSequentialAnimationGroupJob group; + + TestAnimation *anim1 = new TestAnimation(0); + TestAnimation *anim2 = new TestAnimation(100); + TestValueAnimation *anim3 = new TestValueAnimation(0); + anim3->end = 100; + + StateChangeListener stateChangedSpy; + anim1->addAnimationChangeListener(&stateChangedSpy, QAbstractAnimationJob::StateChange); + + group.appendAnimation(anim1); + group.appendAnimation(anim2); + group.appendAnimation(anim3); + group.setLoopCount(2); + group.start(); + + QCOMPARE(stateChangedSpy.count(), 2); + QCOMPARE(stateChangedSpy.states.at(0), QAnimationGroupJob::Running); + QCOMPARE(stateChangedSpy.states.at(1), QAnimationGroupJob::Stopped); + + QCOMPARE(anim1->state(), QAnimationGroupJob::Stopped); + QCOMPARE(anim2->state(), QAnimationGroupJob::Running); + QCOMPARE(group.state(), QAnimationGroupJob::Running); + + //now let's try to seek to the next loop + group.setCurrentTime(group.duration() + 1); + QCOMPARE(anim1->state(), QAnimationGroupJob::Stopped); + QCOMPARE(anim2->state(), QAnimationGroupJob::Running); + QCOMPARE(anim3->state(), QAnimationGroupJob::Stopped); + QCOMPARE(group.state(), QAnimationGroupJob::Running); + //TODO: test that anim3 was run + QCOMPARE(anim3->value, qreal(100)); //anim3 should have been run + + anim1->removeAnimationChangeListener(&stateChangedSpy, QAbstractAnimationJob::StateChange); +} + +void tst_QSequentialAnimationGroupJob::stopUncontrolledAnimations() +{ + QSequentialAnimationGroupJob group; + + UncontrolledAnimation notTimeDriven; + QCOMPARE(notTimeDriven.totalDuration(), -1); + + TestAnimation loopsForever(100); + loopsForever.setLoopCount(-1); + + group.appendAnimation(¬TimeDriven); + group.appendAnimation(&loopsForever); + + group.start(); + + QCOMPARE(group.state(), QAnimationGroupJob::Running); + QCOMPARE(notTimeDriven.state(), QAnimationGroupJob::Running); + QCOMPARE(loopsForever.state(), QAnimationGroupJob::Stopped); + + notTimeDriven.stop(); + + QCOMPARE(group.state(), QAnimationGroupJob::Running); + QCOMPARE(notTimeDriven.state(), QAnimationGroupJob::Stopped); + QCOMPARE(loopsForever.state(), QAnimationGroupJob::Running); + + loopsForever.stop(); + + QCOMPARE(group.state(), QAnimationGroupJob::Stopped); + QCOMPARE(notTimeDriven.state(), QAnimationGroupJob::Stopped); + QCOMPARE(loopsForever.state(), QAnimationGroupJob::Stopped); +} + +void tst_QSequentialAnimationGroupJob::finishWithUncontrolledAnimation() +{ + //1st case: + //first we test a group with one uncontrolled animation + QSequentialAnimationGroupJob group; + UncontrolledAnimation notTimeDriven; + group.appendAnimation(¬TimeDriven); + FinishedListener spy; + group.addAnimationChangeListener(&spy, QAbstractAnimationJob::Completion); + + group.start(); + QCOMPARE(group.state(), QAnimationGroupJob::Running); + QCOMPARE(notTimeDriven.state(), QAnimationGroupJob::Running); + QCOMPARE(group.currentLoopTime(), 0); + QCOMPARE(notTimeDriven.currentLoopTime(), 0); + + QTest::qWait(300); //wait for the end of notTimeDriven + QTRY_COMPARE(notTimeDriven.state(), QAnimationGroupJob::Stopped); + const int actualDuration = notTimeDriven.currentLoopTime(); + QCOMPARE(group.state(), QAnimationGroupJob::Stopped); + QCOMPARE(group.currentLoopTime(), actualDuration); + QCOMPARE(spy.count(), 1); + + //2nd case: + // lets make sure the seeking will work again + spy.clear(); + TestAnimation anim; + group.appendAnimation(&anim); + StateChangeListener animStateChangedSpy; + anim.addAnimationChangeListener(&animStateChangedSpy, QAbstractAnimationJob::StateChange); + + group.setCurrentTime(300); + QCOMPARE(group.state(), QAnimationGroupJob::Stopped); + QCOMPARE(notTimeDriven.currentLoopTime(), actualDuration); + QCOMPARE(group.currentAnimation(), static_cast<QAbstractAnimationJob*>(&anim)); + + //3rd case: + //now let's add a perfectly defined animation at the end + QCOMPARE(animStateChangedSpy.count(), 0); + group.start(); + QCOMPARE(group.state(), QAnimationGroupJob::Running); + QCOMPARE(notTimeDriven.state(), QAnimationGroupJob::Running); + QCOMPARE(group.currentLoopTime(), 0); + QCOMPARE(notTimeDriven.currentLoopTime(), 0); + + QCOMPARE(animStateChangedSpy.count(), 0); + + QTest::qWait(300); //wait for the end of notTimeDriven + QTRY_COMPARE(notTimeDriven.state(), QAnimationGroupJob::Stopped); + QCOMPARE(group.state(), QAnimationGroupJob::Running); + QCOMPARE(anim.state(), QAnimationGroupJob::Running); + QCOMPARE(group.currentAnimation(), static_cast<QAbstractAnimationJob*>(&anim)); + QCOMPARE(animStateChangedSpy.count(), 1); + QTest::qWait(300); //wait for the end of anim + + QTRY_COMPARE(anim.state(), QAnimationGroupJob::Stopped); + QCOMPARE(anim.currentLoopTime(), anim.duration()); + + //we should simply be at the end + QCOMPARE(spy.count(), 1); + QCOMPARE(animStateChangedSpy.count(), 2); + QCOMPARE(group.currentLoopTime(), notTimeDriven.currentLoopTime() + anim.currentLoopTime()); + + //cleanup + group.removeAnimationChangeListener(&spy, QAbstractAnimationJob::Completion); + anim.removeAnimationChangeListener(&animStateChangedSpy, QAbstractAnimationJob::StateChange); +} + +void tst_QSequentialAnimationGroupJob::addRemoveAnimation() +{ + //this test is specific to the sequential animation group + QSequentialAnimationGroupJob group; + + QCOMPARE(group.duration(), 0); + QCOMPARE(group.currentLoopTime(), 0); + QAbstractAnimationJob *anim1 = new TestAnimation; + group.appendAnimation(anim1); + QCOMPARE(group.duration(), 250); + QCOMPARE(group.currentLoopTime(), 0); + QCOMPARE(group.currentAnimation(), anim1); + + //let's append an animation + QAbstractAnimationJob *anim2 = new TestAnimation; + group.appendAnimation(anim2); + QCOMPARE(group.duration(), 500); + QCOMPARE(group.currentLoopTime(), 0); + QCOMPARE(group.currentAnimation(), anim1); + + //let's prepend an animation + QAbstractAnimationJob *anim0 = new TestAnimation; + group.prependAnimation(anim0); + QCOMPARE(group.duration(), 750); + QCOMPARE(group.currentLoopTime(), 0); + QCOMPARE(group.currentAnimation(), anim0); //anim0 has become the new currentAnimation + + group.setCurrentTime(300); //anim0 | anim1 | anim2 + QCOMPARE(group.currentLoopTime(), 300); + QCOMPARE(group.currentAnimation(), anim1); + QCOMPARE(anim1->currentLoopTime(), 50); + + group.removeAnimation(anim0); //anim1 | anim2 + QCOMPARE(group.currentLoopTime(), 50); + QCOMPARE(group.currentAnimation(), anim1); + QCOMPARE(anim1->currentLoopTime(), 50); + + group.setCurrentTime(0); + group.prependAnimation(anim0); //anim0 | anim1 | anim2 + group.setCurrentTime(300); + QCOMPARE(group.currentLoopTime(), 300); + QCOMPARE(group.currentAnimation(), anim1); + QCOMPARE(anim1->currentLoopTime(), 50); + + group.removeAnimation(anim1); //anim0 | anim2 + QCOMPARE(group.currentLoopTime(), 250); + QCOMPARE(group.currentAnimation(), anim2); + QCOMPARE(anim0->currentLoopTime(), 250); +} + +void tst_QSequentialAnimationGroupJob::currentAnimation() +{ + QSequentialAnimationGroupJob group; + QVERIFY(group.currentAnimation() == 0); + + TestAnimation anim(0); + group.appendAnimation(&anim); + QCOMPARE(group.currentAnimation(), static_cast<QAbstractAnimationJob*>(&anim)); +} + +void tst_QSequentialAnimationGroupJob::currentAnimationWithZeroDuration() +{ + QSequentialAnimationGroupJob group; + QVERIFY(group.currentAnimation() == 0); + + TestAnimation zero1(0); + TestAnimation zero2(0); + + TestAnimation anim; + + TestAnimation zero3(0); + TestAnimation zero4(0); + + group.appendAnimation(&zero1); + group.appendAnimation(&zero2); + group.appendAnimation(&anim); + group.appendAnimation(&zero3); + group.appendAnimation(&zero4); + + QCOMPARE(group.currentAnimation(), static_cast<QAbstractAnimationJob*>(&zero1)); + + group.setCurrentTime(0); + QCOMPARE(group.currentAnimation(), static_cast<QAbstractAnimationJob*>(&anim)); + + group.setCurrentTime(group.duration()); + QCOMPARE(group.currentAnimation(), static_cast<QAbstractAnimationJob*>(&zero4)); + + group.setDirection(QAbstractAnimationJob::Backward); + + group.setCurrentTime(0); + QCOMPARE(group.currentAnimation(), static_cast<QAbstractAnimationJob*>(&zero1)); + + group.setCurrentTime(group.duration()); + QCOMPARE(group.currentAnimation(), static_cast<QAbstractAnimationJob*>(&anim)); +} + +void tst_QSequentialAnimationGroupJob::insertAnimation() +{ + QSequentialAnimationGroupJob group; + group.setLoopCount(2); + TestAnimation *anim = new TestAnimation; + group.appendAnimation(anim); + QCOMPARE(group.duration(), anim->duration()); + group.setCurrentTime(300); + QCOMPARE(group.currentLoop(), 1); + + //this will crash if the sequential group calls duration on the created animation + group.appendAnimation(new TestAnimation); +} + +class ClearFinishedListener: public QAnimation2ChangeListener +{ +public: + ClearFinishedListener(QSequentialAnimationGroupJob *g) : group(g) {} + + virtual void animationFinished(QAbstractAnimationJob *) + { + group->clear(); + } + + QSequentialAnimationGroupJob *group; +}; + +class RefillFinishedListener: public QAnimation2ChangeListener +{ +public: + RefillFinishedListener(QSequentialAnimationGroupJob *g) : group(g) {} + + virtual void animationFinished(QAbstractAnimationJob *) + { + group->stop(); + group->clear(); + group->appendAnimation(new TestAnimation); + group->start(); + } + + QSequentialAnimationGroupJob *group; +}; + +void tst_QSequentialAnimationGroupJob::clear() +{ + QSKIP("deleting an animation when finished is not currently supported"); + QSequentialAnimationGroupJob group; + TestAnimation *anim1 = new TestAnimation; + group.appendAnimation(anim1); + ClearFinishedListener clearListener(&group); + anim1->addAnimationChangeListener(&clearListener, QAbstractAnimationJob::Completion); + + TestAnimation *anim2 = new TestAnimation; + group.appendAnimation(anim2); + QCOMPARE(group.firstChild(), anim1); + QCOMPARE(group.lastChild(), anim2); + + group.start(); + QTest::qWait(anim1->duration() + 100); + QTRY_VERIFY(!group.firstChild()); + QCOMPARE(group.state(), QAbstractAnimationJob::Stopped); + QCOMPARE(group.currentLoopTime(), 0); + + anim1 = new TestAnimation; + group.appendAnimation(anim1); + RefillFinishedListener refillListener(&group); + anim1->addAnimationChangeListener(&refillListener, QAbstractAnimationJob::Completion); + group.start(); + QTest::qWait(anim1->duration() + 100); + QTRY_COMPARE(group.state(), QAbstractAnimationJob::Running); +} + +void tst_QSequentialAnimationGroupJob::pauseResume() +{ + QParallelAnimationGroupJob group; + TestAnimation *anim = new TestAnimation; + group.appendAnimation(anim); + StateChangeListener spy; + anim->addAnimationChangeListener(&spy, QAbstractAnimationJob::StateChange); + QCOMPARE(group.duration(), 250); + group.start(); + QTest::qWait(100); + QTRY_COMPARE(group.state(), QAnimationGroupJob::Running); + QCOMPARE(anim->state(), QAnimationGroupJob::Running); + QCOMPARE(spy.count(), 1); + spy.clear(); + const int currentTime = group.currentLoopTime(); + QCOMPARE(anim->currentLoopTime(), currentTime); + + group.pause(); + QCOMPARE(group.state(), QAnimationGroupJob::Paused); + QCOMPARE(group.currentLoopTime(), currentTime); + QCOMPARE(anim->state(), QAnimationGroupJob::Paused); + QCOMPARE(anim->currentLoopTime(), currentTime); + QCOMPARE(spy.count(), 1); + spy.clear(); + + group.resume(); + QCOMPARE(group.state(), QAnimationGroupJob::Running); + QCOMPARE(group.currentLoopTime(), currentTime); + QCOMPARE(anim->state(), QAnimationGroupJob::Running); + QCOMPARE(anim->currentLoopTime(), currentTime); + QCOMPARE(spy.count(), 1); + + anim->removeAnimationChangeListener(&spy, QAbstractAnimationJob::StateChange); +} + +QTEST_MAIN(tst_QSequentialAnimationGroupJob) +#include "tst_qsequentialanimationgroupjob.moc" |