From: Aaron Kennedy Date: Thu, 6 Oct 2011 07:33:39 +0000 (+1000) Subject: QDeclarativeIncubator autotests X-Git-Url: http://git.silmor.de/gitweb/?a=commitdiff_plain;h=d517e9c541e5869e87006faeccd60be5690bee5b;p=konrad%2Fqtdeclarative.git QDeclarativeIncubator autotests Change-Id: I5c4594c40fccfe6cb8b198a5fd6c11b468b0562e Reviewed-on: http://codereview.qt-project.org/6118 Reviewed-by: Aaron Kennedy --- diff --git a/src/declarative/qml/qdeclarativecomponent.cpp b/src/declarative/qml/qdeclarativecomponent.cpp index e4c3003..f9be4ca 100644 --- a/src/declarative/qml/qdeclarativecomponent.cpp +++ b/src/declarative/qml/qdeclarativecomponent.cpp @@ -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(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); } diff --git a/src/declarative/qml/qdeclarativeincubator.cpp b/src/declarative/qml/qdeclarativeincubator.cpp index ae1ae2d..9644d3a 100644 --- a/src/declarative/qml/qdeclarativeincubator.cpp +++ b/src/declarative/qml/qdeclarativeincubator.cpp @@ -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) { diff --git a/src/declarative/qml/qdeclarativevme.cpp b/src/declarative/qml/qdeclarativevme.cpp index 53bcd8c..dbef83b 100644 --- a/src/declarative/qml/qdeclarativevme.cpp +++ b/src/declarative/qml/qdeclarativevme.cpp @@ -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 *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(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 diff --git a/src/declarative/qml/qdeclarativevme_p.h b/src/declarative/qml/qdeclarativevme_p.h index 5809ccc..19db2ef 100644 --- a/src/declarative/qml/qdeclarativevme_p.h +++ b/src/declarative/qml/qdeclarativevme_p.h @@ -119,7 +119,8 @@ public: QDeclarativeComponentAttached *componentAttached; QList 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 bindValues; QFiniteStack 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 index 0000000..8a3f46e --- /dev/null +++ b/tests/auto/declarative/qdeclarativeincubator/data/AsynchronousIfNestedType.qml @@ -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 index 0000000..18ff4aa --- /dev/null +++ b/tests/auto/declarative/qdeclarativeincubator/data/asynchronousIfNested.1.qml @@ -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 index 0000000..3f6cd93 --- /dev/null +++ b/tests/auto/declarative/qdeclarativeincubator/data/asynchronousIfNested.2.qml @@ -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 index 0000000..4b6b0bd --- /dev/null +++ b/tests/auto/declarative/qdeclarativeincubator/data/nestedComponent.js @@ -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 index 0000000..dd20707 --- /dev/null +++ b/tests/auto/declarative/qdeclarativeincubator/data/nestedComponent.qml @@ -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 index 0000000..18ff4aa --- /dev/null +++ b/tests/auto/declarative/qdeclarativeincubator/data/statusChanged.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +QtObject { + property int a: 10 +} diff --git a/tests/auto/declarative/qdeclarativeincubator/tst_qdeclarativeincubator.cpp b/tests/auto/declarative/qdeclarativeincubator/tst_qdeclarativeincubator.cpp index 52ddf81..7e27146 100644 --- a/tests/auto/declarative/qdeclarativeincubator/tst_qdeclarativeincubator.cpp +++ b/tests/auto/declarative/qdeclarativeincubator/tst_qdeclarativeincubator.cpp @@ -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 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(); + 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"