Revision control
Copy as Markdown
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
#include <QTestEventLoop>
#include <QSignalSpy>
#include <QtTest/private/qpropertytesthelper_p.h>
#include <QIODevice>
#include <QElapsedTimer>
#ifndef QT_NO_WIDGETS
#include <QLabel>
#endif
#include <QMovie>
#include <QProperty>
#include <QtCore/qthread.h>
#include <chrono>
#include <memory>
using namespace std::chrono_literals;
// A file that cannot be rewound, like a socket or a QNetworkReply.
class SequentialFile : public QFile
{
public:
using QFile::QFile;
bool isSequential() const override { return true; }
};
class tst_QMovie : public QObject
{
Q_OBJECT
public:
tst_QMovie();
virtual ~tst_QMovie();
public slots:
void init();
void cleanup();
protected slots:
void exitLoopSlot();
private slots:
void getSetCheck();
void construction();
void playMovie_data();
void playMovie();
void playMovieCreatedInThread_data() { playMovie_data(); }
void playMovieCreatedInThread();
void jumpToFrame_data();
void jumpToFrame();
void frameDelay();
void changeMovieFile();
#ifndef QT_NO_WIDGETS
void infiniteLoop();
#endif
void emptyMovie();
void bindings();
void automatedBindings();
#ifndef QT_NO_ICO
void multiFrameImage();
#endif
void setScaledSize_data();
void setScaledSize();
void sequentialDeviceCannotLoop();
private:
void playMovieImpl(QMovie &movie);
};
// Testing get/set functions
void tst_QMovie::getSetCheck()
{
QMovie obj1;
// QIODevice * QMovie::device()
// void QMovie::setDevice(QIODevice *)
QFile *var1 = new QFile;
obj1.setDevice(var1);
QCOMPARE(obj1.device(), (QIODevice *)var1);
obj1.setDevice((QIODevice *)0);
QCOMPARE(obj1.device(), (QIODevice *)0);
delete var1;
// CacheMode QMovie::cacheMode()
// void QMovie::setCacheMode(CacheMode)
obj1.setCacheMode(QMovie::CacheMode(QMovie::CacheNone));
QCOMPARE(QMovie::CacheMode(QMovie::CacheNone), obj1.cacheMode());
obj1.setCacheMode(QMovie::CacheMode(QMovie::CacheAll));
QCOMPARE(QMovie::CacheMode(QMovie::CacheAll), obj1.cacheMode());
// int QMovie::speed()
// void QMovie::setSpeed(int)
obj1.setSpeed(0);
QCOMPARE(0, obj1.speed());
obj1.setSpeed(INT_MIN);
QCOMPARE(INT_MIN, obj1.speed());
obj1.setSpeed(INT_MAX);
QCOMPARE(INT_MAX, obj1.speed());
}
tst_QMovie::tst_QMovie()
{
}
tst_QMovie::~tst_QMovie()
{
}
void tst_QMovie::init()
{
}
void tst_QMovie::cleanup()
{
}
void tst_QMovie::exitLoopSlot()
{
QTestEventLoop::instance().exitLoop();
}
void tst_QMovie::construction()
{
QMovie movie;
QCOMPARE(movie.device(), (QIODevice *)0);
QCOMPARE(movie.fileName(), QString());
QCOMPARE(movie.state(), QMovie::NotRunning);
}
void tst_QMovie::playMovie_data()
{
QTest::addColumn<QString>("fileName");
QTest::addColumn<int>("frameCount");
#ifdef QTEST_HAVE_GIF
QTest::newRow("comicsecard") << QString("animations/comicsecard.gif") << 5;
QTest::newRow("trolltech") << QString("animations/trolltech.gif") << 34;
#endif
}
void tst_QMovie::playMovie()
{
QFETCH(QString, fileName);
QMovie movie(QFINDTESTDATA(fileName));
playMovieImpl(movie);
}
void tst_QMovie::playMovieCreatedInThread()
{
QFETCH(const QString, fileName);
const auto app = QCoreApplication::instance()->thread();
std::unique_ptr<QMovie> movie;
const std::unique_ptr<QThread> t{QThread::create([&] {
movie = std::make_unique<QMovie>(QFINDTESTDATA(fileName));
movie->moveToThread(app);
})};
t->start();
QVERIFY(t->wait(30s));
QVERIFY(movie);
playMovieImpl(*movie);
}
void tst_QMovie::playMovieImpl(QMovie &movie)
{
QCOMPARE(movie.state(), QMovie::NotRunning);
movie.setSpeed(1000);
movie.start();
QCOMPARE(movie.state(), QMovie::Running);
movie.setPaused(true);
QCOMPARE(movie.state(), QMovie::Paused);
movie.start();
QCOMPARE(movie.state(), QMovie::Running);
movie.stop();
QCOMPARE(movie.state(), QMovie::NotRunning);
movie.jumpToFrame(0);
QCOMPARE(movie.state(), QMovie::NotRunning);
movie.start();
QCOMPARE(movie.state(), QMovie::Running);
connect(&movie, SIGNAL(finished()), this, SLOT(exitLoopSlot()));
#ifndef QT_NO_WIDGETS
QFETCH(int, frameCount);
QLabel label;
label.setMovie(&movie);
label.show();
QTestEventLoop::instance().enterLoop(20);
QVERIFY2(!QTestEventLoop::instance().timeout(),
"Timed out while waiting for finished() signal");
QCOMPARE(movie.state(), QMovie::NotRunning);
QCOMPARE(movie.frameCount(), frameCount);
#endif
movie.stop();
QSignalSpy finishedSpy(&movie, &QMovie::finished);
movie.setSpeed(0);
movie.start();
QCOMPARE(movie.state(), QMovie::Running);
QTestEventLoop::instance().enterLoop(2);
QCOMPARE(finishedSpy.size(), 0);
QCOMPARE(movie.state(), QMovie::Running);
QCOMPARE(movie.currentFrameNumber(), 0);
}
void tst_QMovie::jumpToFrame_data()
{
playMovie_data();
}
void tst_QMovie::jumpToFrame()
{
QFETCH(QString, fileName);
QMovie movie(QFINDTESTDATA(fileName));
movie.start();
movie.stop();
QVERIFY(!movie.jumpToFrame(-1));
QCOMPARE(movie.currentFrameNumber(), 0);
}
void tst_QMovie::frameDelay()
{
QMovie movie(QFINDTESTDATA("animations/comicsecard.gif"));
QList<int> frameDelays{ 200, 800, 800, 2000, 2600 };
for (int i = 0; i < movie.frameCount(); i++) {
movie.jumpToFrame(i);
// Processing may have taken a little time, so round to nearest 100ms
QCOMPARE(100 * qRound(movie.nextFrameDelay() / 100.0f), frameDelays[i]);
}
}
void tst_QMovie::changeMovieFile()
{
QMovie movie(QFINDTESTDATA("animations/comicsecard.gif"));
movie.start();
movie.stop();
movie.setFileName(QFINDTESTDATA("animations/trolltech.gif"));
QCOMPARE(movie.currentFrameNumber(), -1);
}
#ifndef QT_NO_WIDGETS
void tst_QMovie::infiniteLoop()
{
QLabel label;
label.show();
QMovie *movie = new QMovie(QLatin1String(":animations/corrupt.gif"), QByteArray(), &label);
label.setMovie(movie);
movie->start();
QTestEventLoop::instance().enterLoop(1);
QTestEventLoop::instance().timeout();
}
#endif
void tst_QMovie::emptyMovie()
{
QMovie movie;
movie.setCacheMode(QMovie::CacheAll);
movie.jumpToFrame(100);
QCOMPARE(movie.currentFrameNumber(), -1);
}
void tst_QMovie::bindings()
{
QMovie movie;
// speed property
QCOMPARE(movie.speed(), 100);
QProperty<int> speed;
movie.bindableSpeed().setBinding(Qt::makePropertyBinding(speed));
speed = 50;
QCOMPARE(movie.speed(), 50);
QProperty<int> speedObserver;
speedObserver.setBinding([&] { return movie.speed(); });
movie.setSpeed(75);
QCOMPARE(speedObserver, 75);
// chacheMode property
QCOMPARE(movie.cacheMode(), QMovie::CacheNone);
QProperty<QMovie::CacheMode> cacheMode;
movie.bindableCacheMode().setBinding(Qt::makePropertyBinding(cacheMode));
cacheMode = QMovie::CacheAll;
QCOMPARE(movie.cacheMode(), QMovie::CacheAll);
movie.setCacheMode(QMovie::CacheNone);
QProperty<QMovie::CacheMode> cacheModeObserver;
QCOMPARE(cacheModeObserver, QMovie::CacheNone);
cacheModeObserver.setBinding([&] { return movie.cacheMode(); });
movie.setCacheMode(QMovie::CacheAll);
QCOMPARE(cacheModeObserver, QMovie::CacheAll);
}
void tst_QMovie::automatedBindings()
{
QMovie movie;
QTestPrivate::testReadWritePropertyBasics(movie, 50, 100, "speed");
if (QTest::currentTestFailed()) {
qDebug("Failed property test for QMovie::speed");
return;
}
QTestPrivate::testReadWritePropertyBasics(movie, QMovie::CacheAll, QMovie::CacheNone,
"cacheMode");
if (QTest::currentTestFailed()) {
qDebug("Failed property test for QMovie::cacheMode");
return;
}
}
#ifndef QT_NO_ICO
/*! \internal
Test behavior of QMovie with image formats that are multi-frame,
but not normally intended as animation formats (such as tiff and ico).
*/
void tst_QMovie::multiFrameImage()
{
QMovie movie(QFINDTESTDATA("multiframe/Obj_N2_Internal_Mem.ico"));
const int expectedFrameCount = 9;
QCOMPARE(movie.frameCount(), expectedFrameCount);
QVERIFY(movie.isValid());
movie.setSpeed(1000); // speed up the test: play at 10 FPS (1000% of normal)
QElapsedTimer playTimer;
QSignalSpy frameChangedSpy(&movie, &QMovie::frameChanged);
QSignalSpy errorSpy(&movie, &QMovie::error);
QSignalSpy finishedSpy(&movie, &QMovie::finished);
playTimer.start();
movie.start();
QTRY_COMPARE(finishedSpy.size(), 1);
QCOMPARE_GE(playTimer.elapsed(), 100 * expectedFrameCount);
const int delay = movie.nextFrameDelay(); // delay is equal to 100ms minus processing time
QCOMPARE_GE(delay, 50);
QCOMPARE_LE(delay, 100);
QCOMPARE(errorSpy.size(), 0);
QCOMPARE(frameChangedSpy.size(), expectedFrameCount);
}
#endif
void tst_QMovie::setScaledSize_data()
{
QTest::addColumn<QString>("fileName");
QTest::addColumn<QSize>("scaledSize");
QTest::addColumn<QSize>("expectedSize");
QTest::newRow("trolltech (50, 50)") << QString("animations/trolltech.gif") << QSize(50, 50) << QSize(50, 50);
QTest::newRow("trolltech (400, 400)") << QString("animations/trolltech.gif") << QSize(400, 400) << QSize(400, 400);
QTest::newRow("trolltech (50, 0)") << QString("animations/trolltech.gif") << QSize(50, 0) << QSize(50, 25);
QTest::newRow("trolltech (50, -1)") << QString("animations/trolltech.gif") << QSize(50, -1) << QSize(50, 25);
QTest::newRow("trolltech (0, 50)") << QString("animations/trolltech.gif") << QSize(0, 50) << QSize(100, 50);
QTest::newRow("trolltech (-1, 50)") << QString("animations/trolltech.gif") << QSize(-1, 50) << QSize(100, 50);
}
void tst_QMovie::setScaledSize()
{
QFETCH(QString, fileName);
QFETCH(QSize, scaledSize);
QFETCH(QSize, expectedSize);
QMovie movie(QFINDTESTDATA(fileName));
movie.setScaledSize(scaledSize);
movie.start();
QCOMPARE(movie.currentFrameNumber(), 0);
QCOMPARE(movie.currentImage().size(), expectedSize);
}
void tst_QMovie::sequentialDeviceCannotLoop()
{
#ifndef QTEST_HAVE_GIF
QSKIP("This test requires GIF support");
#endif
SequentialFile file(QFINDTESTDATA("animations/comicsecard.gif"));
QVERIFY(file.open(QIODevice::ReadOnly));
QMovie movie;
movie.setDevice(&file);
movie.setCacheMode(QMovie::CacheNone);
movie.setSpeed(1000);
// A sequential device cannot be rewound while uncached, so the animation
// can only be played once. Restarting it from the finished() handler, as
// consumers commonly do to implement looping, must not make QMovie report
// another finish; otherwise the two keep triggering each other.
QSignalSpy finishedSpy(&movie, &QMovie::finished);
connect(&movie, &QMovie::finished, this, [&movie] { movie.start(); });
movie.start();
QTRY_COMPARE(finishedSpy.size(), 1);
QCOMPARE(movie.state(), QMovie::NotRunning);
}
QTEST_MAIN(tst_QMovie)
#include "tst_qmovie.moc"