Fix QQuickImage tiling_QTBUG_6716 and mirror tests.
[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     QMap<QQuickImage::FillMode, QImage> screenshots;
286     QList<QQuickImage::FillMode> fillModes;
287     fillModes << QQuickImage::Stretch << QQuickImage::PreserveAspectFit << QQuickImage::PreserveAspectCrop
288               << QQuickImage::Tile << QQuickImage::TileVertically << QQuickImage::TileHorizontally << QQuickImage::Pad;
289
290     qreal width = 300;
291     qreal height = 250;
292
293     foreach (QQuickImage::FillMode fillMode, fillModes) {
294         QQuickView *canvas = new QQuickView;
295         canvas->setSource(testFileUrl("mirror.qml"));
296
297         QQuickImage *obj = canvas->rootObject()->findChild<QQuickImage*>("image");
298         QVERIFY(obj != 0);
299
300         obj->setFillMode(fillMode);
301         obj->setProperty("mirror", true);
302         canvas->show();
303         canvas->requestActivateWindow();
304         QTest::qWaitForWindowShown(canvas);
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         QPoint offset(width / 2 - srcPixmap.width() / 2, height / 2 - srcPixmap.height() / 2);
323
324         switch (fillMode) {
325         case QQuickImage::Stretch:
326             p_e.drawPixmap(QRect(0, 0, width, height), srcPixmap, QRect(0, 0, srcPixmap.width(), srcPixmap.height()));
327             break;
328         case QQuickImage::PreserveAspectFit:
329             p_e.drawPixmap(QRect(25, 0, height, height), srcPixmap, QRect(0, 0, srcPixmap.width(), srcPixmap.height()));
330             break;
331         case QQuickImage::PreserveAspectCrop:
332         {
333             qreal ratio = width/srcPixmap.width(); // width is the longer side
334             QRect rect(0, 0, srcPixmap.width()*ratio, srcPixmap.height()*ratio);
335             rect.moveCenter(QRect(0, 0, width, height).center());
336             p_e.drawPixmap(rect, srcPixmap, QRect(0, 0, srcPixmap.width(), srcPixmap.height()));
337             break;
338         }
339         case QQuickImage::Tile:
340             p_e.drawTiledPixmap(QRect(0, 0, width, height), srcPixmap, -offset);
341             break;
342         case QQuickImage::TileVertically:
343             transform.scale(width / srcPixmap.width(), 1.0);
344             p_e.setTransform(transform);
345             p_e.drawTiledPixmap(QRect(0, 0, width, height), srcPixmap, QPoint(0, -offset.y()));
346             break;
347         case QQuickImage::TileHorizontally:
348             transform.scale(1.0, height / srcPixmap.height());
349             p_e.setTransform(transform);
350             p_e.drawTiledPixmap(QRect(0, 0, width, height), srcPixmap, QPoint(-offset.x(), 0));
351             break;
352         case QQuickImage::Pad:
353             p_e.drawPixmap(offset, srcPixmap);
354             break;
355         }
356
357         QImage img = expected.toImage();
358         QCOMPARE(screenshots[fillMode], img);
359     }
360 }
361
362 void tst_qquickimage::svg()
363 {
364     if (!QImageReader::supportedImageFormats().contains("svg"))
365         QSKIP("svg support not available");
366
367     QString src = testFileUrl("heart.svg").toString();
368     QString componentStr = "import QtQuick 2.0\nImage { source: \"" + src + "\"; sourceSize.width: 300; sourceSize.height: 300 }";
369     QQmlComponent component(&engine);
370     component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
371     QQuickImage *obj = qobject_cast<QQuickImage*>(component.create());
372     QVERIFY(obj != 0);
373     QCOMPARE(obj->width(), 300.0);
374     QCOMPARE(obj->height(), 273.0);
375     obj->setSourceSize(QSize(200,200));
376
377     QCOMPARE(obj->width(), 200.0);
378     QCOMPARE(obj->height(), 182.0);
379     delete obj;
380 }
381
382 void tst_qquickimage::geometry_data()
383 {
384     QTest::addColumn<QString>("fillMode");
385     QTest::addColumn<bool>("explicitWidth");
386     QTest::addColumn<bool>("explicitHeight");
387     QTest::addColumn<double>("itemWidth");
388     QTest::addColumn<double>("paintedWidth");
389     QTest::addColumn<double>("boundingWidth");
390     QTest::addColumn<double>("itemHeight");
391     QTest::addColumn<double>("paintedHeight");
392     QTest::addColumn<double>("boundingHeight");
393
394     // tested image has width 200, height 100
395
396     // bounding rect and item rect are equal with fillMode PreserveAspectFit, painted rect may be smaller if the aspect ratio doesn't match
397     QTest::newRow("PreserveAspectFit") << "PreserveAspectFit" << false << false << 200.0 << 200.0 << 200.0 << 100.0 << 100.0 << 100.0;
398     QTest::newRow("PreserveAspectFit explicit width 300") << "PreserveAspectFit" << true << false << 300.0 << 200.0 << 300.0 << 100.0 << 100.0 << 100.0;
399     QTest::newRow("PreserveAspectFit explicit height 400") << "PreserveAspectFit" << false << true << 200.0 << 200.0 << 200.0 << 400.0 << 100.0 << 400.0;
400     QTest::newRow("PreserveAspectFit explicit width 300, height 400") << "PreserveAspectFit" << true << true << 300.0 << 300.0 << 300.0 << 400.0 << 150.0 << 400.0;
401
402     // bounding rect and painted rect are equal with fillMode PreserveAspectCrop, item rect may be smaller if the aspect ratio doesn't match
403     QTest::newRow("PreserveAspectCrop") << "PreserveAspectCrop" << false << false << 200.0 << 200.0 << 200.0 << 100.0 << 100.0 << 100.0;
404     QTest::newRow("PreserveAspectCrop explicit width 300") << "PreserveAspectCrop" << true << false << 300.0 << 300.0 << 300.0 << 100.0 << 150.0 << 150.0;
405     QTest::newRow("PreserveAspectCrop explicit height 400") << "PreserveAspectCrop" << false << true << 200.0 << 800.0 << 800.0 << 400.0 << 400.0 << 400.0;
406     QTest::newRow("PreserveAspectCrop explicit width 300, height 400") << "PreserveAspectCrop" << true << true << 300.0 << 800.0 << 800.0 << 400.0 << 400.0 << 400.0;
407
408     // bounding rect, painted rect and item rect are equal in stretching and tiling images
409     QStringList fillModes;
410     fillModes << "Stretch" << "Tile" << "TileVertically" << "TileHorizontally";
411     foreach (QString fillMode, fillModes) {
412         QTest::newRow(fillMode.toLatin1()) << fillMode << false << false << 200.0 << 200.0 << 200.0 << 100.0 << 100.0 << 100.0;
413         QTest::newRow(QString(fillMode + " explicit width 300").toLatin1()) << fillMode << true << false << 300.0 << 300.0 << 300.0 << 100.0 << 100.0 << 100.0;
414         QTest::newRow(QString(fillMode + " explicit height 400").toLatin1()) << fillMode << false << true << 200.0 << 200.0 << 200.0 << 400.0 << 400.0 << 400.0;
415         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;
416     }
417 }
418
419 void tst_qquickimage::geometry()
420 {
421     QFETCH(QString, fillMode);
422     QFETCH(bool, explicitWidth);
423     QFETCH(bool, explicitHeight);
424     QFETCH(double, itemWidth);
425     QFETCH(double, itemHeight);
426     QFETCH(double, paintedWidth);
427     QFETCH(double, paintedHeight);
428     QFETCH(double, boundingWidth);
429     QFETCH(double, boundingHeight);
430
431     QString src = testFileUrl("rect.png").toString();
432     QString componentStr = "import QtQuick 2.0\nImage { source: \"" + src + "\"; fillMode: Image." + fillMode + "; ";
433
434     if (explicitWidth)
435         componentStr.append("width: 300; ");
436     if (explicitHeight)
437         componentStr.append("height: 400; ");
438     componentStr.append("}");
439     QQmlComponent component(&engine);
440     component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
441     QQuickImage *obj = qobject_cast<QQuickImage*>(component.create());
442     QVERIFY(obj != 0);
443
444     QCOMPARE(obj->width(), itemWidth);
445     QCOMPARE(obj->paintedWidth(), paintedWidth);
446     QCOMPARE(obj->boundingRect().width(), boundingWidth);
447
448     QCOMPARE(obj->height(), itemHeight);
449     QCOMPARE(obj->paintedHeight(), paintedHeight);
450     QCOMPARE(obj->boundingRect().height(), boundingHeight);
451     delete obj;
452 }
453
454 void tst_qquickimage::big()
455 {
456     // If the JPEG loader does not implement scaling efficiently, it would
457     // have to build a 400 MB image. That would be a bug in the JPEG loader.
458
459     QString src = testFileUrl("big.jpeg").toString();
460     QString componentStr = "import QtQuick 2.0\nImage { source: \"" + src + "\"; width: 100; sourceSize.height: 256 }";
461
462     QQmlComponent component(&engine);
463     component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
464     QQuickImage *obj = qobject_cast<QQuickImage*>(component.create());
465     QVERIFY(obj != 0);
466     QCOMPARE(obj->width(), 100.0);
467     QCOMPARE(obj->height(), 256.0);
468
469     delete obj;
470 }
471
472 void tst_qquickimage::tiling_QTBUG_6716()
473 {
474     QFETCH(QString, source);
475
476     QQuickView view(testFileUrl(source));
477     view.show();
478     view.requestActivateWindow();
479     QTest::qWaitForWindowShown(&view);
480
481     QQuickImage *tiling = findItem<QQuickImage>(view.rootObject(), "tiling");
482
483     QVERIFY(tiling != 0);
484     QImage img = view.grabFrameBuffer();
485     for (int x = 0; x < tiling->width(); ++x) {
486         for (int y = 0; y < tiling->height(); ++y) {
487             QVERIFY(img.pixel(x, y) == qRgb(0, 255, 0));
488         }
489     }
490 }
491
492 void tst_qquickimage::tiling_QTBUG_6716_data()
493 {
494     QTest::addColumn<QString>("source");
495     QTest::newRow("vertical_tiling") << "vtiling.qml";
496     QTest::newRow("horizontal_tiling") << "htiling.qml";
497 }
498
499 void tst_qquickimage::noLoading()
500 {
501     qRegisterMetaType<QQuickImageBase::Status>();
502
503     TestHTTPServer server(SERVER_PORT);
504     QVERIFY(server.isValid());
505     server.serveDirectory(dataDirectory());
506     server.addRedirect("oldcolors.png", SERVER_ADDR "/colors.png");
507
508     QString componentStr = "import QtQuick 2.0\nImage { source: srcImage; cache: true }";
509     QQmlContext *ctxt = engine.rootContext();
510     ctxt->setContextProperty("srcImage", testFileUrl("heart.png"));
511     QQmlComponent component(&engine);
512     component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
513     QQuickImage *obj = qobject_cast<QQuickImage*>(component.create());
514     QVERIFY(obj != 0);
515     QVERIFY(obj->status() == QQuickImage::Ready);
516
517     QSignalSpy sourceSpy(obj, SIGNAL(sourceChanged(const QUrl &)));
518     QSignalSpy progressSpy(obj, SIGNAL(progressChanged(qreal)));
519     QSignalSpy statusSpy(obj, SIGNAL(statusChanged(QQuickImageBase::Status)));
520
521     // Loading local file
522     ctxt->setContextProperty("srcImage", testFileUrl("green.png"));
523     QTRY_VERIFY(obj->status() == QQuickImage::Ready);
524     QTRY_VERIFY(obj->progress() == 1.0);
525     QTRY_COMPARE(sourceSpy.count(), 1);
526     QTRY_COMPARE(progressSpy.count(), 0);
527     QTRY_COMPARE(statusSpy.count(), 0);
528
529     // Loading remote file
530     ctxt->setContextProperty("srcImage", QString(SERVER_ADDR) + "/rect.png");
531     QTRY_VERIFY(obj->status() == QQuickImage::Loading);
532     QTRY_VERIFY(obj->progress() == 0.0);
533     QTRY_VERIFY(obj->status() == QQuickImage::Ready);
534     QTRY_VERIFY(obj->progress() == 1.0);
535     QTRY_COMPARE(sourceSpy.count(), 2);
536     QTRY_COMPARE(progressSpy.count(), 2);
537     QTRY_COMPARE(statusSpy.count(), 2);
538
539     // Loading remote file again - should not go through 'Loading' state.
540     ctxt->setContextProperty("srcImage", testFileUrl("green.png"));
541     ctxt->setContextProperty("srcImage", QString(SERVER_ADDR) + "/rect.png");
542     QTRY_VERIFY(obj->status() == QQuickImage::Ready);
543     QTRY_VERIFY(obj->progress() == 1.0);
544     QTRY_COMPARE(sourceSpy.count(), 4);
545     QTRY_COMPARE(progressSpy.count(), 2);
546     QTRY_COMPARE(statusSpy.count(), 2);
547
548     delete obj;
549 }
550
551 void tst_qquickimage::paintedWidthHeight()
552 {
553     {
554         QString src = testFileUrl("heart.png").toString();
555         QString componentStr = "import QtQuick 2.0\nImage { source: \"" + src + "\"; width: 200; height: 25; fillMode: Image.PreserveAspectFit }";
556
557         QQmlComponent component(&engine);
558         component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
559         QQuickImage *obj = qobject_cast<QQuickImage*>(component.create());
560         QVERIFY(obj != 0);
561         QCOMPARE(obj->width(), 200.0);
562         QCOMPARE(obj->height(), 25.0);
563         QCOMPARE(obj->paintedWidth(), 25.0);
564         QCOMPARE(obj->paintedHeight(), 25.0);
565
566         delete obj;
567     }
568
569     {
570         QString src = testFileUrl("heart.png").toString();
571         QString componentStr = "import QtQuick 2.0\nImage { source: \"" + src + "\"; width: 26; height: 175; fillMode: Image.PreserveAspectFit }";
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(), 26.0);
577         QCOMPARE(obj->height(), 175.0);
578         QCOMPARE(obj->paintedWidth(), 26.0);
579         QCOMPARE(obj->paintedHeight(), 26.0);
580
581         delete obj;
582     }
583 }
584
585 void tst_qquickimage::sourceSize_QTBUG_14303()
586 {
587     QString componentStr = "import QtQuick 2.0\nImage { source: srcImage }";
588     QQmlContext *ctxt = engine.rootContext();
589     ctxt->setContextProperty("srcImage", testFileUrl("heart200.png"));
590     QQmlComponent component(&engine);
591     component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
592     QQuickImage *obj = qobject_cast<QQuickImage*>(component.create());
593
594     QSignalSpy sourceSizeSpy(obj, SIGNAL(sourceSizeChanged()));
595
596     QTRY_VERIFY(obj != 0);
597     QTRY_VERIFY(obj->status() == QQuickImage::Ready);
598
599     QTRY_COMPARE(obj->sourceSize().width(), 200);
600     QTRY_COMPARE(obj->sourceSize().height(), 200);
601     QTRY_COMPARE(sourceSizeSpy.count(), 0);
602
603     ctxt->setContextProperty("srcImage", testFileUrl("colors.png"));
604     QTRY_COMPARE(obj->sourceSize().width(), 120);
605     QTRY_COMPARE(obj->sourceSize().height(), 120);
606     QTRY_COMPARE(sourceSizeSpy.count(), 1);
607
608     ctxt->setContextProperty("srcImage", testFileUrl("heart200.png"));
609     QTRY_COMPARE(obj->sourceSize().width(), 200);
610     QTRY_COMPARE(obj->sourceSize().height(), 200);
611     QTRY_COMPARE(sourceSizeSpy.count(), 2);
612
613     delete obj;
614 }
615
616 void tst_qquickimage::sourceSize_QTBUG_16389()
617 {
618     QQuickView *canvas = new QQuickView(0);
619     canvas->setSource(testFileUrl("qtbug_16389.qml"));
620     canvas->show();
621     qApp->processEvents();
622
623     QQuickImage *image = findItem<QQuickImage>(canvas->rootObject(), "iconImage");
624     QQuickItem *handle = findItem<QQuickItem>(canvas->rootObject(), "blueHandle");
625
626     QCOMPARE(image->sourceSize().width(), 200);
627     QCOMPARE(image->sourceSize().height(), 200);
628     QCOMPARE(image->paintedWidth(), 0.0);
629     QCOMPARE(image->paintedHeight(), 0.0);
630
631     handle->setY(20);
632
633     QCOMPARE(image->sourceSize().width(), 200);
634     QCOMPARE(image->sourceSize().height(), 200);
635     QCOMPARE(image->paintedWidth(), 20.0);
636     QCOMPARE(image->paintedHeight(), 20.0);
637
638     delete canvas;
639 }
640
641 static int numberOfWarnings = 0;
642 static void checkWarnings(QtMsgType, const char *msg)
643 {
644     if (!QString(msg).contains("QGLContext::makeCurrent(): Failed."))
645         numberOfWarnings++;
646 }
647
648 // QTBUG-15690
649 void tst_qquickimage::nullPixmapPaint()
650 {
651     QQuickView *canvas = new QQuickView(0);
652     canvas->setSource(testFileUrl("nullpixmap.qml"));
653     canvas->show();
654
655     QQuickImage *image = qobject_cast<QQuickImage*>(canvas->rootObject());
656     QTRY_VERIFY(image != 0);
657     image->setSource(SERVER_ADDR + QString("/no-such-file.png"));
658
659     QtMsgHandler previousMsgHandler = qInstallMsgHandler(checkWarnings);
660
661     // used to print "QTransform::translate with NaN called"
662     QPixmap pm = QPixmap::fromImage(canvas->grabFrameBuffer());
663     qInstallMsgHandler(previousMsgHandler);
664     QVERIFY(numberOfWarnings == 0);
665     delete image;
666
667     delete canvas;
668 }
669
670 void tst_qquickimage::imageCrash_QTBUG_22125()
671 {
672     TestHTTPServer server(SERVER_PORT);
673     QVERIFY(server.isValid());
674     server.serveDirectory(dataDirectory(), TestHTTPServer::Delay);
675
676     {
677         QQuickView view(testFileUrl("qtbug_22125.qml"));
678         view.show();
679         qApp->processEvents();
680         qApp->processEvents();
681         // shouldn't crash when the view drops out of scope due to
682         // QQuickPixmapData attempting to dereference a pointer to
683         // the destroyed reader.
684     }
685
686     // shouldn't crash when deleting cancelled QQmlPixmapReplys.
687     QTest::qWait(520); // Delay mode delays for 500 ms.
688     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
689     QCoreApplication::processEvents();
690 }
691
692 void tst_qquickimage::sourceSize_data()
693 {
694     QTest::addColumn<int>("sourceWidth");
695     QTest::addColumn<int>("sourceHeight");
696     QTest::addColumn<qreal>("implicitWidth");
697     QTest::addColumn<qreal>("implicitHeight");
698
699     QTest::newRow("unscaled") << 0 << 0 << 300.0 << 300.0;
700     QTest::newRow("scale width") << 100 << 0 << 100.0 << 100.0;
701     QTest::newRow("scale height") << 0 << 150 << 150.0 << 150.0;
702     QTest::newRow("larger sourceSize") << 400 << 400 << 300.0 << 300.0;
703 }
704
705 void tst_qquickimage::sourceSize()
706 {
707     QFETCH(int, sourceWidth);
708     QFETCH(int, sourceHeight);
709     QFETCH(qreal, implicitWidth);
710     QFETCH(qreal, implicitHeight);
711
712     QQuickView *canvas = new QQuickView(0);
713     QQmlContext *ctxt = canvas->rootContext();
714     ctxt->setContextProperty("srcWidth", sourceWidth);
715     ctxt->setContextProperty("srcHeight", sourceHeight);
716
717     canvas->setSource(testFileUrl("sourceSize.qml"));
718     canvas->show();
719     qApp->processEvents();
720
721     QQuickImage *image = qobject_cast<QQuickImage*>(canvas->rootObject());
722     QVERIFY(image);
723
724     QCOMPARE(image->sourceSize().width(), sourceWidth);
725     QCOMPARE(image->sourceSize().height(), sourceHeight);
726     QCOMPARE(image->implicitWidth(), implicitWidth);
727     QCOMPARE(image->implicitHeight(), implicitHeight);
728
729     delete canvas;
730 }
731
732 QTEST_MAIN(tst_qquickimage)
733
734 #include "tst_qquickimage.moc"