1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the test suite of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
45 #include <QQmlContext>
46 #include <QQmlComponent>
47 #include <QQmlExpression>
48 #include <private/qqmlcontext_p.h>
49 #include "../../shared/util.h"
51 class tst_qqmlcontext : public QQmlDataTest
62 void setContextProperty();
63 void setContextObject();
65 void idAsContextProperty();
66 void readOnlyContexts();
69 void refreshExpressions();
70 void refreshExpressionsCrash();
71 void refreshExpressionsRootContext();
74 void evalAfterInvalidate();
80 void tst_qqmlcontext::baseUrl()
82 QQmlContext ctxt(&engine);
84 QCOMPARE(ctxt.baseUrl(), QUrl());
86 ctxt.setBaseUrl(QUrl("http://www.nokia.com/"));
88 QCOMPARE(ctxt.baseUrl(), QUrl("http://www.nokia.com/"));
91 void tst_qqmlcontext::resolvedUrl()
93 // Relative to the component
95 QQmlContext ctxt(&engine);
96 ctxt.setBaseUrl(QUrl("http://www.nokia.com/"));
98 QCOMPARE(ctxt.resolvedUrl(QUrl("main.qml")), QUrl("http://www.nokia.com/main.qml"));
101 // Relative to a parent
103 QQmlContext ctxt(&engine);
104 ctxt.setBaseUrl(QUrl("http://www.nokia.com/"));
106 QQmlContext ctxt2(&ctxt);
107 QCOMPARE(ctxt2.resolvedUrl(QUrl("main2.qml")), QUrl("http://www.nokia.com/main2.qml"));
110 // Relative to the engine
112 QQmlContext ctxt(&engine);
113 QCOMPARE(ctxt.resolvedUrl(QUrl("main.qml")), engine.baseUrl().resolved(QUrl("main.qml")));
116 // Relative to a deleted parent
118 QQmlContext *ctxt = new QQmlContext(&engine);
119 ctxt->setBaseUrl(QUrl("http://www.nokia.com/"));
121 QQmlContext ctxt2(ctxt);
122 QCOMPARE(ctxt2.resolvedUrl(QUrl("main2.qml")), QUrl("http://www.nokia.com/main2.qml"));
124 delete ctxt; ctxt = 0;
126 QCOMPARE(ctxt2.resolvedUrl(QUrl("main2.qml")), QUrl());
131 QQmlContext ctxt(&engine);
133 QCOMPARE(ctxt.resolvedUrl(QUrl("http://www.nokia.com/main2.qml")), QUrl("http://www.nokia.com/main2.qml"));
134 QCOMPARE(ctxt.resolvedUrl(QUrl("file:///main2.qml")), QUrl("file:///main2.qml"));
138 void tst_qqmlcontext::engineMethod()
140 QQmlEngine *engine = new QQmlEngine;
142 QQmlContext ctxt(engine);
143 QQmlContext ctxt2(&ctxt);
144 QQmlContext ctxt3(&ctxt2);
145 QQmlContext ctxt4(&ctxt2);
147 QCOMPARE(ctxt.engine(), engine);
148 QCOMPARE(ctxt2.engine(), engine);
149 QCOMPARE(ctxt3.engine(), engine);
150 QCOMPARE(ctxt4.engine(), engine);
152 delete engine; engine = 0;
154 QCOMPARE(ctxt.engine(), engine);
155 QCOMPARE(ctxt2.engine(), engine);
156 QCOMPARE(ctxt3.engine(), engine);
157 QCOMPARE(ctxt4.engine(), engine);
160 void tst_qqmlcontext::parentContext()
162 QQmlEngine *engine = new QQmlEngine;
164 QCOMPARE(engine->rootContext()->parentContext(), (QQmlContext *)0);
166 QQmlContext *ctxt = new QQmlContext(engine);
167 QQmlContext *ctxt2 = new QQmlContext(ctxt);
168 QQmlContext *ctxt3 = new QQmlContext(ctxt2);
169 QQmlContext *ctxt4 = new QQmlContext(ctxt2);
170 QQmlContext *ctxt5 = new QQmlContext(ctxt);
171 QQmlContext *ctxt6 = new QQmlContext(engine);
172 QQmlContext *ctxt7 = new QQmlContext(engine->rootContext());
174 QCOMPARE(ctxt->parentContext(), engine->rootContext());
175 QCOMPARE(ctxt2->parentContext(), ctxt);
176 QCOMPARE(ctxt3->parentContext(), ctxt2);
177 QCOMPARE(ctxt4->parentContext(), ctxt2);
178 QCOMPARE(ctxt5->parentContext(), ctxt);
179 QCOMPARE(ctxt6->parentContext(), engine->rootContext());
180 QCOMPARE(ctxt7->parentContext(), engine->rootContext());
182 delete ctxt2; ctxt2 = 0;
184 QCOMPARE(ctxt->parentContext(), engine->rootContext());
185 QCOMPARE(ctxt3->parentContext(), (QQmlContext *)0);
186 QCOMPARE(ctxt4->parentContext(), (QQmlContext *)0);
187 QCOMPARE(ctxt5->parentContext(), ctxt);
188 QCOMPARE(ctxt6->parentContext(), engine->rootContext());
189 QCOMPARE(ctxt7->parentContext(), engine->rootContext());
191 delete engine; engine = 0;
193 QCOMPARE(ctxt->parentContext(), (QQmlContext *)0);
194 QCOMPARE(ctxt3->parentContext(), (QQmlContext *)0);
195 QCOMPARE(ctxt4->parentContext(), (QQmlContext *)0);
196 QCOMPARE(ctxt5->parentContext(), (QQmlContext *)0);
197 QCOMPARE(ctxt6->parentContext(), (QQmlContext *)0);
198 QCOMPARE(ctxt7->parentContext(), (QQmlContext *)0);
208 class TestObject : public QObject
211 Q_PROPERTY(int a READ a NOTIFY aChanged)
212 Q_PROPERTY(int b READ b NOTIFY bChanged)
213 Q_PROPERTY(int c READ c NOTIFY cChanged)
216 TestObject() : _a(10), _b(10), _c(10) {}
218 int a() const { return _a; }
219 void setA(int a) { _a = a; emit aChanged(); }
221 int b() const { return _b; }
222 void setB(int b) { _b = b; emit bChanged(); }
224 int c() const { return _c; }
225 void setC(int c) { _c = c; emit cChanged(); }
238 #define TEST_CONTEXT_PROPERTY(ctxt, name, value) \
240 QQmlComponent component(&engine); \
241 component.setData("import QtQuick 2.0; QtObject { property variant test: " #name " }", QUrl()); \
243 QObject *obj = component.create(ctxt); \
245 QCOMPARE(obj->property("test"), value); \
250 void tst_qqmlcontext::setContextProperty()
252 QQmlContext ctxt(&engine);
253 QQmlContext ctxt2(&ctxt);
260 // Static context properties
261 ctxt.setContextProperty("a", QVariant(10));
262 ctxt.setContextProperty("b", QVariant(9));
263 ctxt2.setContextProperty("d", &obj2);
264 ctxt2.setContextProperty("b", QVariant(19));
265 ctxt2.setContextProperty("c", QVariant(QString("Hello World!")));
266 ctxt.setContextProperty("d", &obj1);
267 ctxt.setContextProperty("e", &obj1);
269 TEST_CONTEXT_PROPERTY(&ctxt2, a, QVariant(10));
270 TEST_CONTEXT_PROPERTY(&ctxt2, b, QVariant(19));
271 TEST_CONTEXT_PROPERTY(&ctxt2, c, QVariant(QString("Hello World!")));
272 TEST_CONTEXT_PROPERTY(&ctxt2, d.a, QVariant(-19));
273 TEST_CONTEXT_PROPERTY(&ctxt2, e.a, QVariant(3345));
275 ctxt.setContextProperty("a", QVariant(13));
276 ctxt.setContextProperty("b", QVariant(4));
277 ctxt2.setContextProperty("b", QVariant(8));
278 ctxt2.setContextProperty("c", QVariant(QString("Hi World!")));
279 ctxt2.setContextProperty("d", &obj1);
282 TEST_CONTEXT_PROPERTY(&ctxt2, a, QVariant(13));
283 TEST_CONTEXT_PROPERTY(&ctxt2, b, QVariant(8));
284 TEST_CONTEXT_PROPERTY(&ctxt2, c, QVariant(QString("Hi World!")));
285 TEST_CONTEXT_PROPERTY(&ctxt2, d.a, QVariant(12));
286 TEST_CONTEXT_PROPERTY(&ctxt2, e.a, QVariant(12));
288 // Changes in context properties
290 QQmlComponent component(&engine);
291 component.setData("import QtQuick 2.0; QtObject { property variant test: a }", QUrl());
293 QObject *obj = component.create(&ctxt2);
295 QCOMPARE(obj->property("test"), QVariant(13));
296 ctxt.setContextProperty("a", QVariant(19));
297 QCOMPARE(obj->property("test"), QVariant(19));
302 QQmlComponent component(&engine);
303 component.setData("import QtQuick 2.0; QtObject { property variant test: b }", QUrl());
305 QObject *obj = component.create(&ctxt2);
307 QCOMPARE(obj->property("test"), QVariant(8));
308 ctxt.setContextProperty("b", QVariant(5));
309 QCOMPARE(obj->property("test"), QVariant(8));
310 ctxt2.setContextProperty("b", QVariant(1912));
311 QCOMPARE(obj->property("test"), QVariant(1912));
316 QQmlComponent component(&engine);
317 component.setData("import QtQuick 2.0; QtObject { property variant test: e.a }", QUrl());
319 QObject *obj = component.create(&ctxt2);
321 QCOMPARE(obj->property("test"), QVariant(12));
323 QCOMPARE(obj->property("test"), QVariant(13));
328 // New context properties
330 QQmlComponent component(&engine);
331 component.setData("import QtQuick 2.0; QtObject { property variant test: a }", QUrl());
333 QObject *obj = component.create(&ctxt2);
335 QCOMPARE(obj->property("test"), QVariant(19));
336 ctxt2.setContextProperty("a", QVariant(1945));
337 QCOMPARE(obj->property("test"), QVariant(1945));
342 // Setting an object-variant context property
344 QQmlComponent component(&engine);
345 component.setData("import QtQuick 2.0; QtObject { id: root; property int a: 10; property int test: ctxtProp.a; property variant obj: root; }", QUrl());
347 QQmlContext ctxt(engine.rootContext());
348 ctxt.setContextProperty("ctxtProp", QVariant());
350 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: TypeError: Cannot read property 'a' of undefined");
351 QObject *obj = component.create(&ctxt);
353 QVariant v = obj->property("obj");
355 ctxt.setContextProperty("ctxtProp", v);
357 QCOMPARE(obj->property("test"), QVariant(10));
363 void tst_qqmlcontext::setContextObject()
365 QQmlContext ctxt(&engine);
373 ctxt.setContextObject(&to);
374 ctxt.setContextProperty("c", QVariant(9));
376 // Static context properties
377 TEST_CONTEXT_PROPERTY(&ctxt, a, QVariant(2));
378 TEST_CONTEXT_PROPERTY(&ctxt, b, QVariant(192));
379 TEST_CONTEXT_PROPERTY(&ctxt, c, QVariant(9));
384 ctxt.setContextProperty("c", QVariant(3));
386 TEST_CONTEXT_PROPERTY(&ctxt, a, QVariant(12));
387 TEST_CONTEXT_PROPERTY(&ctxt, b, QVariant(100));
388 TEST_CONTEXT_PROPERTY(&ctxt, c, QVariant(3));
390 // Changes in context properties
392 QQmlComponent component(&engine);
393 component.setData("import QtQuick 2.0; QtObject { property variant test: a }", QUrl());
395 QObject *obj = component.create(&ctxt);
397 QCOMPARE(obj->property("test"), QVariant(12));
399 QCOMPARE(obj->property("test"), QVariant(14));
405 void tst_qqmlcontext::destruction()
407 QQmlContext *ctxt = new QQmlContext(&engine);
410 QQmlEngine::setContextForObject(&obj, ctxt);
411 QQmlExpression expr(ctxt, 0, "a");
413 QCOMPARE(ctxt, QQmlEngine::contextForObject(&obj));
414 QCOMPARE(ctxt, expr.context());
416 delete ctxt; ctxt = 0;
418 QCOMPARE(ctxt, QQmlEngine::contextForObject(&obj));
419 QCOMPARE(ctxt, expr.context());
422 void tst_qqmlcontext::idAsContextProperty()
424 QQmlComponent component(&engine);
425 component.setData("import QtQuick 2.0; QtObject { property variant a; a: QtObject { id: myObject } }", QUrl());
427 QObject *obj = component.create();
430 QVariant a = obj->property("a");
431 QVERIFY(a.userType() == QMetaType::QObjectStar);
433 QVariant ctxt = qmlContext(obj)->contextProperty("myObject");
434 QVERIFY(ctxt.userType() == QMetaType::QObjectStar);
441 // Internal contexts should be read-only
442 void tst_qqmlcontext::readOnlyContexts()
444 QQmlComponent component(&engine);
445 component.setData("import QtQuick 2.0; QtObject { id: me }", QUrl());
447 QObject *obj = component.create();
450 QQmlContext *context = qmlContext(obj);
453 QVERIFY(qvariant_cast<QObject*>(context->contextProperty("me")) == obj);
454 QVERIFY(context->contextObject() == obj);
456 QTest::ignoreMessage(QtWarningMsg, "QQmlContext: Cannot set property on internal context.");
457 context->setContextProperty("hello", 12);
458 QVERIFY(context->contextProperty("hello") == QVariant());
460 QTest::ignoreMessage(QtWarningMsg, "QQmlContext: Cannot set property on internal context.");
461 context->setContextProperty("hello", obj);
462 QVERIFY(context->contextProperty("hello") == QVariant());
464 QTest::ignoreMessage(QtWarningMsg, "QQmlContext: Cannot set context object for internal context.");
465 context->setContextObject(0);
466 QVERIFY(context->contextObject() == obj);
471 void tst_qqmlcontext::nameForObject()
479 // As a context property
480 engine.rootContext()->setContextProperty("o1", &o1);
481 engine.rootContext()->setContextProperty("o2", &o2);
482 engine.rootContext()->setContextProperty("o1_2", &o1);
484 QCOMPARE(engine.rootContext()->nameForObject(&o1), QString("o1"));
485 QCOMPARE(engine.rootContext()->nameForObject(&o2), QString("o2"));
486 QCOMPARE(engine.rootContext()->nameForObject(&o3), QString());
489 QQmlComponent component(&engine);
490 component.setData("import QtQuick 2.0; QtObject { id: root; property QtObject o: QtObject { id: nested } }", QUrl());
492 QObject *o = component.create();
495 QCOMPARE(qmlContext(o)->nameForObject(o), QString("root"));
496 QCOMPARE(qmlContext(o)->nameForObject(qvariant_cast<QObject*>(o->property("o"))), QString("nested"));
497 QCOMPARE(qmlContext(o)->nameForObject(&o1), QString());
502 class DeleteCommand : public QObject
506 DeleteCommand() : object(0) {}
511 void doCommand() { if (object) delete object; object = 0; }
514 // Calling refresh expressions would crash if an expression or context was deleted during
516 void tst_qqmlcontext::refreshExpressionsCrash()
521 DeleteCommand command;
522 engine.rootContext()->setContextProperty("deleteCommand", &command);
523 // We use a fresh context here to bypass any root-context optimizations in
525 QQmlContext ctxt(engine.rootContext());
527 QQmlComponent component(&engine);
528 component.setData("import QtQuick 2.0; QtObject { property var binding: deleteCommand.doCommand() }", QUrl());
529 QVERIFY(component.isReady());
531 QObject *o1 = component.create(&ctxt);
532 QObject *o2 = component.create(&ctxt);
536 QQmlContextData::get(&ctxt)->refreshExpressions();
543 DeleteCommand command;
544 engine.rootContext()->setContextProperty("deleteCommand", &command);
545 // We use a fresh context here to bypass any root-context optimizations in
547 QQmlContext ctxt(engine.rootContext());
549 QQmlComponent component(&engine);
550 component.setData("import QtQuick 2.0; QtObject { property var binding: deleteCommand.doCommand() }", QUrl());
551 QVERIFY(component.isReady());
553 QObject *o1 = component.create(&ctxt);
554 QObject *o2 = component.create(&ctxt);
558 QQmlContextData::get(&ctxt)->refreshExpressions();
564 class CountCommand : public QObject
568 CountCommand() : count(0) {}
573 void doCommand() { ++count; }
577 // Test that calling refresh expressions causes all the expressions to refresh
578 void tst_qqmlcontext::refreshExpressions()
581 QQmlComponent component(&engine, testFileUrl("refreshExpressions.qml"));
582 QQmlComponent component2(&engine, testFileUrl("RefreshExpressionsType.qml"));
584 CountCommand command;
585 engine.rootContext()->setContextProperty("countCommand", &command);
587 // We use a fresh context here to bypass any root-context optimizations in
589 QQmlContext context(engine.rootContext());
590 QQmlContext context2(&context);
592 QObject *o1 = component.create(&context);
593 QObject *o2 = component.create(&context2);
594 QObject *o3 = component2.create(&context);
596 QCOMPARE(command.count, 5);
598 QQmlContextData::get(&context)->refreshExpressions();
600 QCOMPARE(command.count, 10);
607 // Test that updating the root context, only causes expressions in contexts with an
608 // unresolved name to reevaluate
609 void tst_qqmlcontext::refreshExpressionsRootContext()
613 CountCommand command;
614 engine.rootContext()->setContextProperty("countCommand", &command);
616 QQmlComponent component(&engine, testFileUrl("refreshExpressions.qml"));
617 QQmlComponent component2(&engine, testFileUrl("refreshExpressionsRootContext.qml"));
619 QQmlContext context(engine.rootContext());
620 QQmlContext context2(engine.rootContext());
622 QString warning = component2.url().toString() + QLatin1String(":4: ReferenceError: unresolvedName is not defined");
624 QObject *o1 = component.create(&context);
626 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
627 QObject *o2 = component2.create(&context2);
629 QCOMPARE(command.count, 3);
631 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
632 QQmlContextData::get(engine.rootContext())->refreshExpressions();
634 QCOMPARE(command.count, 4);
640 void tst_qqmlcontext::qtbug_22535()
643 QQmlComponent component(&engine, testFileUrl("qtbug_22535.qml"));
644 QQmlContext context(engine.rootContext());
646 QObject *o = component.create(&context);
652 void tst_qqmlcontext::evalAfterInvalidate()
655 QQmlComponent component(&engine, testFileUrl("evalAfterInvalidate.qml"));
656 QScopedPointer<QObject> o(component.create());
658 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
659 QCoreApplication::processEvents();
662 QTEST_MAIN(tst_qqmlcontext)
664 #include "tst_qqmlcontext.moc"