1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
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 <QtQuick/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 #define PIXMAP_DATA_LEAK_TEST 0
57 class tst_qdeclarativepixmapcache : public QDeclarativeDataTest
61 tst_qdeclarativepixmapcache() : server(14452) {}
72 #ifndef QT_NO_CONCURRENT
76 #if PIXMAP_DATA_LEAK_TEST
80 QDeclarativeEngine engine;
81 TestHTTPServer server;
84 static int slotters=0;
86 class Slotter : public QObject
103 QTestEventLoop::instance().exitLoop();
107 #ifndef QT_NO_LOCALFILE_OPTIMIZED_QML
108 static const bool localfile_optimized = true;
110 static const bool localfile_optimized = false;
114 void tst_qdeclarativepixmapcache::initTestCase()
116 QDeclarativeDataTest::initTestCase();
117 server.serveDirectory(testFile("http"));
120 void tst_qdeclarativepixmapcache::single_data()
122 // Note, since QDeclarativePixmapCache is shared, tests affect each other!
123 // so use different files fore all test functions.
125 QTest::addColumn<QUrl>("target");
126 QTest::addColumn<bool>("incache");
127 QTest::addColumn<bool>("exists");
128 QTest::addColumn<bool>("neterror");
130 // File URLs are optimized
131 QTest::newRow("local") << testFileUrl("exists.png") << localfile_optimized << true << false;
132 QTest::newRow("local") << testFileUrl("notexists.png") << localfile_optimized << false << false;
133 QTest::newRow("remote") << QUrl("http://127.0.0.1:14452/exists.png") << false << true << false;
134 QTest::newRow("remote") << QUrl("http://127.0.0.1:14452/notexists.png") << false << false << true;
137 void tst_qdeclarativepixmapcache::single()
139 QFETCH(QUrl, target);
140 QFETCH(bool, incache);
141 QFETCH(bool, exists);
142 QFETCH(bool, neterror);
144 QString expectedError;
146 expectedError = "Error downloading " + target.toString() + " - server replied: Not found";
147 } else if (!exists) {
148 expectedError = "Cannot open: " + target.toString();
151 QDeclarativePixmap pixmap;
152 QVERIFY(pixmap.width() <= 0); // Check Qt assumption
154 pixmap.load(&engine, target);
157 QCOMPARE(pixmap.error(), expectedError);
159 QVERIFY(pixmap.status() == QDeclarativePixmap::Ready);
160 QVERIFY(pixmap.width() > 0);
162 QVERIFY(pixmap.status() == QDeclarativePixmap::Error);
163 QVERIFY(pixmap.width() <= 0);
166 QVERIFY(pixmap.width() <= 0);
169 pixmap.connectFinished(&getter, SLOT(got()));
170 QTestEventLoop::instance().enterLoop(10);
171 QVERIFY(!QTestEventLoop::instance().timeout());
172 QVERIFY(getter.gotslot);
174 QVERIFY(pixmap.status() == QDeclarativePixmap::Ready);
175 QVERIFY(pixmap.width() > 0);
177 QVERIFY(pixmap.status() == QDeclarativePixmap::Error);
178 QVERIFY(pixmap.width() <= 0);
180 QCOMPARE(pixmap.error(), expectedError);
184 void tst_qdeclarativepixmapcache::parallel_data()
186 // Note, since QDeclarativePixmapCache is shared, tests affect each other!
187 // so use different files fore all test functions.
189 QTest::addColumn<QUrl>("target1");
190 QTest::addColumn<QUrl>("target2");
191 QTest::addColumn<int>("incache");
192 QTest::addColumn<int>("cancel"); // which one to cancel
194 QTest::newRow("local")
195 << testFileUrl("exists1.png")
196 << testFileUrl("exists2.png")
197 << (localfile_optimized ? 2 : 0)
200 QTest::newRow("remote")
201 << QUrl("http://127.0.0.1:14452/exists2.png")
202 << QUrl("http://127.0.0.1:14452/exists3.png")
206 QTest::newRow("remoteagain")
207 << QUrl("http://127.0.0.1:14452/exists2.png")
208 << QUrl("http://127.0.0.1:14452/exists3.png")
212 QTest::newRow("remotecopy")
213 << QUrl("http://127.0.0.1:14452/exists4.png")
214 << QUrl("http://127.0.0.1:14452/exists4.png")
218 QTest::newRow("remotecopycancel")
219 << QUrl("http://127.0.0.1:14452/exists5.png")
220 << QUrl("http://127.0.0.1:14452/exists5.png")
225 void tst_qdeclarativepixmapcache::parallel()
227 QFETCH(QUrl, target1);
228 QFETCH(QUrl, target2);
229 QFETCH(int, incache);
233 targets << target1 << target2;
235 QList<QDeclarativePixmap *> pixmaps;
237 QList<Slotter*> getters;
239 for (int i=0; i<targets.count(); ++i) {
240 QUrl target = targets.at(i);
241 QDeclarativePixmap *pixmap = new QDeclarativePixmap;
243 pixmap->load(&engine, target);
245 QVERIFY(pixmap->status() != QDeclarativePixmap::Error);
246 pixmaps.append(pixmap);
247 if (pixmap->isReady()) {
248 QVERIFY(pixmap->width() > 0);
250 pending.append(false);
252 QVERIFY(pixmap->width() <= 0);
253 getters.append(new Slotter);
254 pixmap->connectFinished(getters[i], SLOT(got()));
255 pending.append(true);
259 QCOMPARE(incache+slotters, targets.count());
262 pixmaps.at(cancel)->clear(getters[cancel]);
267 QTestEventLoop::instance().enterLoop(10);
268 QVERIFY(!QTestEventLoop::instance().timeout());
271 for (int i=0; i<targets.count(); ++i) {
272 QDeclarativePixmap *pixmap = pixmaps[i];
275 QVERIFY(!getters[i]->gotslot);
278 QVERIFY(getters[i]->gotslot);
280 QVERIFY(pixmap->isReady());
281 QVERIFY(pixmap->width() > 0);
289 void tst_qdeclarativepixmapcache::massive()
291 QDeclarativeEngine engine;
292 QUrl url = testFileUrl("massive.png");
294 // Confirm that massive images remain in the cache while they are
295 // in use by the application.
298 QDeclarativePixmap p(&engine, url);
299 QVERIFY(p.isReady());
300 QVERIFY(p.image().size() == QSize(10000, 1000));
301 cachekey = p.image().cacheKey();
303 QDeclarativePixmap p2(&engine, url);
304 QVERIFY(p2.isReady());
305 QVERIFY(p2.image().size() == QSize(10000, 1000));
307 QVERIFY(p2.image().cacheKey() == cachekey);
310 // Confirm that massive images are removed from the cache when
311 // they become unused
315 QDeclarativePixmap p(&engine, url);
316 QVERIFY(p.isReady());
317 QVERIFY(p.image().size() == QSize(10000, 1000));
318 cachekey = p.image().cacheKey();
321 QDeclarativePixmap p2(&engine, url);
322 QVERIFY(p2.isReady());
323 QVERIFY(p2.image().size() == QSize(10000, 1000));
325 QVERIFY(p2.image().cacheKey() != cachekey);
330 void tst_qdeclarativepixmapcache::cancelcrash()
332 QUrl url("http://127.0.0.1:14452/cancelcrash_notexist.png");
333 for (int ii = 0; ii < 1000; ++ii) {
334 QDeclarativePixmap pix(&engine, url);
338 class MyPixmapProvider : public QDeclarativeImageProvider
342 : QDeclarativeImageProvider(Pixmap) {}
344 virtual QPixmap requestPixmap(const QString &d, QSize *, const QSize &) {
346 QPixmap pix(800, 600);
353 void tst_qdeclarativepixmapcache::shrinkcache()
355 QDeclarativeEngine engine;
356 engine.addImageProvider(QLatin1String("mypixmaps"), new MyPixmapProvider);
358 for (int ii = 0; ii < 4000; ++ii) {
359 QUrl url("image://mypixmaps/" + QString::number(ii));
360 QDeclarativePixmap p(&engine, url);
364 #ifndef QT_NO_CONCURRENT
366 void createNetworkServer()
368 QEventLoop eventLoop;
369 TestHTTPServer server(14453);
370 server.serveDirectory(QDeclarativeDataTest::instance()->testFile("http"));
371 QTimer::singleShot(100, &eventLoop, SLOT(quit()));
375 #ifndef QT_NO_CONCURRENT
377 void tst_qdeclarativepixmapcache::networkCrash()
379 QFuture<void> future = QtConcurrent::run(createNetworkServer);
380 QDeclarativeEngine engine;
381 for (int ii = 0; ii < 100 ; ++ii) {
382 QDeclarativePixmap* pixmap = new QDeclarativePixmap;
383 pixmap->load(&engine, QUrl(QString("http://127.0.0.1:14453/exists.png")));
395 void tst_qdeclarativepixmapcache::lockingCrash()
397 TestHTTPServer server(14453);
398 server.serveDirectory(testFile("http"), TestHTTPServer::Delay);
401 QDeclarativePixmap* p = new QDeclarativePixmap;
403 QDeclarativeEngine e;
404 p->load(&e, QUrl(QString("http://127.0.0.1:14453/exists6.png")));
407 QVERIFY(p->isNull());
413 #if PIXMAP_DATA_LEAK_TEST
414 // This test should not be enabled by default as it
415 // produces spurious output in the expected case.
416 #include <QtQuick/QQuickView>
417 class DataLeakView : public QQuickView
422 explicit DataLeakView() : QQuickView()
424 setSource(testFileUrl("dataLeak.qml"));
427 void showFor2Seconds()
430 QTimer::singleShot(2000, this, SIGNAL(ready()));
438 Q_GLOBAL_STATIC(QDeclarativePixmap, dataLeakPixmap)
439 void tst_qdeclarativepixmapcache::dataLeak()
441 // Should not leak cached QDeclarativePixmapData.
442 // Unfortunately, since the QDeclarativePixmapStore
443 // is a global static, and it releases the cache
444 // entries on dtor (application exit), we must use
445 // valgrind to determine whether it leaks or not.
446 QDeclarativePixmap *p1 = new QDeclarativePixmap;
447 QDeclarativePixmap *p2 = new QDeclarativePixmap;
449 QScopedPointer<DataLeakView> test(new DataLeakView);
450 test->showFor2Seconds();
451 dataLeakPixmap()->load(test->engine(), testFileUrl("exists.png"));
452 p1->load(test->engine(), testFileUrl("exists.png"));
453 p2->load(test->engine(), testFileUrl("exists2.png"));
454 QTest::qWait(2005); // 2 seconds + a few more millis.
457 // When the (global static) dataLeakPixmap is deleted, it
458 // shouldn't attempt to dereference a QDeclarativePixmapData
459 // which has been deleted by the QDeclarativePixmapStore
463 #undef PIXMAP_DATA_LEAK_TEST
465 QTEST_MAIN(tst_qdeclarativepixmapcache)
467 #include "tst_qdeclarativepixmapcache.moc"