Remove "All rights reserved" line from license headers.
[profile/ivi/qtdeclarative.git] / tests / auto / declarative / qdeclarativeimageprovider / tst_qdeclarativeimageprovider.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
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 <private/qquickimage_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: Invalid image 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 2.0\nImage { source: \"" + source + "\"; " 
227             + (async ? "asynchronous: true; " : "")
228             + properties + " }";
229     QDeclarativeComponent component(&engine);
230     component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
231     QQuickImage *obj = qobject_cast<QQuickImage*>(component.create());
232     QVERIFY(obj != 0);
233
234     if (async) 
235         QTRY_VERIFY(obj->status() == QQuickImage::Loading);
236
237     QCOMPARE(obj->source(), QUrl(source));
238
239     if (error.isEmpty()) {
240         if (async)
241             QTRY_VERIFY(obj->status() == QQuickImage::Ready);
242         else
243             QVERIFY(obj->status() == QQuickImage::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->fillMode(), QQuickImage::Stretch);
252         QCOMPARE(obj->progress(), 1.0);
253     } else {
254         if (async)
255             QTRY_VERIFY(obj->status() == QQuickImage::Error);
256         else
257             QVERIFY(obj->status() == QQuickImage::Error);
258     }
259
260     delete obj;
261 }
262
263 void tst_qdeclarativeimageprovider::requestImage_sync_data()
264 {
265     fillRequestTestsData("qimage|sync");
266 }
267
268 void tst_qdeclarativeimageprovider::requestImage_sync()
269 {
270     bool deleteWatch = false;
271     runTest(false, new TestQImageProvider(&deleteWatch));
272     QVERIFY(deleteWatch);
273 }
274
275 void tst_qdeclarativeimageprovider::requestImage_async_data()
276 {
277     fillRequestTestsData("qimage|async");
278 }
279
280 void tst_qdeclarativeimageprovider::requestImage_async()
281 {
282     bool deleteWatch = false;
283     runTest(true, new TestQImageProvider(&deleteWatch));
284     QVERIFY(deleteWatch);
285 }
286
287 void tst_qdeclarativeimageprovider::requestPixmap_sync_data()
288 {
289     fillRequestTestsData("qpixmap");
290 }
291
292 void tst_qdeclarativeimageprovider::requestPixmap_sync()
293 {
294     bool deleteWatch = false;
295     runTest(false, new TestQPixmapProvider(&deleteWatch));
296     QVERIFY(deleteWatch);
297 }
298
299 void tst_qdeclarativeimageprovider::requestPixmap_async()
300 {
301     QDeclarativeEngine engine;
302     QDeclarativeImageProvider *provider = new TestQPixmapProvider();
303
304     engine.addImageProvider("test", provider);
305     QVERIFY(engine.imageProvider("test") != 0);
306
307     // pixmaps are loaded synchronously regardless of 'asynchronous' value
308     QString componentStr = "import QtQuick 2.0\nImage { asynchronous: true; source: \"image://test/pixmap-async-test.png\" }";
309     QDeclarativeComponent component(&engine);
310     component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
311     QQuickImage *obj = qobject_cast<QQuickImage*>(component.create());
312     QVERIFY(obj != 0);
313
314     delete obj;
315 }
316
317 void tst_qdeclarativeimageprovider::removeProvider_data()
318 {
319     QTest::addColumn<QDeclarativeImageProvider*>("provider");
320
321     QTest::newRow("qimage") << static_cast<QDeclarativeImageProvider*>(new TestQImageProvider);
322     QTest::newRow("qpixmap") << static_cast<QDeclarativeImageProvider*>(new TestQPixmapProvider);
323 }
324
325 void tst_qdeclarativeimageprovider::removeProvider()
326 {
327     QFETCH(QDeclarativeImageProvider*, provider);
328
329     QDeclarativeEngine engine;
330
331     engine.addImageProvider("test", provider);
332     QVERIFY(engine.imageProvider("test") != 0);
333
334     // add provider, confirm it works
335     QString componentStr = "import QtQuick 2.0\nImage { source: \"" + newImageFileName() + "\" }";
336     QDeclarativeComponent component(&engine);
337     component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
338     QQuickImage *obj = qobject_cast<QQuickImage*>(component.create());
339     QVERIFY(obj != 0);
340
341     QCOMPARE(obj->status(), QQuickImage::Ready);
342
343     // remove the provider and confirm
344     QString fileName = newImageFileName();
345     QString error("file::2:1: QML Image: Invalid image provider: " + fileName);
346     QTest::ignoreMessage(QtWarningMsg, error.toUtf8());
347
348     engine.removeImageProvider("test");
349
350     obj->setSource(QUrl(fileName));
351     QCOMPARE(obj->status(), QQuickImage::Error);
352
353     delete obj;
354 }
355
356 class TestThreadProvider : public QDeclarativeImageProvider
357 {
358     public:
359         TestThreadProvider() : QDeclarativeImageProvider(Image), ok(false) {}
360
361         ~TestThreadProvider() {}
362
363         QImage requestImage(const QString &id, QSize *size, const QSize& requestedSize)
364         {
365             mutex.lock();
366             if (!ok)
367                 cond.wait(&mutex);
368             mutex.unlock();
369             QVector<int> v;
370             for (int i = 0; i < 10000; i++)
371                 v.prepend(i); //do some computation
372             QImage image(50,50, QImage::Format_RGB32);
373             image.fill(QColor(id).rgb());
374             if (size)
375                 *size = image.size();
376             if (requestedSize.isValid())
377                 image = image.scaled(requestedSize);
378             return image;
379         }
380
381         QWaitCondition cond;
382         QMutex mutex;
383         bool ok;
384 };
385
386
387 void tst_qdeclarativeimageprovider::threadTest()
388 {
389     QDeclarativeEngine engine;
390
391     TestThreadProvider *provider = new TestThreadProvider;
392
393     engine.addImageProvider("test_thread", provider);
394     QVERIFY(engine.imageProvider("test_thread") != 0);
395
396     QString componentStr = "import QtQuick 2.0\nItem { \n"
397             "Image { source: \"image://test_thread/blue\";  asynchronous: true; }\n"
398             "Image { source: \"image://test_thread/red\";  asynchronous: true; }\n"
399             "Image { source: \"image://test_thread/green\";  asynchronous: true; }\n"
400             "Image { source: \"image://test_thread/yellow\";  asynchronous: true; }\n"
401             " }";
402     QDeclarativeComponent component(&engine);
403     component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
404     QObject *obj = component.create();
405     //MUST not deadlock
406     QVERIFY(obj != 0);
407     QList<QQuickImage *> images = obj->findChildren<QQuickImage *>();
408     QCOMPARE(images.count(), 4);
409     QTest::qWait(100);
410     foreach (QQuickImage *img, images) {
411         QCOMPARE(img->status(), QQuickImage::Loading);
412     }
413     provider->ok = true;
414     provider->cond.wakeAll();
415     QTest::qWait(250);
416     foreach (QQuickImage *img, images) {
417         QTRY_VERIFY(img->status() == QQuickImage::Ready);
418     }
419 }
420
421
422 QTEST_MAIN(tst_qdeclarativeimageprovider)
423
424 #include "tst_qdeclarativeimageprovider.moc"