39ca8c71d86902cfc05fa884f2f822d32bee43f9
[profile/ivi/qtdeclarative.git] / tests / auto / qtquick1 / qdeclarativeimageprovider / tst_qdeclarativeimageprovider.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 <QtDeclarative/qdeclarativeengine.h>
44 #include <QtDeclarative/qdeclarativeimageprovider.h>
45 #include <QtQuick1/private/qdeclarativeimage_p.h>
46 #include <QImageReader>
47 #include <QWaitCondition>
48
49 Q_DECLARE_METATYPE(QDeclarativeImageProvider*);
50
51 class tst_qdeclarativeimageprovider : public QObject
52 {
53     Q_OBJECT
54 public:
55     tst_qdeclarativeimageprovider()
56     {
57     }
58
59 private slots:
60     void requestImage_sync_data();
61     void requestImage_sync();
62     void requestImage_async_data();
63     void requestImage_async();
64
65     void requestPixmap_sync_data();
66     void requestPixmap_sync();
67     void requestPixmap_async();
68
69     void removeProvider_data();
70     void removeProvider();
71
72     void threadTest();
73
74 private:
75     QString newImageFileName() const;
76     void fillRequestTestsData(const QString &id);
77     void runTest(bool async, QDeclarativeImageProvider *provider);
78 };
79
80
81 class TestQImageProvider : public QDeclarativeImageProvider
82 {
83 public:
84     TestQImageProvider(bool *deleteWatch = 0)
85         : QDeclarativeImageProvider(Image), deleteWatch(deleteWatch)
86     {
87     }
88
89     ~TestQImageProvider()
90     {
91         if (deleteWatch)
92             *deleteWatch = true;
93     }
94
95     QImage requestImage(const QString &id, QSize *size, const QSize& requestedSize)
96     {
97         lastImageId = id;
98
99         if (id == QLatin1String("no-such-file.png"))
100             return QImage();
101
102         int width = 100; 
103         int height = 100;
104         QImage image(width, height, QImage::Format_RGB32);
105         if (size) 
106             *size = QSize(width, height);
107         if (requestedSize.isValid())
108             image = image.scaled(requestedSize);
109         return image;
110     }
111
112     bool *deleteWatch;
113     QString lastImageId;
114 };
115 Q_DECLARE_METATYPE(TestQImageProvider*);
116
117
118 class TestQPixmapProvider : public QDeclarativeImageProvider
119 {
120 public:
121     TestQPixmapProvider(bool *deleteWatch = 0)
122         : QDeclarativeImageProvider(Pixmap), deleteWatch(deleteWatch)
123     {
124     }
125
126     ~TestQPixmapProvider()
127     {
128         if (deleteWatch)
129             *deleteWatch = true;
130     }
131
132     QPixmap requestPixmap(const QString &id, QSize *size, const QSize& requestedSize)
133     {
134         lastImageId = id;
135
136         if (id == QLatin1String("no-such-file.png"))
137             return QPixmap();
138
139         int width = 100; 
140         int height = 100;
141         QPixmap image(width, height);
142         if (size) 
143             *size = QSize(width, height);
144         if (requestedSize.isValid())
145             image = image.scaled(requestedSize);
146         return image;
147     }
148
149     bool *deleteWatch;
150     QString lastImageId;
151 };
152 Q_DECLARE_METATYPE(TestQPixmapProvider*);
153
154
155 QString tst_qdeclarativeimageprovider::newImageFileName() const
156 {
157     // need to generate new filenames each time or else images are loaded
158     // from cache and we won't get loading status changes when testing 
159     // async loading
160     static int count = 0;
161     return QString("image://test/image-%1.png").arg(count++);
162 }
163
164 void tst_qdeclarativeimageprovider::fillRequestTestsData(const QString &id)
165 {
166     QTest::addColumn<QString>("source");
167     QTest::addColumn<QString>("imageId");
168     QTest::addColumn<QString>("properties");
169     QTest::addColumn<QSize>("size");
170     QTest::addColumn<QString>("error");
171
172     QString fileName = newImageFileName();
173     QTest::newRow(QTest::toString(id + " simple test"))
174             << "image://test/" + fileName << fileName << "" << QSize(100,100) << "";
175
176     fileName = newImageFileName();
177     QTest::newRow(QTest::toString(id + " simple test with capitalization"))//As it's a URL, should make no difference
178             << "image://Test/" + fileName << fileName << "" << QSize(100,100) << "";
179
180     fileName = newImageFileName();
181     QTest::newRow(QTest::toString(id + " url with no id"))
182         << "image://test/" + fileName << "" + fileName << "" << QSize(100,100) << "";
183
184     fileName = newImageFileName();
185     QTest::newRow(QTest::toString(id + " url with path"))
186         << "image://test/test/path" + fileName << "test/path" + fileName << "" << QSize(100,100) << "";
187
188     fileName = newImageFileName();
189     QTest::newRow(QTest::toString(id + " url with fragment"))
190         << "image://test/faq.html?#question13" + fileName << "faq.html?#question13" + fileName << "" << QSize(100,100) << "";
191
192     fileName = newImageFileName();
193     QTest::newRow(QTest::toString(id + " url with query"))
194         << "image://test/cgi-bin/drawgraph.cgi?type=pie&color=green" + fileName << "cgi-bin/drawgraph.cgi?type=pie&color=green" + fileName
195         << "" << QSize(100,100) << "";
196
197     fileName = newImageFileName();
198     QTest::newRow(QTest::toString(id + " scaled image"))
199             << "image://test/" + fileName << fileName << "sourceSize: \"80x30\"" << QSize(80,30) << "";
200
201     QTest::newRow(QTest::toString(id + " missing"))
202         << "image://test/no-such-file.png" << "no-such-file.png" << "" << QSize(100,100)
203         << "file::2:1: QML Image: Failed to get image from provider: image://test/no-such-file.png";
204
205     QTest::newRow(QTest::toString(id + " unknown provider"))
206         << "image://bogus/exists.png" << "" << "" << QSize()
207         << "file::2:1: QML Image: Failed to get image from provider: image://bogus/exists.png";
208 }
209
210 void tst_qdeclarativeimageprovider::runTest(bool async, QDeclarativeImageProvider *provider)
211 {
212     QFETCH(QString, source);
213     QFETCH(QString, imageId);
214     QFETCH(QString, properties);
215     QFETCH(QSize, size);
216     QFETCH(QString, error);
217
218     if (!error.isEmpty())
219         QTest::ignoreMessage(QtWarningMsg, error.toUtf8());
220
221     QDeclarativeEngine engine;
222
223     engine.addImageProvider("test", provider);
224     QVERIFY(engine.imageProvider("test") != 0);
225
226     QString componentStr = "import QtQuick 1.0\nImage { source: \"" + source + "\"; " 
227             + (async ? "asynchronous: true; " : "")
228             + properties + " }";
229     QDeclarativeComponent component(&engine);
230     component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
231     QDeclarative1Image *obj = qobject_cast<QDeclarative1Image*>(component.create());
232     QVERIFY(obj != 0);
233
234     if (async) 
235         QTRY_VERIFY(obj->status() == QDeclarative1Image::Loading);
236
237     QCOMPARE(obj->source(), QUrl(source));
238
239     if (error.isEmpty()) {
240         if (async)
241             QTRY_VERIFY(obj->status() == QDeclarative1Image::Ready);
242         else
243             QVERIFY(obj->status() == QDeclarative1Image::Ready);
244         if (QByteArray(QTest::currentDataTag()).startsWith("qimage"))
245             QCOMPARE(static_cast<TestQImageProvider*>(provider)->lastImageId, imageId);
246         else
247             QCOMPARE(static_cast<TestQPixmapProvider*>(provider)->lastImageId, imageId);
248
249         QCOMPARE(obj->width(), qreal(size.width()));
250         QCOMPARE(obj->height(), qreal(size.height()));
251         QCOMPARE(obj->pixmap().width(), size.width());
252         QCOMPARE(obj->pixmap().height(), size.height());
253         QCOMPARE(obj->fillMode(), QDeclarative1Image::Stretch);
254         QCOMPARE(obj->progress(), 1.0);
255     } else {
256         if (async)
257             QTRY_VERIFY(obj->status() == QDeclarative1Image::Error);
258         else
259             QVERIFY(obj->status() == QDeclarative1Image::Error);
260     }
261
262     delete obj;
263 }
264
265 void tst_qdeclarativeimageprovider::requestImage_sync_data()
266 {
267     fillRequestTestsData("qimage|sync");
268 }
269
270 void tst_qdeclarativeimageprovider::requestImage_sync()
271 {
272     bool deleteWatch = false;
273     runTest(false, new TestQImageProvider(&deleteWatch));
274     QVERIFY(deleteWatch);
275 }
276
277 void tst_qdeclarativeimageprovider::requestImage_async_data()
278 {
279     fillRequestTestsData("qimage|async");
280 }
281
282 void tst_qdeclarativeimageprovider::requestImage_async()
283 {
284     bool deleteWatch = false;
285     runTest(true, new TestQImageProvider(&deleteWatch));
286     QVERIFY(deleteWatch);
287 }
288
289 void tst_qdeclarativeimageprovider::requestPixmap_sync_data()
290 {
291     fillRequestTestsData("qpixmap");
292 }
293
294 void tst_qdeclarativeimageprovider::requestPixmap_sync()
295 {
296     bool deleteWatch = false;
297     runTest(false, new TestQPixmapProvider(&deleteWatch));
298     QVERIFY(deleteWatch);
299 }
300
301 void tst_qdeclarativeimageprovider::requestPixmap_async()
302 {
303     QDeclarativeEngine engine;
304     QDeclarativeImageProvider *provider = new TestQPixmapProvider();
305
306     engine.addImageProvider("test", provider);
307     QVERIFY(engine.imageProvider("test") != 0);
308
309     // pixmaps are loaded synchronously regardless of 'asynchronous' value
310     QString componentStr = "import QtQuick 1.0\nImage { asynchronous: true; source: \"image://test/pixmap-async-test.png\" }";
311     QDeclarativeComponent component(&engine);
312     component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
313     QDeclarative1Image *obj = qobject_cast<QDeclarative1Image*>(component.create());
314     QVERIFY(obj != 0);
315
316     delete obj;
317 }
318
319 void tst_qdeclarativeimageprovider::removeProvider_data()
320 {
321     QTest::addColumn<QDeclarativeImageProvider*>("provider");
322
323     QTest::newRow("qimage") << static_cast<QDeclarativeImageProvider*>(new TestQImageProvider);
324     QTest::newRow("qpixmap") << static_cast<QDeclarativeImageProvider*>(new TestQPixmapProvider);
325 }
326
327 void tst_qdeclarativeimageprovider::removeProvider()
328 {
329     QFETCH(QDeclarativeImageProvider*, provider);
330
331     QDeclarativeEngine engine;
332
333     engine.addImageProvider("test", provider);
334     QVERIFY(engine.imageProvider("test") != 0);
335
336     // add provider, confirm it works
337     QString componentStr = "import QtQuick 1.0\nImage { source: \"" + newImageFileName() + "\" }";
338     QDeclarativeComponent component(&engine);
339     component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
340     QDeclarative1Image *obj = qobject_cast<QDeclarative1Image*>(component.create());
341     QVERIFY(obj != 0);
342
343     QCOMPARE(obj->status(), QDeclarative1Image::Ready);
344
345     // remove the provider and confirm
346     QString fileName = newImageFileName();
347     QString error("file::2:1: QML Image: Failed to get image from provider: " + fileName);
348     QTest::ignoreMessage(QtWarningMsg, error.toUtf8());
349
350     engine.removeImageProvider("test");
351
352     obj->setSource(QUrl(fileName));
353     QCOMPARE(obj->status(), QDeclarative1Image::Error);
354
355     delete obj;
356 }
357
358 class TestThreadProvider : public QDeclarativeImageProvider
359 {
360     public:
361         TestThreadProvider() : QDeclarativeImageProvider(Image), ok(false) {}
362
363         ~TestThreadProvider() {}
364
365         QImage requestImage(const QString &id, QSize *size, const QSize& requestedSize)
366         {
367             mutex.lock();
368             if (!ok)
369                 cond.wait(&mutex);
370             mutex.unlock();
371             QVector<int> v;
372             for (int i = 0; i < 10000; i++)
373                 v.prepend(i); //do some computation
374             QImage image(50,50, QImage::Format_RGB32);
375             image.fill(QColor(id).rgb());
376             if (size)
377                 *size = image.size();
378             if (requestedSize.isValid())
379                 image = image.scaled(requestedSize);
380             return image;
381         }
382
383         QWaitCondition cond;
384         QMutex mutex;
385         bool ok;
386 };
387
388
389 void tst_qdeclarativeimageprovider::threadTest()
390 {
391     QDeclarativeEngine engine;
392
393     TestThreadProvider *provider = new TestThreadProvider;
394
395     engine.addImageProvider("test_thread", provider);
396     QVERIFY(engine.imageProvider("test_thread") != 0);
397
398     QString componentStr = "import QtQuick 1.0\nItem { \n"
399             "Image { source: \"image://test_thread/blue\";  asynchronous: true; }\n"
400             "Image { source: \"image://test_thread/red\";  asynchronous: true; }\n"
401             "Image { source: \"image://test_thread/green\";  asynchronous: true; }\n"
402             "Image { source: \"image://test_thread/yellow\";  asynchronous: true; }\n"
403             " }";
404     QDeclarativeComponent component(&engine);
405     component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
406     QObject *obj = component.create();
407     //MUST not deadlock
408     QVERIFY(obj != 0);
409     QList<QDeclarative1Image *> images = obj->findChildren<QDeclarative1Image *>();
410     QCOMPARE(images.count(), 4);
411     QTest::qWait(100);
412     foreach(QDeclarative1Image *img, images) {
413         QCOMPARE(img->status(), QDeclarative1Image::Loading);
414     }
415     provider->ok = true;
416     provider->cond.wakeAll();
417     QTest::qWait(250);
418     foreach(QDeclarative1Image *img, images) {
419         QTRY_VERIFY(img->status() == QDeclarative1Image::Ready);
420     }
421 }
422
423
424 QTEST_MAIN(tst_qdeclarativeimageprovider)
425
426 #include "tst_qdeclarativeimageprovider.moc"