1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the test suite of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
45 #include <QtQml/qqmlengine.h>
46 #include <QtQml/qqmlcomponent.h>
47 #include <QtQml/qqmlincubator.h>
48 #include <private/qquickloader_p.h>
49 #include "testhttpserver.h"
50 #include "../../shared/util.h"
52 #define SERVER_PORT 14450
54 class PeriodicIncubationController : public QObject,
55 public QQmlIncubationController
58 PeriodicIncubationController() {
66 virtual void timerEvent(QTimerEvent *) {
70 virtual void incubatingObjectCountChanged(int count) {
76 class tst_QQuickLoader : public QQmlDataTest
84 void sourceOrComponent();
85 void sourceOrComponent_data();
87 void urlToComponent();
88 void componentToUrl();
89 void anchoredLoader();
90 void sizeLoaderToItem();
91 void sizeItemToLoader();
93 void networkRequestUrl();
94 void failNetworkRequest();
95 void networkComponent();
97 void initialPropertyValues_data();
98 void initialPropertyValues();
99 void initialPropertyValuesBinding();
100 void initialPropertyValuesError_data();
101 void initialPropertyValuesError();
103 void deleteComponentCrash();
106 void creationContext();
110 void asynchronous_data();
112 void asynchronous_clear();
113 void simultaneousSyncAsync();
124 tst_QQuickLoader::tst_QQuickLoader()
128 void tst_QQuickLoader::sourceOrComponent()
130 QFETCH(QString, sourceOrComponent);
131 QFETCH(QString, sourceDefinition);
132 QFETCH(QUrl, sourceUrl);
133 QFETCH(QString, errorString);
135 bool error = !errorString.isEmpty();
137 QTest::ignoreMessage(QtWarningMsg, errorString.toUtf8().constData());
139 QQmlComponent component(&engine);
140 component.setData(QByteArray(
141 "import QtQuick 2.0\n"
143 " property int onItemChangedCount: 0\n"
144 " property int onSourceChangedCount: 0\n"
145 " property int onSourceComponentChangedCount: 0\n"
146 " property int onStatusChangedCount: 0\n"
147 " property int onProgressChangedCount: 0\n"
148 " property int onLoadedCount: 0\n")
149 + sourceDefinition.toUtf8()
151 " onItemChanged: onItemChangedCount += 1\n"
152 " onSourceChanged: onSourceChangedCount += 1\n"
153 " onSourceComponentChanged: onSourceComponentChangedCount += 1\n"
154 " onStatusChanged: onStatusChangedCount += 1\n"
155 " onProgressChanged: onProgressChangedCount += 1\n"
156 " onLoaded: onLoadedCount += 1\n"
158 , dataDirectoryUrl());
160 QQuickLoader *loader = qobject_cast<QQuickLoader*>(component.create());
161 QVERIFY(loader != 0);
162 QCOMPARE(loader->item() == 0, error);
163 QCOMPARE(loader->source(), sourceUrl);
164 QCOMPARE(loader->progress(), 1.0);
166 QCOMPARE(loader->status(), error ? QQuickLoader::Error : QQuickLoader::Ready);
167 QCOMPARE(static_cast<QQuickItem*>(loader)->childItems().count(), error ? 0: 1);
170 bool sourceComponentIsChildOfLoader = false;
171 for (int ii = 0; ii < loader->children().size(); ++ii) {
172 QQmlComponent *c = qobject_cast<QQmlComponent*>(loader->children().at(ii));
173 if (c && c == loader->sourceComponent()) {
174 sourceComponentIsChildOfLoader = true;
177 QVERIFY(sourceComponentIsChildOfLoader);
180 if (sourceOrComponent == "component") {
181 QCOMPARE(loader->property("onSourceComponentChangedCount").toInt(), 1);
182 QCOMPARE(loader->property("onSourceChangedCount").toInt(), 0);
184 QCOMPARE(loader->property("onSourceComponentChangedCount").toInt(), 0);
185 QCOMPARE(loader->property("onSourceChangedCount").toInt(), 1);
187 QCOMPARE(loader->property("onStatusChangedCount").toInt(), 1);
188 QCOMPARE(loader->property("onProgressChangedCount").toInt(), 1);
190 QCOMPARE(loader->property("onItemChangedCount").toInt(), error ? 0 : 1);
191 QCOMPARE(loader->property("onLoadedCount").toInt(), error ? 0 : 1);
196 void tst_QQuickLoader::sourceOrComponent_data()
198 QTest::addColumn<QString>("sourceOrComponent");
199 QTest::addColumn<QString>("sourceDefinition");
200 QTest::addColumn<QUrl>("sourceUrl");
201 QTest::addColumn<QString>("errorString");
203 QTest::newRow("source") << "source" << "source: 'Rect120x60.qml'\n" << testFileUrl("Rect120x60.qml") << "";
204 QTest::newRow("sourceComponent") << "component" << "Component { id: comp; Rectangle { width: 100; height: 50 } }\n sourceComponent: comp\n" << QUrl() << "";
205 QTest::newRow("invalid source") << "source" << "source: 'IDontExist.qml'\n" << testFileUrl("IDontExist.qml")
206 << QString(testFileUrl("IDontExist.qml").toString() + ": File not found");
209 void tst_QQuickLoader::clear()
212 QQmlComponent component(&engine);
213 component.setData(QByteArray(
214 "import QtQuick 2.0\n"
215 " Loader { id: loader\n"
216 " source: 'Rect120x60.qml'\n"
217 " Timer { interval: 200; running: true; onTriggered: loader.source = '' }\n"
219 , dataDirectoryUrl());
220 QQuickLoader *loader = qobject_cast<QQuickLoader*>(component.create());
221 QVERIFY(loader != 0);
222 QVERIFY(loader->item());
223 QCOMPARE(loader->progress(), 1.0);
224 QCOMPARE(static_cast<QQuickItem*>(loader)->childItems().count(), 1);
226 QTRY_VERIFY(loader->item() == 0);
227 QCOMPARE(loader->progress(), 0.0);
228 QCOMPARE(loader->status(), QQuickLoader::Null);
229 QCOMPARE(static_cast<QQuickItem*>(loader)->childItems().count(), 0);
234 QQmlComponent component(&engine, testFileUrl("/SetSourceComponent.qml"));
235 QQuickItem *item = qobject_cast<QQuickItem*>(component.create());
238 QQuickLoader *loader = qobject_cast<QQuickLoader*>(item->QQuickItem::childItems().at(0));
240 QVERIFY(loader->item());
241 QCOMPARE(loader->progress(), 1.0);
242 QCOMPARE(static_cast<QQuickItem*>(loader)->childItems().count(), 1);
244 loader->setSourceComponent(0);
246 QVERIFY(loader->item() == 0);
247 QCOMPARE(loader->progress(), 0.0);
248 QCOMPARE(loader->status(), QQuickLoader::Null);
249 QCOMPARE(static_cast<QQuickItem*>(loader)->childItems().count(), 0);
254 QQmlComponent component(&engine, testFileUrl("/SetSourceComponent.qml"));
255 QQuickItem *item = qobject_cast<QQuickItem*>(component.create());
258 QQuickLoader *loader = qobject_cast<QQuickLoader*>(item->QQuickItem::childItems().at(0));
260 QVERIFY(loader->item());
261 QCOMPARE(loader->progress(), 1.0);
262 QCOMPARE(static_cast<QQuickItem*>(loader)->childItems().count(), 1);
264 QMetaObject::invokeMethod(item, "clear");
266 QVERIFY(loader->item() == 0);
267 QCOMPARE(loader->progress(), 0.0);
268 QCOMPARE(loader->status(), QQuickLoader::Null);
269 QCOMPARE(static_cast<QQuickItem*>(loader)->childItems().count(), 0);
275 void tst_QQuickLoader::urlToComponent()
277 QQmlComponent component(&engine);
278 component.setData(QByteArray("import QtQuick 2.0\n"
281 " Component { id: myComp; Rectangle { width: 10; height: 10 } }\n"
282 " source: \"Rect120x60.qml\"\n"
283 " Timer { interval: 100; running: true; onTriggered: loader.sourceComponent = myComp }\n"
285 , dataDirectoryUrl());
286 QQuickLoader *loader = qobject_cast<QQuickLoader*>(component.create());
288 QTRY_VERIFY(loader != 0);
289 QVERIFY(loader->item());
290 QCOMPARE(loader->progress(), 1.0);
291 QCOMPARE(static_cast<QQuickItem*>(loader)->childItems().count(), 1);
292 QCOMPARE(loader->width(), 10.0);
293 QCOMPARE(loader->height(), 10.0);
298 void tst_QQuickLoader::componentToUrl()
300 QQmlComponent component(&engine, testFileUrl("/SetSourceComponent.qml"));
301 QQuickItem *item = qobject_cast<QQuickItem*>(component.create());
304 QQuickLoader *loader = qobject_cast<QQuickLoader*>(item->QQuickItem::childItems().at(0));
306 QVERIFY(loader->item());
307 QCOMPARE(loader->progress(), 1.0);
308 QCOMPARE(static_cast<QQuickItem*>(loader)->childItems().count(), 1);
310 loader->setSource(testFileUrl("/Rect120x60.qml"));
311 QVERIFY(loader->item());
312 QCOMPARE(loader->progress(), 1.0);
313 QCOMPARE(static_cast<QQuickItem*>(loader)->childItems().count(), 1);
314 QCOMPARE(loader->width(), 120.0);
315 QCOMPARE(loader->height(), 60.0);
320 void tst_QQuickLoader::anchoredLoader()
322 QQmlComponent component(&engine, testFileUrl("/AnchoredLoader.qml"));
323 QQuickItem *rootItem = qobject_cast<QQuickItem*>(component.create());
324 QVERIFY(rootItem != 0);
325 QQuickItem *loader = rootItem->findChild<QQuickItem*>("loader");
326 QQuickItem *sourceElement = rootItem->findChild<QQuickItem*>("sourceElement");
328 QVERIFY(loader != 0);
329 QVERIFY(sourceElement != 0);
331 QCOMPARE(rootItem->width(), 300.0);
332 QCOMPARE(rootItem->height(), 200.0);
334 QCOMPARE(loader->width(), 300.0);
335 QCOMPARE(loader->height(), 200.0);
337 QCOMPARE(sourceElement->width(), 300.0);
338 QCOMPARE(sourceElement->height(), 200.0);
341 void tst_QQuickLoader::sizeLoaderToItem()
343 QQmlComponent component(&engine, testFileUrl("/SizeToItem.qml"));
344 QQuickLoader *loader = qobject_cast<QQuickLoader*>(component.create());
345 QVERIFY(loader != 0);
346 QCOMPARE(loader->width(), 120.0);
347 QCOMPARE(loader->height(), 60.0);
350 QQuickItem *rect = qobject_cast<QQuickItem*>(loader->item());
354 QCOMPARE(loader->width(), 150.0);
355 QCOMPARE(loader->height(), 45.0);
357 // Check explicit width
358 loader->setWidth(200.0);
359 QCOMPARE(loader->width(), 200.0);
360 QCOMPARE(rect->width(), 200.0);
361 rect->setWidth(100.0); // when rect changes ...
362 QCOMPARE(rect->width(), 100.0); // ... it changes
363 QCOMPARE(loader->width(), 200.0); // ... but loader stays the same
365 // Check explicit height
366 loader->setHeight(200.0);
367 QCOMPARE(loader->height(), 200.0);
368 QCOMPARE(rect->height(), 200.0);
369 rect->setHeight(100.0); // when rect changes ...
370 QCOMPARE(rect->height(), 100.0); // ... it changes
371 QCOMPARE(loader->height(), 200.0); // ... but loader stays the same
374 loader->setWidth(180);
375 loader->setHeight(30);
376 QCOMPARE(rect->width(), 180.0);
377 QCOMPARE(rect->height(), 30.0);
382 void tst_QQuickLoader::sizeItemToLoader()
384 QQmlComponent component(&engine, testFileUrl("/SizeToLoader.qml"));
385 QQuickLoader *loader = qobject_cast<QQuickLoader*>(component.create());
386 QVERIFY(loader != 0);
387 QCOMPARE(loader->width(), 200.0);
388 QCOMPARE(loader->height(), 80.0);
390 QQuickItem *rect = qobject_cast<QQuickItem*>(loader->item());
392 QCOMPARE(rect->width(), 200.0);
393 QCOMPARE(rect->height(), 80.0);
396 loader->setWidth(180);
397 loader->setHeight(30);
398 QCOMPARE(rect->width(), 180.0);
399 QCOMPARE(rect->height(), 30.0);
402 loader->resetWidth(); // reset explicit size
403 loader->resetHeight();
406 QCOMPARE(loader->width(), 160.0);
407 QCOMPARE(loader->height(), 45.0);
412 void tst_QQuickLoader::noResize()
414 QQmlComponent component(&engine, testFileUrl("/NoResize.qml"));
415 QQuickItem* item = qobject_cast<QQuickItem*>(component.create());
417 QCOMPARE(item->width(), 200.0);
418 QCOMPARE(item->height(), 80.0);
423 void tst_QQuickLoader::networkRequestUrl()
425 TestHTTPServer server(SERVER_PORT);
426 QVERIFY(server.isValid());
427 server.serveDirectory(dataDirectory());
429 QQmlComponent component(&engine);
430 component.setData(QByteArray("import QtQuick 2.0\nLoader { property int signalCount : 0; source: \"http://127.0.0.1:14450/Rect120x60.qml\"; onLoaded: signalCount += 1 }"), testFileUrl("../dummy.qml"));
431 if (component.isError())
432 qDebug() << component.errors();
433 QQuickLoader *loader = qobject_cast<QQuickLoader*>(component.create());
434 QVERIFY(loader != 0);
436 QTRY_VERIFY(loader->status() == QQuickLoader::Ready);
438 QVERIFY(loader->item());
439 QCOMPARE(loader->progress(), 1.0);
440 QCOMPARE(loader->property("signalCount").toInt(), 1);
441 QCOMPARE(static_cast<QQuickItem*>(loader)->childItems().count(), 1);
446 /* XXX Component waits until all dependencies are loaded. Is this actually possible? */
447 void tst_QQuickLoader::networkComponent()
449 TestHTTPServer server(SERVER_PORT);
450 QVERIFY(server.isValid());
451 server.serveDirectory(dataDirectory(), TestHTTPServer::Delay);
453 QQmlComponent component(&engine);
454 component.setData(QByteArray(
455 "import QtQuick 2.0\n"
456 "import \"http://127.0.0.1:14450/\" as NW\n"
458 " Component { id: comp; NW.Rect120x60 {} }\n"
459 " Loader { sourceComponent: comp } }")
462 QQuickItem *item = qobject_cast<QQuickItem*>(component.create());
465 QQuickLoader *loader = qobject_cast<QQuickLoader*>(item->QQuickItem::children().at(1));
467 QTRY_VERIFY(loader->status() == QQuickLoader::Ready);
469 QVERIFY(loader->item());
470 QCOMPARE(loader->progress(), 1.0);
471 QCOMPARE(loader->status(), QQuickLoader::Ready);
472 QCOMPARE(static_cast<QQuickItem*>(loader)->children().count(), 1);
477 void tst_QQuickLoader::failNetworkRequest()
479 TestHTTPServer server(SERVER_PORT);
480 QVERIFY(server.isValid());
481 server.serveDirectory(dataDirectory());
483 QTest::ignoreMessage(QtWarningMsg, "http://127.0.0.1:14450/IDontExist.qml: File not found");
485 QQmlComponent component(&engine);
486 component.setData(QByteArray("import QtQuick 2.0\nLoader { property int did_load: 123; source: \"http://127.0.0.1:14450/IDontExist.qml\"; onLoaded: did_load=456 }"), QUrl::fromLocalFile("http://127.0.0.1:14450/dummy.qml"));
487 QQuickLoader *loader = qobject_cast<QQuickLoader*>(component.create());
488 QVERIFY(loader != 0);
490 QTRY_VERIFY(loader->status() == QQuickLoader::Error);
492 QVERIFY(loader->item() == 0);
493 QCOMPARE(loader->progress(), 1.0);
494 QCOMPARE(loader->property("did_load").toInt(), 123);
495 QCOMPARE(static_cast<QQuickItem*>(loader)->childItems().count(), 0);
500 void tst_QQuickLoader::active()
502 // check that the item isn't instantiated until active is set to true
504 QQmlComponent component(&engine, testFileUrl("active.1.qml"));
505 QObject *object = component.create();
506 QVERIFY(object != 0);
507 QQuickLoader *loader = object->findChild<QQuickLoader*>("loader");
509 QVERIFY(loader->active() == false); // set manually to false
510 QVERIFY(loader->item() == 0);
511 QMetaObject::invokeMethod(object, "doSetSourceComponent");
512 QVERIFY(loader->item() == 0);
513 QMetaObject::invokeMethod(object, "doSetSource");
514 QVERIFY(loader->item() == 0);
515 QMetaObject::invokeMethod(object, "doSetActive");
516 QVERIFY(loader->item() != 0);
521 // check that the status is Null if active is set to false
523 QQmlComponent component(&engine, testFileUrl("active.2.qml"));
524 QObject *object = component.create();
525 QVERIFY(object != 0);
526 QQuickLoader *loader = object->findChild<QQuickLoader*>("loader");
528 QVERIFY(loader->active() == true); // active is true by default
529 QCOMPARE(loader->status(), QQuickLoader::Ready);
530 int currStatusChangedCount = loader->property("statusChangedCount").toInt();
531 QMetaObject::invokeMethod(object, "doSetInactive");
532 QCOMPARE(loader->status(), QQuickLoader::Null);
533 QCOMPARE(loader->property("statusChangedCount").toInt(), (currStatusChangedCount+1));
538 // check that the source is not cleared if active is set to false
540 QQmlComponent component(&engine, testFileUrl("active.3.qml"));
541 QObject *object = component.create();
542 QVERIFY(object != 0);
543 QQuickLoader *loader = object->findChild<QQuickLoader*>("loader");
545 QVERIFY(loader->active() == true); // active is true by default
546 QVERIFY(!loader->source().isEmpty());
547 int currSourceChangedCount = loader->property("sourceChangedCount").toInt();
548 QMetaObject::invokeMethod(object, "doSetInactive");
549 QVERIFY(!loader->source().isEmpty());
550 QCOMPARE(loader->property("sourceChangedCount").toInt(), currSourceChangedCount);
555 // check that the sourceComponent is not cleared if active is set to false
557 QQmlComponent component(&engine, testFileUrl("active.4.qml"));
558 QObject *object = component.create();
559 QVERIFY(object != 0);
560 QQuickLoader *loader = object->findChild<QQuickLoader*>("loader");
562 QVERIFY(loader->active() == true); // active is true by default
563 QVERIFY(loader->sourceComponent() != 0);
564 int currSourceComponentChangedCount = loader->property("sourceComponentChangedCount").toInt();
565 QMetaObject::invokeMethod(object, "doSetInactive");
566 QVERIFY(loader->sourceComponent() != 0);
567 QCOMPARE(loader->property("sourceComponentChangedCount").toInt(), currSourceComponentChangedCount);
572 // check that the item is released if active is set to false
574 QQmlComponent component(&engine, testFileUrl("active.5.qml"));
575 QObject *object = component.create();
576 QVERIFY(object != 0);
577 QQuickLoader *loader = object->findChild<QQuickLoader*>("loader");
579 QVERIFY(loader->active() == true); // active is true by default
580 QVERIFY(loader->item() != 0);
581 int currItemChangedCount = loader->property("itemChangedCount").toInt();
582 QMetaObject::invokeMethod(object, "doSetInactive");
583 QVERIFY(loader->item() == 0);
584 QCOMPARE(loader->property("itemChangedCount").toInt(), (currItemChangedCount+1));
589 // check that the activeChanged signal is emitted correctly
591 QQmlComponent component(&engine, testFileUrl("active.6.qml"));
592 QObject *object = component.create();
593 QVERIFY(object != 0);
594 QQuickLoader *loader = object->findChild<QQuickLoader*>("loader");
596 QVERIFY(loader->active() == true); // active is true by default
597 loader->setActive(true); // no effect
598 QCOMPARE(loader->property("activeChangedCount").toInt(), 0);
599 loader->setActive(false); // change signal should be emitted
600 QCOMPARE(loader->property("activeChangedCount").toInt(), 1);
601 loader->setActive(false); // no effect
602 QCOMPARE(loader->property("activeChangedCount").toInt(), 1);
603 loader->setActive(true); // change signal should be emitted
604 QCOMPARE(loader->property("activeChangedCount").toInt(), 2);
605 loader->setActive(false); // change signal should be emitted
606 QCOMPARE(loader->property("activeChangedCount").toInt(), 3);
607 QMetaObject::invokeMethod(object, "doSetActive");
608 QCOMPARE(loader->property("activeChangedCount").toInt(), 4);
609 QMetaObject::invokeMethod(object, "doSetActive");
610 QCOMPARE(loader->property("activeChangedCount").toInt(), 4);
611 QMetaObject::invokeMethod(object, "doSetInactive");
612 QCOMPARE(loader->property("activeChangedCount").toInt(), 5);
613 loader->setActive(true); // change signal should be emitted
614 QCOMPARE(loader->property("activeChangedCount").toInt(), 6);
619 // check that the component isn't loaded until active is set to true
621 QQmlComponent component(&engine, testFileUrl("active.7.qml"));
622 QObject *object = component.create();
623 QVERIFY(object != 0);
624 QCOMPARE(object->property("success").toBool(), true);
628 // check that the component is loaded if active is not set (true by default)
630 QQmlComponent component(&engine, testFileUrl("active.8.qml"));
631 QObject *object = component.create();
632 QVERIFY(object != 0);
633 QCOMPARE(object->property("success").toBool(), true);
638 void tst_QQuickLoader::initialPropertyValues_data()
640 QTest::addColumn<QUrl>("qmlFile");
641 QTest::addColumn<QStringList>("expectedWarnings");
642 QTest::addColumn<QStringList>("propertyNames");
643 QTest::addColumn<QVariantList>("propertyValues");
645 QTest::newRow("source url with value set in onLoaded, initially active = true") << testFileUrl("initialPropertyValues.1.qml")
647 << (QStringList() << "initialValue" << "behaviorCount")
648 << (QVariantList() << 1 << 1);
650 QTest::newRow("set source with initial property values specified, active = true") << testFileUrl("initialPropertyValues.2.qml")
652 << (QStringList() << "initialValue" << "behaviorCount")
653 << (QVariantList() << 2 << 0);
655 QTest::newRow("set source with initial property values specified, active = false") << testFileUrl("initialPropertyValues.3.qml")
656 << (QStringList() << QString(testFileUrl("initialPropertyValues.3.qml").toString() + QLatin1String(":16: TypeError: Cannot read property 'canary' of null")))
660 QTest::newRow("set source with initial property values specified, active = false, with active set true later") << testFileUrl("initialPropertyValues.4.qml")
662 << (QStringList() << "initialValue" << "behaviorCount")
663 << (QVariantList() << 4 << 0);
665 QTest::newRow("set source without initial property values specified, active = true") << testFileUrl("initialPropertyValues.5.qml")
667 << (QStringList() << "initialValue" << "behaviorCount")
668 << (QVariantList() << 0 << 0);
670 QTest::newRow("set source with initial property values specified with binding, active = true") << testFileUrl("initialPropertyValues.6.qml")
672 << (QStringList() << "initialValue" << "behaviorCount")
673 << (QVariantList() << 6 << 0);
675 QTest::newRow("ensure initial property value semantics mimic createObject") << testFileUrl("initialPropertyValues.7.qml")
677 << (QStringList() << "loaderValue" << "createObjectValue")
678 << (QVariantList() << 1 << 1);
680 QTest::newRow("ensure initial property values aren't disposed prior to component completion") << testFileUrl("initialPropertyValues.8.qml")
682 << (QStringList() << "initialValue")
683 << (QVariantList() << 6);
686 void tst_QQuickLoader::initialPropertyValues()
688 QFETCH(QUrl, qmlFile);
689 QFETCH(QStringList, expectedWarnings);
690 QFETCH(QStringList, propertyNames);
691 QFETCH(QVariantList, propertyValues);
693 TestHTTPServer server(SERVER_PORT);
694 QVERIFY(server.isValid());
695 server.serveDirectory(dataDirectory());
697 foreach (const QString &warning, expectedWarnings)
698 QTest::ignoreMessage(QtWarningMsg, warning.toAscii().constData());
700 QQmlComponent component(&engine, qmlFile);
701 QObject *object = component.create();
702 QVERIFY(object != 0);
703 qApp->processEvents();
706 for (int i = 0; i < propertyNames.size(); ++i)
707 QCOMPARE(object->property(propertyNames.at(i).toAscii().constData()), propertyValues.at(i));
712 void tst_QQuickLoader::initialPropertyValuesBinding()
714 QQmlComponent component(&engine, testFileUrl("initialPropertyValues.binding.qml"));
715 QObject *object = component.create();
716 QVERIFY(object != 0);
718 QVERIFY(object->setProperty("bindable", QVariant(8)));
719 QCOMPARE(object->property("canaryValue").toInt(), 8);
724 void tst_QQuickLoader::initialPropertyValuesError_data()
726 QTest::addColumn<QUrl>("qmlFile");
727 QTest::addColumn<QStringList>("expectedWarnings");
729 QTest::newRow("invalid initial property values object") << testFileUrl("initialPropertyValues.error.1.qml")
730 << (QStringList() << QString(testFileUrl("initialPropertyValues.error.1.qml").toString() + ":6:5: QML Loader: setSource: value is not an object"));
732 QTest::newRow("nonexistent source url") << testFileUrl("initialPropertyValues.error.2.qml")
733 << (QStringList() << QString(testFileUrl("NonexistentSourceComponent.qml").toString() + ": File not found"));
735 QTest::newRow("invalid source url") << testFileUrl("initialPropertyValues.error.3.qml")
736 << (QStringList() << QString(testFileUrl("InvalidSourceComponent.qml").toString() + ":5:1: Syntax error"));
738 QTest::newRow("invalid initial property values object with invalid property access") << testFileUrl("initialPropertyValues.error.4.qml")
739 << (QStringList() << QString(testFileUrl("initialPropertyValues.error.4.qml").toString() + ":7:5: QML Loader: setSource: value is not an object")
740 << QString(testFileUrl("initialPropertyValues.error.4.qml").toString() + ":5: TypeError: Cannot read property 'canary' of null"));
743 void tst_QQuickLoader::initialPropertyValuesError()
745 QFETCH(QUrl, qmlFile);
746 QFETCH(QStringList, expectedWarnings);
748 foreach (const QString &warning, expectedWarnings)
749 QTest::ignoreMessage(QtWarningMsg, warning.toUtf8().constData());
751 QQmlComponent component(&engine, qmlFile);
752 QObject *object = component.create();
753 QVERIFY(object != 0);
754 QQuickLoader *loader = object->findChild<QQuickLoader*>("loader");
755 QVERIFY(loader != 0);
756 QVERIFY(loader->item() == 0);
761 void tst_QQuickLoader::deleteComponentCrash()
763 QQmlComponent component(&engine, testFileUrl("crash.qml"));
764 QQuickItem *item = qobject_cast<QQuickItem*>(component.create());
767 item->metaObject()->invokeMethod(item, "setLoaderSource");
769 QQuickLoader *loader = qobject_cast<QQuickLoader*>(item->QQuickItem::childItems().at(0));
771 QVERIFY(loader->item());
772 QCOMPARE(loader->item()->objectName(), QLatin1String("blue"));
773 QCOMPARE(loader->progress(), 1.0);
774 QCOMPARE(loader->status(), QQuickLoader::Ready);
775 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
776 QCoreApplication::processEvents();
777 QTRY_COMPARE(static_cast<QQuickItem*>(loader)->childItems().count(), 1);
778 QVERIFY(loader->source() == testFileUrl("BlueRect.qml"));
783 void tst_QQuickLoader::nonItem()
785 QQmlComponent component(&engine, testFileUrl("nonItem.qml"));
786 QString err = testFileUrl("nonItem.qml").toString() + ":3:1: QML Loader: Loader does not support loading non-visual elements.";
788 QTest::ignoreMessage(QtWarningMsg, err.toLatin1().constData());
789 QQuickLoader *loader = qobject_cast<QQuickLoader*>(component.create());
791 QVERIFY(loader->item() == 0);
796 void tst_QQuickLoader::vmeErrors()
798 QQmlComponent component(&engine, testFileUrl("vmeErrors.qml"));
799 QString err = testFileUrl("VmeError.qml").toString() + ":6: Cannot assign object type QObject with no default method";
800 QTest::ignoreMessage(QtWarningMsg, err.toLatin1().constData());
801 QQuickLoader *loader = qobject_cast<QQuickLoader*>(component.create());
803 QVERIFY(loader->item() == 0);
809 void tst_QQuickLoader::creationContext()
811 QQmlComponent component(&engine, testFileUrl("creationContext.qml"));
813 QObject *o = component.create();
816 QCOMPARE(o->property("test").toBool(), true);
821 void tst_QQuickLoader::QTBUG_16928()
823 QQmlComponent component(&engine, testFileUrl("QTBUG_16928.qml"));
824 QQuickItem *item = qobject_cast<QQuickItem*>(component.create());
827 QCOMPARE(item->width(), 250.);
828 QCOMPARE(item->height(), 250.);
833 void tst_QQuickLoader::implicitSize()
835 QQmlComponent component(&engine, testFileUrl("implicitSize.qml"));
836 QQuickItem *item = qobject_cast<QQuickItem*>(component.create());
839 QCOMPARE(item->width(), 150.);
840 QCOMPARE(item->height(), 150.);
842 QCOMPARE(item->property("implHeight").toReal(), 100.);
843 QCOMPARE(item->property("implWidth").toReal(), 100.);
845 QQuickLoader *loader = item->findChild<QQuickLoader*>("loader");
846 QSignalSpy implWidthSpy(loader, SIGNAL(implicitWidthChanged()));
847 QSignalSpy implHeightSpy(loader, SIGNAL(implicitHeightChanged()));
849 QMetaObject::invokeMethod(item, "changeImplicitSize");
851 QCOMPARE(loader->property("implicitWidth").toReal(), 200.);
852 QCOMPARE(loader->property("implicitHeight").toReal(), 300.);
854 QCOMPARE(implWidthSpy.count(), 1);
855 QCOMPARE(implHeightSpy.count(), 1);
860 void tst_QQuickLoader::QTBUG_17114()
862 QQmlComponent component(&engine, testFileUrl("QTBUG_17114.qml"));
863 QQuickItem *item = qobject_cast<QQuickItem*>(component.create());
866 QCOMPARE(item->property("loaderWidth").toReal(), 32.);
867 QCOMPARE(item->property("loaderHeight").toReal(), 32.);
872 void tst_QQuickLoader::asynchronous_data()
874 QTest::addColumn<QUrl>("qmlFile");
875 QTest::addColumn<QStringList>("expectedWarnings");
877 QTest::newRow("Valid component") << testFileUrl("BigComponent.qml")
880 QTest::newRow("Non-existent component") << testFileUrl("IDoNotExist.qml")
881 << (QStringList() << QString(testFileUrl("IDoNotExist.qml").toString() + ": File not found"));
883 QTest::newRow("Invalid component") << testFileUrl("InvalidSourceComponent.qml")
884 << (QStringList() << QString(testFileUrl("InvalidSourceComponent.qml").toString() + ":5:1: Syntax error"));
887 void tst_QQuickLoader::asynchronous()
889 QFETCH(QUrl, qmlFile);
890 QFETCH(QStringList, expectedWarnings);
892 if (!engine.incubationController())
893 engine.setIncubationController(new PeriodicIncubationController);
894 PeriodicIncubationController *controller = static_cast<PeriodicIncubationController*>(engine.incubationController());
895 controller->incubated = false;
896 QQmlComponent component(&engine, testFileUrl("asynchronous.qml"));
897 QQuickItem *root = qobject_cast<QQuickItem*>(component.create());
900 QQuickLoader *loader = root->findChild<QQuickLoader*>("loader");
903 foreach (const QString &warning, expectedWarnings)
904 QTest::ignoreMessage(QtWarningMsg, warning.toUtf8().constData());
906 QVERIFY(!loader->item());
907 QCOMPARE(loader->progress(), 0.0);
908 root->setProperty("comp", qmlFile.toString());
909 QMetaObject::invokeMethod(root, "loadComponent");
910 QVERIFY(!loader->item());
912 if (expectedWarnings.isEmpty()) {
913 QCOMPARE(loader->status(), QQuickLoader::Loading);
914 QVERIFY(!controller->incubated); // asynchronous compilation means not immediately compiled/incubating.
915 QTRY_VERIFY(controller->incubated); // but should start incubating once compilation is complete.
916 QTRY_VERIFY(loader->item());
917 QCOMPARE(loader->progress(), 1.0);
918 QCOMPARE(loader->status(), QQuickLoader::Ready);
920 QTRY_COMPARE(loader->progress(), 1.0);
921 QTRY_COMPARE(loader->status(), QQuickLoader::Error);
927 void tst_QQuickLoader::asynchronous_clear()
929 if (!engine.incubationController())
930 engine.setIncubationController(new PeriodicIncubationController);
931 QQmlComponent component(&engine, testFileUrl("asynchronous.qml"));
932 QQuickItem *root = qobject_cast<QQuickItem*>(component.create());
935 QQuickLoader *loader = root->findChild<QQuickLoader*>("loader");
938 QVERIFY(!loader->item());
939 root->setProperty("comp", "BigComponent.qml");
940 QMetaObject::invokeMethod(root, "loadComponent");
941 QVERIFY(!loader->item());
943 QCOMPARE(loader->status(), QQuickLoader::Loading);
944 QTRY_COMPARE(engine.incubationController()->incubatingObjectCount(), 1);
946 // clear before component created
947 root->setProperty("comp", "");
948 QMetaObject::invokeMethod(root, "loadComponent");
949 QVERIFY(!loader->item());
950 QCOMPARE(engine.incubationController()->incubatingObjectCount(), 0);
952 QCOMPARE(loader->progress(), 0.0);
953 QCOMPARE(loader->status(), QQuickLoader::Null);
954 QCOMPARE(static_cast<QQuickItem*>(loader)->childItems().count(), 0);
956 // check loading component
957 root->setProperty("comp", "BigComponent.qml");
958 QMetaObject::invokeMethod(root, "loadComponent");
959 QVERIFY(!loader->item());
961 QCOMPARE(loader->status(), QQuickLoader::Loading);
962 QCOMPARE(engine.incubationController()->incubatingObjectCount(), 1);
964 QTRY_VERIFY(loader->item());
965 QCOMPARE(loader->progress(), 1.0);
966 QCOMPARE(loader->status(), QQuickLoader::Ready);
967 QCOMPARE(static_cast<QQuickItem*>(loader)->childItems().count(), 1);
972 void tst_QQuickLoader::simultaneousSyncAsync()
974 if (!engine.incubationController())
975 engine.setIncubationController(new PeriodicIncubationController);
976 PeriodicIncubationController *controller = static_cast<PeriodicIncubationController*>(engine.incubationController());
977 controller->incubated = false;
978 QQmlComponent component(&engine, testFileUrl("simultaneous.qml"));
979 QQuickItem *root = qobject_cast<QQuickItem*>(component.create());
982 QQuickLoader *asyncLoader = root->findChild<QQuickLoader*>("asyncLoader");
983 QQuickLoader *syncLoader = root->findChild<QQuickLoader*>("syncLoader");
984 QVERIFY(asyncLoader);
987 QVERIFY(!asyncLoader->item());
988 QVERIFY(!syncLoader->item());
989 QMetaObject::invokeMethod(root, "loadComponents");
990 QVERIFY(!asyncLoader->item());
991 QVERIFY(syncLoader->item());
993 QCOMPARE(asyncLoader->status(), QQuickLoader::Loading);
994 QVERIFY(!controller->incubated); // asynchronous compilation means not immediately compiled/incubating.
995 QTRY_VERIFY(controller->incubated); // but should start incubating once compilation is complete.
996 QTRY_VERIFY(asyncLoader->item());
997 QCOMPARE(asyncLoader->progress(), 1.0);
998 QCOMPARE(asyncLoader->status(), QQuickLoader::Ready);
1003 void tst_QQuickLoader::loadedSignal()
1006 // ensure that triggering loading (by setting active = true)
1007 // and then immediately setting active to false, causes the
1008 // loader to be deactivated, including disabling the incubator.
1009 QQmlComponent component(&engine, testFileUrl("loadedSignal.qml"));
1010 QObject *obj = component.create();
1012 QMetaObject::invokeMethod(obj, "triggerLoading");
1013 QTest::qWait(100); // ensure that loading would have finished if it wasn't deactivated
1014 QCOMPARE(obj->property("loadCount").toInt(), 0);
1015 QVERIFY(obj->property("success").toBool());
1017 QMetaObject::invokeMethod(obj, "triggerLoading");
1019 QCOMPARE(obj->property("loadCount").toInt(), 0);
1020 QVERIFY(obj->property("success").toBool());
1022 QMetaObject::invokeMethod(obj, "triggerMultipleLoad");
1024 QCOMPARE(obj->property("loadCount").toInt(), 1); // only one loaded signal should be emitted.
1025 QVERIFY(obj->property("success").toBool());
1031 // ensure that an error doesn't result in the onLoaded signal being emitted.
1032 QQmlComponent component(&engine, testFileUrl("loadedSignal.2.qml"));
1033 QObject *obj = component.create();
1035 QMetaObject::invokeMethod(obj, "triggerLoading");
1037 QCOMPARE(obj->property("loadCount").toInt(), 0);
1038 QVERIFY(obj->property("success").toBool());
1044 void tst_QQuickLoader::parented()
1046 QQmlComponent component(&engine, testFileUrl("parented.qml"));
1047 QQuickItem *root = qobject_cast<QQuickItem*>(component.create());
1050 QQuickItem *item = root->findChild<QQuickItem*>("comp");
1053 QVERIFY(item->parentItem() == root);
1055 QCOMPARE(item->width(), 300.);
1056 QCOMPARE(item->height(), 300.);
1061 void tst_QQuickLoader::sizeBound()
1063 QQmlComponent component(&engine, testFileUrl("sizebound.qml"));
1064 QQuickItem *root = qobject_cast<QQuickItem*>(component.create());
1066 QQuickLoader *loader = root->findChild<QQuickLoader*>("loader");
1067 QVERIFY(loader != 0);
1069 QVERIFY(loader->item());
1071 QCOMPARE(loader->width(), 50.0);
1072 QCOMPARE(loader->height(), 60.0);
1074 QMetaObject::invokeMethod(root, "switchComponent");
1076 QCOMPARE(loader->width(), 80.0);
1077 QCOMPARE(loader->height(), 90.0);
1083 QTEST_MAIN(tst_QQuickLoader)
1085 #include "tst_qquickloader.moc"