/****************************************************************************
**
-** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
+** 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_END_LICENSE$
**
****************************************************************************/
#include <QDeclarativeContext>
#include <QDeclarativeComponent>
#include <QDeclarativeExpression>
+#include <private/qdeclarativecontext_p.h>
+#include "../../shared/util.h"
-#ifdef Q_OS_SYMBIAN
-// In Symbian OS test data is located in applications private dir
-#define SRCDIR "."
-#endif
-
-class tst_qdeclarativecontext : public QObject
+class tst_qdeclarativecontext : public QDeclarativeDataTest
{
Q_OBJECT
public:
void destruction();
void idAsContextProperty();
void readOnlyContexts();
+ void nameForObject();
+
+ void refreshExpressions();
+ void refreshExpressionsCrash();
+ void refreshExpressionsRootContext();
+ void qtbug_22535();
private:
QDeclarativeEngine engine;
};
QDeclarativeContext ctxt(engine.rootContext());
ctxt.setContextProperty("ctxtProp", QVariant());
- QTest::ignoreMessage(QtWarningMsg, "<Unknown File>:1: TypeError: Result of expression 'ctxtProp' [undefined] is not an object.");
+ QTest::ignoreMessage(QtWarningMsg, "<Unknown File>:1: TypeError: Cannot read property 'a' of undefined");
QObject *obj = component.create(&ctxt);
QVariant v = obj->property("obj");
delete obj;
}
+void tst_qdeclarativecontext::nameForObject()
+{
+ QObject o1;
+ QObject o2;
+ QObject o3;
+
+ QDeclarativeEngine engine;
+
+ // As a context property
+ engine.rootContext()->setContextProperty("o1", &o1);
+ engine.rootContext()->setContextProperty("o2", &o2);
+ engine.rootContext()->setContextProperty("o1_2", &o1);
+
+ QCOMPARE(engine.rootContext()->nameForObject(&o1), QString("o1"));
+ QCOMPARE(engine.rootContext()->nameForObject(&o2), QString("o2"));
+ QCOMPARE(engine.rootContext()->nameForObject(&o3), QString());
+
+ // As an id
+ QDeclarativeComponent component(&engine);
+ component.setData("import QtQuick 1.0; QtObject { id: root; property QtObject o: QtObject { id: nested } }", QUrl());
+
+ QObject *o = component.create();
+ QVERIFY(o != 0);
+
+ QCOMPARE(qmlContext(o)->nameForObject(o), QString("root"));
+ QCOMPARE(qmlContext(o)->nameForObject(qvariant_cast<QObject*>(o->property("o"))), QString("nested"));
+ QCOMPARE(qmlContext(o)->nameForObject(&o1), QString());
+
+ delete o;
+}
+
+class DeleteCommand : public QObject
+{
+Q_OBJECT
+public:
+ DeleteCommand() : object(0) {}
+
+ QObject *object;
+
+public slots:
+ void doCommand() { if (object) delete object; object = 0; }
+};
+
+// Calling refresh expressions would crash if an expression or context was deleted during
+// the refreshing
+void tst_qdeclarativecontext::refreshExpressionsCrash()
+{
+ {
+ QDeclarativeEngine engine;
+
+ DeleteCommand command;
+ engine.rootContext()->setContextProperty("deleteCommand", &command);
+ // We use a fresh context here to bypass any root-context optimizations in
+ // the engine
+ QDeclarativeContext ctxt(engine.rootContext());
+
+ QDeclarativeComponent component(&engine);
+ component.setData("import QtQuick 2.0; QtObject { property var binding: deleteCommand.doCommand() }", QUrl());
+ QVERIFY(component.isReady());
+
+ QObject *o1 = component.create(&ctxt);
+ QObject *o2 = component.create(&ctxt);
+
+ command.object = o2;
+
+ QDeclarativeContextData::get(&ctxt)->refreshExpressions();
+
+ delete o1;
+ }
+ {
+ QDeclarativeEngine engine;
+
+ DeleteCommand command;
+ engine.rootContext()->setContextProperty("deleteCommand", &command);
+ // We use a fresh context here to bypass any root-context optimizations in
+ // the engine
+ QDeclarativeContext ctxt(engine.rootContext());
+
+ QDeclarativeComponent component(&engine);
+ component.setData("import QtQuick 2.0; QtObject { property var binding: deleteCommand.doCommand() }", QUrl());
+ QVERIFY(component.isReady());
+
+ QObject *o1 = component.create(&ctxt);
+ QObject *o2 = component.create(&ctxt);
+
+ command.object = o1;
+
+ QDeclarativeContextData::get(&ctxt)->refreshExpressions();
+
+ delete o2;
+ }
+}
+
+class CountCommand : public QObject
+{
+Q_OBJECT
+public:
+ CountCommand() : count(0) {}
+
+ int count;
+
+public slots:
+ void doCommand() { ++count; }
+};
+
+
+// Test that calling refresh expressions causes all the expressions to refresh
+void tst_qdeclarativecontext::refreshExpressions()
+{
+ QDeclarativeEngine engine;
+ QDeclarativeComponent component(&engine, testFileUrl("refreshExpressions.qml"));
+ QDeclarativeComponent component2(&engine, testFileUrl("RefreshExpressionsType.qml"));
+
+ CountCommand command;
+ engine.rootContext()->setContextProperty("countCommand", &command);
+
+ // We use a fresh context here to bypass any root-context optimizations in
+ // the engine
+ QDeclarativeContext context(engine.rootContext());
+ QDeclarativeContext context2(&context);
+
+ QObject *o1 = component.create(&context);
+ QObject *o2 = component.create(&context2);
+ QObject *o3 = component2.create(&context);
+
+ QCOMPARE(command.count, 5);
+
+ QDeclarativeContextData::get(&context)->refreshExpressions();
+
+ QCOMPARE(command.count, 10);
+
+ delete o3;
+ delete o2;
+ delete o1;
+}
+
+// Test that updating the root context, only causes expressions in contexts with an
+// unresolved name to reevaluate
+void tst_qdeclarativecontext::refreshExpressionsRootContext()
+{
+ QDeclarativeEngine engine;
+
+ CountCommand command;
+ engine.rootContext()->setContextProperty("countCommand", &command);
+
+ QDeclarativeComponent component(&engine, testFileUrl("refreshExpressions.qml"));
+ QDeclarativeComponent component2(&engine, testFileUrl("refreshExpressionsRootContext.qml"));
+
+ QDeclarativeContext context(engine.rootContext());
+ QDeclarativeContext context2(engine.rootContext());
+
+ QString warning = component2.url().toString() + QLatin1String(":4: ReferenceError: Can't find variable: unresolvedName");
+
+ QObject *o1 = component.create(&context);
+
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
+ QObject *o2 = component2.create(&context2);
+
+ QCOMPARE(command.count, 3);
+
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
+ QDeclarativeContextData::get(engine.rootContext())->refreshExpressions();
+
+ QCOMPARE(command.count, 4);
+
+ delete o2;
+ delete o1;
+}
+
+void tst_qdeclarativecontext::qtbug_22535()
+{
+ QDeclarativeEngine engine;
+ QDeclarativeComponent component(&engine, testFileUrl("qtbug_22535.qml"));
+ QDeclarativeContext context(engine.rootContext());
+
+ QObject *o = component.create(&context);
+
+ // Don't crash!
+ delete o;
+}
+
QTEST_MAIN(tst_qdeclarativecontext)
#include "tst_qdeclarativecontext.moc"