Allow initial property values to be defined with QSGLoader
authorChris Adams <christopher.adams@nokia.com>
Wed, 17 Aug 2011 07:46:54 +0000 (17:46 +1000)
committerQt by Nokia <qt-info@nokia.com>
Wed, 31 Aug 2011 08:11:56 +0000 (10:11 +0200)
This commit adds an "active" property to QSGLoader, which can be
used to delay instantiation of the item until the user wishes to
activate the loader.  The property is true by default in order to
maintain compatibility with previous behaviour.

The commit also adds a "setSource(v8object)" function to QSGLoader,
which behaves identically to setSource() property mutator except that
it takes a JavaScript object parameter which defines the initial
property values of the item (in a manner similar to that of
QDeclarativeComponent::createObject()).

Task-number: QTBUG-17009
Change-Id: Ifd824b518b60ef7aa3017c384835abb552e65cf1
Reviewed-on: http://codereview.qt.nokia.com/3364
Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com>
Reviewed-by: Michael Brasser <michael.brasser@nokia.com>

27 files changed:
src/declarative/items/qsgloader.cpp
src/declarative/items/qsgloader_p.h
src/declarative/items/qsgloader_p_p.h
src/declarative/qml/qdeclarativecomponent.cpp
src/declarative/qml/qdeclarativecomponent_p.h
tests/auto/declarative/qsgloader/data/ActiveComponent.qml [new file with mode: 0644]
tests/auto/declarative/qsgloader/data/InitialPropertyValuesComponent.qml [new file with mode: 0644]
tests/auto/declarative/qsgloader/data/InvalidSourceComponent.qml [new file with mode: 0644]
tests/auto/declarative/qsgloader/data/active.1.qml [new file with mode: 0644]
tests/auto/declarative/qsgloader/data/active.2.qml [new file with mode: 0644]
tests/auto/declarative/qsgloader/data/active.3.qml [new file with mode: 0644]
tests/auto/declarative/qsgloader/data/active.4.qml [new file with mode: 0644]
tests/auto/declarative/qsgloader/data/active.5.qml [new file with mode: 0644]
tests/auto/declarative/qsgloader/data/active.6.qml [new file with mode: 0644]
tests/auto/declarative/qsgloader/data/initialPropertyValues.1.qml [new file with mode: 0644]
tests/auto/declarative/qsgloader/data/initialPropertyValues.2.qml [new file with mode: 0644]
tests/auto/declarative/qsgloader/data/initialPropertyValues.3.qml [new file with mode: 0644]
tests/auto/declarative/qsgloader/data/initialPropertyValues.4.qml [new file with mode: 0644]
tests/auto/declarative/qsgloader/data/initialPropertyValues.5.qml [new file with mode: 0644]
tests/auto/declarative/qsgloader/data/initialPropertyValues.6.qml [new file with mode: 0644]
tests/auto/declarative/qsgloader/data/initialPropertyValues.7.qml [new file with mode: 0644]
tests/auto/declarative/qsgloader/data/initialPropertyValues.binding.qml [new file with mode: 0644]
tests/auto/declarative/qsgloader/data/initialPropertyValues.error.1.qml [new file with mode: 0644]
tests/auto/declarative/qsgloader/data/initialPropertyValues.error.2.qml [new file with mode: 0644]
tests/auto/declarative/qsgloader/data/initialPropertyValues.error.3.qml [new file with mode: 0644]
tests/auto/declarative/qsgloader/data/initialPropertyValues.error.4.qml [new file with mode: 0644]
tests/auto/declarative/qsgloader/tst_qsgloader.cpp

index 999c174..0a0a3b9 100644 (file)
 #include <private/qdeclarativeengine_p.h>
 #include <private/qdeclarativeglobal_p.h>
 
+#include <private/qdeclarativecomponent_p.h>
+
+#include <private/qv8_p.h>
+
 QT_BEGIN_NAMESPACE
 
 QSGLoaderPrivate::QSGLoaderPrivate()
     : item(0), component(0), ownComponent(false), updatingSize(false),
-      itemWidthValid(false), itemHeightValid(false)
+      itemWidthValid(false), itemHeightValid(false), active(true)
 {
 }
 
 QSGLoaderPrivate::~QSGLoaderPrivate()
 {
+    disposeInitialPropertyValues();
 }
 
 void QSGLoaderPrivate::itemGeometryChanged(QSGItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry)
@@ -72,6 +77,8 @@ void QSGLoaderPrivate::itemGeometryChanged(QSGItem *resizeItem, const QRectF &ne
 
 void QSGLoaderPrivate::clear()
 {
+    disposeInitialPropertyValues();
+
     if (ownComponent) {
         component->deleteLater();
         component = 0;
@@ -228,6 +235,59 @@ QSGLoader::~QSGLoader()
 }
 
 /*!
+    \qmlproperty bool QtQuick2::Loader::active
+    This property is \c true if the Loader is currently active.
+    The default value for the \l active property is \c true.
+
+    If the Loader is inactive, changing the \l source or \l sourceComponent
+    will not cause the item to be instantiated until the Loader is made active.
+
+    Setting the value to inactive will cause any \l item loaded by the loader
+    to be released, but will not affect the \l source or \l sourceComponent.
+
+    The \l status of an inactive loader is always \c Null.
+
+    \sa source, sourceComponent
+ */
+bool QSGLoader::active() const
+{
+    Q_D(const QSGLoader);
+    return d->active;
+}
+
+void QSGLoader::setActive(bool newVal)
+{
+    Q_D(QSGLoader);
+    if (d->active != newVal) {
+        d->active = newVal;
+        if (newVal == true) {
+            if (d->loadingFromSource) {
+                loadFromSource();
+            } else {
+                loadFromSourceComponent();
+            }
+            d->disposeInitialPropertyValues(); // release persistent handles
+        } else {
+            if (d->item) {
+                QSGItemPrivate *p = QSGItemPrivate::get(d->item);
+                p->removeItemChangeListener(d, QSGItemPrivate::Geometry);
+
+                // We can't delete immediately because our item may have triggered
+                // the Loader to load a different item.
+                d->item->setParentItem(0);
+                d->item->setVisible(false);
+                d->item->deleteLater();
+                d->item = 0;
+                emit itemChanged();
+            }
+            emit statusChanged();
+        }
+        emit activeChanged();
+    }
+}
+
+
+/*!
     \qmlproperty url QtQuick2::Loader::source
     This property holds the URL of the QML component to instantiate.
 
@@ -248,13 +308,30 @@ QUrl QSGLoader::source() const
 
 void QSGLoader::setSource(const QUrl &url)
 {
+    setSource(url, true); // clear previous values
+}
+
+void QSGLoader::setSource(const QUrl &url, bool needsClear)
+{
     Q_D(QSGLoader);
     if (d->source == url)
         return;
 
-    d->clear();
+    if (needsClear)
+        d->clear();
 
     d->source = url;
+    d->loadingFromSource = true;
+
+    if (d->active)
+        loadFromSource();
+    else
+        emit sourceChanged();
+}
+
+void QSGLoader::loadFromSource()
+{
+    Q_D(QSGLoader);
     if (d->source.isEmpty()) {
         emit sourceChanged();
         emit statusChanged();
@@ -308,6 +385,22 @@ void QSGLoader::setSourceComponent(QDeclarativeComponent *comp)
 
     d->component = comp;
     d->ownComponent = false;
+    d->loadingFromSource = false;
+
+    if (d->active)
+        loadFromSourceComponent();
+    else
+        emit sourceComponentChanged();
+}
+
+void QSGLoader::resetSourceComponent()
+{
+    setSourceComponent(0);
+}
+
+void QSGLoader::loadFromSourceComponent()
+{
+    Q_D(QSGLoader);
     if (!d->component) {
         emit sourceComponentChanged();
         emit statusChanged();
@@ -320,9 +413,95 @@ void QSGLoader::setSourceComponent(QDeclarativeComponent *comp)
         d->load();
 }
 
-void QSGLoader::resetSourceComponent()
+/*!
+    \qmlmethod object QtQuick2::Loader::setSource(url source, object properties)
+
+    Creates an object instance of the given \a source component that will have
+    the given \a properties. The \a properties argument is optional.  The instance
+    will be accessible via the \l item property once loading and instantiation
+    is complete.
+
+    If the \l active property is \c false at the time when this function is called,
+    the given \a source component will not be loaded but the \a source and initial
+    \a properties will be cached.  When the loader is made \l active, an instance of
+    the \a source component will be created with the initial \a properties set.
+
+    Setting the initial property values of an instance of a component in this manner
+    will \e not trigger any associated \l{Behavior}s.
+
+    Note that the cached \a properties will be cleared if the \l source or \l sourceComponent
+    is changed after calling this function but prior to setting the loader \l active.
+
+    Example:
+    \table
+    \row
+    \o
+    \qml
+    // ExampleComponent.qml
+    import QtQuick 2.0
+    Rectangle {
+        id: rect
+        color: "red"
+        width: 10
+        height: 10
+
+        Behavior on color {
+            NumberAnimation {
+                target: rect
+                property: "width"
+                to: (rect.width + 20)
+                duration: 0
+            }
+        }
+    }
+    \endqml
+    \o
+    \qml
+    // example.qml
+    import QtQuick 2.0
+    Item {
+        Loader {
+            id: squareLoader
+            onLoaded: console.log(squareLoader.item.width); // prints [10], not [30]
+        }
+
+        Component.onCompleted: {
+            squareLoader.setSource("ExampleComponent.qml", { "color": "blue" });
+            // will trigger the onLoaded code when complete.
+        }
+    }
+    \endqml
+    \endtable
+
+    \sa source, active
+*/
+void QSGLoader::setSource(QDeclarativeV8Function *args)
 {
-    setSourceComponent(0);
+    Q_ASSERT(args);
+    Q_D(QSGLoader);
+
+    bool ipvError = false;
+    args->returnValue(v8::Undefined());
+    v8::Handle<v8::Object> ipv = d->extractInitialPropertyValues(args, this, &ipvError);
+    if (ipvError)
+        return;
+
+    d->clear();
+    QUrl sourceUrl = d->resolveSourceUrl(args);
+    if (!ipv.IsEmpty()) {
+        d->initialPropertyValues = qPersistentNew(ipv);
+        d->qmlGlobalForIpv = qPersistentNew(args->qmlGlobal());
+    }
+
+    setSource(sourceUrl, false); // already cleared and set ipv above.
+}
+
+void QSGLoaderPrivate::disposeInitialPropertyValues()
+{
+    if (!initialPropertyValues.IsEmpty())
+        qPersistentDispose(initialPropertyValues);
+    if (!qmlGlobalForIpv.IsEmpty())
+        qPersistentDispose(qmlGlobalForIpv);
 }
 
 void QSGLoaderPrivate::load()
@@ -377,7 +556,7 @@ void QSGLoaderPrivate::_q_sourceLoaded()
             // component to be set to something else. In that case we just
             // need to cleanup.
             if (c)
-                c->completeCreate();
+                completeCreateWithInitialPropertyValues(c, obj, initialPropertyValues, qmlGlobalForIpv);
             delete obj;
             delete ctxt;
             return;
@@ -402,7 +581,7 @@ void QSGLoaderPrivate::_q_sourceLoaded()
             delete ctxt;
             source = QUrl();
         }
-        component->completeCreate();
+        completeCreateWithInitialPropertyValues(component, obj, initialPropertyValues, qmlGlobalForIpv);
         if (ownComponent)
             emit q->sourceChanged();
         else
@@ -419,7 +598,7 @@ void QSGLoaderPrivate::_q_sourceLoaded()
 
     This property holds the status of QML loading.  It can be one of:
     \list
-    \o Loader.Null - no QML source has been set
+    \o Loader.Null - the loader is inactive or no QML source has been set
     \o Loader.Ready - the QML source has been loaded
     \o Loader.Loading - the QML source is currently being loaded
     \o Loader.Error - an error occurred while loading the QML source
@@ -458,6 +637,9 @@ QSGLoader::Status QSGLoader::status() const
 {
     Q_D(const QSGLoader);
 
+    if (!d->active)
+        return Null;
+
     if (d->component)
         return static_cast<QSGLoader::Status>(d->component->status());
 
@@ -548,6 +730,47 @@ void QSGLoader::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeom
     QSGItem::geometryChanged(newGeometry, oldGeometry);
 }
 
+QUrl QSGLoaderPrivate::resolveSourceUrl(QDeclarativeV8Function *args)
+{
+    QV8Engine *v8engine = args->engine();
+    QString arg = v8engine->toString((*args)[0]->ToString());
+    if (arg.isEmpty())
+        return QUrl();
+
+    QDeclarativeContextData *context = args->context();
+    Q_ASSERT(context);
+    return context->resolvedUrl(QUrl(arg));
+}
+
+v8::Handle<v8::Object> QSGLoaderPrivate::extractInitialPropertyValues(QDeclarativeV8Function *args, QObject *loader, bool *error)
+{
+    v8::Local<v8::Object> valuemap;
+    if (args->Length() >= 2) {
+        v8::Local<v8::Value> v = (*args)[1];
+        if (!v->IsObject() || v->IsArray()) {
+            *error = true;
+            qmlInfo(loader) << loader->tr("setSource: value is not an object");
+        } else {
+            *error = false;
+            valuemap = v8::Local<v8::Object>::Cast(v);
+        }
+    }
+
+    return valuemap;
+}
+
+void QSGLoaderPrivate::completeCreateWithInitialPropertyValues(QDeclarativeComponent *component, QObject *object, v8::Handle<v8::Object> initialPropertyValues, v8::Handle<v8::Object> qmlGlobal)
+{
+    if (initialPropertyValues.IsEmpty()) {
+        component->completeCreate();
+        return;
+    }
+
+    QDeclarativeComponentPrivate *d = QDeclarativeComponentPrivate::get(component);
+    Q_ASSERT(d && d->engine);
+    d->completeCreateObjectWithInitialProperties(qmlGlobal, initialPropertyValues, object);
+}
+
 #include <moc_qsgloader_p.cpp>
 
 QT_END_NAMESPACE
index 832d3a6..c3ce160 100644 (file)
@@ -57,6 +57,7 @@ class Q_AUTOTEST_EXPORT QSGLoader : public QSGImplicitSizeItem
     Q_OBJECT
     Q_ENUMS(Status)
 
+    Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged)
     Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
     Q_PROPERTY(QDeclarativeComponent *sourceComponent READ sourceComponent WRITE setSourceComponent RESET resetSourceComponent NOTIFY sourceComponentChanged)
     Q_PROPERTY(QSGItem *item READ item NOTIFY itemChanged)
@@ -67,6 +68,11 @@ public:
     QSGLoader(QSGItem *parent = 0);
     virtual ~QSGLoader();
 
+    bool active() const;
+    void setActive(bool newVal);
+
+    Q_INVOKABLE void setSource(QDeclarativeV8Function *);
+
     QUrl source() const;
     void setSource(const QUrl &);
 
@@ -82,6 +88,7 @@ public:
 
 Q_SIGNALS:
     void itemChanged();
+    void activeChanged();
     void sourceChanged();
     void sourceComponentChanged();
     void statusChanged();
@@ -93,6 +100,9 @@ protected:
     void componentComplete();
 
 private:
+    void setSource(const QUrl &sourceUrl, bool needsClear);
+    void loadFromSource();
+    void loadFromSourceComponent();
     Q_DISABLE_COPY(QSGLoader)
     Q_DECLARE_PRIVATE(QSGLoader)
     Q_PRIVATE_SLOT(d_func(), void _q_sourceLoaded())
index 306e5ea..732ec86 100644 (file)
@@ -58,6 +58,8 @@
 #include "qsgimplicitsizeitem_p_p.h"
 #include "qsgitemchangelistener_p.h"
 
+#include <private/qv8_p.h>
+
 QT_BEGIN_NAMESPACE
 
 class QDeclarativeContext;
@@ -74,13 +76,22 @@ public:
     void initResize();
     void load();
 
+    void disposeInitialPropertyValues();
+    QUrl resolveSourceUrl(QDeclarativeV8Function *args);
+    v8::Handle<v8::Object> extractInitialPropertyValues(QDeclarativeV8Function *args, QObject *loader, bool *error);
+    void completeCreateWithInitialPropertyValues(QDeclarativeComponent *component, QObject *object, v8::Handle<v8::Object> initialPropertyValues, v8::Handle<v8::Object> qmlGlobal);
+
     QUrl source;
     QSGItem *item;
     QDeclarativeComponent *component;
+    v8::Persistent<v8::Object> initialPropertyValues;
+    v8::Persistent<v8::Object> qmlGlobalForIpv;
     bool ownComponent : 1;
     bool updatingSize: 1;
     bool itemWidthValid : 1;
     bool itemHeightValid : 1;
+    bool active : 1;
+    bool loadingFromSource : 1;
 
     void _q_sourceLoaded();
     void _q_updateSize(bool loaderGeometryChanged = true);
index eea94ce..49aa6d9 100644 (file)
@@ -55,6 +55,9 @@
 #include "private/qdeclarativedebugtrace_p.h"
 #include "private/qdeclarativeenginedebug_p.h"
 
+#include "private/qv8engine_p.h"
+#include "private/qv8include_p.h"
+
 #include <QStack>
 #include <QStringList>
 #include <QtCore/qdebug.h>
@@ -663,35 +666,45 @@ void QDeclarativeComponent::createObject(QDeclarativeV8Function *args)
 {
     Q_ASSERT(args);
 
-#define RETURN(result) { args->returnValue((result)); return; }
-
     Q_D(QDeclarativeComponent);
 
     Q_ASSERT(d->engine);
 
-    QDeclarativeEngine *engine = d->engine;
-    QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
-    QV8Engine *v8engine = ep->v8engine();
-
-    QDeclarativeContext *ctxt = creationContext();
-    if (!ctxt) ctxt = engine->rootContext();
+    QObject *parent = args->Length()?QDeclarativeEnginePrivate::get(d->engine)->v8engine()->toQObject((*args)[0]):0;
 
     v8::Local<v8::Object> valuemap;
     if (args->Length() >= 2) {
         v8::Local<v8::Value> v = (*args)[1];
         if (!v->IsObject() || v->IsArray()) {
             qmlInfo(this) << tr("createObject: value is not an object");
-            RETURN(v8::Null());
+            args->returnValue(v8::Null());
+            return;
         }
         valuemap = v8::Local<v8::Object>::Cast(v);
     }
 
-    QObject *parent = args->Length()?v8engine->toQObject((*args)[0]):0;
+    QV8Engine *v8engine = QDeclarativeEnginePrivate::get(d->engine)->v8engine();
+    QObject *retn = d->createObjectWithInitialProperties(args->qmlGlobal(), valuemap, parent);
+    if (!retn)
+        args->returnValue(v8::Null());
+    else
+        args->returnValue(v8engine->newQObject(retn));
+}
+
+QObject *QDeclarativeComponentPrivate::createObjectWithInitialProperties(v8::Handle<v8::Object> qmlGlobal, v8::Handle<v8::Object> valuemap, QObject *parentObject)
+{
+    Q_Q(QDeclarativeComponent);
+    Q_ASSERT(engine);
 
-    QObject *ret = beginCreate(ctxt);
+    QDeclarativeContext *ctxt = q->creationContext();
+    if (!ctxt) ctxt = engine->rootContext();
+
+    QObject *parent = parentObject;
+
+    QObject *ret = q->beginCreate(ctxt);
     if (!ret) {
-        completeCreate();
-        RETURN(v8::Null());
+        q->completeCreate();
+        return 0;
     }
 
     if (parent) {
@@ -715,7 +728,14 @@ void QDeclarativeComponent::createObject(QDeclarativeV8Function *args)
             qWarning("QDeclarativeComponent: Created graphical object was not placed in the graphics scene.");
     }
 
-    v8::Handle<v8::Value> ov = v8engine->newQObject(ret);
+    return completeCreateObjectWithInitialProperties(qmlGlobal, valuemap, ret);
+}
+
+QObject *QDeclarativeComponentPrivate::completeCreateObjectWithInitialProperties(v8::Handle<v8::Object> qmlGlobal, v8::Handle<v8::Object> valuemap, QObject *toCreate)
+{
+    QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
+    QV8Engine *v8engine = ep->v8engine();
+    v8::Handle<v8::Value> ov = v8engine->newQObject(toCreate);
     Q_ASSERT(ov->IsObject());
     v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(ov);
 
@@ -738,7 +758,7 @@ void QDeclarativeComponent::createObject(QDeclarativeV8Function *args)
         "})"
 
         v8::Local<v8::Script> script = v8engine->qmlModeCompile(SET_ARGS_SOURCE);
-        v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(script->Run(args->qmlGlobal()));
+        v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(script->Run(qmlGlobal));
 
         // Try catch isn't needed as the function itself is loaded with try/catch
         v8::Handle<v8::Value> args[] = { object, valuemap };
@@ -746,14 +766,12 @@ void QDeclarativeComponent::createObject(QDeclarativeV8Function *args)
     }
 
     completeCreate();
-    
-    QDeclarativeData *ddata = QDeclarativeData::get(ret);
+
+    QDeclarativeData *ddata = QDeclarativeData::get(toCreate);
     Q_ASSERT(ddata);
     ddata->setImplicitDestructible();
 
-    RETURN(object);
-
-#undef RETURN
+    return v8engine->toQObject(object);
 }
 
 /*!
index 7677f30..ba93749 100644 (file)
@@ -55,6 +55,7 @@
 
 #include "qdeclarativecomponent.h"
 
+#include "private/qv8_p.h"
 #include "private/qdeclarativeengine_p.h"
 #include "private/qdeclarativetypeloader_p.h"
 #include "private/qbitfield_p.h"
@@ -69,6 +70,8 @@
 
 QT_BEGIN_NAMESPACE
 
+class QV8Engine;
+
 class QDeclarativeComponent;
 class QDeclarativeEngine;
 class QDeclarativeCompiledData;
@@ -83,6 +86,8 @@ public:
 
     QObject *beginCreate(QDeclarativeContextData *, const QBitField &);
     void completeCreate();
+    QObject *createObjectWithInitialProperties(v8::Handle<v8::Object> qmlGlobal, v8::Handle<v8::Object> valuemap, QObject *parentObject);
+    QObject *completeCreateObjectWithInitialProperties(v8::Handle<v8::Object> qmlGlobal, v8::Handle<v8::Object> valuemap, QObject *toCreate);
 
     QDeclarativeTypeData *typeData;
     virtual void typeDataReady(QDeclarativeTypeData *);
@@ -115,6 +120,7 @@ public:
                               ConstructionState *state);
     static void complete(QDeclarativeEnginePrivate *enginePriv, ConstructionState *state);
 
+
     QDeclarativeEngine *engine;
     QDeclarativeGuardedContextData creationContext;
 
diff --git a/tests/auto/declarative/qsgloader/data/ActiveComponent.qml b/tests/auto/declarative/qsgloader/data/ActiveComponent.qml
new file mode 100644 (file)
index 0000000..24c6f7a
--- /dev/null
@@ -0,0 +1,11 @@
+import QtQuick 2.0
+
+Item {
+    id: behaviorCounter
+    property int behaviorCount: 0
+    property int canary: 0
+
+    Behavior on canary {
+        NumberAnimation { target: behaviorCounter; property: "behaviorCount"; to: (behaviorCounter.behaviorCount + 1); duration: 0 }
+    }
+}
diff --git a/tests/auto/declarative/qsgloader/data/InitialPropertyValuesComponent.qml b/tests/auto/declarative/qsgloader/data/InitialPropertyValuesComponent.qml
new file mode 100644 (file)
index 0000000..24c6f7a
--- /dev/null
@@ -0,0 +1,11 @@
+import QtQuick 2.0
+
+Item {
+    id: behaviorCounter
+    property int behaviorCount: 0
+    property int canary: 0
+
+    Behavior on canary {
+        NumberAnimation { target: behaviorCounter; property: "behaviorCount"; to: (behaviorCounter.behaviorCount + 1); duration: 0 }
+    }
+}
diff --git a/tests/auto/declarative/qsgloader/data/InvalidSourceComponent.qml b/tests/auto/declarative/qsgloader/data/InvalidSourceComponent.qml
new file mode 100644 (file)
index 0000000..7efa4a5
--- /dev/null
@@ -0,0 +1,5 @@
+import QtQuick 2.0
+
+Item {
+    RandomError
+}
diff --git a/tests/auto/declarative/qsgloader/data/active.1.qml b/tests/auto/declarative/qsgloader/data/active.1.qml
new file mode 100644 (file)
index 0000000..2dbd1a0
--- /dev/null
@@ -0,0 +1,31 @@
+import QtQuick 2.0
+
+Item {
+    id: root
+
+    Loader {
+        id: loader
+        objectName: "loader"
+        active: false
+    }
+
+    Component {
+        id: inlineTestComponent
+        Item {
+            id: inlineTestItem
+            property int someProperty: 5
+        }
+    }
+
+    function doSetSource() {
+        loader.source = "ActiveComponent.qml";
+    }
+
+    function doSetSourceComponent() {
+        loader.sourceComponent = inlineTestComponent;
+    }
+
+    function doSetActive() {
+        loader.active = true;
+    }
+}
diff --git a/tests/auto/declarative/qsgloader/data/active.2.qml b/tests/auto/declarative/qsgloader/data/active.2.qml
new file mode 100644 (file)
index 0000000..e561744
--- /dev/null
@@ -0,0 +1,18 @@
+import QtQuick 2.0
+
+Item {
+    id: root
+
+    Loader {
+        id: loader
+        objectName: "loader"
+        source: "ActiveComponent.qml";
+
+        property int statusChangedCount:  0
+        onStatusChanged: statusChangedCount = statusChangedCount + 1
+    }
+
+    function doSetInactive() {
+        loader.active = false;
+    }
+}
diff --git a/tests/auto/declarative/qsgloader/data/active.3.qml b/tests/auto/declarative/qsgloader/data/active.3.qml
new file mode 100644 (file)
index 0000000..0fbba95
--- /dev/null
@@ -0,0 +1,18 @@
+import QtQuick 2.0
+
+Item {
+    id: root
+
+    Loader {
+        id: loader
+        objectName: "loader"
+        source: "ActiveComponent.qml";
+
+        property int sourceChangedCount:  0
+        onSourceChanged: sourceChangedCount = sourceChangedCount + 1
+    }
+
+    function doSetInactive() {
+        loader.active = false;
+    }
+}
diff --git a/tests/auto/declarative/qsgloader/data/active.4.qml b/tests/auto/declarative/qsgloader/data/active.4.qml
new file mode 100644 (file)
index 0000000..63fd46e
--- /dev/null
@@ -0,0 +1,26 @@
+import QtQuick 2.0
+
+Item {
+    id: root
+
+    Component {
+        id: inlineTestComponent
+        Item {
+            id: inlineTestItem
+            property int someProperty: 5
+        }
+    }
+
+    Loader {
+        id: loader
+        objectName: "loader"
+        sourceComponent: inlineTestComponent
+
+        property int sourceComponentChangedCount:  0
+        onSourceComponentChanged: sourceComponentChangedCount = sourceComponentChangedCount + 1
+    }
+
+    function doSetInactive() {
+        loader.active = false;
+    }
+}
diff --git a/tests/auto/declarative/qsgloader/data/active.5.qml b/tests/auto/declarative/qsgloader/data/active.5.qml
new file mode 100644 (file)
index 0000000..903f458
--- /dev/null
@@ -0,0 +1,18 @@
+import QtQuick 2.0
+
+Item {
+    id: root
+
+    Loader {
+        id: loader
+        objectName: "loader"
+        source: "ActiveComponent.qml";
+
+        property int itemChangedCount:  0
+        onItemChanged: itemChangedCount = itemChangedCount + 1
+    }
+
+    function doSetInactive() {
+        loader.active = false;
+    }
+}
diff --git a/tests/auto/declarative/qsgloader/data/active.6.qml b/tests/auto/declarative/qsgloader/data/active.6.qml
new file mode 100644 (file)
index 0000000..f769a4e
--- /dev/null
@@ -0,0 +1,21 @@
+import QtQuick 2.0
+
+Item {
+    id: root
+
+    Loader {
+        id: loader
+        objectName: "loader"
+
+        property int activeChangedCount: 0
+        onActiveChanged: activeChangedCount = activeChangedCount + 1
+    }
+
+    function doSetActive() {
+        loader.active = true;
+    }
+
+    function doSetInactive() {
+        loader.active = false;
+    }
+}
diff --git a/tests/auto/declarative/qsgloader/data/initialPropertyValues.1.qml b/tests/auto/declarative/qsgloader/data/initialPropertyValues.1.qml
new file mode 100644 (file)
index 0000000..ae37179
--- /dev/null
@@ -0,0 +1,22 @@
+import QtQuick 2.0
+
+Item {
+    id: root
+    property int initialValue: 0
+    property int behaviorCount: 0
+
+    Loader {
+        id: loader
+        objectName: "loader"
+
+        onLoaded: {
+            loader.item.canary = 1; // will trigger the behavior, setting behaviorCount -> 1
+        }
+    }
+
+    Component.onCompleted: {
+        loader.source = "InitialPropertyValuesComponent.qml";
+        root.initialValue = loader.item.canary;         // should be one, since onLoaded will have triggered by now
+        root.behaviorCount = loader.item.behaviorCount; // should be one, since onLoaded will have triggered by now
+    }
+}
diff --git a/tests/auto/declarative/qsgloader/data/initialPropertyValues.2.qml b/tests/auto/declarative/qsgloader/data/initialPropertyValues.2.qml
new file mode 100644 (file)
index 0000000..76c7bc2
--- /dev/null
@@ -0,0 +1,20 @@
+import QtQuick 2.0
+
+Item {
+    id: root
+    property int initialValue: 0
+    property int behaviorCount: 0
+
+    Loader {
+        id: loader
+        objectName: "loader"
+        onLoaded: {
+            root.initialValue = loader.item.canary;         // should be two
+            root.behaviorCount = loader.item.behaviorCount; // should be zero
+        }
+    }
+
+    Component.onCompleted: {
+        loader.setSource("InitialPropertyValuesComponent.qml", {"canary": 2});
+    }
+}
diff --git a/tests/auto/declarative/qsgloader/data/initialPropertyValues.3.qml b/tests/auto/declarative/qsgloader/data/initialPropertyValues.3.qml
new file mode 100644 (file)
index 0000000..3b08e6e
--- /dev/null
@@ -0,0 +1,18 @@
+import QtQuick 2.0
+
+Item {
+    id: root
+    property int initialValue: 0
+    property int behaviorCount: 0
+
+    Loader {
+        id: loader
+        objectName: "loader"
+        active: false
+    }
+
+    Component.onCompleted: {
+        loader.setSource("InitialPropertyValuesComponent.qml", {"canary": 3});
+        root.initialValue = loader.item.canary; // error - item should not yet exist.
+    }
+}
diff --git a/tests/auto/declarative/qsgloader/data/initialPropertyValues.4.qml b/tests/auto/declarative/qsgloader/data/initialPropertyValues.4.qml
new file mode 100644 (file)
index 0000000..e831004
--- /dev/null
@@ -0,0 +1,22 @@
+import QtQuick 2.0
+
+Item {
+    id: root
+    property int initialValue: 0
+    property int behaviorCount: 0
+
+    Loader {
+        id: loader
+        objectName: "loader"
+        active: false
+        onLoaded: {
+            root.initialValue = loader.item.canary;         // should be four
+            root.behaviorCount = loader.item.behaviorCount; // should be zero
+        }
+    }
+
+    Component.onCompleted: {
+        loader.setSource("InitialPropertyValuesComponent.qml", {"canary": 4});
+        loader.active = true
+    }
+}
diff --git a/tests/auto/declarative/qsgloader/data/initialPropertyValues.5.qml b/tests/auto/declarative/qsgloader/data/initialPropertyValues.5.qml
new file mode 100644 (file)
index 0000000..03ee599
--- /dev/null
@@ -0,0 +1,20 @@
+import QtQuick 2.0
+
+Item {
+    id: root
+    property int initialValue: 0
+    property int behaviorCount: 0
+
+    Loader {
+        id: loader
+        objectName: "loader"
+        onLoaded: {
+            root.initialValue = loader.item.canary;         // should be zero, but no error
+            root.behaviorCount = loader.item.behaviorCount; // should be zero
+        }
+    }
+
+    Component.onCompleted: {
+        loader.setSource("InitialPropertyValuesComponent.qml");
+    }
+}
diff --git a/tests/auto/declarative/qsgloader/data/initialPropertyValues.6.qml b/tests/auto/declarative/qsgloader/data/initialPropertyValues.6.qml
new file mode 100644 (file)
index 0000000..66452b5
--- /dev/null
@@ -0,0 +1,25 @@
+import QtQuick 2.0
+
+Item {
+    id: root
+    property int initialValue: 0
+    property int behaviorCount: 0
+
+    Loader {
+        id: loader
+        objectName: "loader"
+        onLoaded: {
+            root.initialValue = loader.item.canary;         // should be six
+            root.behaviorCount = loader.item.behaviorCount; // should be zero
+        }
+    }
+
+    Item {
+        id: child
+        property int bindable: 6
+    }
+
+    Component.onCompleted: {
+        loader.setSource("InitialPropertyValuesComponent.qml", {"canary": child.bindable});
+    }
+}
diff --git a/tests/auto/declarative/qsgloader/data/initialPropertyValues.7.qml b/tests/auto/declarative/qsgloader/data/initialPropertyValues.7.qml
new file mode 100644 (file)
index 0000000..02349f7
--- /dev/null
@@ -0,0 +1,29 @@
+import QtQuick 2.0
+
+Item {
+    id: root
+    property int loaderValue: 0
+    property int createObjectValue: 0
+
+    Loader {
+        id: loader
+        objectName: "loader"
+        onLoaded: {
+            root.loaderValue = loader.item.canary; // should still be one
+        }
+    }
+
+    Item {
+        id: child
+        property int bindable: 1;
+    }
+
+    property InitialPropertyValuesComponent ipvc
+    Component.onCompleted: {
+        loader.setSource("InitialPropertyValuesComponent.qml", {"canary": child.bindable});
+        var dynComp = Qt.createComponent("InitialPropertyValuesComponent.qml");
+        ipvc = dynComp.createObject(root, {"canary": child.bindable});
+        child.bindable = 7; // won't cause re-evaluation, since not used in a binding.
+        root.createObjectValue = ipvc.canary;   // should still be one
+    }
+}
diff --git a/tests/auto/declarative/qsgloader/data/initialPropertyValues.binding.qml b/tests/auto/declarative/qsgloader/data/initialPropertyValues.binding.qml
new file mode 100644 (file)
index 0000000..e0df50a
--- /dev/null
@@ -0,0 +1,21 @@
+import QtQuick 2.0
+
+Item {
+    id: root
+
+    property InitialPropertyValuesComponent testInstance
+    testInstance: loader.item
+
+    property int bindable:  1
+    property int canaryValue: testInstance.canary
+    property int behaviorCount: testInstance.behaviorCount
+
+    Loader {
+        id: loader
+        objectName: "loader"
+    }
+
+    Component.onCompleted: {
+        loader.setSource("InitialPropertyValuesComponent.qml", {"canary": (function() { return root.bindable })});
+    }
+}
diff --git a/tests/auto/declarative/qsgloader/data/initialPropertyValues.error.1.qml b/tests/auto/declarative/qsgloader/data/initialPropertyValues.error.1.qml
new file mode 100644 (file)
index 0000000..f324dbd
--- /dev/null
@@ -0,0 +1,14 @@
+import QtQuick 2.0
+
+Item {
+    id: root
+
+    Loader {
+        id: loader
+        objectName: "loader"
+    }
+
+    Component.onCompleted: {
+        loader.setSource("InitialPropertyValuesComponent.qml", 3); // invalid initial properties object
+    }
+}
diff --git a/tests/auto/declarative/qsgloader/data/initialPropertyValues.error.2.qml b/tests/auto/declarative/qsgloader/data/initialPropertyValues.error.2.qml
new file mode 100644 (file)
index 0000000..89aba31
--- /dev/null
@@ -0,0 +1,14 @@
+import QtQuick 2.0
+
+Item {
+    id: root
+
+    Loader {
+        id: loader
+        objectName: "loader"
+    }
+
+    Component.onCompleted: {
+        loader.setSource("NonexistentSourceComponent.qml", {"canary":3});
+    }
+}
diff --git a/tests/auto/declarative/qsgloader/data/initialPropertyValues.error.3.qml b/tests/auto/declarative/qsgloader/data/initialPropertyValues.error.3.qml
new file mode 100644 (file)
index 0000000..c862007
--- /dev/null
@@ -0,0 +1,14 @@
+import QtQuick 2.0
+
+Item {
+    id: root
+
+    Loader {
+        id: loader
+        objectName: "loader"
+    }
+
+    Component.onCompleted: {
+        loader.setSource("InvalidSourceComponent.qml", {"canary":3});
+    }
+}
diff --git a/tests/auto/declarative/qsgloader/data/initialPropertyValues.error.4.qml b/tests/auto/declarative/qsgloader/data/initialPropertyValues.error.4.qml
new file mode 100644 (file)
index 0000000..9a80b21
--- /dev/null
@@ -0,0 +1,15 @@
+import QtQuick 2.0
+
+Item {
+    id: root
+    property int canary: loader.item.canary
+
+    Loader {
+        id: loader
+        objectName: "loader"
+    }
+
+    Component.onCompleted: {
+        loader.setSource("InitialPropertyValuesComponent.qml", 3); // invalid initial properties object
+    }
+}
index c7e01da..daf9e2a 100644 (file)
@@ -41,6 +41,7 @@
 #include <qtest.h>
 
 #include <QSignalSpy>
+
 #include <QtDeclarative/qdeclarativeengine.h>
 #include <QtDeclarative/qdeclarativecomponent.h>
 #include <private/qsgloader_p.h>
@@ -79,6 +80,12 @@ private slots:
     void networkRequestUrl();
     void failNetworkRequest();
 //    void networkComponent();
+    void active();
+    void initialPropertyValues_data();
+    void initialPropertyValues();
+    void initialPropertyValuesBinding();
+    void initialPropertyValuesError_data();
+    void initialPropertyValuesError();
 
     void deleteComponentCrash();
     void nonItem();
@@ -465,6 +472,237 @@ void tst_QSGLoader::failNetworkRequest()
     delete loader;
 }
 
+void tst_QSGLoader::active()
+{
+    // check that the item isn't instantiated until active is set to true
+    {
+        QDeclarativeComponent component(&engine, TEST_FILE("active.1.qml"));
+        QObject *object = component.create();
+        QVERIFY(object != 0);
+        QSGLoader *loader = object->findChild<QSGLoader*>("loader");
+
+        QVERIFY(loader->active() == false); // set manually to false
+        QVERIFY(loader->item() == 0);
+        QMetaObject::invokeMethod(object, "doSetSourceComponent");
+        QVERIFY(loader->item() == 0);
+        QMetaObject::invokeMethod(object, "doSetSource");
+        QVERIFY(loader->item() == 0);
+        QMetaObject::invokeMethod(object, "doSetActive");
+        QVERIFY(loader->item() != 0);
+
+        delete object;
+    }
+
+    // check that the status is Null if active is set to false
+    {
+        QDeclarativeComponent component(&engine, TEST_FILE("active.2.qml"));
+        QObject *object = component.create();
+        QVERIFY(object != 0);
+        QSGLoader *loader = object->findChild<QSGLoader*>("loader");
+
+        QVERIFY(loader->active() == true); // active is true by default
+        QCOMPARE(loader->status(), QSGLoader::Ready);
+        int currStatusChangedCount = loader->property("statusChangedCount").toInt();
+        QMetaObject::invokeMethod(object, "doSetInactive");
+        QCOMPARE(loader->status(), QSGLoader::Null);
+        QCOMPARE(loader->property("statusChangedCount").toInt(), (currStatusChangedCount+1));
+
+        delete object;
+    }
+
+    // check that the source is not cleared if active is set to false
+    {
+        QDeclarativeComponent component(&engine, TEST_FILE("active.3.qml"));
+        QObject *object = component.create();
+        QVERIFY(object != 0);
+        QSGLoader *loader = object->findChild<QSGLoader*>("loader");
+
+        QVERIFY(loader->active() == true); // active is true by default
+        QVERIFY(!loader->source().isEmpty());
+        int currSourceChangedCount = loader->property("sourceChangedCount").toInt();
+        QMetaObject::invokeMethod(object, "doSetInactive");
+        QVERIFY(!loader->source().isEmpty());
+        QCOMPARE(loader->property("sourceChangedCount").toInt(), currSourceChangedCount);
+
+        delete object;
+    }
+
+    // check that the sourceComponent is not cleared if active is set to false
+    {
+        QDeclarativeComponent component(&engine, TEST_FILE("active.4.qml"));
+        QObject *object = component.create();
+        QVERIFY(object != 0);
+        QSGLoader *loader = object->findChild<QSGLoader*>("loader");
+
+        QVERIFY(loader->active() == true); // active is true by default
+        QVERIFY(loader->sourceComponent() != 0);
+        int currSourceComponentChangedCount = loader->property("sourceComponentChangedCount").toInt();
+        QMetaObject::invokeMethod(object, "doSetInactive");
+        QVERIFY(loader->sourceComponent() != 0);
+        QCOMPARE(loader->property("sourceComponentChangedCount").toInt(), currSourceComponentChangedCount);
+
+        delete object;
+    }
+
+    // check that the item is released if active is set to false
+    {
+        QDeclarativeComponent component(&engine, TEST_FILE("active.5.qml"));
+        QObject *object = component.create();
+        QVERIFY(object != 0);
+        QSGLoader *loader = object->findChild<QSGLoader*>("loader");
+
+        QVERIFY(loader->active() == true); // active is true by default
+        QVERIFY(loader->item() != 0);
+        int currItemChangedCount = loader->property("itemChangedCount").toInt();
+        QMetaObject::invokeMethod(object, "doSetInactive");
+        QVERIFY(loader->item() == 0);
+        QCOMPARE(loader->property("itemChangedCount").toInt(), (currItemChangedCount+1));
+
+        delete object;
+    }
+
+    // check that the activeChanged signal is emitted correctly
+    {
+        QDeclarativeComponent component(&engine, TEST_FILE("active.6.qml"));
+        QObject *object = component.create();
+        QVERIFY(object != 0);
+        QSGLoader *loader = object->findChild<QSGLoader*>("loader");
+
+        QVERIFY(loader->active() == true); // active is true by default
+        loader->setActive(true);           // no effect
+        QCOMPARE(loader->property("activeChangedCount").toInt(), 0);
+        loader->setActive(false);          // change signal should be emitted
+        QCOMPARE(loader->property("activeChangedCount").toInt(), 1);
+        loader->setActive(false);          // no effect
+        QCOMPARE(loader->property("activeChangedCount").toInt(), 1);
+        loader->setActive(true);           // change signal should be emitted
+        QCOMPARE(loader->property("activeChangedCount").toInt(), 2);
+        loader->setActive(false);          // change signal should be emitted
+        QCOMPARE(loader->property("activeChangedCount").toInt(), 3);
+        QMetaObject::invokeMethod(object, "doSetActive");
+        QCOMPARE(loader->property("activeChangedCount").toInt(), 4);
+        QMetaObject::invokeMethod(object, "doSetActive");
+        QCOMPARE(loader->property("activeChangedCount").toInt(), 4);
+        QMetaObject::invokeMethod(object, "doSetInactive");
+        QCOMPARE(loader->property("activeChangedCount").toInt(), 5);
+        loader->setActive(true);           // change signal should be emitted
+        QCOMPARE(loader->property("activeChangedCount").toInt(), 6);
+
+        delete object;
+    }
+}
+
+void tst_QSGLoader::initialPropertyValues_data()
+{
+    QTest::addColumn<QUrl>("qmlFile");
+    QTest::addColumn<QStringList>("expectedWarnings");
+    QTest::addColumn<QStringList>("propertyNames");
+    QTest::addColumn<QVariantList>("propertyValues");
+
+    QTest::newRow("source url with value set in onLoaded, initially active = true") << TEST_FILE("initialPropertyValues.1.qml")
+            << QStringList()
+            << (QStringList() << "initialValue" << "behaviorCount")
+            << (QVariantList() << 1 << 1);
+
+    QTest::newRow("set source with initial property values specified, active = true") << TEST_FILE("initialPropertyValues.2.qml")
+            << QStringList()
+            << (QStringList() << "initialValue" << "behaviorCount")
+            << (QVariantList() << 2 << 0);
+
+    QTest::newRow("set source with initial property values specified, active = false") << TEST_FILE("initialPropertyValues.3.qml")
+            << (QStringList() << QString(QLatin1String("file://") + TEST_FILE("initialPropertyValues.3.qml").toLocalFile() + QLatin1String(":16: TypeError: Cannot read property 'canary' of null")))
+            << (QStringList())
+            << (QVariantList());
+
+    QTest::newRow("set source with initial property values specified, active = false, with active set true later") << TEST_FILE("initialPropertyValues.4.qml")
+            << QStringList()
+            << (QStringList() << "initialValue" << "behaviorCount")
+            << (QVariantList() << 4 << 0);
+
+    QTest::newRow("set source without initial property values specified, active = true") << TEST_FILE("initialPropertyValues.5.qml")
+            << QStringList()
+            << (QStringList() << "initialValue" << "behaviorCount")
+            << (QVariantList() << 0 << 0);
+
+    QTest::newRow("set source with initial property values specified with binding, active = true") << TEST_FILE("initialPropertyValues.6.qml")
+            << QStringList()
+            << (QStringList() << "initialValue" << "behaviorCount")
+            << (QVariantList() << 6 << 0);
+
+    QTest::newRow("ensure initial property value semantics mimic createObject") << TEST_FILE("initialPropertyValues.7.qml")
+            << QStringList()
+            << (QStringList() << "loaderValue" << "createObjectValue")
+            << (QVariantList() << 1 << 1);
+}
+
+void tst_QSGLoader::initialPropertyValues()
+{
+    QFETCH(QUrl, qmlFile);
+    QFETCH(QStringList, expectedWarnings);
+    QFETCH(QStringList, propertyNames);
+    QFETCH(QVariantList, propertyValues);
+
+    foreach (const QString &warning, expectedWarnings)
+        QTest::ignoreMessage(QtWarningMsg, warning.toAscii().constData());
+
+    QDeclarativeComponent component(&engine, qmlFile);
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+
+    for (int i = 0; i < propertyNames.size(); ++i)
+        QCOMPARE(object->property(propertyNames.at(i).toAscii().constData()), propertyValues.at(i));
+
+    delete object;
+}
+
+void tst_QSGLoader::initialPropertyValuesBinding()
+{
+    QDeclarativeComponent component(&engine, TEST_FILE("initialPropertyValues.binding.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+
+    QVERIFY(object->setProperty("bindable", QVariant(8)));
+    QCOMPARE(object->property("canaryValue").toInt(), 8);
+
+    delete object;
+}
+
+void tst_QSGLoader::initialPropertyValuesError_data()
+{
+    QTest::addColumn<QUrl>("qmlFile");
+    QTest::addColumn<QStringList>("expectedWarnings");
+
+    QTest::newRow("invalid initial property values object") << TEST_FILE("initialPropertyValues.error.1.qml")
+            << (QStringList() << QString(TEST_FILE("initialPropertyValues.error.1.qml").toString() + ":6:5: QML Loader: setSource: value is not an object"));
+
+    QTest::newRow("nonexistent source url") << TEST_FILE("initialPropertyValues.error.2.qml")
+            << (QStringList() << QString(TEST_FILE("NonexistentSourceComponent.qml").toString() + ": File not found"));
+
+    QTest::newRow("invalid source url") << TEST_FILE("initialPropertyValues.error.3.qml")
+            << (QStringList() << QString(TEST_FILE("InvalidSourceComponent.qml").toString() + ":5:1: Syntax error"));
+
+    QTest::newRow("invalid initial property values object with invalid property access") << TEST_FILE("initialPropertyValues.error.4.qml")
+            << (QStringList() << QString(TEST_FILE("initialPropertyValues.error.4.qml").toString() + ":7:5: QML Loader: setSource: value is not an object")
+                              << QString(TEST_FILE("initialPropertyValues.error.4.qml").toString() + ":5: TypeError: Cannot read property 'canary' of null"));
+}
+
+void tst_QSGLoader::initialPropertyValuesError()
+{
+    QFETCH(QUrl, qmlFile);
+    QFETCH(QStringList, expectedWarnings);
+
+    foreach (const QString &warning, expectedWarnings)
+        QTest::ignoreMessage(QtWarningMsg, warning.toUtf8().constData());
+
+    QDeclarativeComponent component(&engine, qmlFile);
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+    QSGLoader *loader = object->findChild<QSGLoader*>("loader");
+    QVERIFY(loader != 0);
+    QVERIFY(loader->item() == 0);
+    delete object;
+}
+
 // QTBUG-9241
 void tst_QSGLoader::deleteComponentCrash()
 {