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 ****************************************************************************/
44 #include <QDeclarativeEngine>
45 #include <QDeclarativeContext>
46 #include <QDeclarativeComponent>
47 #include <QDeclarativeExpression>
48 #include <private/qdeclarativecontext_p.h>
49 #include "../../shared/util.h"
51 class tst_qdeclarativecontext : public QDeclarativeDataTest
55 tst_qdeclarativecontext() {}
62 void setContextProperty();
63 void setContextObject();
65 void idAsContextProperty();
66 void readOnlyContexts();
69 void refreshExpressions();
70 void refreshExpressionsCrash();
71 void refreshExpressionsRootContext();
75 QDeclarativeEngine engine;
78 void tst_qdeclarativecontext::baseUrl()
80 QDeclarativeContext ctxt(&engine);
82 QCOMPARE(ctxt.baseUrl(), QUrl());
84 ctxt.setBaseUrl(QUrl("http://www.nokia.com/"));
86 QCOMPARE(ctxt.baseUrl(), QUrl("http://www.nokia.com/"));
89 void tst_qdeclarativecontext::resolvedUrl()
91 // Relative to the component
93 QDeclarativeContext ctxt(&engine);
94 ctxt.setBaseUrl(QUrl("http://www.nokia.com/"));
96 QCOMPARE(ctxt.resolvedUrl(QUrl("main.qml")), QUrl("http://www.nokia.com/main.qml"));
99 // Relative to a parent
101 QDeclarativeContext ctxt(&engine);
102 ctxt.setBaseUrl(QUrl("http://www.nokia.com/"));
104 QDeclarativeContext ctxt2(&ctxt);
105 QCOMPARE(ctxt2.resolvedUrl(QUrl("main2.qml")), QUrl("http://www.nokia.com/main2.qml"));
108 // Relative to the engine
110 QDeclarativeContext ctxt(&engine);
111 QCOMPARE(ctxt.resolvedUrl(QUrl("main.qml")), engine.baseUrl().resolved(QUrl("main.qml")));
114 // Relative to a deleted parent
116 QDeclarativeContext *ctxt = new QDeclarativeContext(&engine);
117 ctxt->setBaseUrl(QUrl("http://www.nokia.com/"));
119 QDeclarativeContext ctxt2(ctxt);
120 QCOMPARE(ctxt2.resolvedUrl(QUrl("main2.qml")), QUrl("http://www.nokia.com/main2.qml"));
122 delete ctxt; ctxt = 0;
124 QCOMPARE(ctxt2.resolvedUrl(QUrl("main2.qml")), QUrl());
129 QDeclarativeContext ctxt(&engine);
131 QCOMPARE(ctxt.resolvedUrl(QUrl("http://www.nokia.com/main2.qml")), QUrl("http://www.nokia.com/main2.qml"));
132 QCOMPARE(ctxt.resolvedUrl(QUrl("file:///main2.qml")), QUrl("file:///main2.qml"));
136 void tst_qdeclarativecontext::engineMethod()
138 QDeclarativeEngine *engine = new QDeclarativeEngine;
140 QDeclarativeContext ctxt(engine);
141 QDeclarativeContext ctxt2(&ctxt);
142 QDeclarativeContext ctxt3(&ctxt2);
143 QDeclarativeContext ctxt4(&ctxt2);
145 QCOMPARE(ctxt.engine(), engine);
146 QCOMPARE(ctxt2.engine(), engine);
147 QCOMPARE(ctxt3.engine(), engine);
148 QCOMPARE(ctxt4.engine(), engine);
150 delete engine; engine = 0;
152 QCOMPARE(ctxt.engine(), engine);
153 QCOMPARE(ctxt2.engine(), engine);
154 QCOMPARE(ctxt3.engine(), engine);
155 QCOMPARE(ctxt4.engine(), engine);
158 void tst_qdeclarativecontext::parentContext()
160 QDeclarativeEngine *engine = new QDeclarativeEngine;
162 QCOMPARE(engine->rootContext()->parentContext(), (QDeclarativeContext *)0);
164 QDeclarativeContext *ctxt = new QDeclarativeContext(engine);
165 QDeclarativeContext *ctxt2 = new QDeclarativeContext(ctxt);
166 QDeclarativeContext *ctxt3 = new QDeclarativeContext(ctxt2);
167 QDeclarativeContext *ctxt4 = new QDeclarativeContext(ctxt2);
168 QDeclarativeContext *ctxt5 = new QDeclarativeContext(ctxt);
169 QDeclarativeContext *ctxt6 = new QDeclarativeContext(engine);
170 QDeclarativeContext *ctxt7 = new QDeclarativeContext(engine->rootContext());
172 QCOMPARE(ctxt->parentContext(), engine->rootContext());
173 QCOMPARE(ctxt2->parentContext(), ctxt);
174 QCOMPARE(ctxt3->parentContext(), ctxt2);
175 QCOMPARE(ctxt4->parentContext(), ctxt2);
176 QCOMPARE(ctxt5->parentContext(), ctxt);
177 QCOMPARE(ctxt6->parentContext(), engine->rootContext());
178 QCOMPARE(ctxt7->parentContext(), engine->rootContext());
180 delete ctxt2; ctxt2 = 0;
182 QCOMPARE(ctxt->parentContext(), engine->rootContext());
183 QCOMPARE(ctxt3->parentContext(), (QDeclarativeContext *)0);
184 QCOMPARE(ctxt4->parentContext(), (QDeclarativeContext *)0);
185 QCOMPARE(ctxt5->parentContext(), ctxt);
186 QCOMPARE(ctxt6->parentContext(), engine->rootContext());
187 QCOMPARE(ctxt7->parentContext(), engine->rootContext());
189 delete engine; engine = 0;
191 QCOMPARE(ctxt->parentContext(), (QDeclarativeContext *)0);
192 QCOMPARE(ctxt3->parentContext(), (QDeclarativeContext *)0);
193 QCOMPARE(ctxt4->parentContext(), (QDeclarativeContext *)0);
194 QCOMPARE(ctxt5->parentContext(), (QDeclarativeContext *)0);
195 QCOMPARE(ctxt6->parentContext(), (QDeclarativeContext *)0);
196 QCOMPARE(ctxt7->parentContext(), (QDeclarativeContext *)0);
206 class TestObject : public QObject
209 Q_PROPERTY(int a READ a NOTIFY aChanged)
210 Q_PROPERTY(int b READ b NOTIFY bChanged)
211 Q_PROPERTY(int c READ c NOTIFY cChanged)
214 TestObject() : _a(10), _b(10), _c(10) {}
216 int a() const { return _a; }
217 void setA(int a) { _a = a; emit aChanged(); }
219 int b() const { return _b; }
220 void setB(int b) { _b = b; emit bChanged(); }
222 int c() const { return _c; }
223 void setC(int c) { _c = c; emit cChanged(); }
236 #define TEST_CONTEXT_PROPERTY(ctxt, name, value) \
238 QDeclarativeComponent component(&engine); \
239 component.setData("import QtQuick 1.0; QtObject { property variant test: " #name " }", QUrl()); \
241 QObject *obj = component.create(ctxt); \
243 QCOMPARE(obj->property("test"), value); \
248 void tst_qdeclarativecontext::setContextProperty()
250 QDeclarativeContext ctxt(&engine);
251 QDeclarativeContext ctxt2(&ctxt);
258 // Static context properties
259 ctxt.setContextProperty("a", QVariant(10));
260 ctxt.setContextProperty("b", QVariant(9));
261 ctxt2.setContextProperty("d", &obj2);
262 ctxt2.setContextProperty("b", QVariant(19));
263 ctxt2.setContextProperty("c", QVariant(QString("Hello World!")));
264 ctxt.setContextProperty("d", &obj1);
265 ctxt.setContextProperty("e", &obj1);
267 TEST_CONTEXT_PROPERTY(&ctxt2, a, QVariant(10));
268 TEST_CONTEXT_PROPERTY(&ctxt2, b, QVariant(19));
269 TEST_CONTEXT_PROPERTY(&ctxt2, c, QVariant(QString("Hello World!")));
270 TEST_CONTEXT_PROPERTY(&ctxt2, d.a, QVariant(-19));
271 TEST_CONTEXT_PROPERTY(&ctxt2, e.a, QVariant(3345));
273 ctxt.setContextProperty("a", QVariant(13));
274 ctxt.setContextProperty("b", QVariant(4));
275 ctxt2.setContextProperty("b", QVariant(8));
276 ctxt2.setContextProperty("c", QVariant(QString("Hi World!")));
277 ctxt2.setContextProperty("d", &obj1);
280 TEST_CONTEXT_PROPERTY(&ctxt2, a, QVariant(13));
281 TEST_CONTEXT_PROPERTY(&ctxt2, b, QVariant(8));
282 TEST_CONTEXT_PROPERTY(&ctxt2, c, QVariant(QString("Hi World!")));
283 TEST_CONTEXT_PROPERTY(&ctxt2, d.a, QVariant(12));
284 TEST_CONTEXT_PROPERTY(&ctxt2, e.a, QVariant(12));
286 // Changes in context properties
288 QDeclarativeComponent component(&engine);
289 component.setData("import QtQuick 1.0; QtObject { property variant test: a }", QUrl());
291 QObject *obj = component.create(&ctxt2);
293 QCOMPARE(obj->property("test"), QVariant(13));
294 ctxt.setContextProperty("a", QVariant(19));
295 QCOMPARE(obj->property("test"), QVariant(19));
300 QDeclarativeComponent component(&engine);
301 component.setData("import QtQuick 1.0; QtObject { property variant test: b }", QUrl());
303 QObject *obj = component.create(&ctxt2);
305 QCOMPARE(obj->property("test"), QVariant(8));
306 ctxt.setContextProperty("b", QVariant(5));
307 QCOMPARE(obj->property("test"), QVariant(8));
308 ctxt2.setContextProperty("b", QVariant(1912));
309 QCOMPARE(obj->property("test"), QVariant(1912));
314 QDeclarativeComponent component(&engine);
315 component.setData("import QtQuick 1.0; QtObject { property variant test: e.a }", QUrl());
317 QObject *obj = component.create(&ctxt2);
319 QCOMPARE(obj->property("test"), QVariant(12));
321 QCOMPARE(obj->property("test"), QVariant(13));
326 // New context properties
328 QDeclarativeComponent component(&engine);
329 component.setData("import QtQuick 1.0; QtObject { property variant test: a }", QUrl());
331 QObject *obj = component.create(&ctxt2);
333 QCOMPARE(obj->property("test"), QVariant(19));
334 ctxt2.setContextProperty("a", QVariant(1945));
335 QCOMPARE(obj->property("test"), QVariant(1945));
340 // Setting an object-variant context property
342 QDeclarativeComponent component(&engine);
343 component.setData("import QtQuick 1.0; QtObject { id: root; property int a: 10; property int test: ctxtProp.a; property variant obj: root; }", QUrl());
345 QDeclarativeContext ctxt(engine.rootContext());
346 ctxt.setContextProperty("ctxtProp", QVariant());
348 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>:1: TypeError: Cannot read property 'a' of undefined");
349 QObject *obj = component.create(&ctxt);
351 QVariant v = obj->property("obj");
353 ctxt.setContextProperty("ctxtProp", v);
355 QCOMPARE(obj->property("test"), QVariant(10));
361 void tst_qdeclarativecontext::setContextObject()
363 QDeclarativeContext ctxt(&engine);
371 ctxt.setContextObject(&to);
372 ctxt.setContextProperty("c", QVariant(9));
374 // Static context properties
375 TEST_CONTEXT_PROPERTY(&ctxt, a, QVariant(2));
376 TEST_CONTEXT_PROPERTY(&ctxt, b, QVariant(192));
377 TEST_CONTEXT_PROPERTY(&ctxt, c, QVariant(9));
382 ctxt.setContextProperty("c", QVariant(3));
384 TEST_CONTEXT_PROPERTY(&ctxt, a, QVariant(12));
385 TEST_CONTEXT_PROPERTY(&ctxt, b, QVariant(100));
386 TEST_CONTEXT_PROPERTY(&ctxt, c, QVariant(3));
388 // Changes in context properties
390 QDeclarativeComponent component(&engine);
391 component.setData("import QtQuick 1.0; QtObject { property variant test: a }", QUrl());
393 QObject *obj = component.create(&ctxt);
395 QCOMPARE(obj->property("test"), QVariant(12));
397 QCOMPARE(obj->property("test"), QVariant(14));
403 void tst_qdeclarativecontext::destruction()
405 QDeclarativeContext *ctxt = new QDeclarativeContext(&engine);
408 QDeclarativeEngine::setContextForObject(&obj, ctxt);
409 QDeclarativeExpression expr(ctxt, 0, "a");
411 QCOMPARE(ctxt, QDeclarativeEngine::contextForObject(&obj));
412 QCOMPARE(ctxt, expr.context());
414 delete ctxt; ctxt = 0;
416 QCOMPARE(ctxt, QDeclarativeEngine::contextForObject(&obj));
417 QCOMPARE(ctxt, expr.context());
420 void tst_qdeclarativecontext::idAsContextProperty()
422 QDeclarativeComponent component(&engine);
423 component.setData("import QtQuick 1.0; QtObject { property variant a; a: QtObject { id: myObject } }", QUrl());
425 QObject *obj = component.create();
428 QVariant a = obj->property("a");
429 QVERIFY(a.userType() == QMetaType::QObjectStar);
431 QVariant ctxt = qmlContext(obj)->contextProperty("myObject");
432 QVERIFY(ctxt.userType() == QMetaType::QObjectStar);
439 // Internal contexts should be read-only
440 void tst_qdeclarativecontext::readOnlyContexts()
442 QDeclarativeComponent component(&engine);
443 component.setData("import QtQuick 1.0; QtObject { id: me }", QUrl());
445 QObject *obj = component.create();
448 QDeclarativeContext *context = qmlContext(obj);
451 QVERIFY(qvariant_cast<QObject*>(context->contextProperty("me")) == obj);
452 QVERIFY(context->contextObject() == obj);
454 QTest::ignoreMessage(QtWarningMsg, "QDeclarativeContext: Cannot set property on internal context.");
455 context->setContextProperty("hello", 12);
456 QVERIFY(context->contextProperty("hello") == QVariant());
458 QTest::ignoreMessage(QtWarningMsg, "QDeclarativeContext: Cannot set property on internal context.");
459 context->setContextProperty("hello", obj);
460 QVERIFY(context->contextProperty("hello") == QVariant());
462 QTest::ignoreMessage(QtWarningMsg, "QDeclarativeContext: Cannot set context object for internal context.");
463 context->setContextObject(0);
464 QVERIFY(context->contextObject() == obj);
469 void tst_qdeclarativecontext::nameForObject()
475 QDeclarativeEngine engine;
477 // As a context property
478 engine.rootContext()->setContextProperty("o1", &o1);
479 engine.rootContext()->setContextProperty("o2", &o2);
480 engine.rootContext()->setContextProperty("o1_2", &o1);
482 QCOMPARE(engine.rootContext()->nameForObject(&o1), QString("o1"));
483 QCOMPARE(engine.rootContext()->nameForObject(&o2), QString("o2"));
484 QCOMPARE(engine.rootContext()->nameForObject(&o3), QString());
487 QDeclarativeComponent component(&engine);
488 component.setData("import QtQuick 1.0; QtObject { id: root; property QtObject o: QtObject { id: nested } }", QUrl());
490 QObject *o = component.create();
493 QCOMPARE(qmlContext(o)->nameForObject(o), QString("root"));
494 QCOMPARE(qmlContext(o)->nameForObject(qvariant_cast<QObject*>(o->property("o"))), QString("nested"));
495 QCOMPARE(qmlContext(o)->nameForObject(&o1), QString());
500 class DeleteCommand : public QObject
504 DeleteCommand() : object(0) {}
509 void doCommand() { if (object) delete object; object = 0; }
512 // Calling refresh expressions would crash if an expression or context was deleted during
514 void tst_qdeclarativecontext::refreshExpressionsCrash()
517 QDeclarativeEngine engine;
519 DeleteCommand command;
520 engine.rootContext()->setContextProperty("deleteCommand", &command);
521 // We use a fresh context here to bypass any root-context optimizations in
523 QDeclarativeContext ctxt(engine.rootContext());
525 QDeclarativeComponent component(&engine);
526 component.setData("import QtQuick 2.0; QtObject { property var binding: deleteCommand.doCommand() }", QUrl());
527 QVERIFY(component.isReady());
529 QObject *o1 = component.create(&ctxt);
530 QObject *o2 = component.create(&ctxt);
534 QDeclarativeContextData::get(&ctxt)->refreshExpressions();
539 QDeclarativeEngine engine;
541 DeleteCommand command;
542 engine.rootContext()->setContextProperty("deleteCommand", &command);
543 // We use a fresh context here to bypass any root-context optimizations in
545 QDeclarativeContext ctxt(engine.rootContext());
547 QDeclarativeComponent component(&engine);
548 component.setData("import QtQuick 2.0; QtObject { property var binding: deleteCommand.doCommand() }", QUrl());
549 QVERIFY(component.isReady());
551 QObject *o1 = component.create(&ctxt);
552 QObject *o2 = component.create(&ctxt);
556 QDeclarativeContextData::get(&ctxt)->refreshExpressions();
562 class CountCommand : public QObject
566 CountCommand() : count(0) {}
571 void doCommand() { ++count; }
575 // Test that calling refresh expressions causes all the expressions to refresh
576 void tst_qdeclarativecontext::refreshExpressions()
578 QDeclarativeEngine engine;
579 QDeclarativeComponent component(&engine, testFileUrl("refreshExpressions.qml"));
580 QDeclarativeComponent component2(&engine, testFileUrl("RefreshExpressionsType.qml"));
582 CountCommand command;
583 engine.rootContext()->setContextProperty("countCommand", &command);
585 // We use a fresh context here to bypass any root-context optimizations in
587 QDeclarativeContext context(engine.rootContext());
588 QDeclarativeContext context2(&context);
590 QObject *o1 = component.create(&context);
591 QObject *o2 = component.create(&context2);
592 QObject *o3 = component2.create(&context);
594 QCOMPARE(command.count, 5);
596 QDeclarativeContextData::get(&context)->refreshExpressions();
598 QCOMPARE(command.count, 10);
605 // Test that updating the root context, only causes expressions in contexts with an
606 // unresolved name to reevaluate
607 void tst_qdeclarativecontext::refreshExpressionsRootContext()
609 QDeclarativeEngine engine;
611 CountCommand command;
612 engine.rootContext()->setContextProperty("countCommand", &command);
614 QDeclarativeComponent component(&engine, testFileUrl("refreshExpressions.qml"));
615 QDeclarativeComponent component2(&engine, testFileUrl("refreshExpressionsRootContext.qml"));
617 QDeclarativeContext context(engine.rootContext());
618 QDeclarativeContext context2(engine.rootContext());
620 QString warning = component2.url().toString() + QLatin1String(":4: ReferenceError: Can't find variable: unresolvedName");
622 QObject *o1 = component.create(&context);
624 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
625 QObject *o2 = component2.create(&context2);
627 QCOMPARE(command.count, 3);
629 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
630 QDeclarativeContextData::get(engine.rootContext())->refreshExpressions();
632 QCOMPARE(command.count, 4);
638 void tst_qdeclarativecontext::qtbug_22535()
640 QDeclarativeEngine engine;
641 QDeclarativeComponent component(&engine, testFileUrl("qtbug_22535.qml"));
642 QDeclarativeContext context(engine.rootContext());
644 QObject *o = component.create(&context);
650 QTEST_MAIN(tst_qdeclarativecontext)
652 #include "tst_qdeclarativecontext.moc"