1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the test suite of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include <QtTest/QtTest>
43 #include <private/qdeclarativepixmapcache_p.h>
44 #include <QtDeclarative/qdeclarativeengine.h>
45 #include <QtDeclarative/qdeclarativeimageprovider.h>
46 #include <QNetworkReply>
47 #include "../shared/util.h"
48 #include "testhttpserver.h"
50 #ifndef QT_NO_CONCURRENT
51 #include <qtconcurrentrun.h>
55 inline QUrl TEST_FILE(const QString &filename)
57 return QUrl::fromLocalFile(TESTDATA(filename));
60 class tst_qdeclarativepixmapcache : public QObject
64 tst_qdeclarativepixmapcache() :
67 server.serveDirectory(TESTDATA("http"));
78 #ifndef QT_NO_CONCURRENT
84 QDeclarativeEngine engine;
85 TestHTTPServer server;
88 static int slotters=0;
90 class Slotter : public QObject
107 QTestEventLoop::instance().exitLoop();
111 #ifndef QT_NO_LOCALFILE_OPTIMIZED_QML
112 static const bool localfile_optimized = true;
114 static const bool localfile_optimized = false;
117 void tst_qdeclarativepixmapcache::single_data()
119 // Note, since QDeclarativePixmapCache is shared, tests affect each other!
120 // so use different files fore all test functions.
122 QTest::addColumn<QUrl>("target");
123 QTest::addColumn<bool>("incache");
124 QTest::addColumn<bool>("exists");
125 QTest::addColumn<bool>("neterror");
127 // File URLs are optimized
128 QTest::newRow("local") << TEST_FILE("exists.png") << localfile_optimized << true << false;
129 QTest::newRow("local") << TEST_FILE("notexists.png") << localfile_optimized << false << false;
130 QTest::newRow("remote") << QUrl("http://127.0.0.1:14452/exists.png") << false << true << false;
131 QTest::newRow("remote") << QUrl("http://127.0.0.1:14452/notexists.png") << false << false << true;
134 void tst_qdeclarativepixmapcache::single()
136 QFETCH(QUrl, target);
137 QFETCH(bool, incache);
138 QFETCH(bool, exists);
139 QFETCH(bool, neterror);
141 QString expectedError;
143 expectedError = "Error downloading " + target.toString() + " - server replied: Not found";
144 } else if (!exists) {
145 expectedError = "Cannot open: " + target.toString();
148 QDeclarativePixmap pixmap;
149 QVERIFY(pixmap.width() <= 0); // Check Qt assumption
151 pixmap.load(&engine, target);
154 QCOMPARE(pixmap.error(), expectedError);
156 QVERIFY(pixmap.status() == QDeclarativePixmap::Ready);
157 QVERIFY(pixmap.width() > 0);
159 QVERIFY(pixmap.status() == QDeclarativePixmap::Error);
160 QVERIFY(pixmap.width() <= 0);
163 QVERIFY(pixmap.width() <= 0);
166 pixmap.connectFinished(&getter, SLOT(got()));
167 QTestEventLoop::instance().enterLoop(10);
168 QVERIFY(!QTestEventLoop::instance().timeout());
169 QVERIFY(getter.gotslot);
171 QVERIFY(pixmap.status() == QDeclarativePixmap::Ready);
172 QVERIFY(pixmap.width() > 0);
174 QVERIFY(pixmap.status() == QDeclarativePixmap::Error);
175 QVERIFY(pixmap.width() <= 0);
177 QCOMPARE(pixmap.error(), expectedError);
181 void tst_qdeclarativepixmapcache::parallel_data()
183 // Note, since QDeclarativePixmapCache is shared, tests affect each other!
184 // so use different files fore all test functions.
186 QTest::addColumn<QUrl>("target1");
187 QTest::addColumn<QUrl>("target2");
188 QTest::addColumn<int>("incache");
189 QTest::addColumn<int>("cancel"); // which one to cancel
191 QTest::newRow("local")
192 << TEST_FILE("exists1.png")
193 << TEST_FILE("exists2.png")
194 << (localfile_optimized ? 2 : 0)
197 QTest::newRow("remote")
198 << QUrl("http://127.0.0.1:14452/exists2.png")
199 << QUrl("http://127.0.0.1:14452/exists3.png")
203 QTest::newRow("remoteagain")
204 << QUrl("http://127.0.0.1:14452/exists2.png")
205 << QUrl("http://127.0.0.1:14452/exists3.png")
209 QTest::newRow("remotecopy")
210 << QUrl("http://127.0.0.1:14452/exists4.png")
211 << QUrl("http://127.0.0.1:14452/exists4.png")
215 QTest::newRow("remotecopycancel")
216 << QUrl("http://127.0.0.1:14452/exists5.png")
217 << QUrl("http://127.0.0.1:14452/exists5.png")
222 void tst_qdeclarativepixmapcache::parallel()
224 QFETCH(QUrl, target1);
225 QFETCH(QUrl, target2);
226 QFETCH(int, incache);
230 targets << target1 << target2;
232 QList<QDeclarativePixmap *> pixmaps;
234 QList<Slotter*> getters;
236 for (int i=0; i<targets.count(); ++i) {
237 QUrl target = targets.at(i);
238 QDeclarativePixmap *pixmap = new QDeclarativePixmap;
240 pixmap->load(&engine, target);
242 QVERIFY(pixmap->status() != QDeclarativePixmap::Error);
243 pixmaps.append(pixmap);
244 if (pixmap->isReady()) {
245 QVERIFY(pixmap->width() > 0);
247 pending.append(false);
249 QVERIFY(pixmap->width() <= 0);
250 getters.append(new Slotter);
251 pixmap->connectFinished(getters[i], SLOT(got()));
252 pending.append(true);
256 QCOMPARE(incache+slotters, targets.count());
259 pixmaps.at(cancel)->clear(getters[cancel]);
264 QTestEventLoop::instance().enterLoop(10);
265 QVERIFY(!QTestEventLoop::instance().timeout());
268 for (int i=0; i<targets.count(); ++i) {
269 QDeclarativePixmap *pixmap = pixmaps[i];
272 QVERIFY(!getters[i]->gotslot);
275 QVERIFY(getters[i]->gotslot);
277 QVERIFY(pixmap->isReady());
278 QVERIFY(pixmap->width() > 0);
286 void tst_qdeclarativepixmapcache::massive()
288 QDeclarativeEngine engine;
289 QUrl url = TEST_FILE("massive.png");
291 // Confirm that massive images remain in the cache while they are
292 // in use by the application.
295 QDeclarativePixmap p(&engine, url);
296 QVERIFY(p.isReady());
297 QVERIFY(p.pixmap().size() == QSize(10000, 1000));
298 cachekey = p.pixmap().cacheKey();
300 QDeclarativePixmap p2(&engine, url);
301 QVERIFY(p2.isReady());
302 QVERIFY(p2.pixmap().size() == QSize(10000, 1000));
304 QVERIFY(p2.pixmap().cacheKey() == cachekey);
307 // Confirm that massive images are removed from the cache when
308 // they become unused
312 QDeclarativePixmap p(&engine, url);
313 QVERIFY(p.isReady());
314 QVERIFY(p.pixmap().size() == QSize(10000, 1000));
315 cachekey = p.pixmap().cacheKey();
318 QDeclarativePixmap p2(&engine, url);
319 QVERIFY(p2.isReady());
320 QVERIFY(p2.pixmap().size() == QSize(10000, 1000));
322 QVERIFY(p2.pixmap().cacheKey() != cachekey);
327 void tst_qdeclarativepixmapcache::cancelcrash()
329 QUrl url("http://127.0.0.1:14452/cancelcrash_notexist.png");
330 for (int ii = 0; ii < 1000; ++ii) {
331 QDeclarativePixmap pix(&engine, url);
335 class MyPixmapProvider : public QDeclarativeImageProvider
339 : QDeclarativeImageProvider(Pixmap) {}
341 virtual QPixmap requestPixmap(const QString &d, QSize *, const QSize &) {
343 QPixmap pix(800, 600);
350 void tst_qdeclarativepixmapcache::shrinkcache()
352 QDeclarativeEngine engine;
353 engine.addImageProvider(QLatin1String("mypixmaps"), new MyPixmapProvider);
355 for (int ii = 0; ii < 4000; ++ii) {
356 QUrl url("image://mypixmaps/" + QString::number(ii));
357 QDeclarativePixmap p(&engine, url);
361 #ifndef QT_NO_CONCURRENT
363 void createNetworkServer()
365 QEventLoop eventLoop;
366 TestHTTPServer server(14453);
367 server.serveDirectory(TESTDATA("http"));
368 QTimer::singleShot(100, &eventLoop, SLOT(quit()));
372 #ifndef QT_NO_CONCURRENT
374 void tst_qdeclarativepixmapcache::networkCrash()
376 QFuture<void> future = QtConcurrent::run(createNetworkServer);
377 QDeclarativeEngine engine;
378 for (int ii = 0; ii < 100 ; ++ii) {
379 QDeclarativePixmap* pixmap = new QDeclarativePixmap;
380 pixmap->load(&engine, QUrl(QString("http://127.0.0.1:14453/exists.png")));
392 void tst_qdeclarativepixmapcache::lockingCrash()
394 TestHTTPServer server(14453);
395 server.serveDirectory(TESTDATA("http"), TestHTTPServer::Delay);
398 QDeclarativePixmap* p = new QDeclarativePixmap;
400 QDeclarativeEngine e;
401 p->load(&e, QUrl(QString("http://127.0.0.1:14453/exists6.png")));
404 QVERIFY(p->isNull());
409 #include <QQuickView>
410 class DataLeakView : public QQuickView
415 explicit DataLeakView() : QQuickView()
417 setSource(TEST_FILE("dataLeak.qml"));
420 void showFor2Seconds()
423 QTimer::singleShot(2000, this, SIGNAL(ready()));
431 Q_GLOBAL_STATIC(QDeclarativePixmap, dataLeakPixmap)
432 void tst_qdeclarativepixmapcache::dataLeak()
434 // Should not leak cached QDeclarativePixmapData.
435 // Unfortunately, since the QDeclarativePixmapStore
436 // is a global static, and it releases the cache
437 // entries on dtor (application exit), we must use
438 // valgrind to determine whether it leaks or not.
439 QDeclarativePixmap *p1 = new QDeclarativePixmap;
440 QDeclarativePixmap *p2 = new QDeclarativePixmap;
442 QScopedPointer<DataLeakView> test(new DataLeakView);
443 test->showFor2Seconds();
444 dataLeakPixmap()->load(test->engine(), TEST_FILE("exists.png"));
445 p1->load(test->engine(), TEST_FILE("exists.png"));
446 p2->load(test->engine(), TEST_FILE("exists2.png"));
447 QTest::qWait(2005); // 2 seconds + a few more millis.
450 // When the (global static) dataLeakPixmap is deleted, it
451 // shouldn't attempt to dereference a QDeclarativePixmapData
452 // which has been deleted by the QDeclarativePixmapStore
456 QTEST_MAIN(tst_qdeclarativepixmapcache)
458 #include "tst_qdeclarativepixmapcache.moc"