| /**************************************************************************** |
| ** |
| ** 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$ |
| ** |
| ****************************************************************************/ |
| #include <qtest.h> |
| #include <QtQml/qqmlengine.h> |
| #include <QtQml/qqmlexpression.h> |
| #include <QtQml/qqmlcomponent.h> |
| #include <QtQuick/qquickview.h> |
| #include <QtQuick/private/qquickrectangle_p.h> |
| #include <private/qquickimage_p.h> |
| #include <private/qquickanimatedimage_p.h> |
| #include <QSignalSpy> |
| #include <QtQml/qqmlcontext.h> |
| |
| #include "../../shared/testhttpserver.h" |
| #include "../../shared/util.h" |
| |
| Q_DECLARE_METATYPE(QQuickImageBase::Status) |
| |
| template <typename T> static T evaluate(QObject *scope, const QString &expression) |
| { |
| QQmlExpression expr(qmlContext(scope), scope, expression); |
| QVariant result = expr.evaluate(); |
| if (expr.hasError()) |
| qWarning() << expr.error().toString(); |
| return result.value<T>(); |
| } |
| |
| template <> void evaluate<void>(QObject *scope, const QString &expression) |
| { |
| QQmlExpression expr(qmlContext(scope), scope, expression); |
| expr.evaluate(); |
| if (expr.hasError()) |
| qWarning() << expr.error().toString(); |
| } |
| |
| class tst_qquickanimatedimage : public QQmlDataTest |
| { |
| Q_OBJECT |
| public: |
| tst_qquickanimatedimage() {} |
| |
| private slots: |
| void cleanup(); |
| void play(); |
| void pause(); |
| void stopped(); |
| void setFrame(); |
| void frameCount(); |
| void mirror_running(); |
| void mirror_notRunning(); |
| void mirror_notRunning_data(); |
| void remote(); |
| void remote_data(); |
| void sourceSize(); |
| void sourceSizeChanges(); |
| void sourceSizeChanges_intermediate(); |
| void sourceSizeReadOnly(); |
| void invalidSource(); |
| void qtbug_16520(); |
| void progressAndStatusChanges(); |
| void playingAndPausedChanges(); |
| void noCaching(); |
| void sourceChangesOnFrameChanged(); |
| void currentFrame(); |
| }; |
| |
| void tst_qquickanimatedimage::cleanup() |
| { |
| QQuickWindow window; |
| window.releaseResources(); |
| } |
| |
| void tst_qquickanimatedimage::play() |
| { |
| QQmlEngine engine; |
| QQmlComponent component(&engine, testFileUrl("stickman.qml")); |
| QQuickAnimatedImage *anim = qobject_cast<QQuickAnimatedImage *>(component.create()); |
| QVERIFY(anim); |
| QVERIFY(anim->isPlaying()); |
| |
| delete anim; |
| } |
| |
| void tst_qquickanimatedimage::pause() |
| { |
| QQmlEngine engine; |
| QQmlComponent component(&engine, testFileUrl("stickmanpause.qml")); |
| QQuickAnimatedImage *anim = qobject_cast<QQuickAnimatedImage *>(component.create()); |
| QVERIFY(anim); |
| |
| QTRY_VERIFY(anim->isPaused()); |
| QTRY_VERIFY(anim->isPlaying()); |
| |
| delete anim; |
| } |
| |
| void tst_qquickanimatedimage::stopped() |
| { |
| QQmlEngine engine; |
| QQmlComponent component(&engine, testFileUrl("stickmanstopped.qml")); |
| QQuickAnimatedImage *anim = qobject_cast<QQuickAnimatedImage *>(component.create()); |
| QVERIFY(anim); |
| QTRY_VERIFY(!anim->isPlaying()); |
| QCOMPARE(anim->currentFrame(), 0); |
| |
| delete anim; |
| } |
| |
| void tst_qquickanimatedimage::setFrame() |
| { |
| QQmlEngine engine; |
| QQmlComponent component(&engine, testFileUrl("stickmanpause.qml")); |
| QQuickAnimatedImage *anim = qobject_cast<QQuickAnimatedImage *>(component.create()); |
| QVERIFY(anim); |
| QVERIFY(anim->isPlaying()); |
| QCOMPARE(anim->currentFrame(), 2); |
| |
| delete anim; |
| } |
| |
| void tst_qquickanimatedimage::frameCount() |
| { |
| QQmlEngine engine; |
| QQmlComponent component(&engine, testFileUrl("colors.qml")); |
| QQuickAnimatedImage *anim = qobject_cast<QQuickAnimatedImage *>(component.create()); |
| QVERIFY(anim); |
| QVERIFY(anim->isPlaying()); |
| QCOMPARE(anim->frameCount(), 3); |
| |
| QSignalSpy frameCountChangedSpy(anim, &QQuickAnimatedImage::frameCountChanged); |
| |
| const QUrl origSource = anim->source(); |
| anim->setSource(QUrl()); |
| QCOMPARE(anim->frameCount(), 0); |
| QCOMPARE(frameCountChangedSpy.count(), 1); |
| anim->setSource(origSource); |
| QCOMPARE(anim->frameCount(), 3); |
| QCOMPARE(frameCountChangedSpy.count(), 2); |
| |
| delete anim; |
| } |
| |
| void tst_qquickanimatedimage::mirror_running() |
| { |
| // test where mirror is set to true after animation has started |
| |
| QQuickView window; |
| window.setSource(testFileUrl("hearts.qml")); |
| window.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&window)); |
| |
| QQuickAnimatedImage *anim = qobject_cast<QQuickAnimatedImage *>(window.rootObject()); |
| QVERIFY(anim); |
| |
| int width = anim->property("width").toInt(); |
| |
| QCOMPARE(anim->frameCount(), 2); |
| |
| QCOMPARE(anim->currentFrame(), 0); |
| QImage frame0 = window.grabWindow(); |
| |
| anim->setCurrentFrame(1); |
| QCOMPARE(anim->currentFrame(), 1); |
| QImage frame1 = window.grabWindow(); |
| |
| anim->setCurrentFrame(0); |
| |
| QSignalSpy spy(anim, SIGNAL(frameChanged())); |
| QVERIFY(spy.isValid()); |
| anim->setPlaying(true); |
| |
| QTRY_VERIFY(spy.count() == 1); spy.clear(); |
| anim->setMirror(true); |
| |
| QCOMPARE(anim->currentFrame(), 1); |
| QImage frame1_flipped = window.grabWindow(); |
| |
| QTRY_VERIFY(spy.count() == 1); spy.clear(); |
| QCOMPARE(anim->currentFrame(), 0); // animation only has 2 frames, should cycle back to first |
| QImage frame0_flipped = window.grabWindow(); |
| |
| QTransform transform; |
| transform.translate(width, 0).scale(-1, 1.0); |
| QImage frame0_expected = frame0.transformed(transform); |
| QImage frame1_expected = frame1.transformed(transform); |
| |
| if (window.devicePixelRatio() != 1.0 && window.rendererInterface()->graphicsApi() == QSGRendererInterface::Software) |
| QSKIP("QTBUG-53823"); |
| QCOMPARE(frame0_flipped, frame0_expected); |
| if (window.devicePixelRatio() != 1.0 && window.rendererInterface()->graphicsApi() == QSGRendererInterface::Software) |
| QSKIP("QTBUG-53823"); |
| QCOMPARE(frame1_flipped, frame1_expected); |
| |
| delete anim; |
| } |
| |
| void tst_qquickanimatedimage::mirror_notRunning() |
| { |
| QFETCH(QUrl, fileUrl); |
| |
| QQuickView window; |
| window.setSource(fileUrl); |
| window.show(); |
| QTRY_VERIFY(window.isExposed()); |
| |
| QQuickAnimatedImage *anim = qobject_cast<QQuickAnimatedImage *>(window.rootObject()); |
| QVERIFY(anim); |
| |
| int width = anim->property("width").toInt(); |
| QImage screenshot = window.grabWindow(); |
| |
| QTransform transform; |
| transform.translate(width, 0).scale(-1, 1.0); |
| QImage expected = screenshot.transformed(transform); |
| |
| int frame = anim->currentFrame(); |
| bool playing = anim->isPlaying(); |
| bool paused = anim->isPaused(); |
| |
| anim->setProperty("mirror", true); |
| screenshot = window.grabWindow(); |
| |
| screenshot.save("screen.png"); |
| if (window.devicePixelRatio() != 1.0 && window.rendererInterface()->graphicsApi() == QSGRendererInterface::Software) |
| QSKIP("QTBUG-53823"); |
| QCOMPARE(screenshot, expected); |
| |
| // mirroring should not change the current frame or playing status |
| QCOMPARE(anim->currentFrame(), frame); |
| QCOMPARE(anim->isPlaying(), playing); |
| QCOMPARE(anim->isPaused(), paused); |
| |
| delete anim; |
| } |
| |
| void tst_qquickanimatedimage::mirror_notRunning_data() |
| { |
| QTest::addColumn<QUrl>("fileUrl"); |
| |
| QTest::newRow("paused") << testFileUrl("stickmanpause.qml"); |
| QTest::newRow("stopped") << testFileUrl("stickmanstopped.qml"); |
| } |
| |
| void tst_qquickanimatedimage::remote_data() |
| { |
| QTest::addColumn<QString>("fileName"); |
| QTest::addColumn<bool>("paused"); |
| |
| QTest::newRow("playing") << "stickman.qml" << false; |
| QTest::newRow("paused") << "stickmanpause.qml" << true; |
| } |
| |
| void tst_qquickanimatedimage::remote() |
| { |
| QFETCH(QString, fileName); |
| QFETCH(bool, paused); |
| |
| ThreadedTestHTTPServer server(dataDirectory()); |
| |
| QQmlEngine engine; |
| QQmlComponent component(&engine, server.url(fileName)); |
| QTRY_VERIFY(component.isReady()); |
| |
| QQuickAnimatedImage *anim = qobject_cast<QQuickAnimatedImage *>(component.create()); |
| QVERIFY(anim); |
| |
| QTRY_VERIFY(anim->isPlaying()); |
| if (paused) { |
| QTRY_VERIFY(anim->isPaused()); |
| QCOMPARE(anim->currentFrame(), 2); |
| } |
| QVERIFY(anim->status() != QQuickAnimatedImage::Error); |
| |
| delete anim; |
| } |
| |
| void tst_qquickanimatedimage::sourceSize() |
| { |
| QQmlEngine engine; |
| QQmlComponent component(&engine, testFileUrl("stickmanscaled.qml")); |
| QQuickAnimatedImage *anim = qobject_cast<QQuickAnimatedImage *>(component.create()); |
| QVERIFY(anim); |
| QCOMPARE(anim->width(),240.0); |
| QCOMPARE(anim->height(),180.0); |
| QCOMPARE(anim->sourceSize(),QSize(160,120)); |
| |
| delete anim; |
| } |
| |
| void tst_qquickanimatedimage::sourceSizeReadOnly() |
| { |
| QQmlEngine engine; |
| QQmlComponent component(&engine, testFileUrl("stickmanerror1.qml")); |
| QVERIFY(component.isError()); |
| QCOMPARE(component.errors().at(0).description(), QString("Invalid property assignment: \"sourceSize\" is a read-only property")); |
| } |
| |
| void tst_qquickanimatedimage::invalidSource() |
| { |
| QQmlEngine engine; |
| QQmlComponent component(&engine); |
| component.setData("import QtQuick 2.0\n AnimatedImage { source: \"no-such-file.gif\" }", QUrl::fromLocalFile("relative")); |
| QVERIFY(component.isReady()); |
| |
| QTest::ignoreMessage(QtWarningMsg, "file:relative:2:2: QML AnimatedImage: Error Reading Animated Image File file:no-such-file.gif"); |
| |
| QQuickAnimatedImage *anim = qobject_cast<QQuickAnimatedImage *>(component.create()); |
| QVERIFY(anim); |
| |
| QVERIFY(anim->isPlaying()); |
| QVERIFY(!anim->isPaused()); |
| QCOMPARE(anim->currentFrame(), 0); |
| QCOMPARE(anim->frameCount(), 0); |
| QTRY_COMPARE(anim->status(), QQuickAnimatedImage::Error); |
| |
| delete anim; |
| } |
| |
| void tst_qquickanimatedimage::sourceSizeChanges() |
| { |
| TestHTTPServer server; |
| QVERIFY2(server.listen(), qPrintable(server.errorString())); |
| server.serveDirectory(dataDirectory()); |
| |
| QQmlEngine engine; |
| QQmlComponent component(&engine); |
| component.setData("import QtQuick 2.0\nAnimatedImage { source: srcImage }", QUrl::fromLocalFile("")); |
| QTRY_VERIFY(component.isReady()); |
| QQmlContext *ctxt = engine.rootContext(); |
| ctxt->setContextProperty("srcImage", ""); |
| QQuickAnimatedImage *anim = qobject_cast<QQuickAnimatedImage*>(component.create()); |
| QVERIFY(anim != nullptr); |
| |
| QSignalSpy sourceSizeSpy(anim, SIGNAL(sourceSizeChanged())); |
| |
| // Local |
| ctxt->setContextProperty("srcImage", QUrl("")); |
| QTRY_COMPARE(anim->status(), QQuickAnimatedImage::Null); |
| QTRY_COMPARE(sourceSizeSpy.count(), 0); |
| |
| ctxt->setContextProperty("srcImage", testFileUrl("hearts.gif")); |
| QTRY_COMPARE(anim->status(), QQuickAnimatedImage::Ready); |
| QTRY_COMPARE(sourceSizeSpy.count(), 1); |
| |
| ctxt->setContextProperty("srcImage", testFileUrl("hearts.gif")); |
| QTRY_COMPARE(anim->status(), QQuickAnimatedImage::Ready); |
| QTRY_COMPARE(sourceSizeSpy.count(), 1); |
| |
| ctxt->setContextProperty("srcImage", testFileUrl("hearts_copy.gif")); |
| QTRY_COMPARE(anim->status(), QQuickAnimatedImage::Ready); |
| QTRY_COMPARE(sourceSizeSpy.count(), 1); |
| |
| ctxt->setContextProperty("srcImage", testFileUrl("colors.gif")); |
| QTRY_COMPARE(anim->status(), QQuickAnimatedImage::Ready); |
| QTRY_COMPARE(sourceSizeSpy.count(), 2); |
| |
| ctxt->setContextProperty("srcImage", QUrl("")); |
| QTRY_COMPARE(anim->status(), QQuickAnimatedImage::Null); |
| QTRY_COMPARE(sourceSizeSpy.count(), 3); |
| |
| // Remote |
| ctxt->setContextProperty("srcImage", server.url("/hearts.gif")); |
| QTRY_COMPARE(anim->status(), QQuickAnimatedImage::Ready); |
| QTRY_COMPARE(sourceSizeSpy.count(), 4); |
| |
| ctxt->setContextProperty("srcImage", server.url("/hearts.gif")); |
| QTRY_COMPARE(anim->status(), QQuickAnimatedImage::Ready); |
| QTRY_COMPARE(sourceSizeSpy.count(), 4); |
| |
| ctxt->setContextProperty("srcImage", server.url("/hearts_copy.gif")); |
| QTRY_COMPARE(anim->status(), QQuickAnimatedImage::Ready); |
| QTRY_COMPARE(sourceSizeSpy.count(), 4); |
| |
| ctxt->setContextProperty("srcImage", server.url("/colors.gif")); |
| QTRY_COMPARE(anim->status(), QQuickAnimatedImage::Ready); |
| QTRY_COMPARE(sourceSizeSpy.count(), 5); |
| |
| ctxt->setContextProperty("srcImage", QUrl("")); |
| QTRY_COMPARE(anim->status(), QQuickAnimatedImage::Null); |
| QTRY_COMPARE(sourceSizeSpy.count(), 6); |
| |
| delete anim; |
| } |
| |
| void tst_qquickanimatedimage::sourceSizeChanges_intermediate() |
| { |
| QQmlEngine engine; |
| QQmlComponent component(&engine); |
| component.setData("import QtQuick 2.0\nAnimatedImage { readonly property int testWidth: status === AnimatedImage.Ready ? sourceSize.width : -1; source: srcImage }", QUrl::fromLocalFile("")); |
| QTRY_VERIFY(component.isReady()); |
| QQmlContext *ctxt = engine.rootContext(); |
| ctxt->setContextProperty("srcImage", ""); |
| |
| QScopedPointer<QQuickAnimatedImage> anim(qobject_cast<QQuickAnimatedImage*>(component.create())); |
| QVERIFY(anim != nullptr); |
| |
| ctxt->setContextProperty("srcImage", testFileUrl("hearts.gif")); |
| QTRY_COMPARE(anim->status(), QQuickAnimatedImage::Ready); |
| QTRY_COMPARE(anim->property("testWidth").toInt(), anim->sourceSize().width()); |
| |
| ctxt->setContextProperty("srcImage", testFileUrl("hearts_copy.gif")); |
| QTRY_COMPARE(anim->status(), QQuickAnimatedImage::Ready); |
| QTRY_COMPARE(anim->property("testWidth").toInt(), anim->sourceSize().width()); |
| } |
| |
| |
| void tst_qquickanimatedimage::qtbug_16520() |
| { |
| TestHTTPServer server; |
| QVERIFY2(server.listen(), qPrintable(server.errorString())); |
| server.serveDirectory(dataDirectory()); |
| |
| QQmlEngine engine; |
| QQmlComponent component(&engine, testFileUrl("qtbug-16520.qml")); |
| QTRY_VERIFY(component.isReady()); |
| |
| QQuickRectangle *root = qobject_cast<QQuickRectangle *>(component.create()); |
| QVERIFY(root); |
| QQuickAnimatedImage *anim = root->findChild<QQuickAnimatedImage*>("anim"); |
| QVERIFY(anim != nullptr); |
| |
| anim->setProperty("source", server.urlString("/stickman.gif")); |
| QTRY_COMPARE(anim->opacity(), qreal(0)); |
| QTRY_COMPARE(anim->opacity(), qreal(1)); |
| |
| delete anim; |
| delete root; |
| } |
| |
| void tst_qquickanimatedimage::progressAndStatusChanges() |
| { |
| TestHTTPServer server; |
| QVERIFY2(server.listen(), qPrintable(server.errorString())); |
| server.serveDirectory(dataDirectory()); |
| |
| QQmlEngine engine; |
| QString componentStr = "import QtQuick 2.0\nAnimatedImage { source: srcImage }"; |
| QQmlContext *ctxt = engine.rootContext(); |
| ctxt->setContextProperty("srcImage", testFileUrl("stickman.gif")); |
| QQmlComponent component(&engine); |
| component.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); |
| QQuickImage *obj = qobject_cast<QQuickImage*>(component.create()); |
| QVERIFY(obj != nullptr); |
| QCOMPARE(obj->status(), QQuickImage::Ready); |
| QTRY_COMPARE(obj->progress(), 1.0); |
| |
| qRegisterMetaType<QQuickImageBase::Status>(); |
| QSignalSpy sourceSpy(obj, SIGNAL(sourceChanged(QUrl))); |
| QSignalSpy progressSpy(obj, SIGNAL(progressChanged(qreal))); |
| QSignalSpy statusSpy(obj, SIGNAL(statusChanged(QQuickImageBase::Status))); |
| |
| // Same image |
| ctxt->setContextProperty("srcImage", testFileUrl("stickman.gif")); |
| QTRY_COMPARE(obj->status(), QQuickImage::Ready); |
| QTRY_COMPARE(obj->progress(), 1.0); |
| QTRY_COMPARE(sourceSpy.count(), 0); |
| QTRY_COMPARE(progressSpy.count(), 0); |
| QTRY_COMPARE(statusSpy.count(), 0); |
| |
| // Loading local file |
| ctxt->setContextProperty("srcImage", testFileUrl("colors.gif")); |
| QTRY_COMPARE(obj->status(), QQuickImage::Ready); |
| QTRY_COMPARE(obj->progress(), 1.0); |
| QTRY_COMPARE(sourceSpy.count(), 1); |
| QTRY_COMPARE(progressSpy.count(), 0); |
| QTRY_COMPARE(statusSpy.count(), 1); |
| |
| // Loading remote file |
| ctxt->setContextProperty("srcImage", server.url("/stickman.gif")); |
| QTRY_COMPARE(obj->status(), QQuickImage::Loading); |
| QTRY_COMPARE(obj->progress(), 0.0); |
| QTRY_COMPARE(obj->status(), QQuickImage::Ready); |
| QTRY_COMPARE(obj->progress(), 1.0); |
| QTRY_COMPARE(sourceSpy.count(), 2); |
| QTRY_VERIFY(progressSpy.count() > 1); |
| QTRY_COMPARE(statusSpy.count(), 3); |
| |
| ctxt->setContextProperty("srcImage", ""); |
| QTRY_COMPARE(obj->status(), QQuickImage::Null); |
| QTRY_COMPARE(obj->progress(), 0.0); |
| QTRY_COMPARE(sourceSpy.count(), 3); |
| QTRY_VERIFY(progressSpy.count() > 2); |
| QTRY_COMPARE(statusSpy.count(), 4); |
| |
| delete obj; |
| } |
| |
| void tst_qquickanimatedimage::playingAndPausedChanges() |
| { |
| QQmlEngine engine; |
| QString componentStr = "import QtQuick 2.0\nAnimatedImage { source: srcImage }"; |
| QQmlContext *ctxt = engine.rootContext(); |
| ctxt->setContextProperty("srcImage", QUrl("")); |
| QQmlComponent component(&engine); |
| component.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); |
| QQuickAnimatedImage *obj = qobject_cast<QQuickAnimatedImage*>(component.create()); |
| QVERIFY(obj != nullptr); |
| QCOMPARE(obj->status(), QQuickAnimatedImage::Null); |
| QTRY_VERIFY(obj->isPlaying()); |
| QTRY_VERIFY(!obj->isPaused()); |
| QSignalSpy playingSpy(obj, SIGNAL(playingChanged())); |
| QSignalSpy pausedSpy(obj, SIGNAL(pausedChanged())); |
| |
| // initial state |
| obj->setProperty("playing", true); |
| obj->setProperty("paused", false); |
| QTRY_VERIFY(obj->isPlaying()); |
| QTRY_VERIFY(!obj->isPaused()); |
| QTRY_COMPARE(playingSpy.count(), 0); |
| QTRY_COMPARE(pausedSpy.count(), 0); |
| |
| obj->setProperty("playing", false); |
| obj->setProperty("paused", true); |
| QTRY_VERIFY(!obj->isPlaying()); |
| QTRY_VERIFY(obj->isPaused()); |
| QTRY_COMPARE(playingSpy.count(), 1); |
| QTRY_COMPARE(pausedSpy.count(), 1); |
| |
| obj->setProperty("playing", true); |
| obj->setProperty("paused", false); |
| QTRY_VERIFY(obj->isPlaying()); |
| QTRY_VERIFY(!obj->isPaused()); |
| QTRY_COMPARE(playingSpy.count(), 2); |
| QTRY_COMPARE(pausedSpy.count(), 2); |
| |
| ctxt->setContextProperty("srcImage", testFileUrl("stickman.gif")); |
| QTRY_VERIFY(obj->isPlaying()); |
| QTRY_VERIFY(!obj->isPaused()); |
| QTRY_COMPARE(playingSpy.count(), 2); |
| QTRY_COMPARE(pausedSpy.count(), 2); |
| |
| obj->setProperty("paused", true); |
| QTRY_VERIFY(obj->isPlaying()); |
| QTRY_VERIFY(obj->isPaused()); |
| QTRY_COMPARE(playingSpy.count(), 2); |
| QTRY_COMPARE(pausedSpy.count(), 3); |
| |
| obj->setProperty("playing", false); |
| QTRY_VERIFY(!obj->isPlaying()); |
| QTRY_VERIFY(!obj->isPaused()); |
| QTRY_COMPARE(playingSpy.count(), 3); |
| QTRY_COMPARE(pausedSpy.count(), 4); |
| |
| obj->setProperty("playing", true); |
| |
| // Cannot animate this image, playing will be false |
| ctxt->setContextProperty("srcImage", testFileUrl("green.png")); |
| QTRY_VERIFY(!obj->isPlaying()); |
| QTRY_VERIFY(!obj->isPaused()); |
| QTRY_COMPARE(playingSpy.count(), 5); |
| QTRY_COMPARE(pausedSpy.count(), 4); |
| |
| delete obj; |
| } |
| |
| void tst_qquickanimatedimage::noCaching() |
| { |
| QQuickView window, window_nocache; |
| window.setSource(testFileUrl("colors.qml")); |
| window_nocache.setSource(testFileUrl("colors_nocache.qml")); |
| window.show(); |
| window_nocache.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&window)); |
| QVERIFY(QTest::qWaitForWindowExposed(&window_nocache)); |
| |
| QQuickAnimatedImage *anim = qobject_cast<QQuickAnimatedImage *>(window.rootObject()); |
| QVERIFY(anim); |
| |
| QQuickAnimatedImage *anim_nocache = qobject_cast<QQuickAnimatedImage *>(window_nocache.rootObject()); |
| QVERIFY(anim_nocache); |
| |
| QCOMPARE(anim->frameCount(), anim_nocache->frameCount()); |
| |
| // colors.gif only has 3 frames so this should be fast |
| for (int loops = 0; loops <= 2; ++loops) { |
| for (int frame = 0; frame < anim->frameCount(); ++frame) { |
| anim->setCurrentFrame(frame); |
| anim_nocache->setCurrentFrame(frame); |
| |
| QImage image_cache = window.grabWindow(); |
| QImage image_nocache = window_nocache.grabWindow(); |
| |
| QCOMPARE(image_cache, image_nocache); |
| } |
| } |
| } |
| |
| void tst_qquickanimatedimage::sourceChangesOnFrameChanged() |
| { |
| QQmlEngine engine; |
| QQmlComponent component(&engine, testFileUrl("colors.qml")); |
| QVector<QQuickAnimatedImage*> images; |
| |
| // Run multiple animations in parallel, this should be fast |
| for (int loops = 0; loops < 25; ++loops) { |
| QQuickAnimatedImage *anim = qobject_cast<QQuickAnimatedImage *>(component.create()); |
| |
| // QTBUG-67427: this should not produce a segfault |
| QObject::connect(anim, |
| &QQuickAnimatedImage::frameChanged, |
| [this, anim]() { anim->setSource(testFileUrl("hearts.gif")); }); |
| |
| QVERIFY(anim); |
| QVERIFY(anim->isPlaying()); |
| |
| images.append(anim); |
| } |
| |
| for (auto *anim : images) |
| QTRY_COMPARE(anim->source(), testFileUrl("hearts.gif")); |
| |
| qDeleteAll(images); |
| } |
| |
| void tst_qquickanimatedimage::currentFrame() |
| { |
| QQuickView window; |
| window.setSource(testFileUrl("currentframe.qml")); |
| window.show(); |
| QVERIFY(QTest::qWaitForWindowExposed(&window)); |
| |
| QQuickAnimatedImage *anim = qobject_cast<QQuickAnimatedImage *>(window.rootObject()); |
| QVERIFY(anim); |
| QSignalSpy frameChangedSpy(anim, SIGNAL(frameChanged())); |
| QSignalSpy currentFrameChangedSpy(anim, SIGNAL(currentFrameChanged())); |
| |
| anim->setCurrentFrame(1); |
| QCOMPARE(anim->currentFrame(), 1); |
| QCOMPARE(frameChangedSpy.count(), 1); |
| QCOMPARE(currentFrameChangedSpy.count(), 1); |
| QCOMPARE(anim->property("currentFrameChangeCount"), 1); |
| QCOMPARE(anim->property("frameChangeCount"), 1); |
| |
| evaluate<void>(anim, "scriptedSetCurrentFrame(2)"); |
| QCOMPARE(anim->currentFrame(), 2); |
| QCOMPARE(frameChangedSpy.count(), 2); |
| QCOMPARE(currentFrameChangedSpy.count(), 2); |
| QCOMPARE(anim->property("currentFrameChangeCount"), 2); |
| QCOMPARE(anim->property("frameChangeCount"), 2); |
| } |
| |
| QTEST_MAIN(tst_qquickanimatedimage) |
| |
| #include "tst_qquickanimatedimage.moc" |