diff options
Diffstat (limited to 'tests/auto/qpropertyanimation/tst_qpropertyanimation.cpp')
-rw-r--r-- | tests/auto/qpropertyanimation/tst_qpropertyanimation.cpp | 958 |
1 files changed, 958 insertions, 0 deletions
diff --git a/tests/auto/qpropertyanimation/tst_qpropertyanimation.cpp b/tests/auto/qpropertyanimation/tst_qpropertyanimation.cpp new file mode 100644 index 0000000..0aeac91 --- /dev/null +++ b/tests/auto/qpropertyanimation/tst_qpropertyanimation.cpp @@ -0,0 +1,958 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> + +#include <QtCore/qpropertyanimation.h> +#include <QtCore/qvariantanimation.h> +#include <QtGui/qwidget.h> + +//TESTED_CLASS=QPropertyAnimation +//TESTED_FILES= + +class UncontrolledAnimation : public QPropertyAnimation +{ + Q_OBJECT +public: + int duration() const { return -1; /* not time driven */ } + +protected: + void updateCurrentTime(int msecs) + { + QPropertyAnimation::updateCurrentTime(msecs); + if (msecs >= QPropertyAnimation::duration()) + stop(); + } +}; + +class MyObject : public QObject +{ + Q_OBJECT + Q_PROPERTY(qreal x READ x WRITE setX) +public: + MyObject() : m_x(0) { } + qreal x() const { return m_x; } + void setX(qreal x) { m_x = x; } +private: + qreal m_x; +}; + +class tst_QPropertyAnimation : public QObject +{ + Q_OBJECT +public: + tst_QPropertyAnimation(); + virtual ~tst_QPropertyAnimation(); + +public Q_SLOTS: + void init(); + void cleanup(); + +private slots: + void construction(); + void setCurrentTime_data(); + void setCurrentTime(); + void statesAndSignals_data(); + void statesAndSignals(); + void deletion1(); + void deletion2(); + void deletion3(); + void duration0(); + void noStartValue(); + void noStartValueWithLoop(); + void startWhenAnotherIsRunning(); + void easingcurve_data(); + void easingcurve(); + void startWithoutStartValue(); + void playForwardBackward(); + void interpolated(); + void setStartEndValues_data(); + void setStartEndValues(); + void zeroDurationStart(); + void operationsInStates_data(); + void operationsInStates(); + void oneKeyValue(); + void updateOnSetKeyValues(); +}; + +tst_QPropertyAnimation::tst_QPropertyAnimation() +{ +} + +tst_QPropertyAnimation::~tst_QPropertyAnimation() +{ +} + +void tst_QPropertyAnimation::init() +{ + qRegisterMetaType<QAbstractAnimation::State>("QAbstractAnimation::State"); + qRegisterMetaType<QAbstractAnimation::DeletionPolicy>("QAbstractAnimation::DeletionPolicy"); +} + +void tst_QPropertyAnimation::cleanup() +{ +} + +class AnimationObject : public QObject +{ + Q_OBJECT + Q_PROPERTY(int value READ value WRITE setValue) + Q_PROPERTY(qreal realValue READ realValue WRITE setRealValue) +public: + AnimationObject(int startValue = 0) + : v(startValue) + { } + + int value() const { return v; } + void setValue(int value) { v = value; } + + qreal realValue() const { return rv; } + void setRealValue(qreal value) { rv = value; } + + int v; + qreal rv; +}; + + +void tst_QPropertyAnimation::construction() +{ + QPropertyAnimation panimation; +} + +void tst_QPropertyAnimation::setCurrentTime_data() +{ + QTest::addColumn<int>("duration"); + QTest::addColumn<int>("loopCount"); + QTest::addColumn<int>("currentTime"); + QTest::addColumn<int>("testCurrentTime"); + QTest::addColumn<int>("testCurrentLoop"); + + QTest::newRow("-1") << -1 << 1 << 0 << 0 << 0; + QTest::newRow("0") << 0 << 1 << 0 << 0 << 0; + QTest::newRow("1") << 0 << 1 << 1 << 0 << 0; + QTest::newRow("2") << 0 << 2 << 1 << 0 << 0; + QTest::newRow("3") << 1 << 1 << 0 << 0 << 0; + QTest::newRow("4") << 1 << 1 << 1 << 1 << 0; + QTest::newRow("5") << 1 << 2 << 1 << 0 << 1; + QTest::newRow("6") << 1 << 2 << 2 << 1 << 1; + QTest::newRow("7") << 1 << 2 << 3 << 1 << 1; + QTest::newRow("8") << 1 << 3 << 2 << 0 << 2; + QTest::newRow("9") << 1 << 3 << 3 << 1 << 2; + QTest::newRow("a") << 10 << 1 << 0 << 0 << 0; + QTest::newRow("b") << 10 << 1 << 1 << 1 << 0; + QTest::newRow("c") << 10 << 1 << 10 << 10 << 0; + QTest::newRow("d") << 10 << 2 << 10 << 0 << 1; + QTest::newRow("e") << 10 << 2 << 11 << 1 << 1; + QTest::newRow("f") << 10 << 2 << 20 << 10 << 1; + QTest::newRow("g") << 10 << 2 << 21 << 10 << 1; + QTest::newRow("negloop 0") << 10 << -1 << 0 << 0 << 0; + QTest::newRow("negloop 1") << 10 << -1 << 10 << 0 << 1; + QTest::newRow("negloop 2") << 10 << -1 << 15 << 5 << 1; + QTest::newRow("negloop 3") << 10 << -1 << 20 << 0 << 2; + QTest::newRow("negloop 4") << 10 << -1 << 30 << 0 << 3; +} + +void tst_QPropertyAnimation::setCurrentTime() +{ + QFETCH(int, duration); + QFETCH(int, loopCount); + QFETCH(int, currentTime); + QFETCH(int, testCurrentTime); + QFETCH(int, testCurrentLoop); + + QPropertyAnimation animation; + if (duration < 0) + QTest::ignoreMessage(QtWarningMsg, "QVariantAnimation::setDuration: cannot set a negative duration"); + animation.setDuration(duration); + animation.setLoopCount(loopCount); + animation.setCurrentTime(currentTime); + + QCOMPARE(animation.currentTime(), testCurrentTime); + QCOMPARE(animation.currentLoop(), testCurrentLoop); +} + +void tst_QPropertyAnimation::statesAndSignals_data() +{ + QTest::addColumn<bool>("uncontrolled"); + QTest::newRow("normal animation") << false; + QTest::newRow("animation with undefined duration") << true; +} + +void tst_QPropertyAnimation::statesAndSignals() +{ + QFETCH(bool, uncontrolled); + QPropertyAnimation *anim = uncontrolled ? new UncontrolledAnimation : new QPropertyAnimation; + anim->setDuration(100); + + QSignalSpy finishedSpy(anim, SIGNAL(finished())); + QSignalSpy runningSpy(anim, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + QSignalSpy currentLoopSpy(anim, SIGNAL(currentLoopChanged(int))); + + anim->setCurrentTime(1); + anim->setCurrentTime(100); + QCOMPARE(finishedSpy.count(), 0); + QCOMPARE(runningSpy.count(), 0); + QCOMPARE(currentLoopSpy.count(), 0); + QCOMPARE(anim->state(), QAnimationGroup::Stopped); + + anim->setLoopCount(3); + anim->setCurrentTime(101); + + if (uncontrolled) + QSKIP("Uncontrolled animations don't handle looping", SkipSingle); + + QCOMPARE(currentLoopSpy.count(), 1); + QCOMPARE(anim->currentLoop(), 1); + + anim->setCurrentTime(0); + QCOMPARE(currentLoopSpy.count(), 2); + QCOMPARE(anim->currentLoop(), 0); + + anim->start(); + QCOMPARE(anim->state(), QAnimationGroup::Running); + QCOMPARE(runningSpy.count(), 1); //anim must have started + QCOMPARE(anim->currentLoop(), 0); + runningSpy.clear(); + + anim->stop(); + QCOMPARE(anim->state(), QAnimationGroup::Stopped); + QCOMPARE(runningSpy.count(), 1); //anim must have stopped + QCOMPARE(finishedSpy.count(), 0); + QCOMPARE(anim->currentTime(), 0); + QCOMPARE(anim->currentLoop(), 0); + QCOMPARE(currentLoopSpy.count(), 2); + runningSpy.clear(); + + anim->start(); + QTest::qWait(1000); + QCOMPARE(anim->state(), QAnimationGroup::Stopped); + QCOMPARE(runningSpy.count(), 2); //started and stopped again + runningSpy.clear(); + QCOMPARE(finishedSpy.count(), 1); + QCOMPARE(anim->currentTime(), 100); + QCOMPARE(anim->currentLoop(), 2); + QCOMPARE(currentLoopSpy.count(), 4); + + anim->start(); // auto-rewinds + QCOMPARE(anim->state(), QAnimationGroup::Running); + QCOMPARE(anim->currentTime(), 0); + QCOMPARE(anim->currentLoop(), 0); + QCOMPARE(currentLoopSpy.count(), 5); + QCOMPARE(runningSpy.count(), 1); // anim has started + QCOMPARE(finishedSpy.count(), 1); + QCOMPARE(anim->currentLoop(), 0); + runningSpy.clear(); + + QTest::qWait(1000); + + QCOMPARE(currentLoopSpy.count(), 7); + QCOMPARE(anim->state(), QAnimationGroup::Stopped); + QCOMPARE(anim->currentLoop(), 2); + QCOMPARE(runningSpy.count(), 1); // anim has stopped + QCOMPARE(finishedSpy.count(), 2); + QCOMPARE(anim->currentTime(), 100); + + delete anim; +} + +void tst_QPropertyAnimation::deletion1() +{ + QObject *object = new QWidget; + QPointer<QPropertyAnimation> anim = new QPropertyAnimation(object, "minimumWidth"); + + //test that the animation is deleted correctly depending of the deletion flag passed in start() + QSignalSpy runningSpy(anim, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + QSignalSpy finishedSpy(anim, SIGNAL(finished())); + anim->setStartValue(10); + anim->setEndValue(20); + anim->setDuration(200); + anim->start(); + QCOMPARE(runningSpy.count(), 1); + QCOMPARE(finishedSpy.count(), 0); + + QVERIFY(anim); + QCOMPARE(anim->state(), QAnimationGroup::Running); + QTest::qWait(100); + QVERIFY(anim); + QCOMPARE(anim->state(), QAnimationGroup::Running); + QTest::qWait(150); + QVERIFY(anim); //The animation should not have been deleted + QCOMPARE(anim->state(), QAnimationGroup::Stopped); + QCOMPARE(runningSpy.count(), 2); + QCOMPARE(finishedSpy.count(), 1); + + anim->start(QVariantAnimation::DeleteWhenStopped); + QVERIFY(anim); + QCOMPARE(anim->state(), QAnimationGroup::Running); + QTest::qWait(100); + QVERIFY(anim); + QCOMPARE(anim->state(), QAnimationGroup::Running); + QTest::qWait(150); + QVERIFY(!anim); //The animation must have been deleted + QCOMPARE(runningSpy.count(), 4); + QCOMPARE(finishedSpy.count(), 2); + delete object; +} + +void tst_QPropertyAnimation::deletion2() +{ + //test that the animation get deleted if the object is deleted + QObject *object = new QWidget; + QPointer<QPropertyAnimation> anim = new QPropertyAnimation(object,"minimumWidth"); + anim->setStartValue(10); + anim->setEndValue(20); + anim->setDuration(200); + + QSignalSpy runningSpy(anim, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + QSignalSpy finishedSpy(anim, SIGNAL(finished())); + + anim->setStartValue(10); + anim->setEndValue(20); + anim->setDuration(200); + anim->start(); + + QTest::qWait(50); + QVERIFY(anim); + QCOMPARE(anim->state(), QAnimationGroup::Running); + + QCOMPARE(runningSpy.count(), 1); + QCOMPARE(finishedSpy.count(), 0); + + //we can't call deletaLater directly because the delete would only happen in the next loop of _this_ event loop + QTimer::singleShot(0, object, SLOT(deleteLater())); + QTest::qWait(50); + + QVERIFY(anim->targetObject() == 0); +} + +void tst_QPropertyAnimation::deletion3() +{ + //test that the stopped signal is emit when the animation is destroyed + QObject *object = new QWidget; + QPropertyAnimation *anim = new QPropertyAnimation(object,"minimumWidth"); + anim->setStartValue(10); + anim->setEndValue(20); + anim->setDuration(200); + + QSignalSpy runningSpy(anim, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + QSignalSpy finishedSpy(anim, SIGNAL(finished())); + anim->start(); + + QTest::qWait(50); + QCOMPARE(anim->state(), QAnimationGroup::Running); + QCOMPARE(runningSpy.count(), 1); + QCOMPARE(finishedSpy.count(), 0); + delete anim; + QCOMPARE(runningSpy.count(), 2); + QCOMPARE(finishedSpy.count(), 0); +} + +void tst_QPropertyAnimation::duration0() +{ + QObject o; + o.setProperty("ole", 42); + QCOMPARE(o.property("ole").toInt(), 42); + + QPropertyAnimation animation(&o, "ole"); + animation.setEndValue(43); + QVERIFY(!animation.currentValue().isValid()); + QCOMPARE(animation.currentValue().toInt(), 0); + QCOMPARE(o.property("ole").toInt(), 42); + animation.setDuration(0); + animation.start(); + QCOMPARE(animation.state(), QAnimationGroup::Stopped); + QCOMPARE(animation.currentTime(), 0); + QCOMPARE(o.property("ole").toInt(), 43); +} + +class StartValueTester : public QObject +{ + Q_OBJECT + Q_PROPERTY(int ole READ ole WRITE setOle) +public: + StartValueTester() : o(0) { } + int ole() const { return o; } + void setOle(int v) { o = v; values << v; } + + int o; + QList<int> values; +}; + +void tst_QPropertyAnimation::noStartValue() +{ + StartValueTester o; + o.setProperty("ole", 42); + o.values.clear(); + + QPropertyAnimation a(&o, "ole"); + a.setEndValue(420); + a.setDuration(250); + a.start(); + + QTest::qWait(300); + + QCOMPARE(o.values.first(), 42); + QCOMPARE(o.values.last(), 420); +} + +void tst_QPropertyAnimation::noStartValueWithLoop() +{ + StartValueTester o; + o.setProperty("ole", 42); + o.values.clear(); + + QPropertyAnimation a(&o, "ole"); + a.setEndValue(420); + a.setDuration(250); + a.setLoopCount(2); + a.start(); + + a.setCurrentTime(250); + QCOMPARE(o.values.first(), 42); + QCOMPARE(a.currentValue().toInt(), 42); + QCOMPARE(o.values.last(), 42); + + a.setCurrentTime(500); + QCOMPARE(a.currentValue().toInt(), 420); +} + +void tst_QPropertyAnimation::startWhenAnotherIsRunning() +{ + StartValueTester o; + o.setProperty("ole", 42); + o.values.clear(); + + { + //normal case: the animation finishes and is deleted + QPointer<QVariantAnimation> anim = new QPropertyAnimation(&o, "ole"); + QSignalSpy runningSpy(anim, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + anim->start(QVariantAnimation::DeleteWhenStopped); + QTest::qWait(anim->duration() + 50); + QCOMPARE(runningSpy.count(), 2); //started and then stopped + QVERIFY(!anim); + } + + { + QPointer<QVariantAnimation> anim = new QPropertyAnimation(&o, "ole"); + QSignalSpy runningSpy(anim, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + anim->start(QVariantAnimation::DeleteWhenStopped); + QTest::qWait(anim->duration()/2); + QPointer<QVariantAnimation> anim2 = new QPropertyAnimation(&o, "ole"); + QCOMPARE(runningSpy.count(), 1); + QCOMPARE(anim->state(), QVariantAnimation::Running); + + //anim2 will interrupt anim1 + QMetaObject::invokeMethod(anim2, "start", Qt::QueuedConnection, Q_ARG(QAbstractAnimation::DeletionPolicy, QVariantAnimation::DeleteWhenStopped)); + QTest::qWait(50); + QVERIFY(!anim); //anim should have been deleted + QVERIFY(anim2); + QTest::qWait(anim2->duration()); + QVERIFY(!anim2); //anim2 is finished: it should have been deleted by now + QVERIFY(!anim); + } + +} + +// copy from easing.cpp in case that function changes definition +static qreal easeInOutBack(qreal t) +{ + qreal s = 1.70158; + qreal t_adj = 2.0f * (qreal)t; + if (t_adj < 1) { + s *= 1.525f; + return 1.0/2*(t_adj*t_adj*((s+1)*t_adj - s)); + } else { + t_adj -= 2; + s *= 1.525f; + return 1.0/2*(t_adj*t_adj*((s+1)*t_adj + s) + 2); + } +} + +void tst_QPropertyAnimation::easingcurve_data() +{ + QTest::addColumn<int>("currentTime"); + QTest::addColumn<int>("expectedvalue"); + + QTest::newRow("interpolation1") << 0 << 0; + QTest::newRow("interpolation2") << 1000 << 1000; + QTest::newRow("extrapolationbelow") << 250 << -99; + QTest::newRow("extrapolationabove") << 750 << 1099; +} + +void tst_QPropertyAnimation::easingcurve() +{ + QFETCH(int, currentTime); + QFETCH(int, expectedvalue); + QObject o; + o.setProperty("ole", 42); + QCOMPARE(o.property("ole").toInt(), 42); + + QPropertyAnimation pAnimation(&o, "ole"); + pAnimation.setStartValue(0); + pAnimation.setEndValue(1000); + pAnimation.setDuration(1000); + + // this easingcurve assumes that we extrapolate before startValue and after endValue + QEasingCurve easingCurve; + easingCurve.setCustomType(easeInOutBack); + pAnimation.setEasingCurve(easingCurve); + pAnimation.start(); + pAnimation.pause(); + pAnimation.setCurrentTime(currentTime); + QCOMPARE(o.property("ole").toInt(), expectedvalue); +} + +void tst_QPropertyAnimation::startWithoutStartValue() +{ + QObject o; + o.setProperty("ole", 42); + QCOMPARE(o.property("ole").toInt(), 42); + + QPropertyAnimation anim(&o, "ole"); + anim.setEndValue(100); + + anim.start(); + + QTest::qWait(100); + int current = anim.currentValue().toInt(); + //it is somewhere in the animation + QVERIFY(current > 42); + QVERIFY(current < 100); + + QTest::qWait(200); + QCOMPARE(anim.state(), QVariantAnimation::Stopped); + + anim.setEndValue(110); + anim.start(); + current = anim.currentValue().toInt(); + // the default start value will reevaluate the current property + // and set it to the end value of the last iteration + QCOMPARE(current, 100); + QTest::qWait(100); + current = anim.currentValue().toInt(); + //it is somewhere in the animation + QVERIFY(current >= 100); + QVERIFY(current <= 110); +} + +void tst_QPropertyAnimation::playForwardBackward() +{ + QObject o; + o.setProperty("ole", 0); + QCOMPARE(o.property("ole").toInt(), 0); + + QPropertyAnimation anim(&o, "ole"); + anim.setEndValue(100); + anim.start(); + QTest::qWait(anim.duration() + 50); + QCOMPARE(anim.state(), QAbstractAnimation::Stopped); + QCOMPARE(anim.currentTime(), anim.duration()); + + //the animation is at the end + anim.setDirection(QVariantAnimation::Backward); + anim.start(); + QCOMPARE(anim.state(), QAbstractAnimation::Running); + QTest::qWait(anim.duration() + 50); + QCOMPARE(anim.state(), QAbstractAnimation::Stopped); + QCOMPARE(anim.currentTime(), 0); + + //the direction is backward + //restarting should jump to the end + anim.start(); + QCOMPARE(anim.state(), QAbstractAnimation::Running); + QCOMPARE(anim.currentTime(), anim.duration()); + QTest::qWait(anim.duration() + 50); + QCOMPARE(anim.state(), QAbstractAnimation::Stopped); + QCOMPARE(anim.currentTime(), 0); +} + +struct Number +{ + Number() {} + Number(int n) + : n(n) {} + + Number(const Number &other) + : n(other.n){} + + Number &operator=(const Number &other) { + n = other.n; + return *this; + } + bool operator==(const Number &other) const { + return n == other.n; + } + + int n; +}; + +Q_DECLARE_METATYPE(Number) +Q_DECLARE_METATYPE(QAbstractAnimation::State) + +QVariant numberInterpolator(const Number &f, const Number &t, qreal progress) +{ + return qVariantFromValue<Number>(Number(f.n + (t.n - f.n)*progress)); +} + +QVariant xaxisQPointInterpolator(const QPointF &f, const QPointF &t, qreal progress) +{ + return QPointF(f.x() + (t.x() - f.x())*progress, f.y()); +} + +void tst_QPropertyAnimation::interpolated() +{ + QObject o; + o.setProperty("point", QPointF()); //this will avoid warnings + o.setProperty("number", qVariantFromValue<Number>(Number(42))); + QCOMPARE(qVariantValue<Number>(o.property("number")), Number(42)); + { + qRegisterAnimationInterpolator<Number>(numberInterpolator); + QPropertyAnimation anim(&o, "number"); + anim.setStartValue(qVariantFromValue<Number>(Number(0))); + anim.setEndValue(qVariantFromValue<Number>(Number(100))); + anim.setDuration(1000); + anim.start(); + anim.pause(); + anim.setCurrentTime(100); + Number t(qVariantValue<Number>(o.property("number"))); + QCOMPARE(t, Number(10)); + anim.setCurrentTime(500); + QCOMPARE(qVariantValue<Number>(o.property("number")), Number(50)); + } + { + qRegisterAnimationInterpolator<QPointF>(xaxisQPointInterpolator); + QPropertyAnimation anim(&o, "point"); + anim.setStartValue(QPointF(0,0)); + anim.setEndValue(QPointF(100, 100)); + anim.setDuration(1000); + anim.start(); + anim.pause(); + anim.setCurrentTime(100); + QCOMPARE(o.property("point"), QVariant(QPointF(10, 0))); + anim.setCurrentTime(500); + QCOMPARE(o.property("point"), QVariant(QPointF(50, 0))); + } + { + // unregister it and see if we get back the default behaviour + qRegisterAnimationInterpolator<QPointF>(0); + QPropertyAnimation anim(&o, "point"); + anim.setStartValue(QPointF(0,0)); + anim.setEndValue(QPointF(100, 100)); + anim.setDuration(1000); + anim.start(); + anim.pause(); + anim.setCurrentTime(100); + QCOMPARE(o.property("point").toPointF(), QPointF(10, 10)); + anim.setCurrentTime(500); + QCOMPARE(o.property("point").toPointF(), QPointF(50, 50)); + } + + { + // Interpolate a qreal property with a int interpolator + AnimationObject o1; + o1.setRealValue(42.42); + QPropertyAnimation anim(&o1, "realValue"); + anim.setStartValue(0); + anim.setEndValue(100); + anim.start(); + QCOMPARE(o1.realValue(), qreal(0)); + anim.setCurrentTime(250); + QCOMPARE(o1.realValue(), qreal(100)); + } +} + +Q_DECLARE_METATYPE(QVariant) + +void tst_QPropertyAnimation::setStartEndValues_data() +{ + QTest::addColumn<QByteArray>("propertyName"); + QTest::addColumn<QVariant>("initialValue"); + QTest::addColumn<QVariant>("startValue"); + QTest::addColumn<QVariant>("endValue"); + + QTest::newRow("dynamic property") << QByteArray("ole") << QVariant(42) << QVariant(0) << QVariant(10); + QTest::newRow("real property, with unmatching types") << QByteArray("x") << QVariant(42.) << QVariant(0) << QVariant(10.); +} + +void tst_QPropertyAnimation::setStartEndValues() +{ + MyObject object; + QFETCH(QByteArray, propertyName); + QFETCH(QVariant, initialValue); + QFETCH(QVariant, startValue); + QFETCH(QVariant, endValue); + + //this tests the start value, end value and default start value + object.setProperty(propertyName, initialValue); + QPropertyAnimation anim(&object, propertyName); + QVariantAnimation::KeyValues values; + QCOMPARE(anim.keyValues(), values); + + //let's add a start value + anim.setStartValue(startValue); + values << QVariantAnimation::KeyValue(0, startValue); + QCOMPARE(anim.keyValues(), values); + + anim.setEndValue(endValue); + values << QVariantAnimation::KeyValue(1, endValue); + QCOMPARE(anim.keyValues(), values); + + //now we can play with objects + QCOMPARE(object.property(propertyName).toDouble(), initialValue.toDouble()); + anim.start(); + QVERIFY(anim.startValue().isValid()); + QCOMPARE(object.property(propertyName), anim.startValue()); + anim.setCurrentTime(anim.duration()/2); + QCOMPARE(object.property(propertyName).toDouble(), (startValue.toDouble() + endValue.toDouble())/2 ); //just in the middle of the animation + anim.setCurrentTime(anim.duration()); //we go to the end of the animation + QCOMPARE(anim.state(), QAnimationGroup::Stopped); //it should have stopped + QVERIFY(anim.endValue().isValid()); + QCOMPARE(object.property(propertyName), anim.endValue()); //end of the animations + + //now we remove the explicit start value and test the implicit one + anim.stop(); + object.setProperty(propertyName, initialValue); + + //let's reset the start value + values.remove(0); + anim.setStartValue(QVariant()); + QCOMPARE(anim.keyValues(), values); + QVERIFY(!anim.startValue().isValid()); + + anim.start(); + QCOMPARE(object.property(propertyName), initialValue); + anim.setCurrentTime(anim.duration()/2); + QCOMPARE(object.property(propertyName).toDouble(), (initialValue.toDouble() + endValue.toDouble())/2 ); //just in the middle of the animation + anim.setCurrentTime(anim.duration()); //we go to the end of the animation + QCOMPARE(anim.state(), QAnimationGroup::Stopped); //it should have stopped + QVERIFY(anim.endValue().isValid()); + QCOMPARE(object.property(propertyName), anim.endValue()); //end of the animations + + //now we set back the startValue + anim.setStartValue(startValue); + QVERIFY(anim.startValue().isValid()); + anim.start(); + QCOMPARE(object.property(propertyName), startValue); +} + +void tst_QPropertyAnimation::zeroDurationStart() +{ + QPropertyAnimation anim; + QSignalSpy spy(&anim, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + anim.setDuration(0); + QCOMPARE(anim.state(), QAbstractAnimation::Stopped); + anim.start(); + //the animation stops immediately + QCOMPARE(anim.state(), QAbstractAnimation::Stopped); + QCOMPARE(spy.count(), 2); + + //let's check the first state change + const QVariantList firstChange = spy.first(); + //old state + QCOMPARE(qVariantValue<QAbstractAnimation::State>(firstChange.first()), QAbstractAnimation::Stopped); + //new state + QCOMPARE(qVariantValue<QAbstractAnimation::State>(firstChange.last()), QAbstractAnimation::Running); + + //let's check the first state change + const QVariantList secondChange = spy.last(); + //old state + QCOMPARE(qVariantValue<QAbstractAnimation::State>(secondChange.first()), QAbstractAnimation::Running); + //new state + QCOMPARE(qVariantValue<QAbstractAnimation::State>(secondChange.last()), QAbstractAnimation::Stopped); +} + +#define Pause 1 +#define Start 2 +#define Resume 3 +#define Stop 4 + +void tst_QPropertyAnimation::operationsInStates_data() +{ + QTest::addColumn<QAbstractAnimation::State>("originState"); + QTest::addColumn<int>("operation"); + QTest::addColumn<QString>("expectedWarning"); + QTest::addColumn<QAbstractAnimation::State>("expectedState"); + + QString pauseWarn(QLatin1String("QAbstractAnimation::pause: Cannot pause a stopped animation")); + QString resumeWarn(QLatin1String("QAbstractAnimation::resume: Cannot resume an animation that is not paused")); + + QTest::newRow("S-pause") << QAbstractAnimation::Stopped << Pause << pauseWarn << QAbstractAnimation::Stopped; + QTest::newRow("S-start") << QAbstractAnimation::Stopped << Start << QString() << QAbstractAnimation::Running; + QTest::newRow("S-resume") << QAbstractAnimation::Stopped << Resume << resumeWarn << QAbstractAnimation::Stopped; + QTest::newRow("S-stop") << QAbstractAnimation::Stopped << Stop << QString() << QAbstractAnimation::Stopped; + + QTest::newRow("P-pause") << QAbstractAnimation::Paused << Pause << QString() << QAbstractAnimation::Paused; + QTest::newRow("P-start") << QAbstractAnimation::Paused << Start << QString() << QAbstractAnimation::Running; + QTest::newRow("P-resume") << QAbstractAnimation::Paused << Resume << QString() << QAbstractAnimation::Running; + QTest::newRow("P-stop") << QAbstractAnimation::Paused << Stop << QString() << QAbstractAnimation::Stopped; + + QTest::newRow("R-pause") << QAbstractAnimation::Running << Pause << QString() << QAbstractAnimation::Paused; + QTest::newRow("R-start") << QAbstractAnimation::Running << Start << QString() << QAbstractAnimation::Running; + QTest::newRow("R-resume") << QAbstractAnimation::Running << Resume << resumeWarn << QAbstractAnimation::Running; + QTest::newRow("R-stop") << QAbstractAnimation::Running << Stop << QString() << QAbstractAnimation::Stopped; +} + +void tst_QPropertyAnimation::operationsInStates() +{ +/** + * | pause() |start() |resume() |stop() + * ----------+------------+-----------+-----------+-------------------+ + * Stopped | Stopped |Running |Stopped |Stopped | + * _| qWarning |restart |qWarning | | + * Paused | Paused |Running |Running |Stopped | + * _| | | | | + * Running | Paused |Running |Running |Stopped | + * | |restart |qWarning | | + * ----------+------------+-----------+-----------+-------------------+ +**/ + + QFETCH(QAbstractAnimation::State, originState); + QFETCH(int, operation); + QFETCH(QString, expectedWarning); + QFETCH(QAbstractAnimation::State, expectedState); + + QObject o; + o.setProperty("ole", 42); + QPropertyAnimation anim(&o, "ole"); + QSignalSpy spy(&anim, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + + anim.stop(); + switch (originState) { + case QAbstractAnimation::Stopped: + break; + case QAbstractAnimation::Paused: + anim.start(); + anim.pause(); + break; + case QAbstractAnimation::Running: + anim.start(); + break; + } + if (!expectedWarning.isEmpty()) { + QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); + } + QCOMPARE(anim.state(), originState); + switch (operation) { + case Pause: + anim.pause(); + break; + case Start: + anim.start(); + break; + case Resume: + anim.resume(); + break; + case Stop: + anim.stop(); + break; + } + + QCOMPARE(anim.state(), expectedState); +} +#undef Pause +#undef Start +#undef Resume +#undef Stop + +void tst_QPropertyAnimation::oneKeyValue() +{ + QObject o; + o.setProperty("ole", 42); + QCOMPARE(o.property("ole").toInt(), 42); + + QPropertyAnimation animation(&o, "ole"); + animation.setStartValue(43); + animation.setEndValue(44); + animation.setDuration(100); + + animation.setCurrentTime(0); + + QVERIFY(animation.currentValue().isValid()); + QCOMPARE(animation.currentValue().toInt(), 43); + QCOMPARE(o.property("ole").toInt(), 42); + + // remove the last key value + animation.setKeyValueAt(1.0, QVariant()); + + // we will neither interpolate, nor update the current value + // since there is only one 1 key value defined + animation.setCurrentTime(100); + + // the animation should not have been modified + QVERIFY(animation.currentValue().isValid()); + QCOMPARE(animation.currentValue().toInt(), 43); + QCOMPARE(o.property("ole").toInt(), 42); +} + +void tst_QPropertyAnimation::updateOnSetKeyValues() +{ + QObject o; + o.setProperty("ole", 100); + QCOMPARE(o.property("ole").toInt(), 100); + + QPropertyAnimation animation(&o, "ole"); + animation.setStartValue(100); + animation.setEndValue(200); + animation.setDuration(100); + + animation.setCurrentTime(50); + QCOMPARE(animation.currentValue().toInt(), 150); + animation.setKeyValueAt(0.0, 300); + QCOMPARE(animation.currentValue().toInt(), 250); + + o.setProperty("ole", 100); + QPropertyAnimation animation2(&o, "ole"); + QVariantAnimation::KeyValues kValues; + kValues << QVariantAnimation::KeyValue(0.0, 100) << QVariantAnimation::KeyValue(1.0, 200); + animation2.setKeyValues(kValues); + animation2.setDuration(100); + animation2.setCurrentTime(50); + QCOMPARE(animation2.currentValue().toInt(), 150); + + kValues.clear(); + kValues << QVariantAnimation::KeyValue(0.0, 300) << QVariantAnimation::KeyValue(1.0, 200); + animation2.setKeyValues(kValues); + + QCOMPARE(animation2.currentValue().toInt(), animation.currentValue().toInt()); +} + +QTEST_MAIN(tst_QPropertyAnimation) +#include "tst_qpropertyanimation.moc" |