95a06c81bfde52852d41550e3a7a931f074c714e
[profile/ivi/qtdeclarative.git] / tests / auto / qtquick2 / qdeclarativepixmapcache / tst_qdeclarativepixmapcache.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
6 **
7 ** This file is part of the test suite of the Qt Toolkit.
8 **
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.
17 **
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.
21 **
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.
29 **
30 ** Other Usage
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.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 #include <qtest.h>
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"
49
50 #ifndef QT_NO_CONCURRENT
51 #include <qtconcurrentrun.h>
52 #include <qfuture.h>
53 #endif
54
55 #define PIXMAP_DATA_LEAK_TEST 0
56
57 class tst_qdeclarativepixmapcache : public QDeclarativeDataTest
58 {
59     Q_OBJECT
60 public:
61     tst_qdeclarativepixmapcache() : server(14452) {}
62
63 private slots:
64     void initTestCase();
65     void single();
66     void single_data();
67     void parallel();
68     void parallel_data();
69     void massive();
70     void cancelcrash();
71     void shrinkcache();
72 #ifndef QT_NO_CONCURRENT
73     void networkCrash();
74 #endif
75     void lockingCrash();
76 #if PIXMAP_DATA_LEAK_TEST
77     void dataLeak();
78 #endif
79 private:
80     QDeclarativeEngine engine;
81     TestHTTPServer server;
82 };
83
84 static int slotters=0;
85
86 class Slotter : public QObject
87 {
88     Q_OBJECT
89 public:
90     Slotter()
91     {
92         gotslot = false;
93         slotters++;
94     }
95     bool gotslot;
96
97 public slots:
98     void got()
99     {
100         gotslot = true;
101         --slotters;
102         if (slotters==0)
103             QTestEventLoop::instance().exitLoop();
104     }
105 };
106
107 #ifndef QT_NO_LOCALFILE_OPTIMIZED_QML
108 static const bool localfile_optimized = true;
109 #else
110 static const bool localfile_optimized = false;
111 #endif
112
113
114 void tst_qdeclarativepixmapcache::initTestCase()
115 {
116     QDeclarativeDataTest::initTestCase();
117     server.serveDirectory(testFile("http"));
118 }
119
120 void tst_qdeclarativepixmapcache::single_data()
121 {
122     // Note, since QDeclarativePixmapCache is shared, tests affect each other!
123     // so use different files fore all test functions.
124
125     QTest::addColumn<QUrl>("target");
126     QTest::addColumn<bool>("incache");
127     QTest::addColumn<bool>("exists");
128     QTest::addColumn<bool>("neterror");
129
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;
135 }
136
137 void tst_qdeclarativepixmapcache::single()
138 {
139     QFETCH(QUrl, target);
140     QFETCH(bool, incache);
141     QFETCH(bool, exists);
142     QFETCH(bool, neterror);
143
144     QString expectedError;
145     if (neterror) {
146         expectedError = "Error downloading " + target.toString() + " - server replied: Not found";
147     } else if (!exists) {
148         expectedError = "Cannot open: " + target.toString();
149     }
150
151     QDeclarativePixmap pixmap;
152     QVERIFY(pixmap.width() <= 0); // Check Qt assumption
153
154     pixmap.load(&engine, target);
155
156     if (incache) {
157         QCOMPARE(pixmap.error(), expectedError);
158         if (exists) {
159             QVERIFY(pixmap.status() == QDeclarativePixmap::Ready);
160             QVERIFY(pixmap.width() > 0);
161         } else {
162             QVERIFY(pixmap.status() == QDeclarativePixmap::Error);
163             QVERIFY(pixmap.width() <= 0);
164         }
165     } else {
166         QVERIFY(pixmap.width() <= 0);
167
168         Slotter getter;
169         pixmap.connectFinished(&getter, SLOT(got()));
170         QTestEventLoop::instance().enterLoop(10);
171         QVERIFY(!QTestEventLoop::instance().timeout());
172         QVERIFY(getter.gotslot);
173         if (exists) {
174             QVERIFY(pixmap.status() == QDeclarativePixmap::Ready);
175             QVERIFY(pixmap.width() > 0);
176         } else {
177             QVERIFY(pixmap.status() == QDeclarativePixmap::Error);
178             QVERIFY(pixmap.width() <= 0);
179         }
180         QCOMPARE(pixmap.error(), expectedError);
181     }
182 }
183
184 void tst_qdeclarativepixmapcache::parallel_data()
185 {
186     // Note, since QDeclarativePixmapCache is shared, tests affect each other!
187     // so use different files fore all test functions.
188
189     QTest::addColumn<QUrl>("target1");
190     QTest::addColumn<QUrl>("target2");
191     QTest::addColumn<int>("incache");
192     QTest::addColumn<int>("cancel"); // which one to cancel
193
194     QTest::newRow("local")
195             << testFileUrl("exists1.png")
196             << testFileUrl("exists2.png")
197             << (localfile_optimized ? 2 : 0)
198             << -1;
199
200     QTest::newRow("remote")
201             << QUrl("http://127.0.0.1:14452/exists2.png")
202             << QUrl("http://127.0.0.1:14452/exists3.png")
203             << 0
204             << -1;
205
206     QTest::newRow("remoteagain")
207             << QUrl("http://127.0.0.1:14452/exists2.png")
208             << QUrl("http://127.0.0.1:14452/exists3.png")
209             << 2
210             << -1;
211
212     QTest::newRow("remotecopy")
213             << QUrl("http://127.0.0.1:14452/exists4.png")
214             << QUrl("http://127.0.0.1:14452/exists4.png")
215             << 0
216             << -1;
217
218     QTest::newRow("remotecopycancel")
219             << QUrl("http://127.0.0.1:14452/exists5.png")
220             << QUrl("http://127.0.0.1:14452/exists5.png")
221             << 0
222             << 0;
223 }
224
225 void tst_qdeclarativepixmapcache::parallel()
226 {
227     QFETCH(QUrl, target1);
228     QFETCH(QUrl, target2);
229     QFETCH(int, incache);
230     QFETCH(int, cancel);
231
232     QList<QUrl> targets;
233     targets << target1 << target2;
234
235     QList<QDeclarativePixmap *> pixmaps;
236     QList<bool> pending;
237     QList<Slotter*> getters;
238
239     for (int i=0; i<targets.count(); ++i) {
240         QUrl target = targets.at(i);
241         QDeclarativePixmap *pixmap = new QDeclarativePixmap;
242
243         pixmap->load(&engine, target);
244
245         QVERIFY(pixmap->status() != QDeclarativePixmap::Error);
246         pixmaps.append(pixmap);
247         if (pixmap->isReady()) {
248             QVERIFY(pixmap->width() >  0);
249             getters.append(0);
250             pending.append(false);
251         } else {
252             QVERIFY(pixmap->width() <= 0);
253             getters.append(new Slotter);
254             pixmap->connectFinished(getters[i], SLOT(got()));
255             pending.append(true);
256         }
257     }
258
259     QCOMPARE(incache+slotters, targets.count());
260
261     if (cancel >= 0) {
262         pixmaps.at(cancel)->clear(getters[cancel]);
263         slotters--;
264     }
265
266     if (slotters) {
267         QTestEventLoop::instance().enterLoop(10);
268         QVERIFY(!QTestEventLoop::instance().timeout());
269     }
270
271     for (int i=0; i<targets.count(); ++i) {
272         QDeclarativePixmap *pixmap = pixmaps[i];
273
274         if (i == cancel) {
275             QVERIFY(!getters[i]->gotslot);
276         } else {
277             if (pending[i]) 
278                 QVERIFY(getters[i]->gotslot);
279
280             QVERIFY(pixmap->isReady());
281             QVERIFY(pixmap->width() > 0);
282             delete getters[i];
283         }
284     }
285
286     qDeleteAll(pixmaps);
287 }
288
289 void tst_qdeclarativepixmapcache::massive()
290 {
291     QDeclarativeEngine engine;
292     QUrl url = testFileUrl("massive.png");
293
294     // Confirm that massive images remain in the cache while they are
295     // in use by the application.
296     {
297     qint64 cachekey = 0;
298     QDeclarativePixmap p(&engine, url);
299     QVERIFY(p.isReady());
300     QVERIFY(p.image().size() == QSize(10000, 1000));
301     cachekey = p.image().cacheKey();
302
303     QDeclarativePixmap p2(&engine, url);
304     QVERIFY(p2.isReady());
305     QVERIFY(p2.image().size() == QSize(10000, 1000));
306
307     QVERIFY(p2.image().cacheKey() == cachekey);
308     }
309
310     // Confirm that massive images are removed from the cache when
311     // they become unused
312     {
313     qint64 cachekey = 0;
314     {
315         QDeclarativePixmap p(&engine, url);
316         QVERIFY(p.isReady());
317         QVERIFY(p.image().size() == QSize(10000, 1000));
318         cachekey = p.image().cacheKey();
319     }
320
321     QDeclarativePixmap p2(&engine, url);
322     QVERIFY(p2.isReady());
323     QVERIFY(p2.image().size() == QSize(10000, 1000));
324
325     QVERIFY(p2.image().cacheKey() != cachekey);
326     }
327 }
328
329 // QTBUG-12729
330 void tst_qdeclarativepixmapcache::cancelcrash()
331 {
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);
335     }
336 }
337
338 class MyPixmapProvider : public QDeclarativeImageProvider
339 {
340 public:
341     MyPixmapProvider()
342     : QDeclarativeImageProvider(Pixmap) {}
343
344     virtual QPixmap requestPixmap(const QString &d, QSize *, const QSize &) {
345         Q_UNUSED(d)
346         QPixmap pix(800, 600);
347         pix.fill(Qt::red);
348         return pix;
349     }
350 };
351
352 // QTBUG-13345
353 void tst_qdeclarativepixmapcache::shrinkcache()
354 {
355     QDeclarativeEngine engine;
356     engine.addImageProvider(QLatin1String("mypixmaps"), new MyPixmapProvider);
357
358     for (int ii = 0; ii < 4000; ++ii) {
359         QUrl url("image://mypixmaps/" + QString::number(ii));
360         QDeclarativePixmap p(&engine, url);
361     }
362 }
363
364 #ifndef QT_NO_CONCURRENT
365
366 void createNetworkServer()
367 {
368    QEventLoop eventLoop;
369    TestHTTPServer server(14453);
370    server.serveDirectory(QDeclarativeDataTest::instance()->testFile("http"));
371    QTimer::singleShot(100, &eventLoop, SLOT(quit()));
372    eventLoop.exec();
373 }
374
375 #ifndef QT_NO_CONCURRENT
376 // QT-3957
377 void tst_qdeclarativepixmapcache::networkCrash()
378 {
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")));
384         QTest::qSleep(1);
385         pixmap->clear();
386         delete pixmap;
387     }
388     future.cancel();
389 }
390 #endif
391
392 #endif
393
394 // QTBUG-22125
395 void tst_qdeclarativepixmapcache::lockingCrash()
396 {
397     TestHTTPServer server(14453);
398     server.serveDirectory(testFile("http"), TestHTTPServer::Delay);
399
400     {
401         QDeclarativePixmap* p = new QDeclarativePixmap;
402         {
403             QDeclarativeEngine e;
404             p->load(&e,  QUrl(QString("http://127.0.0.1:14453/exists6.png")));
405         }
406         p->clear();
407         QVERIFY(p->isNull());
408         delete p;
409     }
410 }
411
412
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
418 {
419     Q_OBJECT
420
421 public:
422     explicit DataLeakView() : QQuickView()
423     {
424         setSource(testFileUrl("dataLeak.qml"));
425     }
426
427     void showFor2Seconds()
428     {
429         showFullScreen();
430         QTimer::singleShot(2000, this, SIGNAL(ready()));
431     }
432
433 signals:
434     void ready();
435 };
436
437 // QTBUG-22742
438 Q_GLOBAL_STATIC(QDeclarativePixmap, dataLeakPixmap)
439 void tst_qdeclarativepixmapcache::dataLeak()
440 {
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;
448     {
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.
455     }
456
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
460     // destructor.
461 }
462 #endif
463 #undef PIXMAP_DATA_LEAK_TEST
464
465 QTEST_MAIN(tst_qdeclarativepixmapcache)
466
467 #include "tst_qdeclarativepixmapcache.moc"