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