1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28#include <qtest.h>
29#include <QTextDocument>
30#include <QTcpServer>
31#include <QTcpSocket>
32#include <QDir>
33#include <QPainter>
34#include <QSignalSpy>
35
36#include <QtQml/qqmlengine.h>
37#include <QtQml/qqmlcomponent.h>
38#include <private/qquickborderimage_p.h>
39#include <private/qquickimagebase_p.h>
40#include <private/qquickscalegrid_p_p.h>
41#include <private/qquickloader_p.h>
42#include <QtQuick/qquickview.h>
43#include <QtQml/qqmlcontext.h>
44
45#include "../../shared/testhttpserver.h"
46#include "../../shared/util.h"
47#include "../shared/visualtestutil.h"
48
49Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests")
50
51Q_DECLARE_METATYPE(QQuickImageBase::Status)
52
53class tst_qquickborderimage : public QQmlDataTest
54
55{
56 Q_OBJECT
57public:
58 tst_qquickborderimage();
59
60private slots:
61 void cleanup();
62 void noSource();
63 void imageSource();
64 void imageSource_data();
65 void clearSource();
66 void resized();
67 void smooth();
68 void mirror();
69 void tileModes();
70 void sciSource();
71 void sciSource_data();
72 void invalidSciFile();
73 void validSciFiles_data();
74 void validSciFiles();
75 void pendingRemoteRequest();
76 void pendingRemoteRequest_data();
77 void statusChanges();
78 void statusChanges_data();
79 void sourceSizeChanges();
80 void progressAndStatusChanges();
81#if QT_CONFIG(opengl)
82 void borderImageMesh();
83#endif
84 void multiFrame_data();
85 void multiFrame();
86
87private:
88 QQmlEngine engine;
89};
90
91void tst_qquickborderimage::cleanup()
92{
93 QQuickWindow window;
94 window.releaseResources();
95 engine.clearComponentCache();
96}
97
98tst_qquickborderimage::tst_qquickborderimage()
99{
100}
101
102void tst_qquickborderimage::noSource()
103{
104 QString componentStr = "import QtQuick 2.0\nBorderImage { source: \"\" }";
105 QQmlComponent component(&engine);
106 component.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: ""));
107 QQuickBorderImage *obj = qobject_cast<QQuickBorderImage*>(object: component.create());
108 QVERIFY(obj != nullptr);
109 QCOMPARE(obj->source(), QUrl());
110 QCOMPARE(obj->width(), 0.);
111 QCOMPARE(obj->height(), 0.);
112 QCOMPARE(obj->horizontalTileMode(), QQuickBorderImage::Stretch);
113 QCOMPARE(obj->verticalTileMode(), QQuickBorderImage::Stretch);
114
115 delete obj;
116}
117
118void tst_qquickborderimage::imageSource_data()
119{
120 QTest::addColumn<QString>(name: "source");
121 QTest::addColumn<bool>(name: "remote");
122 QTest::addColumn<QString>(name: "error");
123
124 QTest::newRow(dataTag: "local") << testFileUrl(fileName: "colors.png").toString() << false << "";
125 QTest::newRow(dataTag: "local not found") << testFileUrl(fileName: "no-such-file.png").toString() << false
126 << "<Unknown File>:2:1: QML BorderImage: Cannot open: " + testFileUrl(fileName: "no-such-file.png").toString();
127 QTest::newRow(dataTag: "remote") << "/colors.png" << true << "";
128 QTest::newRow(dataTag: "remote not found") << "/no-such-file.png" << true
129 << "<Unknown File>:2:1: QML BorderImage: Error transferring {{ServerBaseUrl}}/no-such-file.png - server replied: Not found";
130}
131
132void tst_qquickborderimage::imageSource()
133{
134 QFETCH(QString, source);
135 QFETCH(bool, remote);
136 QFETCH(QString, error);
137
138#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
139 if (qstrcmp(str1: QTest::currentDataTag(), str2: "remote") == 0
140 || qstrcmp(str1: QTest::currentDataTag(), str2: "remote not found") == 0) {
141 QSKIP("Remote tests cause occasional hangs in the CI system -- QTBUG-45655");
142 }
143#endif
144
145 TestHTTPServer server;
146 if (remote) {
147 QVERIFY2(server.listen(), qPrintable(server.errorString()));
148 server.serveDirectory(dataDirectory());
149 source = server.urlString(documentPath: source);
150 error.replace(QStringLiteral("{{ServerBaseUrl}}"), after: server.baseUrl().toString());
151 }
152
153 if (!error.isEmpty())
154 QTest::ignoreMessage(type: QtWarningMsg, message: error.toUtf8());
155
156 QString componentStr = "import QtQuick 2.0\nBorderImage { source: \"" + source + "\" }";
157 QQmlComponent component(&engine);
158 component.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: ""));
159 QQuickBorderImage *obj = qobject_cast<QQuickBorderImage*>(object: component.create());
160 QVERIFY(obj != nullptr);
161
162 if (remote)
163 QTRY_COMPARE(obj->status(), QQuickBorderImage::Loading);
164
165 QCOMPARE(obj->source(), remote ? source : QUrl(source));
166
167 if (error.isEmpty()) {
168 QTRY_COMPARE(obj->status(), QQuickBorderImage::Ready);
169 QCOMPARE(obj->width(), 120.);
170 QCOMPARE(obj->height(), 120.);
171 QCOMPARE(obj->sourceSize().width(), 120);
172 QCOMPARE(obj->sourceSize().height(), 120);
173 QCOMPARE(obj->horizontalTileMode(), QQuickBorderImage::Stretch);
174 QCOMPARE(obj->verticalTileMode(), QQuickBorderImage::Stretch);
175 } else {
176 QTRY_COMPARE(obj->status(), QQuickBorderImage::Error);
177 }
178
179 delete obj;
180}
181
182void tst_qquickborderimage::clearSource()
183{
184 QString componentStr = "import QtQuick 2.0\nBorderImage { source: srcImage }";
185 QQmlContext *ctxt = engine.rootContext();
186 ctxt->setContextProperty("srcImage", testFileUrl(fileName: "colors.png"));
187 QQmlComponent component(&engine);
188 component.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: ""));
189 QQuickBorderImage *obj = qobject_cast<QQuickBorderImage*>(object: component.create());
190 QVERIFY(obj != nullptr);
191 QCOMPARE(obj->status(), QQuickBorderImage::Ready);
192 QCOMPARE(obj->width(), 120.);
193 QCOMPARE(obj->height(), 120.);
194
195 ctxt->setContextProperty("srcImage", "");
196 QVERIFY(obj->source().isEmpty());
197 QCOMPARE(obj->status(), QQuickBorderImage::Null);
198 QCOMPARE(obj->width(), 0.);
199 QCOMPARE(obj->height(), 0.);
200
201 delete obj;
202}
203
204void tst_qquickborderimage::resized()
205{
206 QString componentStr = "import QtQuick 2.0\nBorderImage { source: \"" + testFileUrl(fileName: "colors.png").toString() + "\"; width: 300; height: 300 }";
207 QQmlComponent component(&engine);
208 component.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: ""));
209 QQuickBorderImage *obj = qobject_cast<QQuickBorderImage*>(object: component.create());
210 QVERIFY(obj != nullptr);
211 QCOMPARE(obj->width(), 300.);
212 QCOMPARE(obj->height(), 300.);
213 QCOMPARE(obj->sourceSize().width(), 120);
214 QCOMPARE(obj->sourceSize().height(), 120);
215 QCOMPARE(obj->horizontalTileMode(), QQuickBorderImage::Stretch);
216 QCOMPARE(obj->verticalTileMode(), QQuickBorderImage::Stretch);
217
218 delete obj;
219}
220
221void tst_qquickborderimage::smooth()
222{
223 QString componentStr = "import QtQuick 2.0\nBorderImage { source: \"" + testFile(fileName: "colors.png") + "\"; smooth: true; width: 300; height: 300 }";
224 QQmlComponent component(&engine);
225 component.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: ""));
226 QQuickBorderImage *obj = qobject_cast<QQuickBorderImage*>(object: component.create());
227 QVERIFY(obj != nullptr);
228 QCOMPARE(obj->width(), 300.);
229 QCOMPARE(obj->height(), 300.);
230 QCOMPARE(obj->smooth(), true);
231 QCOMPARE(obj->horizontalTileMode(), QQuickBorderImage::Stretch);
232 QCOMPARE(obj->verticalTileMode(), QQuickBorderImage::Stretch);
233
234 delete obj;
235}
236
237void tst_qquickborderimage::mirror()
238{
239 QQuickView *window = new QQuickView;
240 window->setSource(testFileUrl(fileName: "mirror.qml"));
241 QQuickBorderImage *image = qobject_cast<QQuickBorderImage*>(object: window->rootObject());
242 QVERIFY(image != nullptr);
243
244 QImage screenshot = window->grabWindow();
245
246 QImage srcPixmap(screenshot);
247 QTransform transform;
248 transform.translate(dx: image->width(), dy: 0).scale(sx: -1, sy: 1.0);
249 srcPixmap = srcPixmap.transformed(matrix: transform);
250
251 image->setProperty(name: "mirror", value: true);
252 screenshot = window->grabWindow();
253
254 window->show();
255 QVERIFY(QTest::qWaitForWindowExposed(window));
256 if (window->rendererInterface()->graphicsApi() == QSGRendererInterface::Software)
257 QSKIP("QTBUG-53823");
258 QCOMPARE(screenshot, srcPixmap);
259
260 delete window;
261}
262
263void tst_qquickborderimage::tileModes()
264{
265 {
266 QString componentStr = "import QtQuick 2.0\nBorderImage { source: \"" + testFile(fileName: "colors.png") + "\"; width: 100; height: 300; horizontalTileMode: BorderImage.Repeat; verticalTileMode: BorderImage.Repeat }";
267 QQmlComponent component(&engine);
268 component.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: ""));
269 QQuickBorderImage *obj = qobject_cast<QQuickBorderImage*>(object: component.create());
270 QVERIFY(obj != nullptr);
271 QCOMPARE(obj->width(), 100.);
272 QCOMPARE(obj->height(), 300.);
273 QCOMPARE(obj->horizontalTileMode(), QQuickBorderImage::Repeat);
274 QCOMPARE(obj->verticalTileMode(), QQuickBorderImage::Repeat);
275
276 delete obj;
277 }
278 {
279 QString componentStr = "import QtQuick 2.0\nBorderImage { source: \"" + testFile(fileName: "colors.png") + "\"; width: 300; height: 150; horizontalTileMode: BorderImage.Round; verticalTileMode: BorderImage.Round }";
280 QQmlComponent component(&engine);
281 component.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: ""));
282 QQuickBorderImage *obj = qobject_cast<QQuickBorderImage*>(object: component.create());
283 QVERIFY(obj != nullptr);
284 QCOMPARE(obj->width(), 300.);
285 QCOMPARE(obj->height(), 150.);
286 QCOMPARE(obj->horizontalTileMode(), QQuickBorderImage::Round);
287 QCOMPARE(obj->verticalTileMode(), QQuickBorderImage::Round);
288
289 delete obj;
290 }
291}
292
293void tst_qquickborderimage::sciSource()
294{
295 QFETCH(QString, source);
296 QFETCH(bool, valid);
297 QFETCH(bool, remote);
298
299 TestHTTPServer server;
300 if (remote) {
301 QVERIFY2(server.listen(), qPrintable(server.errorString()));
302 server.serveDirectory(dataDirectory());
303 source = server.urlString(documentPath: source);
304 server.registerFileNameForContentSubstitution(fileName: QUrl(source).path());
305 }
306
307 QString componentStr = "import QtQuick 2.0\nBorderImage { source: \"" + source + "\"; width: 300; height: 300 }";
308 QQmlComponent component(&engine);
309 component.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: ""));
310 QQuickBorderImage *obj = qobject_cast<QQuickBorderImage*>(object: component.create());
311 QVERIFY(obj != nullptr);
312
313 if (remote)
314 QTRY_COMPARE(obj->status(), QQuickBorderImage::Loading);
315
316 QCOMPARE(obj->source(), remote ? source : QUrl(source));
317 QCOMPARE(obj->width(), 300.);
318 QCOMPARE(obj->height(), 300.);
319
320 if (valid) {
321 QTRY_COMPARE(obj->status(), QQuickBorderImage::Ready);
322 QCOMPARE(obj->border()->left(), 10);
323 QCOMPARE(obj->border()->top(), 20);
324 QCOMPARE(obj->border()->right(), 30);
325 QCOMPARE(obj->border()->bottom(), 40);
326 QCOMPARE(obj->horizontalTileMode(), QQuickBorderImage::Round);
327 QCOMPARE(obj->verticalTileMode(), QQuickBorderImage::Repeat);
328 } else {
329 QTRY_COMPARE(obj->status(), QQuickBorderImage::Error);
330 }
331
332 delete obj;
333}
334
335void tst_qquickborderimage::sciSource_data()
336{
337 QTest::addColumn<QString>(name: "source");
338 QTest::addColumn<bool>(name: "valid");
339 QTest::addColumn<bool>(name: "remote");
340
341 QTest::newRow(dataTag: "local") << testFileUrl(fileName: "colors-round.sci").toString() << true << /*remote*/false;
342 QTest::newRow(dataTag: "local quoted filename") << testFileUrl(fileName: "colors-round-quotes.sci").toString() << true << /*remote*/false;
343 QTest::newRow(dataTag: "local not found") << testFileUrl(fileName: "no-such-file.sci").toString() << false << /*remote*/false;
344 QTest::newRow(dataTag: "remote") << "/colors-round.sci" << true << /*remote*/true;
345 QTest::newRow(dataTag: "remote filename quoted") << "/colors-round-quotes.sci" << true << /*remote*/true;
346 QTest::newRow(dataTag: "remote image") << "/colors-round-remote.sci" << true << /*remote*/true;
347 QTest::newRow(dataTag: "remote not found") << "/no-such-file.sci" << false << /*remote*/true;
348}
349
350void tst_qquickborderimage::invalidSciFile()
351{
352 QTest::ignoreMessage(type: QtWarningMsg, message: "QQuickGridScaledImage: Invalid tile rule specified. Using Stretch."); // for "Roun"
353 QTest::ignoreMessage(type: QtWarningMsg, message: "QQuickGridScaledImage: Invalid tile rule specified. Using Stretch."); // for "Repea"
354
355 QString componentStr = "import QtQuick 2.0\nBorderImage { source: \"" + testFileUrl(fileName: "invalid.sci").toString() +"\"; width: 300; height: 300 }";
356 QQmlComponent component(&engine);
357 component.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: ""));
358 QQuickBorderImage *obj = qobject_cast<QQuickBorderImage*>(object: component.create());
359 QVERIFY(obj != nullptr);
360 QCOMPARE(obj->width(), 300.);
361 QCOMPARE(obj->height(), 300.);
362 QCOMPARE(obj->status(), QQuickImageBase::Error);
363 QCOMPARE(obj->horizontalTileMode(), QQuickBorderImage::Stretch);
364 QCOMPARE(obj->verticalTileMode(), QQuickBorderImage::Stretch);
365
366 delete obj;
367}
368
369void tst_qquickborderimage::validSciFiles_data()
370{
371 QTest::addColumn<QString>(name: "source");
372
373 QTest::newRow(dataTag: "valid1") << testFileUrl(fileName: "valid1.sci").toString();
374 QTest::newRow(dataTag: "valid2") << testFileUrl(fileName: "valid2.sci").toString();
375 QTest::newRow(dataTag: "valid3") << testFileUrl(fileName: "valid3.sci").toString();
376 QTest::newRow(dataTag: "valid4") << testFileUrl(fileName: "valid4.sci").toString();
377}
378
379void tst_qquickborderimage::validSciFiles()
380{
381 QFETCH(QString, source);
382
383 QString componentStr = "import QtQuick 2.0\nBorderImage { source: \"" + source +"\"; width: 300; height: 300 }";
384 QQmlComponent component(&engine);
385 component.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: ""));
386 QQuickBorderImage *obj = qobject_cast<QQuickBorderImage*>(object: component.create());
387 QVERIFY(obj != nullptr);
388 QCOMPARE(obj->width(), 300.);
389 QCOMPARE(obj->height(), 300.);
390 QCOMPARE(obj->horizontalTileMode(), QQuickBorderImage::Round);
391 QCOMPARE(obj->verticalTileMode(), QQuickBorderImage::Repeat);
392
393 delete obj;
394}
395
396void tst_qquickborderimage::pendingRemoteRequest()
397{
398 QFETCH(QString, source);
399
400 QString componentStr = "import QtQuick 2.0\nBorderImage { source: \"" + source + "\" }";
401 QQmlComponent component(&engine);
402 component.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: ""));
403 QQuickBorderImage *obj = qobject_cast<QQuickBorderImage*>(object: component.create());
404 QVERIFY(obj != nullptr);
405 QCOMPARE(obj->status(), QQuickBorderImage::Loading);
406
407 // verify no crash
408 // This will cause a delayed "QThread: Destroyed while thread is still running" warning
409 delete obj;
410 QTest::qWait(ms: 50);
411}
412
413void tst_qquickborderimage::pendingRemoteRequest_data()
414{
415 QTest::addColumn<QString>(name: "source");
416
417 QTest::newRow(dataTag: "png file") << "http://localhost/none.png";
418 QTest::newRow(dataTag: "sci file") << "http://localhost/none.sci";
419}
420
421//QTBUG-26155
422void tst_qquickborderimage::statusChanges_data()
423{
424 QTest::addColumn<QString>(name: "source");
425 QTest::addColumn<int>(name: "emissions");
426 QTest::addColumn<bool>(name: "remote");
427 QTest::addColumn<QQuickImageBase::Status>(name: "finalStatus");
428
429 QTest::newRow(dataTag: "localfile") << testFileUrl(fileName: "colors.png").toString() << 1 << false << QQuickImageBase::Ready;
430 QTest::newRow(dataTag: "nofile") << "" << 0 << false << QQuickImageBase::Null;
431 QTest::newRow(dataTag: "nonexistent") << testFileUrl(fileName: "thisfiledoesnotexist.png").toString() << 1 << false << QQuickImageBase::Error;
432 QTest::newRow(dataTag: "noprotocol") << QString("thisfiledoesnotexisteither.png") << 2 << false << QQuickImageBase::Error;
433 QTest::newRow(dataTag: "remote") << "/colors.png" << 2 << true << QQuickImageBase::Ready;
434}
435
436void tst_qquickborderimage::statusChanges()
437{
438 QFETCH(QString, source);
439 QFETCH(int, emissions);
440 QFETCH(bool, remote);
441 QFETCH(QQuickImageBase::Status, finalStatus);
442
443 TestHTTPServer server;
444 if (remote) {
445 QVERIFY2(server.listen(), qPrintable(server.errorString()));
446 server.serveDirectory(dataDirectory());
447 source = server.urlString(documentPath: source);
448 }
449
450 QString componentStr = "import QtQuick 2.0\nBorderImage { width: 300; height: 300 }";
451 QQmlComponent component(&engine);
452 component.setData(componentStr.toLatin1(), baseUrl: QUrl(""));
453
454 QQuickBorderImage *obj = qobject_cast<QQuickBorderImage*>(object: component.create());
455 qRegisterMetaType<QQuickImageBase::Status>();
456 QSignalSpy spy(obj, SIGNAL(statusChanged(QQuickImageBase::Status)));
457 QVERIFY(obj != nullptr);
458 obj->setSource(source);
459 if (remote)
460 server.sendDelayedItem();
461 QTRY_COMPARE(obj->status(), finalStatus);
462 QCOMPARE(spy.count(), emissions);
463
464 delete obj;
465}
466
467void tst_qquickborderimage::sourceSizeChanges()
468{
469 TestHTTPServer server;
470 QVERIFY2(server.listen(), qPrintable(server.errorString()));
471 server.serveDirectory(dataDirectory());
472
473 QQmlEngine engine;
474 QQmlComponent component(&engine);
475 component.setData("import QtQuick 2.0\nBorderImage { source: srcImage }", baseUrl: QUrl::fromLocalFile(localfile: ""));
476 QTRY_VERIFY(component.isReady());
477 QQmlContext *ctxt = engine.rootContext();
478 ctxt->setContextProperty("srcImage", "");
479 QQuickBorderImage *obj = qobject_cast<QQuickBorderImage*>(object: component.create());
480 QVERIFY(obj != nullptr);
481
482 QSignalSpy sourceSizeSpy(obj, SIGNAL(sourceSizeChanged()));
483
484 // Local
485 ctxt->setContextProperty("srcImage", QUrl(""));
486 QTRY_COMPARE(obj->status(), QQuickBorderImage::Null);
487 QTRY_COMPARE(sourceSizeSpy.count(), 0);
488
489 ctxt->setContextProperty("srcImage", testFileUrl(fileName: "heart200.png"));
490 QTRY_COMPARE(obj->status(), QQuickBorderImage::Ready);
491 QTRY_COMPARE(sourceSizeSpy.count(), 1);
492
493 ctxt->setContextProperty("srcImage", testFileUrl(fileName: "heart200.png"));
494 QTRY_COMPARE(obj->status(), QQuickBorderImage::Ready);
495 QTRY_COMPARE(sourceSizeSpy.count(), 1);
496
497 ctxt->setContextProperty("srcImage", testFileUrl(fileName: "heart200_copy.png"));
498 QTRY_COMPARE(obj->status(), QQuickBorderImage::Ready);
499 QTRY_COMPARE(sourceSizeSpy.count(), 1);
500
501 ctxt->setContextProperty("srcImage", testFileUrl(fileName: "colors.png"));
502 QTRY_COMPARE(obj->status(), QQuickBorderImage::Ready);
503 QTRY_COMPARE(sourceSizeSpy.count(), 2);
504
505 ctxt->setContextProperty("srcImage", QUrl(""));
506 QTRY_COMPARE(obj->status(), QQuickBorderImage::Null);
507 QTRY_COMPARE(sourceSizeSpy.count(), 3);
508
509 // Remote
510 ctxt->setContextProperty("srcImage", server.url(documentPath: "/heart200.png"));
511 QTRY_COMPARE(obj->status(), QQuickBorderImage::Ready);
512 QTRY_COMPARE(sourceSizeSpy.count(), 4);
513
514 ctxt->setContextProperty("srcImage", server.url(documentPath: "/heart200.png"));
515 QTRY_COMPARE(obj->status(), QQuickBorderImage::Ready);
516 QTRY_COMPARE(sourceSizeSpy.count(), 4);
517
518 ctxt->setContextProperty("srcImage", server.url(documentPath: "/heart200_copy.png"));
519 QTRY_COMPARE(obj->status(), QQuickBorderImage::Ready);
520 QTRY_COMPARE(sourceSizeSpy.count(), 4);
521
522 ctxt->setContextProperty("srcImage", server.url(documentPath: "/colors.png"));
523 QTRY_COMPARE(obj->status(), QQuickBorderImage::Ready);
524 QTRY_COMPARE(sourceSizeSpy.count(), 5);
525
526 ctxt->setContextProperty("srcImage", QUrl(""));
527 QTRY_COMPARE(obj->status(), QQuickBorderImage::Null);
528 QTRY_COMPARE(sourceSizeSpy.count(), 6);
529
530 delete obj;
531}
532
533void tst_qquickborderimage::progressAndStatusChanges()
534{
535 TestHTTPServer server;
536 QVERIFY2(server.listen(), qPrintable(server.errorString()));
537 server.serveDirectory(dataDirectory());
538
539 QQmlEngine engine;
540 QString componentStr = "import QtQuick 2.0\nBorderImage { source: srcImage }";
541 QQmlContext *ctxt = engine.rootContext();
542 ctxt->setContextProperty("srcImage", testFileUrl(fileName: "heart200.png"));
543 QQmlComponent component(&engine);
544 component.setData(componentStr.toLatin1(), baseUrl: QUrl::fromLocalFile(localfile: ""));
545 QQuickBorderImage *obj = qobject_cast<QQuickBorderImage*>(object: component.create());
546 QVERIFY(obj != nullptr);
547 QCOMPARE(obj->status(), QQuickBorderImage::Ready);
548 QTRY_COMPARE(obj->progress(), 1.0);
549
550 qRegisterMetaType<QQuickBorderImage::Status>();
551 QSignalSpy sourceSpy(obj, SIGNAL(sourceChanged(QUrl)));
552 QSignalSpy progressSpy(obj, SIGNAL(progressChanged(qreal)));
553 QSignalSpy statusSpy(obj, SIGNAL(statusChanged(QQuickImageBase::Status)));
554
555 // Same file
556 ctxt->setContextProperty("srcImage", testFileUrl(fileName: "heart200.png"));
557 QTRY_COMPARE(obj->status(), QQuickBorderImage::Ready);
558 QTRY_COMPARE(obj->progress(), 1.0);
559 QTRY_COMPARE(sourceSpy.count(), 0);
560 QTRY_COMPARE(progressSpy.count(), 0);
561 QTRY_COMPARE(statusSpy.count(), 0);
562
563 // Loading local file
564 ctxt->setContextProperty("srcImage", testFileUrl(fileName: "colors.png"));
565 QTRY_COMPARE(obj->status(), QQuickBorderImage::Ready);
566 QTRY_COMPARE(obj->progress(), 1.0);
567 QTRY_COMPARE(sourceSpy.count(), 1);
568 QTRY_COMPARE(progressSpy.count(), 0);
569 QTRY_COMPARE(statusSpy.count(), 1);
570
571 // Loading remote file
572 ctxt->setContextProperty("srcImage", server.url(documentPath: "/heart200.png"));
573 QTRY_COMPARE(obj->status(), QQuickBorderImage::Loading);
574 QTRY_COMPARE(obj->progress(), 0.0);
575 QTRY_COMPARE(obj->status(), QQuickBorderImage::Ready);
576 QTRY_COMPARE(obj->progress(), 1.0);
577 QTRY_COMPARE(sourceSpy.count(), 2);
578 QTRY_VERIFY(progressSpy.count() > 1);
579 QTRY_COMPARE(statusSpy.count(), 3);
580
581 ctxt->setContextProperty("srcImage", "");
582 QTRY_COMPARE(obj->status(), QQuickBorderImage::Null);
583 QTRY_COMPARE(obj->progress(), 0.0);
584 QTRY_COMPARE(sourceSpy.count(), 3);
585 QTRY_VERIFY(progressSpy.count() > 2);
586 QTRY_COMPARE(statusSpy.count(), 4);
587
588 delete obj;
589}
590#if QT_CONFIG(opengl)
591void tst_qquickborderimage::borderImageMesh()
592{
593 QQuickView *window = new QQuickView;
594
595 window->setSource(testFileUrl(fileName: "nonmesh.qml"));
596 window->show();
597 QVERIFY(QTest::qWaitForWindowExposed(window));
598 QImage nonmesh = window->grabWindow();
599
600 window->setSource(testFileUrl(fileName: "mesh.qml"));
601 QImage mesh = window->grabWindow();
602
603 QString errorMessage;
604 QVERIFY2(QQuickVisualTestUtil::compareImages(mesh, nonmesh, &errorMessage),
605 qPrintable(errorMessage));
606}
607#endif
608
609void tst_qquickborderimage::multiFrame_data()
610{
611 QTest::addColumn<QString>(name: "qmlfile");
612 QTest::addColumn<bool>(name: "asynchronous");
613
614 QTest::addRow(format: "default") << "multiframe.qml" << false;
615 QTest::addRow(format: "async") << "multiframeAsync.qml" << true;
616}
617
618void tst_qquickborderimage::multiFrame()
619{
620 if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
621 || (QGuiApplication::platformName() == QLatin1String("minimal")))
622 QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms");
623
624 QFETCH(QString, qmlfile);
625 QFETCH(bool, asynchronous);
626 Q_UNUSED(asynchronous)
627
628 QQuickView view(testFileUrl(fileName: qmlfile));
629 QQuickBorderImage *image = qobject_cast<QQuickBorderImage*>(object: view.rootObject());
630 QVERIFY(image);
631 QSignalSpy countSpy(image, SIGNAL(frameCountChanged()));
632 QSignalSpy currentSpy(image, SIGNAL(currentFrameChanged()));
633 if (asynchronous) {
634 QCOMPARE(image->frameCount(), 0);
635 QTRY_COMPARE(image->frameCount(), 4);
636 QCOMPARE(countSpy.count(), 1);
637 } else {
638 QCOMPARE(image->frameCount(), 4);
639 }
640 QCOMPARE(image->currentFrame(), 0);
641 view.show();
642 QVERIFY(QTest::qWaitForWindowExposed(&view));
643 QCoreApplication::processEvents(); // Process all queued events
644
645 QImage contents = view.grabWindow();
646 if (contents.width() < 160)
647 QSKIP("Skipping due to grabWindow not functional");
648
649 // The middle of the first frame looks blue, approximately qRgba(0x43, 0x7e, 0xd6, 0xff)
650 QColor color = contents.pixelColor(x: 60, y: 60);
651 qCDebug(lcTests) << "expected bluish color, got" << color;
652 QVERIFY(color.redF() < 0.75);
653 QVERIFY(color.greenF() < 0.75);
654 QVERIFY(color.blueF() > 0.75);
655
656 image->setCurrentFrame(1);
657 QTRY_COMPARE(image->status(), QQuickImageBase::Ready);
658 QCOMPARE(currentSpy.count(), 1);
659 QCOMPARE(image->currentFrame(), 1);
660 contents = view.grabWindow();
661 // The middle of the second frame looks green, approximately qRgba(0x3a, 0xd2, 0x31, 0xff)
662 color = contents.pixelColor(x: 60, y: 60);
663 qCDebug(lcTests) << "expected greenish color, got" << color;
664 QVERIFY(color.redF() < 0.75);
665 QVERIFY(color.green() > 0.75);
666 QVERIFY(color.blueF() < 0.75);
667}
668
669QTEST_MAIN(tst_qquickborderimage)
670
671#include "tst_qquickborderimage.moc"
672

source code of qtdeclarative/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp