Skip to content

Commit

Permalink
QDeclarativeIncubator autotests
Browse files Browse the repository at this point in the history
Change-Id: I5c4594c40fccfe6cb8b198a5fd6c11b468b0562e
Reviewed-on: http://codereview.qt-project.org/6118
Reviewed-by: Aaron Kennedy <aaron.kennedy@nokia.com>
  • Loading branch information
Aaron Kennedy authored and Qt by Nokia committed Oct 7, 2011
1 parent ff79fc6 commit d517e9c
Show file tree
Hide file tree
Showing 11 changed files with 234 additions and 32 deletions.
27 changes: 2 additions & 25 deletions src/declarative/qml/qdeclarativecomponent.cpp
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
Expand Down
18 changes: 16 additions & 2 deletions src/declarative/qml/qdeclarativeincubator.cpp
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -314,7 +324,11 @@ void QDeclarativeIncubatorPrivate::incubate(QDeclarativeVME::Interrupt &i)

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) {
Expand Down
31 changes: 29 additions & 2 deletions src/declarative/qml/qdeclarativevme.cpp
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -1204,6 +1230,7 @@ void QDeclarativeVME::reset()
finalizeCallbacks.clear();
states.clear();
rootContext = 0;
creationContext = 0;
}

// Must be called with a handle scope and context
Expand Down
4 changes: 3 additions & 1 deletion src/declarative/qml/qdeclarativevme_p.h
Expand Up @@ -119,7 +119,8 @@ class QDeclarativeVME
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();

Expand Down Expand Up @@ -150,6 +151,7 @@ class QDeclarativeVME
QFiniteStack<QDeclarativeAbstractBinding *> bindValues;
QFiniteStack<QDeclarativeParserStatus *> parserStatus;
QDeclarativeGuardedContextData rootContext;
QDeclarativeGuardedContextData creationContext;

struct State {
enum Flag { Deferred = 0x00000001 };
Expand Down
@@ -0,0 +1,7 @@
import Qt.test 1.0

SelfRegistering {
property int dummy1: 11
property int dummy2: 19
}

@@ -0,0 +1,5 @@
import QtQuick 2.0

QtObject {
property int a: 10
}
@@ -0,0 +1,6 @@
import QtQuick 2.0

QtObject {
property QtObject o: AsynchronousIfNestedType { }
property int dummy: 11
}
@@ -0,0 +1 @@
var value = 19988
@@ -0,0 +1,10 @@
import QtQuick 2.0
import "nestedComponent.js" as NestedJS

QtObject {
property Component c: Component {
QtObject {
property int value: NestedJS.value
}
}
}
@@ -0,0 +1,5 @@
import QtQuick 2.0

QtObject {
property int a: 10
}
Expand Up @@ -79,6 +79,9 @@ private slots:
void setInitialState();
void clearDuringCompletion();
void recursiveClear();
void statusChanged();
void asynchronousIfNested();
void nestedComponent();

private:
QDeclarativeIncubationController controller;
Expand Down Expand Up @@ -236,8 +239,6 @@ void tst_qdeclarativeincubator::clear()
delete obj;
QVERIFY(obj.isNull());
}

// XXX Clear in error state
}

void tst_qdeclarativeincubator::noIncubationController()
Expand Down Expand Up @@ -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"

0 comments on commit d517e9c

Please sign in to comment.