QDeclarativeIncubator autotests
authorAaron Kennedy <aaron.kennedy@nokia.com>
Thu, 6 Oct 2011 07:33:39 +0000 (17:33 +1000)
committerQt by Nokia <qt-info@nokia.com>
Fri, 7 Oct 2011 02:26:50 +0000 (04:26 +0200)
Change-Id: I5c4594c40fccfe6cb8b198a5fd6c11b468b0562e
Reviewed-on: http://codereview.qt-project.org/6118
Reviewed-by: Aaron Kennedy <aaron.kennedy@nokia.com>

src/declarative/qml/qdeclarativecomponent.cpp
src/declarative/qml/qdeclarativeincubator.cpp
src/declarative/qml/qdeclarativevme.cpp
src/declarative/qml/qdeclarativevme_p.h
tests/auto/declarative/qdeclarativeincubator/data/AsynchronousIfNestedType.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeincubator/data/asynchronousIfNested.1.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeincubator/data/asynchronousIfNested.2.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeincubator/data/nestedComponent.js [new file with mode: 0644]
tests/auto/declarative/qdeclarativeincubator/data/nestedComponent.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeincubator/data/statusChanged.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeincubator/tst_qdeclarativeincubator.cpp

index e4c3003..f9be4ca 100644 (file)
@@ -747,33 +747,10 @@ QDeclarativeComponentPrivate::beginCreate(QDeclarativeContextData *context)
         QDeclarativeDebugTrace::startRange(QDeclarativeDebugTrace::Creating);
 
     enginePriv->referenceScarceResources();
-    state.vme.init(context, cc, start);
+    state.vme.init(context, cc, start, creationContext);
     QObject *rv = state.vme.execute(&state.errors);
     enginePriv->dereferenceScarceResources();
 
-    if (rv && creationContext && start != -1) {
-        // A component that is logically created within another component instance shares the same
-        // instances of script imports.  For example:
-        //
-        //     import QtQuick 1.0
-        //     import "test.js" as Test
-        //     ListView {
-        //         model: Test.getModel()
-        //         delegate: Component {
-        //             Text { text: Test.getValue(index); }
-        //         }
-        //     }
-        //
-        // Has the same "Test" instance.  To implement this, we simply copy the v8 handles into
-        // the inner context.  We have to create a fresh persistent handle for each to prevent 
-        // double dispose.  It is possible we could do this more efficiently using some form of
-        // referencing instead.
-        QDeclarativeContextData *objectContext = QDeclarativeData::get(rv, false)->outerContext;
-        objectContext->importedScripts = creationContext->importedScripts;
-        for (int ii = 0; ii < objectContext->importedScripts.count(); ++ii)
-            objectContext->importedScripts[ii] = qPersistentNew<v8::Object>(objectContext->importedScripts[ii]);
-    }
-
     if (rv) {
         QDeclarativeData *ddata = QDeclarativeData::get(rv);
         Q_ASSERT(ddata);
@@ -919,7 +896,7 @@ void QDeclarativeComponent::create(QDeclarativeIncubator &i, QDeclarativeContext
     QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(d->engine);
 
     p->component = d->cc; p->component->addref();
-    p->vme.init(contextData, d->cc, d->start);
+    p->vme.init(contextData, d->cc, d->start, d->creationContext);
 
     enginePriv->incubate(i, forContextData);
 }
index ae1ae2d..9644d3a 100644 (file)
@@ -81,6 +81,9 @@ void QDeclarativeEnginePrivate::incubate(QDeclarativeIncubator &i, QDeclarativeC
 
     inProgressCreations++;
 
+    Q_ASSERT(i.isLoading());
+    i.statusChanged(i.status());
+
     if (mode == QDeclarativeIncubator::Synchronous) {
         QDeclarativeVME::Interrupt i;
         p->incubate(i);
@@ -247,6 +250,8 @@ void QDeclarativeIncubatorPrivate::incubate(QDeclarativeVME::Interrupt &i)
     bool guardOk = vmeGuard.isOK();
     vmeGuard.clear();
 
+    QDeclarativeIncubator::Status oldStatus = q->status();
+
     if (!guardOk) {
         QDeclarativeError error;
         error.setUrl(component->url);
@@ -285,7 +290,12 @@ void QDeclarativeIncubatorPrivate::incubate(QDeclarativeVME::Interrupt &i)
         else
             progress = QDeclarativeIncubatorPrivate::Completed;
 
-        q->statusChanged(q->status());
+        QDeclarativeIncubator::Status newStatus = q->status();
+        
+        if (oldStatus != newStatus) {
+            q->statusChanged(newStatus);
+            oldStatus = newStatus;
+        }
 
         if (watcher.hasRecursed())
             return;
@@ -314,7 +324,11 @@ finishIncubate:
 
         enginePriv->inProgressCreations--;
 
-        q->statusChanged(q->status());
+        QDeclarativeIncubator::Status newStatus = q->status();
+        if (newStatus != oldStatus) {
+            q->statusChanged(newStatus);
+            oldStatus = newStatus;
+        }
 
         if (0 == enginePriv->inProgressCreations) {
             while (enginePriv->erroredBindings) {
index 53bcd8c..dbef83b 100644 (file)
@@ -91,12 +91,17 @@ using namespace QDeclarativeVMETypes;
         goto exceptionExit; \
     }
 
-void QDeclarativeVME::init(QDeclarativeContextData *ctxt, QDeclarativeCompiledData *comp, int start)
+void QDeclarativeVME::init(QDeclarativeContextData *ctxt, QDeclarativeCompiledData *comp, int start,
+                           QDeclarativeContextData *creation)
 {
     Q_ASSERT(ctxt);
     Q_ASSERT(comp);
 
-    if (start == -1) start = 0;
+    if (start == -1) {
+        start = 0;
+    } else {
+        creationContext = creation;
+    }
 
     State initState;
     initState.context = ctxt;
@@ -307,6 +312,27 @@ QObject *QDeclarativeVME::run(QList<QDeclarativeError> *errors,
                 rootContext = CTXT;
                 rootContext->activeVME = this;
             }
+            if (states.count() == 1 && !creationContext.isNull()) {
+                // A component that is logically created within another component instance shares the 
+                // same instances of script imports.  For example:
+                //
+                //     import QtQuick 1.0
+                //     import "test.js" as Test
+                //     ListView {
+                //         model: Test.getModel()
+                //         delegate: Component {
+                //             Text { text: Test.getValue(index); }
+                //         }
+                //     }
+                //
+                // Has the same "Test" instance.  To implement this, we simply copy the v8 handles into
+                // the inner context.  We have to create a fresh persistent handle for each to prevent 
+                // double dispose.  It is possible we could do this more efficiently using some form of
+                // referencing instead.
+                CTXT->importedScripts = creationContext->importedScripts;
+                for (int ii = 0; ii < CTXT->importedScripts.count(); ++ii)
+                    CTXT->importedScripts[ii] = qPersistentNew<v8::Object>(CTXT->importedScripts[ii]);
+            }
         QML_END_INSTR(Init)
 
         QML_BEGIN_INSTR(DeferInit)
@@ -1204,6 +1230,7 @@ void QDeclarativeVME::reset()
     finalizeCallbacks.clear();
     states.clear();
     rootContext = 0;
+    creationContext = 0;
 }
 
 // Must be called with a handle scope and context
index 5809ccc..19db2ef 100644 (file)
@@ -119,7 +119,8 @@ public:
     QDeclarativeComponentAttached *componentAttached;
     QList<QDeclarativeEnginePrivate::FinalizeCallback> finalizeCallbacks;
 
-    void init(QDeclarativeContextData *, QDeclarativeCompiledData *, int start);
+    void init(QDeclarativeContextData *, QDeclarativeCompiledData *, int start,
+              QDeclarativeContextData * = 0);
     bool initDeferred(QObject *);
     void reset();
 
@@ -150,6 +151,7 @@ private:
     QFiniteStack<QDeclarativeAbstractBinding *> bindValues;
     QFiniteStack<QDeclarativeParserStatus *> parserStatus;
     QDeclarativeGuardedContextData rootContext;
+    QDeclarativeGuardedContextData creationContext;
 
     struct State {
         enum Flag { Deferred = 0x00000001 };
diff --git a/tests/auto/declarative/qdeclarativeincubator/data/AsynchronousIfNestedType.qml b/tests/auto/declarative/qdeclarativeincubator/data/AsynchronousIfNestedType.qml
new file mode 100644 (file)
index 0000000..8a3f46e
--- /dev/null
@@ -0,0 +1,7 @@
+import Qt.test 1.0
+
+SelfRegistering {
+    property int dummy1: 11
+    property int dummy2: 19
+}
+
diff --git a/tests/auto/declarative/qdeclarativeincubator/data/asynchronousIfNested.1.qml b/tests/auto/declarative/qdeclarativeincubator/data/asynchronousIfNested.1.qml
new file mode 100644 (file)
index 0000000..18ff4aa
--- /dev/null
@@ -0,0 +1,5 @@
+import QtQuick 2.0
+
+QtObject {
+    property int a: 10
+}
diff --git a/tests/auto/declarative/qdeclarativeincubator/data/asynchronousIfNested.2.qml b/tests/auto/declarative/qdeclarativeincubator/data/asynchronousIfNested.2.qml
new file mode 100644 (file)
index 0000000..3f6cd93
--- /dev/null
@@ -0,0 +1,6 @@
+import QtQuick 2.0
+
+QtObject {
+    property QtObject o: AsynchronousIfNestedType { }
+    property int dummy: 11
+}
diff --git a/tests/auto/declarative/qdeclarativeincubator/data/nestedComponent.js b/tests/auto/declarative/qdeclarativeincubator/data/nestedComponent.js
new file mode 100644 (file)
index 0000000..4b6b0bd
--- /dev/null
@@ -0,0 +1 @@
+var value = 19988
diff --git a/tests/auto/declarative/qdeclarativeincubator/data/nestedComponent.qml b/tests/auto/declarative/qdeclarativeincubator/data/nestedComponent.qml
new file mode 100644 (file)
index 0000000..dd20707
--- /dev/null
@@ -0,0 +1,10 @@
+import QtQuick 2.0
+import "nestedComponent.js" as NestedJS
+
+QtObject {
+    property Component c: Component {
+        QtObject {
+            property int value: NestedJS.value
+        }
+    }
+}
diff --git a/tests/auto/declarative/qdeclarativeincubator/data/statusChanged.qml b/tests/auto/declarative/qdeclarativeincubator/data/statusChanged.qml
new file mode 100644 (file)
index 0000000..18ff4aa
--- /dev/null
@@ -0,0 +1,5 @@
+import QtQuick 2.0
+
+QtObject {
+    property int a: 10
+}
index 52ddf81..7e27146 100644 (file)
@@ -79,6 +79,9 @@ private slots:
     void setInitialState();
     void clearDuringCompletion();
     void recursiveClear();
+    void statusChanged();
+    void asynchronousIfNested();
+    void nestedComponent();
 
 private:
     QDeclarativeIncubationController controller;
@@ -236,8 +239,6 @@ void tst_qdeclarativeincubator::clear()
     delete obj;
     QVERIFY(obj.isNull());
     }
-
-    // XXX Clear in error state
 }
 
 void tst_qdeclarativeincubator::noIncubationController()
@@ -473,6 +474,153 @@ void tst_qdeclarativeincubator::recursiveClear()
     switcher.start();
 }
 
+void tst_qdeclarativeincubator::statusChanged()
+{
+    class MyIncubator : public QDeclarativeIncubator
+    {
+    public:
+        MyIncubator(QDeclarativeIncubator::IncubationMode mode = QDeclarativeIncubator::Asynchronous)
+        : QDeclarativeIncubator(mode) {}
+
+        QList<int> statuses;
+    protected:
+        virtual void statusChanged(Status s) { statuses << s; }
+        virtual void setInitialState(QObject *) { statuses << -1; }
+    };
+
+    QDeclarativeComponent component(&engine, TEST_FILE("statusChanged.qml"));
+    QVERIFY(component.isReady());
+
+    {
+    MyIncubator incubator(QDeclarativeIncubator::Synchronous);
+    component.create(incubator);
+    QVERIFY(incubator.isReady());
+    QCOMPARE(incubator.statuses.count(), 3);
+    QCOMPARE(incubator.statuses.at(0), int(QDeclarativeIncubator::Loading));
+    QCOMPARE(incubator.statuses.at(1), -1);
+    QCOMPARE(incubator.statuses.at(2), int(QDeclarativeIncubator::Ready));
+    delete incubator.object();
+    }
+
+    {
+    MyIncubator incubator(QDeclarativeIncubator::Asynchronous);
+    component.create(incubator);
+    QVERIFY(incubator.isLoading());
+    QCOMPARE(incubator.statuses.count(), 1);
+    QCOMPARE(incubator.statuses.at(0), int(QDeclarativeIncubator::Loading));
+
+    {
+    bool b = true;
+    controller.incubateWhile(&b);
+    }
+
+    QCOMPARE(incubator.statuses.count(), 3);
+    QCOMPARE(incubator.statuses.at(0), int(QDeclarativeIncubator::Loading));
+    QCOMPARE(incubator.statuses.at(1), -1);
+    QCOMPARE(incubator.statuses.at(2), int(QDeclarativeIncubator::Ready));
+    delete incubator.object();
+    }
+}
+
+void tst_qdeclarativeincubator::asynchronousIfNested()
+{
+    // Asynchronous if nested within a finalized context behaves synchronously
+    {
+    QDeclarativeComponent component(&engine, TEST_FILE("asynchronousIfNested.1.qml"));
+    QVERIFY(component.isReady());
+
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+    QCOMPARE(object->property("a").toInt(), 10);
+
+    QDeclarativeIncubator incubator(QDeclarativeIncubator::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();
+
+    QDeclarativeComponent component(&engine, TEST_FILE("asynchronousIfNested.2.qml"));
+    QVERIFY(component.isReady());
+
+    QDeclarativeIncubator 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());
+
+    QDeclarativeIncubator nested(QDeclarativeIncubator::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();
+    }
+}
+
+void tst_qdeclarativeincubator::nestedComponent()
+{
+    QDeclarativeComponent component(&engine, TEST_FILE("nestedComponent.qml"));
+    QVERIFY(component.isReady());
+
+    QObject *object = component.create();
+
+    QDeclarativeComponent *nested = object->property("c").value<QDeclarativeComponent*>();
+    QVERIFY(nested);
+    QVERIFY(nested->isReady());
+
+    // Test without incubator
+    {
+    QObject *nestedObject = nested->create();
+    QCOMPARE(nestedObject->property("value").toInt(), 19988);
+    delete nestedObject;
+    }
+
+    // Test with incubator
+    {
+    QDeclarativeIncubator incubator(QDeclarativeIncubator::Synchronous);
+    nested->create(incubator);
+    QVERIFY(incubator.isReady());
+    QVERIFY(incubator.object());
+    QCOMPARE(incubator.object()->property("value").toInt(), 19988);
+    delete incubator.object();
+    }
+
+    delete object;
+}
+
 QTEST_MAIN(tst_qdeclarativeincubator)
 
 #include "tst_qdeclarativeincubator.moc"