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