1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the test suite of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** 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 inline QUrl TEST_FILE(const QString &filename)
53 return QUrl::fromLocalFile(TESTDATA(filename));
56 inline QUrl TEST_FILE(const char *filename)
58 return TEST_FILE(QLatin1String(filename));
61 class tst_qdeclarativecontext : public QObject
65 tst_qdeclarativecontext() {}
72 void setContextProperty();
73 void setContextObject();
75 void idAsContextProperty();
76 void readOnlyContexts();
79 void refreshExpressions();
80 void refreshExpressionsCrash();
81 void refreshExpressionsRootContext();
85 QDeclarativeEngine engine;
88 void tst_qdeclarativecontext::baseUrl()
90 QDeclarativeContext ctxt(&engine);
92 QCOMPARE(ctxt.baseUrl(), QUrl());
94 ctxt.setBaseUrl(QUrl("http://www.nokia.com/"));
96 QCOMPARE(ctxt.baseUrl(), QUrl("http://www.nokia.com/"));
99 void tst_qdeclarativecontext::resolvedUrl()
101 // Relative to the component
103 QDeclarativeContext ctxt(&engine);
104 ctxt.setBaseUrl(QUrl("http://www.nokia.com/"));
106 QCOMPARE(ctxt.resolvedUrl(QUrl("main.qml")), QUrl("http://www.nokia.com/main.qml"));
109 // Relative to a parent
111 QDeclarativeContext ctxt(&engine);
112 ctxt.setBaseUrl(QUrl("http://www.nokia.com/"));
114 QDeclarativeContext ctxt2(&ctxt);
115 QCOMPARE(ctxt2.resolvedUrl(QUrl("main2.qml")), QUrl("http://www.nokia.com/main2.qml"));
118 // Relative to the engine
120 QDeclarativeContext ctxt(&engine);
121 QCOMPARE(ctxt.resolvedUrl(QUrl("main.qml")), engine.baseUrl().resolved(QUrl("main.qml")));
124 // Relative to a deleted parent
126 QDeclarativeContext *ctxt = new QDeclarativeContext(&engine);
127 ctxt->setBaseUrl(QUrl("http://www.nokia.com/"));
129 QDeclarativeContext ctxt2(ctxt);
130 QCOMPARE(ctxt2.resolvedUrl(QUrl("main2.qml")), QUrl("http://www.nokia.com/main2.qml"));
132 delete ctxt; ctxt = 0;
134 QCOMPARE(ctxt2.resolvedUrl(QUrl("main2.qml")), QUrl());
139 QDeclarativeContext ctxt(&engine);
141 QCOMPARE(ctxt.resolvedUrl(QUrl("http://www.nokia.com/main2.qml")), QUrl("http://www.nokia.com/main2.qml"));
142 QCOMPARE(ctxt.resolvedUrl(QUrl("file:///main2.qml")), QUrl("file:///main2.qml"));
146 void tst_qdeclarativecontext::engineMethod()
148 QDeclarativeEngine *engine = new QDeclarativeEngine;
150 QDeclarativeContext ctxt(engine);
151 QDeclarativeContext ctxt2(&ctxt);
152 QDeclarativeContext ctxt3(&ctxt2);
153 QDeclarativeContext ctxt4(&ctxt2);
155 QCOMPARE(ctxt.engine(), engine);
156 QCOMPARE(ctxt2.engine(), engine);
157 QCOMPARE(ctxt3.engine(), engine);
158 QCOMPARE(ctxt4.engine(), engine);
160 delete engine; engine = 0;
162 QCOMPARE(ctxt.engine(), engine);
163 QCOMPARE(ctxt2.engine(), engine);
164 QCOMPARE(ctxt3.engine(), engine);
165 QCOMPARE(ctxt4.engine(), engine);
168 void tst_qdeclarativecontext::parentContext()
170 QDeclarativeEngine *engine = new QDeclarativeEngine;
172 QCOMPARE(engine->rootContext()->parentContext(), (QDeclarativeContext *)0);
174 QDeclarativeContext *ctxt = new QDeclarativeContext(engine);
175 QDeclarativeContext *ctxt2 = new QDeclarativeContext(ctxt);
176 QDeclarativeContext *ctxt3 = new QDeclarativeContext(ctxt2);
177 QDeclarativeContext *ctxt4 = new QDeclarativeContext(ctxt2);
178 QDeclarativeContext *ctxt5 = new QDeclarativeContext(ctxt);
179 QDeclarativeContext *ctxt6 = new QDeclarativeContext(engine);
180 QDeclarativeContext *ctxt7 = new QDeclarativeContext(engine->rootContext());
182 QCOMPARE(ctxt->parentContext(), engine->rootContext());
183 QCOMPARE(ctxt2->parentContext(), ctxt);
184 QCOMPARE(ctxt3->parentContext(), ctxt2);
185 QCOMPARE(ctxt4->parentContext(), ctxt2);
186 QCOMPARE(ctxt5->parentContext(), ctxt);
187 QCOMPARE(ctxt6->parentContext(), engine->rootContext());
188 QCOMPARE(ctxt7->parentContext(), engine->rootContext());
190 delete ctxt2; ctxt2 = 0;
192 QCOMPARE(ctxt->parentContext(), engine->rootContext());
193 QCOMPARE(ctxt3->parentContext(), (QDeclarativeContext *)0);
194 QCOMPARE(ctxt4->parentContext(), (QDeclarativeContext *)0);
195 QCOMPARE(ctxt5->parentContext(), ctxt);
196 QCOMPARE(ctxt6->parentContext(), engine->rootContext());
197 QCOMPARE(ctxt7->parentContext(), engine->rootContext());
199 delete engine; engine = 0;
201 QCOMPARE(ctxt->parentContext(), (QDeclarativeContext *)0);
202 QCOMPARE(ctxt3->parentContext(), (QDeclarativeContext *)0);
203 QCOMPARE(ctxt4->parentContext(), (QDeclarativeContext *)0);
204 QCOMPARE(ctxt5->parentContext(), (QDeclarativeContext *)0);
205 QCOMPARE(ctxt6->parentContext(), (QDeclarativeContext *)0);
206 QCOMPARE(ctxt7->parentContext(), (QDeclarativeContext *)0);
216 class TestObject : public QObject
219 Q_PROPERTY(int a READ a NOTIFY aChanged)
220 Q_PROPERTY(int b READ b NOTIFY bChanged)
221 Q_PROPERTY(int c READ c NOTIFY cChanged)
224 TestObject() : _a(10), _b(10), _c(10) {}
226 int a() const { return _a; }
227 void setA(int a) { _a = a; emit aChanged(); }
229 int b() const { return _b; }
230 void setB(int b) { _b = b; emit bChanged(); }
232 int c() const { return _c; }
233 void setC(int c) { _c = c; emit cChanged(); }
246 #define TEST_CONTEXT_PROPERTY(ctxt, name, value) \
248 QDeclarativeComponent component(&engine); \
249 component.setData("import QtQuick 1.0; QtObject { property variant test: " #name " }", QUrl()); \
251 QObject *obj = component.create(ctxt); \
253 QCOMPARE(obj->property("test"), value); \
258 void tst_qdeclarativecontext::setContextProperty()
260 QDeclarativeContext ctxt(&engine);
261 QDeclarativeContext ctxt2(&ctxt);
268 // Static context properties
269 ctxt.setContextProperty("a", QVariant(10));
270 ctxt.setContextProperty("b", QVariant(9));
271 ctxt2.setContextProperty("d", &obj2);
272 ctxt2.setContextProperty("b", QVariant(19));
273 ctxt2.setContextProperty("c", QVariant(QString("Hello World!")));
274 ctxt.setContextProperty("d", &obj1);
275 ctxt.setContextProperty("e", &obj1);
277 TEST_CONTEXT_PROPERTY(&ctxt2, a, QVariant(10));
278 TEST_CONTEXT_PROPERTY(&ctxt2, b, QVariant(19));
279 TEST_CONTEXT_PROPERTY(&ctxt2, c, QVariant(QString("Hello World!")));
280 TEST_CONTEXT_PROPERTY(&ctxt2, d.a, QVariant(-19));
281 TEST_CONTEXT_PROPERTY(&ctxt2, e.a, QVariant(3345));
283 ctxt.setContextProperty("a", QVariant(13));
284 ctxt.setContextProperty("b", QVariant(4));
285 ctxt2.setContextProperty("b", QVariant(8));
286 ctxt2.setContextProperty("c", QVariant(QString("Hi World!")));
287 ctxt2.setContextProperty("d", &obj1);
290 TEST_CONTEXT_PROPERTY(&ctxt2, a, QVariant(13));
291 TEST_CONTEXT_PROPERTY(&ctxt2, b, QVariant(8));
292 TEST_CONTEXT_PROPERTY(&ctxt2, c, QVariant(QString("Hi World!")));
293 TEST_CONTEXT_PROPERTY(&ctxt2, d.a, QVariant(12));
294 TEST_CONTEXT_PROPERTY(&ctxt2, e.a, QVariant(12));
296 // Changes in context properties
298 QDeclarativeComponent component(&engine);
299 component.setData("import QtQuick 1.0; QtObject { property variant test: a }", QUrl());
301 QObject *obj = component.create(&ctxt2);
303 QCOMPARE(obj->property("test"), QVariant(13));
304 ctxt.setContextProperty("a", QVariant(19));
305 QCOMPARE(obj->property("test"), QVariant(19));
310 QDeclarativeComponent component(&engine);
311 component.setData("import QtQuick 1.0; QtObject { property variant test: b }", QUrl());
313 QObject *obj = component.create(&ctxt2);
315 QCOMPARE(obj->property("test"), QVariant(8));
316 ctxt.setContextProperty("b", QVariant(5));
317 QCOMPARE(obj->property("test"), QVariant(8));
318 ctxt2.setContextProperty("b", QVariant(1912));
319 QCOMPARE(obj->property("test"), QVariant(1912));
324 QDeclarativeComponent component(&engine);
325 component.setData("import QtQuick 1.0; QtObject { property variant test: e.a }", QUrl());
327 QObject *obj = component.create(&ctxt2);
329 QCOMPARE(obj->property("test"), QVariant(12));
331 QCOMPARE(obj->property("test"), QVariant(13));
336 // New context properties
338 QDeclarativeComponent component(&engine);
339 component.setData("import QtQuick 1.0; QtObject { property variant test: a }", QUrl());
341 QObject *obj = component.create(&ctxt2);
343 QCOMPARE(obj->property("test"), QVariant(19));
344 ctxt2.setContextProperty("a", QVariant(1945));
345 QCOMPARE(obj->property("test"), QVariant(1945));
350 // Setting an object-variant context property
352 QDeclarativeComponent component(&engine);
353 component.setData("import QtQuick 1.0; QtObject { id: root; property int a: 10; property int test: ctxtProp.a; property variant obj: root; }", QUrl());
355 QDeclarativeContext ctxt(engine.rootContext());
356 ctxt.setContextProperty("ctxtProp", QVariant());
358 QTest::ignoreMessage(QtWarningMsg, "<Unknown File>:1: TypeError: Cannot read property 'a' of undefined");
359 QObject *obj = component.create(&ctxt);
361 QVariant v = obj->property("obj");
363 ctxt.setContextProperty("ctxtProp", v);
365 QCOMPARE(obj->property("test"), QVariant(10));
371 void tst_qdeclarativecontext::setContextObject()
373 QDeclarativeContext ctxt(&engine);
381 ctxt.setContextObject(&to);
382 ctxt.setContextProperty("c", QVariant(9));
384 // Static context properties
385 TEST_CONTEXT_PROPERTY(&ctxt, a, QVariant(2));
386 TEST_CONTEXT_PROPERTY(&ctxt, b, QVariant(192));
387 TEST_CONTEXT_PROPERTY(&ctxt, c, QVariant(9));
392 ctxt.setContextProperty("c", QVariant(3));
394 TEST_CONTEXT_PROPERTY(&ctxt, a, QVariant(12));
395 TEST_CONTEXT_PROPERTY(&ctxt, b, QVariant(100));
396 TEST_CONTEXT_PROPERTY(&ctxt, c, QVariant(3));
398 // Changes in context properties
400 QDeclarativeComponent component(&engine);
401 component.setData("import QtQuick 1.0; QtObject { property variant test: a }", QUrl());
403 QObject *obj = component.create(&ctxt);
405 QCOMPARE(obj->property("test"), QVariant(12));
407 QCOMPARE(obj->property("test"), QVariant(14));
413 void tst_qdeclarativecontext::destruction()
415 QDeclarativeContext *ctxt = new QDeclarativeContext(&engine);
418 QDeclarativeEngine::setContextForObject(&obj, ctxt);
419 QDeclarativeExpression expr(ctxt, 0, "a");
421 QCOMPARE(ctxt, QDeclarativeEngine::contextForObject(&obj));
422 QCOMPARE(ctxt, expr.context());
424 delete ctxt; ctxt = 0;
426 QCOMPARE(ctxt, QDeclarativeEngine::contextForObject(&obj));
427 QCOMPARE(ctxt, expr.context());
430 void tst_qdeclarativecontext::idAsContextProperty()
432 QDeclarativeComponent component(&engine);
433 component.setData("import QtQuick 1.0; QtObject { property variant a; a: QtObject { id: myObject } }", QUrl());
435 QObject *obj = component.create();
438 QVariant a = obj->property("a");
439 QVERIFY(a.userType() == QMetaType::QObjectStar);
441 QVariant ctxt = qmlContext(obj)->contextProperty("myObject");
442 QVERIFY(ctxt.userType() == QMetaType::QObjectStar);
449 // Internal contexts should be read-only
450 void tst_qdeclarativecontext::readOnlyContexts()
452 QDeclarativeComponent component(&engine);
453 component.setData("import QtQuick 1.0; QtObject { id: me }", QUrl());
455 QObject *obj = component.create();
458 QDeclarativeContext *context = qmlContext(obj);
461 QVERIFY(qvariant_cast<QObject*>(context->contextProperty("me")) == obj);
462 QVERIFY(context->contextObject() == obj);
464 QTest::ignoreMessage(QtWarningMsg, "QDeclarativeContext: Cannot set property on internal context.");
465 context->setContextProperty("hello", 12);
466 QVERIFY(context->contextProperty("hello") == QVariant());
468 QTest::ignoreMessage(QtWarningMsg, "QDeclarativeContext: Cannot set property on internal context.");
469 context->setContextProperty("hello", obj);
470 QVERIFY(context->contextProperty("hello") == QVariant());
472 QTest::ignoreMessage(QtWarningMsg, "QDeclarativeContext: Cannot set context object for internal context.");
473 context->setContextObject(0);
474 QVERIFY(context->contextObject() == obj);
479 void tst_qdeclarativecontext::nameForObject()
485 QDeclarativeEngine engine;
487 // As a context property
488 engine.rootContext()->setContextProperty("o1", &o1);
489 engine.rootContext()->setContextProperty("o2", &o2);
490 engine.rootContext()->setContextProperty("o1_2", &o1);
492 QCOMPARE(engine.rootContext()->nameForObject(&o1), QString("o1"));
493 QCOMPARE(engine.rootContext()->nameForObject(&o2), QString("o2"));
494 QCOMPARE(engine.rootContext()->nameForObject(&o3), QString());
497 QDeclarativeComponent component(&engine);
498 component.setData("import QtQuick 1.0; QtObject { id: root; property QtObject o: QtObject { id: nested } }", QUrl());
500 QObject *o = component.create();
503 QCOMPARE(qmlContext(o)->nameForObject(o), QString("root"));
504 QCOMPARE(qmlContext(o)->nameForObject(qvariant_cast<QObject*>(o->property("o"))), QString("nested"));
505 QCOMPARE(qmlContext(o)->nameForObject(&o1), QString());
510 class DeleteCommand : public QObject
514 DeleteCommand() : object(0) {}
519 void doCommand() { if (object) delete object; object = 0; }
522 // Calling refresh expressions would crash if an expression or context was deleted during
524 void tst_qdeclarativecontext::refreshExpressionsCrash()
527 QDeclarativeEngine engine;
529 DeleteCommand command;
530 engine.rootContext()->setContextProperty("deleteCommand", &command);
531 // We use a fresh context here to bypass any root-context optimizations in
533 QDeclarativeContext ctxt(engine.rootContext());
535 QDeclarativeComponent component(&engine);
536 component.setData("import QtQuick 2.0; QtObject { property var binding: deleteCommand.doCommand() }", QUrl());
537 QVERIFY(component.isReady());
539 QObject *o1 = component.create(&ctxt);
540 QObject *o2 = component.create(&ctxt);
544 QDeclarativeContextData::get(&ctxt)->refreshExpressions();
549 QDeclarativeEngine engine;
551 DeleteCommand command;
552 engine.rootContext()->setContextProperty("deleteCommand", &command);
553 // We use a fresh context here to bypass any root-context optimizations in
555 QDeclarativeContext ctxt(engine.rootContext());
557 QDeclarativeComponent component(&engine);
558 component.setData("import QtQuick 2.0; QtObject { property var binding: deleteCommand.doCommand() }", QUrl());
559 QVERIFY(component.isReady());
561 QObject *o1 = component.create(&ctxt);
562 QObject *o2 = component.create(&ctxt);
566 QDeclarativeContextData::get(&ctxt)->refreshExpressions();
572 class CountCommand : public QObject
576 CountCommand() : count(0) {}
581 void doCommand() { ++count; }
585 // Test that calling refresh expressions causes all the expressions to refresh
586 void tst_qdeclarativecontext::refreshExpressions()
588 QDeclarativeEngine engine;
589 QDeclarativeComponent component(&engine, TEST_FILE("refreshExpressions.qml"));
590 QDeclarativeComponent component2(&engine, TEST_FILE("RefreshExpressionsType.qml"));
592 CountCommand command;
593 engine.rootContext()->setContextProperty("countCommand", &command);
595 // We use a fresh context here to bypass any root-context optimizations in
597 QDeclarativeContext context(engine.rootContext());
598 QDeclarativeContext context2(&context);
600 QObject *o1 = component.create(&context);
601 QObject *o2 = component.create(&context2);
602 QObject *o3 = component2.create(&context);
604 QCOMPARE(command.count, 5);
606 QDeclarativeContextData::get(&context)->refreshExpressions();
608 QCOMPARE(command.count, 10);
615 // Test that updating the root context, only causes expressions in contexts with an
616 // unresolved name to reevaluate
617 void tst_qdeclarativecontext::refreshExpressionsRootContext()
619 QDeclarativeEngine engine;
621 CountCommand command;
622 engine.rootContext()->setContextProperty("countCommand", &command);
624 QDeclarativeComponent component(&engine, TEST_FILE("refreshExpressions.qml"));
625 QDeclarativeComponent component2(&engine, TEST_FILE("refreshExpressionsRootContext.qml"));
627 QDeclarativeContext context(engine.rootContext());
628 QDeclarativeContext context2(engine.rootContext());
630 QString warning = component2.url().toString() + QLatin1String(":4: ReferenceError: Can't find variable: unresolvedName");
632 QObject *o1 = component.create(&context);
634 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
635 QObject *o2 = component2.create(&context2);
637 QCOMPARE(command.count, 3);
639 QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
640 QDeclarativeContextData::get(engine.rootContext())->refreshExpressions();
642 QCOMPARE(command.count, 4);
648 void tst_qdeclarativecontext::qtbug_22535()
650 QDeclarativeEngine engine;
651 QDeclarativeComponent component(&engine, TEST_FILE("qtbug_22535.qml"));
652 QDeclarativeContext context(engine.rootContext());
654 QObject *o = component.create(&context);
660 QTEST_MAIN(tst_qdeclarativecontext)
662 #include "tst_qdeclarativecontext.moc"