aboutsummaryrefslogtreecommitdiffstats
path: root/tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp')
-rw-r--r--tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp1030
1 files changed, 1030 insertions, 0 deletions
diff --git a/tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp b/tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp
new file mode 100644
index 0000000000..fc54f715c9
--- /dev/null
+++ b/tests/auto/qml/qqmlincubator/tst_qqmlincubator.cpp
@@ -0,0 +1,1030 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "testtypes.h"
+
+#include <QUrl>
+#include <QDir>
+#include <QDebug>
+#include <qtest.h>
+#include <QPointer>
+#include <QFileInfo>
+#include <QQmlEngine>
+#include <QQmlContext>
+#include <QQmlProperty>
+#include <QQmlComponent>
+#include <QQmlIncubator>
+#include "../../shared/util.h"
+
+class tst_qqmlincubator : public QQmlDataTest
+{
+ Q_OBJECT
+public:
+ tst_qqmlincubator() {}
+
+private slots:
+ void initTestCase();
+
+ void incubationMode();
+ void objectDeleted();
+ void clear();
+ void noIncubationController();
+ void forceCompletion();
+ void setInitialState();
+ void clearDuringCompletion();
+ void objectDeletionAfterInit();
+ void recursiveClear();
+ void statusChanged();
+ void asynchronousIfNested();
+ void nestedComponent();
+ void chainedAsynchronousIfNested();
+ void chainedAsynchronousIfNestedOnCompleted();
+ void selfDelete();
+ void contextDelete();
+
+private:
+ QQmlIncubationController controller;
+ QQmlEngine engine;
+};
+
+#define VERIFY_ERRORS(component, errorfile) \
+ if (!errorfile) { \
+ if (qgetenv("DEBUG") != "" && !component.errors().isEmpty()) \
+ qWarning() << "Unexpected Errors:" << component.errors(); \
+ QVERIFY(!component.isError()); \
+ QVERIFY(component.errors().isEmpty()); \
+ } else { \
+ QFile file(QQmlDataTest::instance()->testFile(errorfile)); \
+ QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text)); \
+ QByteArray data = file.readAll(); \
+ file.close(); \
+ QList<QByteArray> expected = data.split('\n'); \
+ expected.removeAll(QByteArray("")); \
+ QList<QQmlError> errors = component.errors(); \
+ QList<QByteArray> actual; \
+ for (int ii = 0; ii < errors.count(); ++ii) { \
+ const QQmlError &error = errors.at(ii); \
+ QByteArray errorStr = QByteArray::number(error.line()) + ":" + \
+ QByteArray::number(error.column()) + ":" + \
+ error.description().toUtf8(); \
+ actual << errorStr; \
+ } \
+ if (qgetenv("DEBUG") != "" && expected != actual) \
+ qWarning() << "Expected:" << expected << "Actual:" << actual; \
+ QCOMPARE(expected, actual); \
+ }
+
+void tst_qqmlincubator::initTestCase()
+{
+ QQmlDataTest::initTestCase();
+ registerTypes();
+ engine.setIncubationController(&controller);
+}
+
+void tst_qqmlincubator::incubationMode()
+{
+ {
+ QQmlIncubator incubator;
+ QCOMPARE(incubator.incubationMode(), QQmlIncubator::Asynchronous);
+ }
+ {
+ QQmlIncubator incubator(QQmlIncubator::Asynchronous);
+ QCOMPARE(incubator.incubationMode(), QQmlIncubator::Asynchronous);
+ }
+ {
+ QQmlIncubator incubator(QQmlIncubator::Synchronous);
+ QCOMPARE(incubator.incubationMode(), QQmlIncubator::Synchronous);
+ }
+ {
+ QQmlIncubator incubator(QQmlIncubator::AsynchronousIfNested);
+ QCOMPARE(incubator.incubationMode(), QQmlIncubator::AsynchronousIfNested);
+ }
+}
+
+void tst_qqmlincubator::objectDeleted()
+{
+ SelfRegisteringType::clearMe();
+
+ QQmlComponent component(&engine, testFileUrl("objectDeleted.qml"));
+ QVERIFY(component.isReady());
+
+ QQmlIncubator incubator;
+ component.create(incubator);
+
+ QCOMPARE(incubator.status(), QQmlIncubator::Loading);
+ QVERIFY(SelfRegisteringType::me() == 0);
+
+ while (SelfRegisteringType::me() == 0 && incubator.isLoading()) {
+ bool b = false;
+ controller.incubateWhile(&b);
+ }
+
+ QVERIFY(SelfRegisteringType::me() != 0);
+ QVERIFY(incubator.isLoading());
+
+ delete SelfRegisteringType::me();
+
+ {
+ bool b = true;
+ controller.incubateWhile(&b);
+ }
+
+ QVERIFY(incubator.isError());
+ VERIFY_ERRORS(incubator, "objectDeleted.errors.txt");
+ QVERIFY(incubator.object() == 0);
+}
+
+void tst_qqmlincubator::clear()
+{
+ SelfRegisteringType::clearMe();
+
+ QQmlComponent component(&engine, testFileUrl("clear.qml"));
+ QVERIFY(component.isReady());
+
+ // Clear in null state
+ {
+ QQmlIncubator incubator;
+ QVERIFY(incubator.isNull());
+ incubator.clear(); // no effect
+ QVERIFY(incubator.isNull());
+ }
+
+ // Clear in loading state
+ {
+ QQmlIncubator incubator;
+ component.create(incubator);
+ QVERIFY(incubator.isLoading());
+ incubator.clear();
+ QVERIFY(incubator.isNull());
+ }
+
+ // Clear mid load
+ {
+ QQmlIncubator incubator;
+ component.create(incubator);
+
+ while (SelfRegisteringType::me() == 0 && incubator.isLoading()) {
+ bool b = false;
+ controller.incubateWhile(&b);
+ }
+
+ QVERIFY(incubator.isLoading());
+ QVERIFY(SelfRegisteringType::me() != 0);
+ QPointer<SelfRegisteringType> srt = SelfRegisteringType::me();
+
+ incubator.clear();
+ QVERIFY(incubator.isNull());
+ QVERIFY(srt.isNull());
+ }
+
+ // Clear in ready state
+ {
+ QQmlIncubator incubator;
+ component.create(incubator);
+
+ {
+ bool b = true;
+ controller.incubateWhile(&b);
+ }
+
+ QVERIFY(incubator.isReady());
+ QVERIFY(incubator.object() != 0);
+ QPointer<QObject> obj = incubator.object();
+
+ incubator.clear();
+ QVERIFY(incubator.isNull());
+ QVERIFY(incubator.object() == 0);
+ QVERIFY(!obj.isNull());
+
+ delete obj;
+ QVERIFY(obj.isNull());
+ }
+}
+
+void tst_qqmlincubator::noIncubationController()
+{
+ // All incubators should behave synchronously when there is no controller
+
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("noIncubationController.qml"));
+
+ QVERIFY(component.isReady());
+
+ {
+ QQmlIncubator incubator(QQmlIncubator::Asynchronous);
+ component.create(incubator);
+ QVERIFY(incubator.isReady());
+ QVERIFY(incubator.object());
+ QCOMPARE(incubator.object()->property("testValue").toInt(), 1913);
+ delete incubator.object();
+ }
+
+ {
+ QQmlIncubator incubator(QQmlIncubator::AsynchronousIfNested);
+ component.create(incubator);
+ QVERIFY(incubator.isReady());
+ QVERIFY(incubator.object());
+ QCOMPARE(incubator.object()->property("testValue").toInt(), 1913);
+ delete incubator.object();
+ }
+
+ {
+ QQmlIncubator incubator(QQmlIncubator::Synchronous);
+ component.create(incubator);
+ QVERIFY(incubator.isReady());
+ QVERIFY(incubator.object());
+ QCOMPARE(incubator.object()->property("testValue").toInt(), 1913);
+ delete incubator.object();
+ }
+}
+
+void tst_qqmlincubator::forceCompletion()
+{
+ QQmlComponent component(&engine, testFileUrl("forceCompletion.qml"));
+ QVERIFY(component.isReady());
+
+ {
+ // forceCompletion on a null incubator does nothing
+ QQmlIncubator incubator;
+ QVERIFY(incubator.isNull());
+ incubator.forceCompletion();
+ QVERIFY(incubator.isNull());
+ }
+
+ {
+ // forceCompletion immediately after creating an asynchronous object completes it
+ QQmlIncubator incubator;
+ QVERIFY(incubator.isNull());
+ component.create(incubator);
+ QVERIFY(incubator.isLoading());
+
+ incubator.forceCompletion();
+
+ QVERIFY(incubator.isReady());
+ QVERIFY(incubator.object() != 0);
+ QCOMPARE(incubator.object()->property("testValue").toInt(), 3499);
+
+ delete incubator.object();
+ }
+
+ {
+ // forceCompletion during creation completes it
+ SelfRegisteringType::clearMe();
+
+ QQmlIncubator incubator;
+ QVERIFY(incubator.isNull());
+ component.create(incubator);
+ QVERIFY(incubator.isLoading());
+
+ while (SelfRegisteringType::me() == 0 && incubator.isLoading()) {
+ bool b = false;
+ controller.incubateWhile(&b);
+ }
+
+ QVERIFY(SelfRegisteringType::me() != 0);
+ QVERIFY(incubator.isLoading());
+
+ incubator.forceCompletion();
+
+ QVERIFY(incubator.isReady());
+ QVERIFY(incubator.object() != 0);
+ QCOMPARE(incubator.object()->property("testValue").toInt(), 3499);
+
+ delete incubator.object();
+ }
+
+ {
+ // forceCompletion on a ready incubator has no effect
+ QQmlIncubator incubator;
+ QVERIFY(incubator.isNull());
+ component.create(incubator);
+ QVERIFY(incubator.isLoading());
+
+ incubator.forceCompletion();
+
+ QVERIFY(incubator.isReady());
+ QVERIFY(incubator.object() != 0);
+ QCOMPARE(incubator.object()->property("testValue").toInt(), 3499);
+
+ incubator.forceCompletion();
+
+ QVERIFY(incubator.isReady());
+ QVERIFY(incubator.object() != 0);
+ QCOMPARE(incubator.object()->property("testValue").toInt(), 3499);
+
+ delete incubator.object();
+ }
+}
+
+void tst_qqmlincubator::setInitialState()
+{
+ QQmlComponent component(&engine, testFileUrl("setInitialState.qml"));
+ QVERIFY(component.isReady());
+
+ struct MyIncubator : public QQmlIncubator
+ {
+ MyIncubator(QQmlIncubator::IncubationMode mode)
+ : QQmlIncubator(mode) {}
+
+ virtual void setInitialState(QObject *o) {
+ QQmlProperty::write(o, "test2", 19);
+ QQmlProperty::write(o, "testData1", 201);
+ }
+ };
+
+ {
+ MyIncubator incubator(QQmlIncubator::Asynchronous);
+ component.create(incubator);
+ QVERIFY(incubator.isLoading());
+ bool b = true;
+ controller.incubateWhile(&b);
+ QVERIFY(incubator.isReady());
+ QVERIFY(incubator.object());
+ QCOMPARE(incubator.object()->property("myValueFunctionCalled").toBool(), false);
+ QCOMPARE(incubator.object()->property("test1").toInt(), 502);
+ QCOMPARE(incubator.object()->property("test2").toInt(), 19);
+ delete incubator.object();
+ }
+
+ {
+ MyIncubator incubator(QQmlIncubator::Synchronous);
+ component.create(incubator);
+ QVERIFY(incubator.isReady());
+ QVERIFY(incubator.object());
+ QCOMPARE(incubator.object()->property("myValueFunctionCalled").toBool(), false);
+ QCOMPARE(incubator.object()->property("test1").toInt(), 502);
+ QCOMPARE(incubator.object()->property("test2").toInt(), 19);
+ delete incubator.object();
+ }
+}
+
+void tst_qqmlincubator::clearDuringCompletion()
+{
+ CompletionRegisteringType::clearMe();
+ SelfRegisteringType::clearMe();
+
+ QQmlComponent component(&engine, testFileUrl("clearDuringCompletion.qml"));
+ QVERIFY(component.isReady());
+
+ QQmlIncubator incubator;
+ component.create(incubator);
+
+ QCOMPARE(incubator.status(), QQmlIncubator::Loading);
+ QVERIFY(CompletionRegisteringType::me() == 0);
+
+ while (CompletionRegisteringType::me() == 0 && incubator.isLoading()) {
+ bool b = false;
+ controller.incubateWhile(&b);
+ }
+
+ QVERIFY(CompletionRegisteringType::me() != 0);
+ QVERIFY(SelfRegisteringType::me() != 0);
+ QVERIFY(incubator.isLoading());
+
+ QPointer<QObject> srt = SelfRegisteringType::me();
+
+ incubator.clear();
+ QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
+ QCoreApplication::processEvents();
+ QVERIFY(incubator.isNull());
+ QVERIFY(srt.isNull());
+}
+
+void tst_qqmlincubator::objectDeletionAfterInit()
+{
+ QQmlComponent component(&engine, testFileUrl("clear.qml"));
+ QVERIFY(component.isReady());
+
+ struct MyIncubator : public QQmlIncubator
+ {
+ MyIncubator(QQmlIncubator::IncubationMode mode)
+ : QQmlIncubator(mode), obj(0) {}
+
+ virtual void setInitialState(QObject *o) {
+ obj = o;
+ }
+
+ QObject *obj;
+ };
+
+ SelfRegisteringType::clearMe();
+ MyIncubator incubator(QQmlIncubator::Asynchronous);
+ component.create(incubator);
+
+ while (!incubator.obj && incubator.isLoading()) {
+ bool b = false;
+ controller.incubateWhile(&b);
+ }
+
+ QVERIFY(incubator.isLoading());
+ QVERIFY(SelfRegisteringType::me() != 0);
+
+ delete incubator.obj;
+
+ incubator.clear();
+ QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
+ QCoreApplication::processEvents();
+ QVERIFY(incubator.isNull());
+}
+
+class Switcher : public QObject
+{
+ Q_OBJECT
+public:
+ Switcher(QQmlEngine *e) : QObject(), engine(e) { }
+
+ struct MyIncubator : public QQmlIncubator
+ {
+ MyIncubator(QQmlIncubator::IncubationMode mode, QObject *s)
+ : QQmlIncubator(mode), switcher(s) {}
+
+ virtual void setInitialState(QObject *o) {
+ if (o->objectName() == "switchMe")
+ connect(o, SIGNAL(switchMe()), switcher, SLOT(switchIt()));
+ }
+
+ QObject *switcher;
+ };
+
+ void start()
+ {
+ incubator = new MyIncubator(QQmlIncubator::Synchronous, this);
+ component = new QQmlComponent(engine, QQmlDataTest::instance()->testFileUrl("recursiveClear.1.qml"));
+ component->create(*incubator);
+ }
+
+ QQmlEngine *engine;
+ MyIncubator *incubator;
+ QQmlComponent *component;
+
+public slots:
+ void switchIt() {
+ component->deleteLater();
+ incubator->clear();
+ component = new QQmlComponent(engine, QQmlDataTest::instance()->testFileUrl("recursiveClear.2.qml"));
+ component->create(*incubator);
+ }
+};
+
+void tst_qqmlincubator::recursiveClear()
+{
+ Switcher switcher(&engine);
+ switcher.start();
+}
+
+void tst_qqmlincubator::statusChanged()
+{
+ class MyIncubator : public QQmlIncubator
+ {
+ public:
+ MyIncubator(QQmlIncubator::IncubationMode mode = QQmlIncubator::Asynchronous)
+ : QQmlIncubator(mode) {}
+
+ QList<int> statuses;
+ protected:
+ virtual void statusChanged(Status s) { statuses << s; }
+ virtual void setInitialState(QObject *) { statuses << -1; }
+ };
+
+ {
+ QQmlComponent component(&engine, testFileUrl("statusChanged.qml"));
+ QVERIFY(component.isReady());
+
+ MyIncubator incubator(QQmlIncubator::Synchronous);
+ component.create(incubator);
+ QVERIFY(incubator.isReady());
+ QCOMPARE(incubator.statuses.count(), 3);
+ QCOMPARE(incubator.statuses.at(0), int(QQmlIncubator::Loading));
+ QCOMPARE(incubator.statuses.at(1), -1);
+ QCOMPARE(incubator.statuses.at(2), int(QQmlIncubator::Ready));
+ delete incubator.object();
+ }
+
+ {
+ QQmlComponent component(&engine, testFileUrl("statusChanged.qml"));
+ QVERIFY(component.isReady());
+
+ MyIncubator incubator(QQmlIncubator::Asynchronous);
+ component.create(incubator);
+ QVERIFY(incubator.isLoading());
+ QCOMPARE(incubator.statuses.count(), 1);
+ QCOMPARE(incubator.statuses.at(0), int(QQmlIncubator::Loading));
+
+ {
+ bool b = true;
+ controller.incubateWhile(&b);
+ }
+
+ QCOMPARE(incubator.statuses.count(), 3);
+ QCOMPARE(incubator.statuses.at(0), int(QQmlIncubator::Loading));
+ QCOMPARE(incubator.statuses.at(1), -1);
+ QCOMPARE(incubator.statuses.at(2), int(QQmlIncubator::Ready));
+ delete incubator.object();
+ }
+
+ {
+ QQmlComponent component2(&engine, testFileUrl("statusChanged.nested.qml"));
+ QVERIFY(component2.isReady());
+
+ MyIncubator incubator(QQmlIncubator::Asynchronous);
+ component2.create(incubator);
+ QVERIFY(incubator.isLoading());
+ QCOMPARE(incubator.statuses.count(), 1);
+ QCOMPARE(incubator.statuses.at(0), int(QQmlIncubator::Loading));
+
+ {
+ bool b = true;
+ controller.incubateWhile(&b);
+ }
+
+ QVERIFY(incubator.isReady());
+ QCOMPARE(incubator.statuses.count(), 3);
+ QCOMPARE(incubator.statuses.at(0), int(QQmlIncubator::Loading));
+ QCOMPARE(incubator.statuses.at(1), -1);
+ QCOMPARE(incubator.statuses.at(2), int(QQmlIncubator::Ready));
+ delete incubator.object();
+ }
+}
+
+void tst_qqmlincubator::asynchronousIfNested()
+{
+ // Asynchronous if nested within a finalized context behaves synchronously
+ {
+ QQmlComponent component(&engine, testFileUrl("asynchronousIfNested.1.qml"));
+ QVERIFY(component.isReady());
+
+ QObject *object = component.create();
+ QVERIFY(object != 0);
+ QCOMPARE(object->property("a").toInt(), 10);
+
+ QQmlIncubator incubator(QQmlIncubator::AsynchronousIfNested);
+ component.create(incubator, 0, qmlContext(object));
+
+ QVERIFY(incubator.isReady());
+ QVERIFY(incubator.object());
+ QCOMPARE(incubator.object()->property("a").toInt(), 10);
+ delete incubator.object();
+ delete object;
+ }
+
+ // Asynchronous if nested within an executing context behaves asynchronously, but prevents
+ // the parent from finishing
+ {
+ SelfRegisteringType::clearMe();
+
+ QQmlComponent component(&engine, testFileUrl("asynchronousIfNested.2.qml"));
+ QVERIFY(component.isReady());
+
+ QQmlIncubator incubator;
+ component.create(incubator);
+
+ QVERIFY(incubator.isLoading());
+ QVERIFY(SelfRegisteringType::me() == 0);
+ while (SelfRegisteringType::me() == 0 && incubator.isLoading()) {
+ bool b = false;
+ controller.incubateWhile(&b);
+ }
+
+ QVERIFY(SelfRegisteringType::me() != 0);
+ QVERIFY(incubator.isLoading());
+
+ QQmlIncubator nested(QQmlIncubator::AsynchronousIfNested);
+ component.create(nested, 0, qmlContext(SelfRegisteringType::me()));
+ QVERIFY(nested.isLoading());
+
+ while (nested.isLoading()) {
+ QVERIFY(incubator.isLoading());
+ bool b = false;
+ controller.incubateWhile(&b);
+ }
+
+ QVERIFY(nested.isReady());
+ QVERIFY(incubator.isLoading());
+
+ {
+ bool b = true;
+ controller.incubateWhile(&b);
+ }
+
+ QVERIFY(nested.isReady());
+ QVERIFY(incubator.isReady());
+
+ delete nested.object();
+ delete incubator.object();
+ }
+
+ // AsynchronousIfNested within a synchronous AsynchronousIfNested behaves synchronously
+ {
+ SelfRegisteringType::clearMe();
+
+ QQmlComponent component(&engine, testFileUrl("asynchronousIfNested.3.qml"));
+ QVERIFY(component.isReady());
+
+ struct CallbackData {
+ CallbackData(QQmlEngine *e) : engine(e), pass(false) {}
+ QQmlEngine *engine;
+ bool pass;
+ static void callback(CallbackRegisteringType *o, void *data) {
+ CallbackData *d = (CallbackData *)data;
+
+ QQmlComponent c(d->engine, QQmlDataTest::instance()->testFileUrl("asynchronousIfNested.1.qml"));
+ if (!c.isReady()) return;
+
+ QQmlIncubator incubator(QQmlIncubator::AsynchronousIfNested);
+ c.create(incubator, 0, qmlContext(o));
+
+ if (!incubator.isReady()) return;
+
+ if (incubator.object()->property("a").toInt() != 10) return;
+
+ d->pass = true;
+ }
+ };
+
+ CallbackData cd(&engine);
+ CallbackRegisteringType::registerCallback(&CallbackData::callback, &cd);
+
+ QQmlIncubator incubator(QQmlIncubator::AsynchronousIfNested);
+ component.create(incubator);
+
+ QVERIFY(incubator.isReady());
+ QCOMPARE(cd.pass, true);
+
+ delete incubator.object();
+ }
+}
+
+void tst_qqmlincubator::nestedComponent()
+{
+ QQmlComponent component(&engine, testFileUrl("nestedComponent.qml"));
+ QVERIFY(component.isReady());
+
+ QObject *object = component.create();
+
+ QQmlComponent *nested = object->property("c").value<QQmlComponent*>();
+ QVERIFY(nested);
+ QVERIFY(nested->isReady());
+
+ // Test without incubator
+ {
+ QObject *nestedObject = nested->create();
+ QCOMPARE(nestedObject->property("value").toInt(), 19988);
+ delete nestedObject;
+ }
+
+ // Test with incubator
+ {
+ QQmlIncubator incubator(QQmlIncubator::Synchronous);
+ nested->create(incubator);
+ QVERIFY(incubator.isReady());
+ QVERIFY(incubator.object());
+ QCOMPARE(incubator.object()->property("value").toInt(), 19988);
+ delete incubator.object();
+ }
+
+ delete object;
+}
+
+// Checks that a new AsynchronousIfNested incubator can be correctly started in the
+// statusChanged() callback of another.
+void tst_qqmlincubator::chainedAsynchronousIfNested()
+{
+ SelfRegisteringType::clearMe();
+
+ QQmlComponent component(&engine, testFileUrl("chainedAsynchronousIfNested.qml"));
+ QVERIFY(component.isReady());
+
+ QQmlIncubator incubator(QQmlIncubator::Asynchronous);
+ component.create(incubator);
+
+ QVERIFY(incubator.isLoading());
+ QVERIFY(SelfRegisteringType::me() == 0);
+
+ while (SelfRegisteringType::me() == 0 && incubator.isLoading()) {
+ bool b = false;
+ controller.incubateWhile(&b);
+ }
+
+ QVERIFY(SelfRegisteringType::me() != 0);
+ QVERIFY(incubator.isLoading());
+
+ struct MyIncubator : public QQmlIncubator {
+ MyIncubator(MyIncubator *next, QQmlComponent *component, QQmlContext *ctxt)
+ : QQmlIncubator(AsynchronousIfNested), next(next), component(component), ctxt(ctxt) {}
+
+ protected:
+ virtual void statusChanged(Status s) {
+ if (s == Ready && next)
+ component->create(*next, 0, ctxt);
+ }
+
+ private:
+ MyIncubator *next;
+ QQmlComponent *component;
+ QQmlContext *ctxt;
+ };
+
+ MyIncubator incubator2(0, &component, 0);
+ MyIncubator incubator1(&incubator2, &component, qmlContext(SelfRegisteringType::me()));
+
+ component.create(incubator1, 0, qmlContext(SelfRegisteringType::me()));
+
+ QVERIFY(incubator.isLoading());
+ QVERIFY(incubator1.isLoading());
+ QVERIFY(incubator2.isNull());
+
+ while (incubator1.isLoading()) {
+ QVERIFY(incubator.isLoading());
+ QVERIFY(incubator1.isLoading());
+ QVERIFY(incubator2.isNull());
+
+ bool b = false;
+ controller.incubateWhile(&b);
+ }
+
+ QVERIFY(incubator.isLoading());
+ QVERIFY(incubator1.isReady());
+ QVERIFY(incubator2.isLoading());
+
+ while (incubator2.isLoading()) {
+ QVERIFY(incubator.isLoading());
+ QVERIFY(incubator1.isReady());
+ QVERIFY(incubator2.isLoading());
+
+ bool b = false;
+ controller.incubateWhile(&b);
+ }
+
+ QVERIFY(incubator.isLoading());
+ QVERIFY(incubator1.isReady());
+ QVERIFY(incubator2.isReady());
+
+ {
+ bool b = true;
+ controller.incubateWhile(&b);
+ }
+
+ QVERIFY(incubator.isReady());
+ QVERIFY(incubator1.isReady());
+ QVERIFY(incubator2.isReady());
+}
+
+// Checks that new AsynchronousIfNested incubators can be correctly chained if started in
+// componentCompleted().
+void tst_qqmlincubator::chainedAsynchronousIfNestedOnCompleted()
+{
+ SelfRegisteringType::clearMe();
+
+ QQmlComponent component(&engine, testFileUrl("chainInCompletion.qml"));
+ QVERIFY(component.isReady());
+
+ QQmlComponent c1(&engine, testFileUrl("chainedAsynchronousIfNested.qml"));
+ QVERIFY(c1.isReady());
+
+ struct MyIncubator : public QQmlIncubator {
+ MyIncubator(MyIncubator *next, QQmlComponent *component, QQmlContext *ctxt)
+ : QQmlIncubator(AsynchronousIfNested), next(next), component(component), ctxt(ctxt) {}
+
+ protected:
+ virtual void statusChanged(Status s) {
+ if (s == Ready && next) {
+ component->create(*next, 0, ctxt);
+ }
+ }
+
+ private:
+ MyIncubator *next;
+ QQmlComponent *component;
+ QQmlContext *ctxt;
+ };
+
+ struct CallbackData {
+ CallbackData(QQmlComponent *c, MyIncubator *i, QQmlContext *ct)
+ : component(c), incubator(i), ctxt(ct) {}
+ QQmlComponent *component;
+ MyIncubator *incubator;
+ QQmlContext *ctxt;
+ static void callback(CompletionCallbackType *, void *data) {
+ CallbackData *d = (CallbackData *)data;
+ d->component->create(*d->incubator, 0, d->ctxt);
+ }
+ };
+
+ QQmlIncubator incubator(QQmlIncubator::Asynchronous);
+ component.create(incubator);
+
+ QVERIFY(incubator.isLoading());
+ QVERIFY(SelfRegisteringType::me() == 0);
+
+ while (SelfRegisteringType::me() == 0 && incubator.isLoading()) {
+ bool b = false;
+ controller.incubateWhile(&b);
+ }
+
+ QVERIFY(SelfRegisteringType::me() != 0);
+ QVERIFY(incubator.isLoading());
+
+ MyIncubator incubator3(0, &c1, qmlContext(SelfRegisteringType::me()));
+ MyIncubator incubator2(&incubator3, &c1, qmlContext(SelfRegisteringType::me()));
+ MyIncubator incubator1(&incubator2, &c1, qmlContext(SelfRegisteringType::me()));
+
+ // start incubator1 in componentComplete
+ CallbackData cd(&c1, &incubator1, qmlContext(SelfRegisteringType::me()));
+ CompletionCallbackType::registerCallback(&CallbackData::callback, &cd);
+
+ while (!incubator1.isLoading()) {
+ QVERIFY(incubator.isLoading());
+ QVERIFY(incubator2.isNull());
+ QVERIFY(incubator3.isNull());
+
+ bool b = false;
+ controller.incubateWhile(&b);
+ }
+
+ QVERIFY(incubator.isLoading());
+ QVERIFY(incubator1.isLoading());
+ QVERIFY(incubator2.isNull());
+ QVERIFY(incubator3.isNull());
+
+ while (incubator1.isLoading()) {
+ QVERIFY(incubator.isLoading());
+ QVERIFY(incubator1.isLoading());
+ QVERIFY(incubator2.isNull());
+ QVERIFY(incubator3.isNull());
+
+ bool b = false;
+ controller.incubateWhile(&b);
+ }
+
+ QVERIFY(incubator.isLoading());
+ QVERIFY(incubator1.isReady());
+ QVERIFY(incubator2.isLoading());
+ QVERIFY(incubator3.isNull());
+
+ while (incubator2.isLoading()) {
+ QVERIFY(incubator.isLoading());
+ QVERIFY(incubator1.isReady());
+ QVERIFY(incubator2.isLoading());
+ QVERIFY(incubator3.isNull());
+
+ bool b = false;
+ controller.incubateWhile(&b);
+ }
+
+ QVERIFY(incubator.isLoading());
+ QVERIFY(incubator1.isReady());
+ QVERIFY(incubator2.isReady());
+ QVERIFY(incubator3.isLoading());
+
+ while (incubator3.isLoading()) {
+ QVERIFY(incubator.isLoading());
+ QVERIFY(incubator1.isReady());
+ QVERIFY(incubator2.isReady());
+ QVERIFY(incubator3.isLoading());
+
+ bool b = false;
+ controller.incubateWhile(&b);
+ }
+
+ {
+ bool b = true;
+ controller.incubateWhile(&b);
+ }
+
+ QVERIFY(incubator.isReady());
+ QVERIFY(incubator1.isReady());
+ QVERIFY(incubator2.isReady());
+ QVERIFY(incubator3.isReady());
+}
+
+void tst_qqmlincubator::selfDelete()
+{
+ struct MyIncubator : public QQmlIncubator {
+ MyIncubator(bool *done, Status status, IncubationMode mode)
+ : QQmlIncubator(mode), done(done), status(status) {}
+
+ protected:
+ virtual void statusChanged(Status s) {
+ if (s == status) {
+ *done = true;
+ if (s == Ready) delete object();
+ delete this;
+ }
+ }
+
+ private:
+ bool *done;
+ Status status;
+ };
+
+ {
+ QQmlComponent component(&engine, testFileUrl("selfDelete.qml"));
+
+#define DELETE_TEST(status, mode) { \
+ bool done = false; \
+ component.create(*(new MyIncubator(&done, status, mode))); \
+ bool True = true; \
+ controller.incubateWhile(&True); \
+ QVERIFY(done == true); \
+ }
+
+ DELETE_TEST(QQmlIncubator::Loading, QQmlIncubator::Synchronous);
+ DELETE_TEST(QQmlIncubator::Ready, QQmlIncubator::Synchronous);
+ DELETE_TEST(QQmlIncubator::Loading, QQmlIncubator::Asynchronous);
+ DELETE_TEST(QQmlIncubator::Ready, QQmlIncubator::Asynchronous);
+
+#undef DELETE_TEST
+ }
+
+ // Delete within error status
+ {
+ SelfRegisteringType::clearMe();
+
+ QQmlComponent component(&engine, testFileUrl("objectDeleted.qml"));
+ QVERIFY(component.isReady());
+
+ bool done = false;
+ MyIncubator *incubator = new MyIncubator(&done, QQmlIncubator::Error,
+ QQmlIncubator::Asynchronous);
+ component.create(*incubator);
+
+ QCOMPARE(incubator->QQmlIncubator::status(), QQmlIncubator::Loading);
+ QVERIFY(SelfRegisteringType::me() == 0);
+
+ while (SelfRegisteringType::me() == 0 && incubator->isLoading()) {
+ bool b = false;
+ controller.incubateWhile(&b);
+ }
+
+ QVERIFY(SelfRegisteringType::me() != 0);
+ QVERIFY(incubator->isLoading());
+
+ delete SelfRegisteringType::me();
+
+ {
+ bool b = true;
+ controller.incubateWhile(&b);
+ }
+
+ QVERIFY(done);
+ }
+}
+
+// Test that QML doesn't crash if the context is deleted prior to the incubator
+// first executing.
+void tst_qqmlincubator::contextDelete()
+{
+ QQmlContext *context = new QQmlContext(engine.rootContext());
+ QQmlComponent component(&engine, testFileUrl("contextDelete.qml"));
+
+ QQmlIncubator incubator;
+ component.create(incubator, context);
+
+ delete context;
+
+ {
+ bool b = false;
+ controller.incubateWhile(&b);
+ }
+}
+
+QTEST_MAIN(tst_qqmlincubator)
+
+#include "tst_qqmlincubator.moc"