c54f5e2b24753266b5e979c5df6b209830ddad37
[profile/ivi/qtdeclarative.git] / tests / auto / qtquick2 / qquickimage / tst_qquickimage.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 <QTextDocument>
43 #include <QTcpServer>
44 #include <QTcpSocket>
45 #include <QDir>
46
47 #include <QtDeclarative/qdeclarativeengine.h>
48 #include <QtDeclarative/qdeclarativecomponent.h>
49 #include <QtQuick/qquickview.h>
50 #include <private/qquickimage_p.h>
51 #include <private/qquickimagebase_p.h>
52 #include <private/qquickloader_p.h>
53 #include <QtDeclarative/qdeclarativecontext.h>
54 #include <QtDeclarative/qdeclarativeexpression.h>
55 #include <QtTest/QSignalSpy>
56 #include <QtGui/QPainter>
57 #include <QtGui/QImageReader>
58
59 #include "../../shared/util.h"
60 #include "../../shared/testhttpserver.h"
61
62 #define SERVER_PORT 14451
63 #define SERVER_ADDR "http://127.0.0.1:14451"
64
65 Q_DECLARE_METATYPE(QQuickImageBase::Status)
66
67 class tst_qquickimage : public QDeclarativeDataTest
68 {
69     Q_OBJECT
70 public:
71     tst_qquickimage();
72
73 private slots:
74     void noSource();
75     void imageSource();
76     void imageSource_data();
77     void clearSource();
78     void resized();
79     void preserveAspectRatio();
80     void smooth();
81     void mirror();
82     void svg();
83     void geometry();
84     void geometry_data();
85     void big();
86     void tiling_QTBUG_6716();
87     void tiling_QTBUG_6716_data();
88     void noLoading();
89     void paintedWidthHeight();
90     void sourceSize_QTBUG_14303();
91     void sourceSize_QTBUG_16389();
92     void nullPixmapPaint();
93     void imageCrash_QTBUG_22125();
94
95 private:
96     template<typename T>
97     T *findItem(QQuickItem *parent, const QString &id, int index=-1);
98
99     QDeclarativeEngine engine;
100 };
101
102 tst_qquickimage::tst_qquickimage()
103 {
104 }
105
106 void tst_qquickimage::noSource()
107 {
108     QString componentStr = "import QtQuick 2.0\nImage { source: \"\" }";
109     QDeclarativeComponent component(&engine);
110     component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
111     QQuickImage *obj = qobject_cast<QQuickImage*>(component.create());
112     QVERIFY(obj != 0);
113     QCOMPARE(obj->source(), QUrl());
114     QVERIFY(obj->status() == QQuickImage::Null);
115     QCOMPARE(obj->width(), 0.);
116     QCOMPARE(obj->height(), 0.);
117     QCOMPARE(obj->fillMode(), QQuickImage::Stretch);
118     QCOMPARE(obj->progress(), 0.0);
119
120     delete obj;
121 }
122
123 void tst_qquickimage::imageSource_data()
124 {
125     QTest::addColumn<QString>("source");
126     QTest::addColumn<double>("width");
127     QTest::addColumn<double>("height");
128     QTest::addColumn<bool>("remote");
129     QTest::addColumn<bool>("async");
130     QTest::addColumn<bool>("cache");
131     QTest::addColumn<QString>("error");
132
133     QTest::newRow("local") << testFileUrl("colors.png").toString() << 120.0 << 120.0 << false << false << true << "";
134     QTest::newRow("local no cache") << testFileUrl("colors.png").toString() << 120.0 << 120.0 << false << false << false << "";
135     QTest::newRow("local async") << testFileUrl("colors1.png").toString() << 120.0 << 120.0 << false << true << true << "";
136     QTest::newRow("local not found") << testFileUrl("no-such-file.png").toString() << 0.0 << 0.0 << false
137         << false << true << "file::2:1: QML Image: Cannot open: " + testFileUrl("no-such-file.png").toString();
138     QTest::newRow("local async not found") << testFileUrl("no-such-file-1.png").toString() << 0.0 << 0.0 << false
139         << true << true << "file::2:1: QML Image: Cannot open: " + testFileUrl("no-such-file-1.png").toString();
140     QTest::newRow("remote") << SERVER_ADDR "/colors.png" << 120.0 << 120.0 << true << false << true << "";
141     QTest::newRow("remote redirected") << SERVER_ADDR "/oldcolors.png" << 120.0 << 120.0 << true << false << false << "";
142     if (QImageReader::supportedImageFormats().contains("svg"))
143         QTest::newRow("remote svg") << SERVER_ADDR "/heart.svg" << 550.0 << 500.0 << true << false << false << "";
144
145     QTest::newRow("remote not found") << SERVER_ADDR "/no-such-file.png" << 0.0 << 0.0 << true
146         << false << true << "file::2:1: QML Image: Error downloading " SERVER_ADDR "/no-such-file.png - server replied: Not found";
147
148 }
149
150 void tst_qquickimage::imageSource()
151 {
152     QFETCH(QString, source);
153     QFETCH(double, width);
154     QFETCH(double, height);
155     QFETCH(bool, remote);
156     QFETCH(bool, async);
157     QFETCH(bool, cache);
158     QFETCH(QString, error);
159
160     TestHTTPServer server(SERVER_PORT);
161     if (remote) {
162         QVERIFY(server.isValid());
163         server.serveDirectory(dataDirectory());
164         server.addRedirect("oldcolors.png", SERVER_ADDR "/colors.png");
165     }
166
167     if (!error.isEmpty())
168         QTest::ignoreMessage(QtWarningMsg, error.toUtf8());
169
170     QString componentStr = "import QtQuick 2.0\nImage { source: \"" + source + "\"; asynchronous: "
171         + (async ? QLatin1String("true") : QLatin1String("false")) + "; cache: "
172         + (cache ? QLatin1String("true") : QLatin1String("false")) + " }";
173     QDeclarativeComponent component(&engine);
174     component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
175     QQuickImage *obj = qobject_cast<QQuickImage*>(component.create());
176     QVERIFY(obj != 0);
177
178     if (async)
179         QVERIFY(obj->asynchronous() == true);
180     else
181         QVERIFY(obj->asynchronous() == false);
182
183     if (cache)
184         QVERIFY(obj->cache() == true);
185     else
186         QVERIFY(obj->cache() == false);
187
188     if (remote || async)
189         QTRY_VERIFY(obj->status() == QQuickImage::Loading);
190
191     QCOMPARE(obj->source(), remote ? source : QUrl(source));
192
193     if (error.isEmpty()) {
194         QTRY_VERIFY(obj->status() == QQuickImage::Ready);
195         QCOMPARE(obj->width(), qreal(width));
196         QCOMPARE(obj->height(), qreal(height));
197         QCOMPARE(obj->fillMode(), QQuickImage::Stretch);
198         QCOMPARE(obj->progress(), 1.0);
199     } else {
200         QTRY_VERIFY(obj->status() == QQuickImage::Error);
201     }
202
203     delete obj;
204 }
205
206 void tst_qquickimage::clearSource()
207 {
208     QString componentStr = "import QtQuick 2.0\nImage { source: srcImage }";
209     QDeclarativeContext *ctxt = engine.rootContext();
210     ctxt->setContextProperty("srcImage", testFileUrl("colors.png"));
211     QDeclarativeComponent component(&engine);
212     component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
213     QQuickImage *obj = qobject_cast<QQuickImage*>(component.create());
214     QVERIFY(obj != 0);
215     QVERIFY(obj->status() == QQuickImage::Ready);
216     QCOMPARE(obj->width(), 120.);
217     QCOMPARE(obj->height(), 120.);
218     QCOMPARE(obj->progress(), 1.0);
219
220     ctxt->setContextProperty("srcImage", "");
221     QVERIFY(obj->source().isEmpty());
222     QVERIFY(obj->status() == QQuickImage::Null);
223     QCOMPARE(obj->width(), 0.);
224     QCOMPARE(obj->height(), 0.);
225     QCOMPARE(obj->progress(), 0.0);
226
227     delete obj;
228 }
229
230 void tst_qquickimage::resized()
231 {
232     QString componentStr = "import QtQuick 2.0\nImage { source: \"" + testFile("colors.png") + "\"; width: 300; height: 300 }";
233     QDeclarativeComponent component(&engine);
234     component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
235     QQuickImage *obj = qobject_cast<QQuickImage*>(component.create());
236     QVERIFY(obj != 0);
237     QCOMPARE(obj->width(), 300.);
238     QCOMPARE(obj->height(), 300.);
239     QCOMPARE(obj->fillMode(), QQuickImage::Stretch);
240     delete obj;
241 }
242
243
244 void tst_qquickimage::preserveAspectRatio()
245 {
246     QQuickView *canvas = new QQuickView(0);
247     canvas->show();
248
249     canvas->setSource(testFileUrl("aspectratio.qml"));
250     QQuickImage *image = qobject_cast<QQuickImage*>(canvas->rootObject());
251     QVERIFY(image != 0);
252     image->setWidth(80.0);
253     QCOMPARE(image->width(), 80.);
254     QCOMPARE(image->height(), 80.);
255
256     canvas->setSource(testFileUrl("aspectratio.qml"));
257     image = qobject_cast<QQuickImage*>(canvas->rootObject());
258     image->setHeight(60.0);
259     QVERIFY(image != 0);
260     QCOMPARE(image->height(), 60.);
261     QCOMPARE(image->width(), 60.);
262     delete canvas;
263 }
264
265 void tst_qquickimage::smooth()
266 {
267     QString componentStr = "import QtQuick 2.0\nImage { source: \"" + testFile("colors.png") + "\"; smooth: true; width: 300; height: 300 }";
268     QDeclarativeComponent component(&engine);
269     component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
270     QQuickImage *obj = qobject_cast<QQuickImage*>(component.create());
271     QVERIFY(obj != 0);
272     QCOMPARE(obj->width(), 300.);
273     QCOMPARE(obj->height(), 300.);
274     QCOMPARE(obj->smooth(), true);
275     QCOMPARE(obj->fillMode(), QQuickImage::Stretch);
276
277     delete obj;
278 }
279
280 void tst_qquickimage::mirror()
281 {
282     QSKIP("Test is broken on multiple levels, will need incremental fixes");
283
284     QMap<QQuickImage::FillMode, QImage> screenshots;
285     QList<QQuickImage::FillMode> fillModes;
286     fillModes << QQuickImage::Stretch << QQuickImage::PreserveAspectFit << QQuickImage::PreserveAspectCrop
287               << QQuickImage::Tile << QQuickImage::TileVertically << QQuickImage::TileHorizontally;
288
289     qreal width = 300;
290     qreal height = 250;
291
292     foreach (QQuickImage::FillMode fillMode, fillModes) {
293         QQuickView *canvas = new QQuickView;
294         canvas->setSource(testFileUrl("mirror.qml"));
295
296         QQuickImage *obj = canvas->rootObject()->findChild<QQuickImage*>("image");
297         QVERIFY(obj != 0);
298
299         obj->setFillMode(fillMode);
300         obj->setProperty("mirror", true);
301         canvas->show();
302
303         QImage screenshot = canvas->grabFrameBuffer();
304         screenshots[fillMode] = screenshot;
305         delete canvas;
306     }
307
308     foreach (QQuickImage::FillMode fillMode, fillModes) {
309         QPixmap srcPixmap;
310         QVERIFY(srcPixmap.load(testFile("pattern.png")));
311
312         QPixmap expected(width, height);
313         expected.fill();
314         QPainter p_e(&expected);
315         QTransform transform;
316         transform.translate(width, 0).scale(-1, 1.0);
317         p_e.setTransform(transform);
318
319         switch (fillMode) {
320         case QQuickImage::Stretch:
321             p_e.drawPixmap(QRect(0, 0, width, height), srcPixmap, QRect(0, 0, srcPixmap.width(), srcPixmap.height()));
322             break;
323         case QQuickImage::PreserveAspectFit:
324             p_e.drawPixmap(QRect(25, 0, height, height), srcPixmap, QRect(0, 0, srcPixmap.width(), srcPixmap.height()));
325             break;
326         case QQuickImage::PreserveAspectCrop:
327         {
328             qreal ratio = width/srcPixmap.width(); // width is the longer side
329             QRect rect(0, 0, srcPixmap.width()*ratio, srcPixmap.height()*ratio);
330             rect.moveCenter(QRect(0, 0, width, height).center());
331             p_e.drawPixmap(rect, srcPixmap, QRect(0, 0, srcPixmap.width(), srcPixmap.height()));
332             break;
333         }
334         case QQuickImage::Tile:
335             p_e.drawTiledPixmap(QRect(0, 0, width, height), srcPixmap);
336             break;
337         case QQuickImage::TileVertically:
338             transform.scale(width / srcPixmap.width(), 1.0);
339             p_e.setTransform(transform);
340             p_e.drawTiledPixmap(QRect(0, 0, width, height), srcPixmap);
341             break;
342         case QQuickImage::TileHorizontally:
343             transform.scale(1.0, height / srcPixmap.height());
344             p_e.setTransform(transform);
345             p_e.drawTiledPixmap(QRect(0, 0, width, height), srcPixmap);
346             break;
347         case QQuickImage::Pad:
348             break;
349         }
350
351         QImage img = expected.toImage();
352         QEXPECT_FAIL("", "QTBUG-21005 fails", Continue);
353         QCOMPARE(screenshots[fillMode], img);
354     }
355 }
356
357 void tst_qquickimage::svg()
358 {
359     if (!QImageReader::supportedImageFormats().contains("svg"))
360         QSKIP("svg support not available");
361
362     QString src = testFileUrl("heart.svg").toString();
363     QString componentStr = "import QtQuick 2.0\nImage { source: \"" + src + "\"; sourceSize.width: 300; sourceSize.height: 300 }";
364     QDeclarativeComponent component(&engine);
365     component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
366     QQuickImage *obj = qobject_cast<QQuickImage*>(component.create());
367     QVERIFY(obj != 0);
368     QCOMPARE(obj->width(), 300.0);
369     QCOMPARE(obj->height(), 300.0);
370     obj->setSourceSize(QSize(200,200));
371
372     QCOMPARE(obj->width(), 200.0);
373     QCOMPARE(obj->height(), 200.0);
374     delete obj;
375 }
376
377 void tst_qquickimage::geometry_data()
378 {
379     QTest::addColumn<QString>("fillMode");
380     QTest::addColumn<bool>("explicitWidth");
381     QTest::addColumn<bool>("explicitHeight");
382     QTest::addColumn<double>("itemWidth");
383     QTest::addColumn<double>("paintedWidth");
384     QTest::addColumn<double>("boundingWidth");
385     QTest::addColumn<double>("itemHeight");
386     QTest::addColumn<double>("paintedHeight");
387     QTest::addColumn<double>("boundingHeight");
388
389     // tested image has width 200, height 100
390
391     // bounding rect and item rect are equal with fillMode PreserveAspectFit, painted rect may be smaller if the aspect ratio doesn't match
392     QTest::newRow("PreserveAspectFit") << "PreserveAspectFit" << false << false << 200.0 << 200.0 << 200.0 << 100.0 << 100.0 << 100.0;
393     QTest::newRow("PreserveAspectFit explicit width 300") << "PreserveAspectFit" << true << false << 300.0 << 200.0 << 300.0 << 100.0 << 100.0 << 100.0;
394     QTest::newRow("PreserveAspectFit explicit height 400") << "PreserveAspectFit" << false << true << 200.0 << 200.0 << 200.0 << 400.0 << 100.0 << 400.0;
395     QTest::newRow("PreserveAspectFit explicit width 300, height 400") << "PreserveAspectFit" << true << true << 300.0 << 300.0 << 300.0 << 400.0 << 150.0 << 400.0;
396
397     // bounding rect and painted rect are equal with fillMode PreserveAspectCrop, item rect may be smaller if the aspect ratio doesn't match
398     QTest::newRow("PreserveAspectCrop") << "PreserveAspectCrop" << false << false << 200.0 << 200.0 << 200.0 << 100.0 << 100.0 << 100.0;
399     QTest::newRow("PreserveAspectCrop explicit width 300") << "PreserveAspectCrop" << true << false << 300.0 << 300.0 << 300.0 << 100.0 << 150.0 << 150.0;
400     QTest::newRow("PreserveAspectCrop explicit height 400") << "PreserveAspectCrop" << false << true << 200.0 << 800.0 << 800.0 << 400.0 << 400.0 << 400.0;
401     QTest::newRow("PreserveAspectCrop explicit width 300, height 400") << "PreserveAspectCrop" << true << true << 300.0 << 800.0 << 800.0 << 400.0 << 400.0 << 400.0;
402
403     // bounding rect, painted rect and item rect are equal in stretching and tiling images
404     QStringList fillModes;
405     fillModes << "Stretch" << "Tile" << "TileVertically" << "TileHorizontally";
406     foreach (QString fillMode, fillModes) {
407         QTest::newRow(fillMode.toLatin1()) << fillMode << false << false << 200.0 << 200.0 << 200.0 << 100.0 << 100.0 << 100.0;
408         QTest::newRow(QString(fillMode + " explicit width 300").toLatin1()) << fillMode << true << false << 300.0 << 300.0 << 300.0 << 100.0 << 100.0 << 100.0;
409         QTest::newRow(QString(fillMode + " explicit height 400").toLatin1()) << fillMode << false << true << 200.0 << 200.0 << 200.0 << 400.0 << 400.0 << 400.0;
410         QTest::newRow(QString(fillMode + " explicit width 300, height 400").toLatin1()) << fillMode << true << true << 300.0 << 300.0 << 300.0 << 400.0 << 400.0 << 400.0;
411     }
412 }
413
414 void tst_qquickimage::geometry()
415 {
416     QFETCH(QString, fillMode);
417     QFETCH(bool, explicitWidth);
418     QFETCH(bool, explicitHeight);
419     QFETCH(double, itemWidth);
420     QFETCH(double, itemHeight);
421     QFETCH(double, paintedWidth);
422     QFETCH(double, paintedHeight);
423     QFETCH(double, boundingWidth);
424     QFETCH(double, boundingHeight);
425
426     QString src = testFileUrl("rect.png").toString();
427     QString componentStr = "import QtQuick 2.0\nImage { source: \"" + src + "\"; fillMode: Image." + fillMode + "; ";
428
429     if (explicitWidth)
430         componentStr.append("width: 300; ");
431     if (explicitHeight)
432         componentStr.append("height: 400; ");
433     componentStr.append("}");
434     QDeclarativeComponent component(&engine);
435     component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
436     QQuickImage *obj = qobject_cast<QQuickImage*>(component.create());
437     QVERIFY(obj != 0);
438
439     QCOMPARE(obj->width(), itemWidth);
440     QCOMPARE(obj->paintedWidth(), paintedWidth);
441     QCOMPARE(obj->boundingRect().width(), boundingWidth);
442
443     QCOMPARE(obj->height(), itemHeight);
444     QCOMPARE(obj->paintedHeight(), paintedHeight);
445     QCOMPARE(obj->boundingRect().height(), boundingHeight);
446     delete obj;
447 }
448
449 void tst_qquickimage::big()
450 {
451     // If the JPEG loader does not implement scaling efficiently, it would
452     // have to build a 400 MB image. That would be a bug in the JPEG loader.
453
454     QString src = testFileUrl("big.jpeg").toString();
455     QString componentStr = "import QtQuick 2.0\nImage { source: \"" + src + "\"; width: 100; sourceSize.height: 256 }";
456
457     QDeclarativeComponent component(&engine);
458     component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
459     QQuickImage *obj = qobject_cast<QQuickImage*>(component.create());
460     QVERIFY(obj != 0);
461     QCOMPARE(obj->width(), 100.0);
462     QCOMPARE(obj->height(), 256.0);
463
464     delete obj;
465 }
466
467 // As tiling_QTBUG_6716 doesn't complete, it doesn't delete the
468 // canvas which causes leak warnings.  Use this delete on stack
469 // destruction pattern to work around this.
470 template<typename T>
471 struct AutoDelete {
472     AutoDelete(T *t) : t(t) {}
473     ~AutoDelete() { delete t; }
474 private:
475     T *t;
476 };
477
478 void tst_qquickimage::tiling_QTBUG_6716()
479 {
480     QSKIP("Test is broken on multiple levels, will need incremental fixes");
481
482     QFETCH(QString, source);
483
484     QQuickView *canvas = new QQuickView(0);
485     AutoDelete<QQuickView> del(canvas);
486
487     canvas->setSource(testFileUrl(source));
488     canvas->show();
489     qApp->processEvents();
490
491     QQuickImage *tiling = findItem<QQuickImage>(canvas->rootObject(), "tiling");
492
493     QVERIFY(tiling != 0);
494     QImage img = canvas->grabFrameBuffer();
495     for (int x = 0; x < tiling->width(); ++x) {
496         for (int y = 0; y < tiling->height(); ++y) {
497             QVERIFY(img.pixel(x, y) == qRgb(0, 255, 0));
498         }
499     }
500 }
501
502 void tst_qquickimage::tiling_QTBUG_6716_data()
503 {
504     QTest::addColumn<QString>("source");
505     QTest::newRow("vertical_tiling") << "vtiling.qml";
506     QTest::newRow("horizontal_tiling") << "htiling.qml";
507 }
508
509 void tst_qquickimage::noLoading()
510 {
511     qRegisterMetaType<QQuickImageBase::Status>();
512
513     TestHTTPServer server(SERVER_PORT);
514     QVERIFY(server.isValid());
515     server.serveDirectory(dataDirectory());
516     server.addRedirect("oldcolors.png", SERVER_ADDR "/colors.png");
517
518     QString componentStr = "import QtQuick 2.0\nImage { source: srcImage; cache: true }";
519     QDeclarativeContext *ctxt = engine.rootContext();
520     ctxt->setContextProperty("srcImage", testFileUrl("heart.png"));
521     QDeclarativeComponent component(&engine);
522     component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
523     QQuickImage *obj = qobject_cast<QQuickImage*>(component.create());
524     QVERIFY(obj != 0);
525     QVERIFY(obj->status() == QQuickImage::Ready);
526
527     QSignalSpy sourceSpy(obj, SIGNAL(sourceChanged(const QUrl &)));
528     QSignalSpy progressSpy(obj, SIGNAL(progressChanged(qreal)));
529     QSignalSpy statusSpy(obj, SIGNAL(statusChanged(QQuickImageBase::Status)));
530
531     // Loading local file
532     ctxt->setContextProperty("srcImage", testFileUrl("green.png"));
533     QTRY_VERIFY(obj->status() == QQuickImage::Ready);
534     QTRY_VERIFY(obj->progress() == 1.0);
535     QTRY_COMPARE(sourceSpy.count(), 1);
536     QTRY_COMPARE(progressSpy.count(), 0);
537     QTRY_COMPARE(statusSpy.count(), 0);
538
539     // Loading remote file
540     ctxt->setContextProperty("srcImage", QString(SERVER_ADDR) + "/rect.png");
541     QTRY_VERIFY(obj->status() == QQuickImage::Loading);
542     QTRY_VERIFY(obj->progress() == 0.0);
543     QTRY_VERIFY(obj->status() == QQuickImage::Ready);
544     QTRY_VERIFY(obj->progress() == 1.0);
545     QTRY_COMPARE(sourceSpy.count(), 2);
546     QTRY_COMPARE(progressSpy.count(), 2);
547     QTRY_COMPARE(statusSpy.count(), 2);
548
549     // Loading remote file again - should not go through 'Loading' state.
550     ctxt->setContextProperty("srcImage", testFileUrl("green.png"));
551     ctxt->setContextProperty("srcImage", QString(SERVER_ADDR) + "/rect.png");
552     QTRY_VERIFY(obj->status() == QQuickImage::Ready);
553     QTRY_VERIFY(obj->progress() == 1.0);
554     QTRY_COMPARE(sourceSpy.count(), 4);
555     QTRY_COMPARE(progressSpy.count(), 2);
556     QTRY_COMPARE(statusSpy.count(), 2);
557
558     delete obj;
559 }
560
561 void tst_qquickimage::paintedWidthHeight()
562 {
563     {
564         QString src = testFileUrl("heart.png").toString();
565         QString componentStr = "import QtQuick 2.0\nImage { source: \"" + src + "\"; width: 200; height: 25; fillMode: Image.PreserveAspectFit }";
566
567         QDeclarativeComponent component(&engine);
568         component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
569         QQuickImage *obj = qobject_cast<QQuickImage*>(component.create());
570         QVERIFY(obj != 0);
571         QCOMPARE(obj->width(), 200.0);
572         QCOMPARE(obj->height(), 25.0);
573         QCOMPARE(obj->paintedWidth(), 25.0);
574         QCOMPARE(obj->paintedHeight(), 25.0);
575
576         delete obj;
577     }
578
579     {
580         QString src = testFileUrl("heart.png").toString();
581         QString componentStr = "import QtQuick 2.0\nImage { source: \"" + src + "\"; width: 26; height: 175; fillMode: Image.PreserveAspectFit }";
582         QDeclarativeComponent component(&engine);
583         component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
584         QQuickImage *obj = qobject_cast<QQuickImage*>(component.create());
585         QVERIFY(obj != 0);
586         QCOMPARE(obj->width(), 26.0);
587         QCOMPARE(obj->height(), 175.0);
588         QCOMPARE(obj->paintedWidth(), 26.0);
589         QCOMPARE(obj->paintedHeight(), 26.0);
590
591         delete obj;
592     }
593 }
594
595 void tst_qquickimage::sourceSize_QTBUG_14303()
596 {
597     QString componentStr = "import QtQuick 2.0\nImage { source: srcImage }";
598     QDeclarativeContext *ctxt = engine.rootContext();
599     ctxt->setContextProperty("srcImage", testFileUrl("heart200.png"));
600     QDeclarativeComponent component(&engine);
601     component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
602     QQuickImage *obj = qobject_cast<QQuickImage*>(component.create());
603
604     QSignalSpy sourceSizeSpy(obj, SIGNAL(sourceSizeChanged()));
605
606     QTRY_VERIFY(obj != 0);
607     QTRY_VERIFY(obj->status() == QQuickImage::Ready);
608
609     QTRY_COMPARE(obj->sourceSize().width(), 200);
610     QTRY_COMPARE(obj->sourceSize().height(), 200);
611     QTRY_COMPARE(sourceSizeSpy.count(), 0);
612
613     ctxt->setContextProperty("srcImage", testFileUrl("colors.png"));
614     QTRY_COMPARE(obj->sourceSize().width(), 120);
615     QTRY_COMPARE(obj->sourceSize().height(), 120);
616     QTRY_COMPARE(sourceSizeSpy.count(), 1);
617
618     ctxt->setContextProperty("srcImage", testFileUrl("heart200.png"));
619     QTRY_COMPARE(obj->sourceSize().width(), 200);
620     QTRY_COMPARE(obj->sourceSize().height(), 200);
621     QTRY_COMPARE(sourceSizeSpy.count(), 2);
622
623     delete obj;
624 }
625
626 void tst_qquickimage::sourceSize_QTBUG_16389()
627 {
628     QQuickView *canvas = new QQuickView(0);
629     canvas->setSource(testFileUrl("qtbug_16389.qml"));
630     canvas->show();
631     qApp->processEvents();
632
633     QQuickImage *image = findItem<QQuickImage>(canvas->rootObject(), "iconImage");
634     QQuickItem *handle = findItem<QQuickItem>(canvas->rootObject(), "blueHandle");
635
636     QCOMPARE(image->sourceSize().width(), 200);
637     QCOMPARE(image->sourceSize().height(), 200);
638     QCOMPARE(image->paintedWidth(), 0.0);
639     QCOMPARE(image->paintedHeight(), 0.0);
640
641     handle->setY(20);
642
643     QCOMPARE(image->sourceSize().width(), 200);
644     QCOMPARE(image->sourceSize().height(), 200);
645     QCOMPARE(image->paintedWidth(), 20.0);
646     QCOMPARE(image->paintedHeight(), 20.0);
647
648     delete canvas;
649 }
650
651 static int numberOfWarnings = 0;
652 static void checkWarnings(QtMsgType, const char *msg)
653 {
654     if (!QString(msg).contains("QGLContext::makeCurrent(): Failed."))
655         numberOfWarnings++;
656 }
657
658 // QTBUG-15690
659 void tst_qquickimage::nullPixmapPaint()
660 {
661     QQuickView *canvas = new QQuickView(0);
662     canvas->setSource(testFileUrl("nullpixmap.qml"));
663     canvas->show();
664
665     QQuickImage *image = qobject_cast<QQuickImage*>(canvas->rootObject());
666     QTRY_VERIFY(image != 0);
667     image->setSource(SERVER_ADDR + QString("/no-such-file.png"));
668
669     QtMsgHandler previousMsgHandler = qInstallMsgHandler(checkWarnings);
670
671     // used to print "QTransform::translate with NaN called"
672     QPixmap pm = QPixmap::fromImage(canvas->grabFrameBuffer());
673     qInstallMsgHandler(previousMsgHandler);
674     QVERIFY(numberOfWarnings == 0);
675     delete image;
676 }
677
678 void tst_qquickimage::imageCrash_QTBUG_22125()
679 {
680     TestHTTPServer server(SERVER_PORT);
681     QVERIFY(server.isValid());
682     server.serveDirectory(dataDirectory(), TestHTTPServer::Delay);
683
684     {
685         QQuickView view(testFileUrl("qtbug_22125.qml"));
686         view.show();
687         qApp->processEvents();
688         qApp->processEvents();
689         // shouldn't crash when the view drops out of scope due to
690         // QDeclarativePixmapData attempting to dereference a pointer to
691         // the destroyed reader.
692     }
693
694     // shouldn't crash when deleting cancelled QDeclarativePixmapReplys.
695     QTest::qWait(520); // Delay mode delays for 500 ms.
696     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
697     QCoreApplication::processEvents();
698 }
699
700 /*
701    Find an item with the specified objectName.  If index is supplied then the
702    item must also evaluate the {index} expression equal to index
703 */
704 template<typename T>
705 T *tst_qquickimage::findItem(QQuickItem *parent, const QString &objectName, int index)
706 {
707     const QMetaObject &mo = T::staticMetaObject;
708     //qDebug() << parent->childItems().count() << "children";
709     for (int i = 0; i < parent->childItems().count(); ++i) {
710         QQuickItem *item = qobject_cast<QQuickItem*>(parent->childItems().at(i));
711         if (!item)
712             continue;
713         //qDebug() << "try" << item;
714         if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName)) {
715             if (index != -1) {
716                 QDeclarativeExpression e(qmlContext(item), item, "index");
717                 if (e.evaluate().toInt() == index)
718                     return static_cast<T*>(item);
719             } else {
720                 return static_cast<T*>(item);
721             }
722         }
723         item = findItem<T>(item, objectName, index);
724         if (item)
725             return static_cast<T*>(item);
726     }
727
728     return 0;
729 }
730
731 QTEST_MAIN(tst_qquickimage)
732
733 #include "tst_qquickimage.moc"