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 <QHostAddress>
48 #include <QtDeclarative/qdeclarativeengine.h>
49 #include <QtDeclarative/qdeclarativecontext.h>
50 #include <QtDeclarative/qdeclarativecomponent.h>
51 #include <QtDeclarative/qdeclarativeexpression.h>
52 #include <QtDeclarative/qdeclarativeproperty.h>
53 #include <QtQuick/qquickitem.h>
55 #include <private/qdeclarativebinding_p.h>
56 #include <private/qdeclarativeboundsignal_p.h>
57 #include <private/qdeclarativeenginedebug_p.h>
58 #include <private/qdeclarativedebugservice_p.h>
59 #include <private/qdeclarativemetatype_p.h>
60 #include <private/qdeclarativeproperty_p.h>
62 #include "../shared/debugutil_p.h"
64 Q_DECLARE_METATYPE(QDeclarativeDebugWatch::State)
66 class tst_QDeclarativeEngineDebug : public QObject
71 QDeclarativeDebugObjectReference findRootObject(int context = 0, bool recursive = false);
72 QDeclarativeDebugPropertyReference findProperty(const QList<QDeclarativeDebugPropertyReference> &props, const QString &name) const;
73 void waitForQuery(QDeclarativeDebugQuery *query);
75 void recursiveObjectTest(QObject *o, const QDeclarativeDebugObjectReference &oref, bool recursive) const;
77 void recursiveCompareObjects(const QDeclarativeDebugObjectReference &a, const QDeclarativeDebugObjectReference &b) const;
78 void recursiveCompareContexts(const QDeclarativeDebugContextReference &a, const QDeclarativeDebugContextReference &b) const;
79 void compareProperties(const QDeclarativeDebugPropertyReference &a, const QDeclarativeDebugPropertyReference &b) const;
81 QDeclarativeDebugConnection *m_conn;
82 QDeclarativeEngineDebug *m_dbg;
83 QDeclarativeEngine *m_engine;
84 QQuickItem *m_rootItem;
86 QObjectList m_components;
90 void cleanupTestCase();
92 void watch_property();
94 void watch_expression();
95 void watch_expression_data();
99 void queryAvailableEngines();
100 void queryRootContexts();
102 void queryObject_data();
103 void queryExpressionResult();
104 void queryExpressionResult_data();
106 void tst_QDeclarativeDebugFileReference();
107 void tst_QDeclarativeDebugEngineReference();
108 void tst_QDeclarativeDebugObjectReference();
109 void tst_QDeclarativeDebugContextReference();
110 void tst_QDeclarativeDebugPropertyReference();
112 void setBindingForObject();
113 void setMethodBody();
114 void queryObjectTree();
115 void setBindingInStates();
118 class NonScriptProperty : public QObject {
120 Q_PROPERTY(int nonScriptProp READ nonScriptProp WRITE setNonScriptProp NOTIFY nonScriptPropChanged SCRIPTABLE false)
122 int nonScriptProp() const { return 0; }
123 void setNonScriptProp(int) {}
125 void nonScriptPropChanged();
127 QML_DECLARE_TYPE(NonScriptProperty)
130 QDeclarativeDebugObjectReference tst_QDeclarativeEngineDebug::findRootObject(int context, bool recursive)
132 QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this);
133 waitForQuery(q_engines);
135 if (q_engines->engines().count() == 0)
136 return QDeclarativeDebugObjectReference();
137 QDeclarativeDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this);
138 waitForQuery(q_context);
140 if (q_context->rootContext().objects().count() == 0)
141 return QDeclarativeDebugObjectReference();
142 QDeclarativeDebugObjectQuery *q_obj = recursive ?
143 m_dbg->queryObjectRecursive(q_context->rootContext().objects()[context], this) :
144 m_dbg->queryObject(q_context->rootContext().objects()[context], this);
147 QDeclarativeDebugObjectReference result = q_obj->object();
156 QDeclarativeDebugPropertyReference tst_QDeclarativeEngineDebug::findProperty(const QList<QDeclarativeDebugPropertyReference> &props, const QString &name) const
158 foreach(const QDeclarativeDebugPropertyReference &p, props) {
159 if (p.name() == name)
162 return QDeclarativeDebugPropertyReference();
165 void tst_QDeclarativeEngineDebug::waitForQuery(QDeclarativeDebugQuery *query)
168 QCOMPARE(query->parent(), qobject_cast<QObject*>(this));
169 QVERIFY(query->state() == QDeclarativeDebugQuery::Waiting);
170 if (!QDeclarativeDebugTest::waitForSignal(query, SIGNAL(stateChanged(QDeclarativeDebugQuery::State))))
171 QFAIL("query timed out");
174 void tst_QDeclarativeEngineDebug::recursiveObjectTest(QObject *o, const QDeclarativeDebugObjectReference &oref, bool recursive) const
176 const QMetaObject *meta = o->metaObject();
178 QDeclarativeType *type = QDeclarativeMetaType::qmlType(meta);
179 QString className = type ? QString(type->qmlTypeName()) : QString(meta->className());
180 className = className.mid(className.lastIndexOf(QLatin1Char('/'))+1);
182 QCOMPARE(oref.debugId(), QDeclarativeDebugService::idForObject(o));
183 QCOMPARE(oref.name(), o->objectName());
184 QCOMPARE(oref.className(), className);
185 QCOMPARE(oref.contextDebugId(), QDeclarativeDebugService::idForObject(qmlContext(o)));
187 const QObjectList &children = o->children();
188 for (int i=0; i<children.count(); i++) {
189 QObject *child = children[i];
190 if (!qmlContext(child))
192 int debugId = QDeclarativeDebugService::idForObject(child);
193 QVERIFY(debugId >= 0);
195 QDeclarativeDebugObjectReference cref;
196 foreach (const QDeclarativeDebugObjectReference &ref, oref.children()) {
197 if (ref.debugId() == debugId) {
202 QVERIFY(cref.debugId() >= 0);
205 recursiveObjectTest(child, cref, true);
208 foreach (const QDeclarativeDebugPropertyReference &p, oref.properties()) {
209 QCOMPARE(p.objectDebugId(), QDeclarativeDebugService::idForObject(o));
211 // signal properties are fake - they are generated from QDeclarativeBoundSignal children
212 if (p.name().startsWith("on") && p.name().length() > 2 && p.name()[2].isUpper()) {
213 QList<QDeclarativeBoundSignal*> signalHandlers = o->findChildren<QDeclarativeBoundSignal*>();
214 QString signal = p.value().toString();
216 for (int i = 0; i < signalHandlers.count(); ++i)
217 if (signalHandlers.at(i)->expression()->expression() == signal) {
222 QVERIFY(p.valueTypeName().isEmpty());
223 QVERIFY(p.binding().isEmpty());
224 QVERIFY(!p.hasNotifySignal());
228 QMetaProperty pmeta = meta->property(meta->indexOfProperty(p.name().toUtf8().constData()));
230 QCOMPARE(p.name(), QString::fromUtf8(pmeta.name()));
232 if (pmeta.type() < QVariant::UserType && pmeta.userType() != QMetaType::QVariant) // TODO test complex types
233 QCOMPARE(p.value(), pmeta.read(o));
235 if (p.name() == "parent")
236 QVERIFY(p.valueTypeName() == "QGraphicsObject*" || p.valueTypeName() == "QQuickItem*");
238 QCOMPARE(p.valueTypeName(), QString::fromUtf8(pmeta.typeName()));
240 QDeclarativeAbstractBinding *binding =
241 QDeclarativePropertyPrivate::binding(QDeclarativeProperty(o, p.name()));
243 QCOMPARE(binding->expression(), p.binding());
245 QCOMPARE(p.hasNotifySignal(), pmeta.hasNotifySignal());
247 QVERIFY(pmeta.isValid());
251 void tst_QDeclarativeEngineDebug::recursiveCompareObjects(const QDeclarativeDebugObjectReference &a, const QDeclarativeDebugObjectReference &b) const
253 QCOMPARE(a.debugId(), b.debugId());
254 QCOMPARE(a.className(), b.className());
255 QCOMPARE(a.name(), b.name());
256 QCOMPARE(a.contextDebugId(), b.contextDebugId());
258 QCOMPARE(a.source().url(), b.source().url());
259 QCOMPARE(a.source().lineNumber(), b.source().lineNumber());
260 QCOMPARE(a.source().columnNumber(), b.source().columnNumber());
262 QCOMPARE(a.properties().count(), b.properties().count());
263 QCOMPARE(a.children().count(), b.children().count());
265 QList<QDeclarativeDebugPropertyReference> aprops = a.properties();
266 QList<QDeclarativeDebugPropertyReference> bprops = b.properties();
268 for (int i=0; i<aprops.count(); i++)
269 compareProperties(aprops[i], bprops[i]);
271 for (int i=0; i<a.children().count(); i++)
272 recursiveCompareObjects(a.children()[i], b.children()[i]);
275 void tst_QDeclarativeEngineDebug::recursiveCompareContexts(const QDeclarativeDebugContextReference &a, const QDeclarativeDebugContextReference &b) const
277 QCOMPARE(a.debugId(), b.debugId());
278 QCOMPARE(a.name(), b.name());
279 QCOMPARE(a.objects().count(), b.objects().count());
280 QCOMPARE(a.contexts().count(), b.contexts().count());
282 for (int i=0; i<a.objects().count(); i++)
283 recursiveCompareObjects(a.objects()[i], b.objects()[i]);
285 for (int i=0; i<a.contexts().count(); i++)
286 recursiveCompareContexts(a.contexts()[i], b.contexts()[i]);
289 void tst_QDeclarativeEngineDebug::compareProperties(const QDeclarativeDebugPropertyReference &a, const QDeclarativeDebugPropertyReference &b) const
291 QCOMPARE(a.objectDebugId(), b.objectDebugId());
292 QCOMPARE(a.name(), b.name());
293 QCOMPARE(a.value(), b.value());
294 QCOMPARE(a.valueTypeName(), b.valueTypeName());
295 QCOMPARE(a.binding(), b.binding());
296 QCOMPARE(a.hasNotifySignal(), b.hasNotifySignal());
299 void tst_QDeclarativeEngineDebug::initTestCase()
301 qRegisterMetaType<QDeclarativeDebugWatch::State>();
302 qmlRegisterType<NonScriptProperty>("Test", 1, 0, "NonScriptPropertyElement");
304 QTest::ignoreMessage(QtWarningMsg, "QDeclarativeDebugServer: Waiting for connection on port 3768...");
305 m_engine = new QDeclarativeEngine(this);
307 QList<QByteArray> qml;
308 qml << "import QtQuick 2.0\n"
312 "width: 10; height: 20; scale: blueRect.scale;"
313 "Rectangle { id: blueRect; width: 500; height: 600; color: \"blue\"; }"
314 "Text { color: blueRect.color; }"
316 "onEntered: { console.log('hello') }"
318 "property variant varObj\n"
319 "property variant varObjList: []\n"
320 "Component.onCompleted: {\n"
321 "varObj = blueRect;\n"
322 "var list = varObjList;\n"
323 "list[0] = blueRect;\n"
324 "varObjList = list;\n"
326 "NonScriptPropertyElement {\n"
330 // add second component to test multiple root contexts
331 qml << "import QtQuick 2.0\n"
334 // and a third to test methods
335 qml << "import QtQuick 2.0\n"
337 "function myMethodNoArgs() { return 3; }\n"
338 "function myMethod(a) { return a + 9; }\n"
339 "function myMethodIndirect() { myMethod(3); }\n"
342 // and a fourth to test states
343 qml << "import QtQuick 2.0\n"
350 "PropertyChanges {\n"
360 "PropertyAnimation {\n"
362 "property:\"width\"\n"
370 for (int i=0; i<qml.count(); i++) {
371 QDeclarativeComponent component(m_engine);
372 component.setData(qml[i], QUrl::fromLocalFile(""));
373 QVERIFY(component.isReady()); // fails if bad syntax
374 m_components << qobject_cast<QQuickItem*>(component.create());
376 m_rootItem = qobject_cast<QQuickItem*>(m_components.first());
378 // add an extra context to test for multiple contexts
379 QDeclarativeContext *context = new QDeclarativeContext(m_engine->rootContext(), this);
380 context->setObjectName("tst_QDeclarativeDebug_childContext");
382 m_conn = new QDeclarativeDebugConnection(this);
383 m_conn->connectToHost("127.0.0.1", 3768);
385 QTest::ignoreMessage(QtWarningMsg, "QDeclarativeDebugServer: Connection established");
386 bool ok = m_conn->waitForConnected();
388 QTRY_VERIFY(QDeclarativeDebugService::hasDebuggingClient());
389 m_dbg = new QDeclarativeEngineDebug(m_conn, this);
390 QTRY_VERIFY(m_dbg->status() == QDeclarativeEngineDebug::Enabled);
393 void tst_QDeclarativeEngineDebug::cleanupTestCase()
397 qDeleteAll(m_components);
401 void tst_QDeclarativeEngineDebug::setMethodBody()
403 QDeclarativeDebugObjectReference obj = findRootObject(2);
405 QObject *root = m_components.at(2);
409 QVERIFY(QMetaObject::invokeMethod(root, "myMethodNoArgs", Qt::DirectConnection,
410 Q_RETURN_ARG(QVariant, rv)));
411 QVERIFY(rv == QVariant(qreal(3)));
414 QVERIFY(m_dbg->setMethodBody(obj.debugId(), "myMethodNoArgs", "return 7"));
417 QVERIFY(QMetaObject::invokeMethod(root, "myMethodNoArgs", Qt::DirectConnection,
418 Q_RETURN_ARG(QVariant, rv)));
419 QVERIFY(rv == QVariant(qreal(7)));
425 QVERIFY(QMetaObject::invokeMethod(root, "myMethod", Qt::DirectConnection,
426 Q_RETURN_ARG(QVariant, rv), Q_ARG(QVariant, QVariant(19))));
427 QVERIFY(rv == QVariant(qreal(28)));
429 QVERIFY(m_dbg->setMethodBody(obj.debugId(), "myMethod", "return a + 7"));
432 QVERIFY(QMetaObject::invokeMethod(root, "myMethod", Qt::DirectConnection,
433 Q_RETURN_ARG(QVariant, rv), Q_ARG(QVariant, QVariant(19))));
434 QVERIFY(rv == QVariant(qreal(26)));
438 void tst_QDeclarativeEngineDebug::watch_property()
440 QDeclarativeDebugObjectReference obj = findRootObject();
441 QDeclarativeDebugPropertyReference prop = findProperty(obj.properties(), "width");
443 QDeclarativeDebugPropertyWatch *watch;
445 QDeclarativeEngineDebug *unconnected = new QDeclarativeEngineDebug(0);
446 watch = unconnected->addWatch(prop, this);
447 QCOMPARE(watch->state(), QDeclarativeDebugWatch::Dead);
451 watch = m_dbg->addWatch(QDeclarativeDebugPropertyReference(), this);
452 QVERIFY(QDeclarativeDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QDeclarativeDebugWatch::State))));
453 QCOMPARE(watch->state(), QDeclarativeDebugWatch::Inactive);
456 watch = m_dbg->addWatch(prop, this);
457 QCOMPARE(watch->state(), QDeclarativeDebugWatch::Waiting);
458 QCOMPARE(watch->objectDebugId(), obj.debugId());
459 QCOMPARE(watch->name(), prop.name());
461 QSignalSpy spy(watch, SIGNAL(valueChanged(QByteArray,QVariant)));
463 int origWidth = m_rootItem->property("width").toInt();
464 m_rootItem->setProperty("width", origWidth*2);
466 // stateChanged() is received before valueChanged()
467 QVERIFY(QDeclarativeDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QDeclarativeDebugWatch::State))));
468 QCOMPARE(watch->state(), QDeclarativeDebugWatch::Active);
469 QCOMPARE(spy.count(), 1);
471 m_dbg->removeWatch(watch);
474 // restore original value and verify spy doesn't get additional signal since watch has been removed
475 m_rootItem->setProperty("width", origWidth);
477 QCOMPARE(spy.count(), 1);
479 QCOMPARE(spy.at(0).at(0).value<QByteArray>(), prop.name().toUtf8());
480 QCOMPARE(spy.at(0).at(1).value<QVariant>(), qVariantFromValue(origWidth*2));
483 void tst_QDeclarativeEngineDebug::watch_object()
485 QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this);
486 waitForQuery(q_engines);
488 QVERIFY(q_engines->engines().count() > 0);
489 QDeclarativeDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this);
490 waitForQuery(q_context);
492 QVERIFY(q_context->rootContext().objects().count() > 0);
493 QDeclarativeDebugObjectQuery *q_obj = m_dbg->queryObject(q_context->rootContext().objects()[0], this);
496 QDeclarativeDebugObjectReference obj = q_obj->object();
502 QDeclarativeDebugWatch *watch;
504 QDeclarativeEngineDebug *unconnected = new QDeclarativeEngineDebug(0);
505 watch = unconnected->addWatch(obj, this);
506 QCOMPARE(watch->state(), QDeclarativeDebugWatch::Dead);
510 watch = m_dbg->addWatch(QDeclarativeDebugObjectReference(), this);
511 QVERIFY(QDeclarativeDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QDeclarativeDebugWatch::State))));
512 QCOMPARE(watch->state(), QDeclarativeDebugWatch::Inactive);
515 watch = m_dbg->addWatch(obj, this);
516 QCOMPARE(watch->state(), QDeclarativeDebugWatch::Waiting);
517 QCOMPARE(watch->objectDebugId(), obj.debugId());
519 QSignalSpy spy(watch, SIGNAL(valueChanged(QByteArray,QVariant)));
521 int origWidth = m_rootItem->property("width").toInt();
522 int origHeight = m_rootItem->property("height").toInt();
523 m_rootItem->setProperty("width", origWidth*2);
524 m_rootItem->setProperty("height", origHeight*2);
526 // stateChanged() is received before any valueChanged() signals
527 QVERIFY(QDeclarativeDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QDeclarativeDebugWatch::State))));
528 QCOMPARE(watch->state(), QDeclarativeDebugWatch::Active);
529 QVERIFY(spy.count() > 0);
533 for (int i=0; i<spy.count(); i++) {
534 const QVariantList &values = spy[i];
535 if (values[0].value<QByteArray>() == "width")
536 newWidth = values[1].value<QVariant>().toInt();
537 else if (values[0].value<QByteArray>() == "height")
538 newHeight = values[1].value<QVariant>().toInt();
542 m_dbg->removeWatch(watch);
545 // since watch has been removed, restoring the original values should not trigger a valueChanged()
547 m_rootItem->setProperty("width", origWidth);
548 m_rootItem->setProperty("height", origHeight);
550 QCOMPARE(spy.count(), 0);
552 QCOMPARE(newWidth, origWidth * 2);
553 QCOMPARE(newHeight, origHeight * 2);
556 void tst_QDeclarativeEngineDebug::watch_expression()
558 QFETCH(QString, expr);
559 QFETCH(int, increment);
560 QFETCH(int, incrementCount);
562 int origWidth = m_rootItem->property("width").toInt();
564 QDeclarativeDebugObjectReference obj = findRootObject();
566 QDeclarativeDebugObjectExpressionWatch *watch;
568 QDeclarativeEngineDebug *unconnected = new QDeclarativeEngineDebug(0);
569 watch = unconnected->addWatch(obj, expr, this);
570 QCOMPARE(watch->state(), QDeclarativeDebugWatch::Dead);
574 watch = m_dbg->addWatch(QDeclarativeDebugObjectReference(), expr, this);
575 QVERIFY(QDeclarativeDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QDeclarativeDebugWatch::State))));
576 QCOMPARE(watch->state(), QDeclarativeDebugWatch::Inactive);
579 watch = m_dbg->addWatch(obj, expr, this);
580 QCOMPARE(watch->state(), QDeclarativeDebugWatch::Waiting);
581 QCOMPARE(watch->objectDebugId(), obj.debugId());
582 QCOMPARE(watch->expression(), expr);
584 QSignalSpy spyState(watch, SIGNAL(stateChanged(QDeclarativeDebugWatch::State)));
586 QSignalSpy spy(watch, SIGNAL(valueChanged(QByteArray,QVariant)));
587 int expectedSpyCount = incrementCount + 1; // should also get signal with expression's initial value
589 int width = origWidth;
590 for (int i=0; i<incrementCount+1; i++) {
593 m_rootItem->setProperty("width", width);
595 if (!QDeclarativeDebugTest::waitForSignal(watch, SIGNAL(valueChanged(QByteArray,QVariant))))
596 QFAIL("Did not receive valueChanged() for expression");
599 if (spyState.count() == 0)
600 QVERIFY(QDeclarativeDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QDeclarativeDebugWatch::State))));
601 QCOMPARE(spyState.count(), 1);
602 QCOMPARE(watch->state(), QDeclarativeDebugWatch::Active);
604 m_dbg->removeWatch(watch);
607 // restore original value and verify spy doesn't get a signal since watch has been removed
608 m_rootItem->setProperty("width", origWidth);
610 QCOMPARE(spy.count(), expectedSpyCount);
612 width = origWidth + increment;
613 for (int i=0; i<spy.count(); i++) {
614 QCOMPARE(spy.at(i).at(1).value<QVariant>().toInt(), width);
619 void tst_QDeclarativeEngineDebug::watch_expression_data()
621 QTest::addColumn<QString>("expr");
622 QTest::addColumn<int>("increment");
623 QTest::addColumn<int>("incrementCount");
625 QTest::newRow("width") << "width" << 0 << 0;
626 QTest::newRow("width+10") << "width + 10" << 10 << 5;
629 void tst_QDeclarativeEngineDebug::watch_context()
631 QDeclarativeDebugContextReference c;
632 QTest::ignoreMessage(QtWarningMsg, "QDeclarativeEngineDebug::addWatch(): Not implemented");
633 QVERIFY(!m_dbg->addWatch(c, QString(), this));
636 void tst_QDeclarativeEngineDebug::watch_file()
638 QDeclarativeDebugFileReference f;
639 QTest::ignoreMessage(QtWarningMsg, "QDeclarativeEngineDebug::addWatch(): Not implemented");
640 QVERIFY(!m_dbg->addWatch(f, this));
643 void tst_QDeclarativeEngineDebug::queryAvailableEngines()
645 QDeclarativeDebugEnginesQuery *q_engines;
647 QDeclarativeEngineDebug *unconnected = new QDeclarativeEngineDebug(0);
648 q_engines = unconnected->queryAvailableEngines(0);
649 QCOMPARE(q_engines->state(), QDeclarativeDebugQuery::Error);
653 q_engines = m_dbg->queryAvailableEngines(this);
656 q_engines = m_dbg->queryAvailableEngines(this);
657 QVERIFY(q_engines->engines().isEmpty());
658 waitForQuery(q_engines);
660 // TODO test multiple engines
661 QList<QDeclarativeDebugEngineReference> engines = q_engines->engines();
662 QCOMPARE(engines.count(), 1);
664 foreach(const QDeclarativeDebugEngineReference &e, engines) {
665 QCOMPARE(e.debugId(), QDeclarativeDebugService::idForObject(m_engine));
666 QCOMPARE(e.name(), m_engine->objectName());
669 // Make query invalid by deleting client
670 q_engines = m_dbg->queryAvailableEngines(this);
671 QCOMPARE(q_engines->state(), QDeclarativeDebugQuery::Waiting);
673 QCOMPARE(q_engines->state(), QDeclarativeDebugQuery::Error);
675 m_dbg = new QDeclarativeEngineDebug(m_conn, this);
678 void tst_QDeclarativeEngineDebug::queryRootContexts()
680 QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this);
681 waitForQuery(q_engines);
682 int engineId = q_engines->engines()[0].debugId();
685 QDeclarativeDebugRootContextQuery *q_context;
687 QDeclarativeEngineDebug *unconnected = new QDeclarativeEngineDebug(0);
688 q_context = unconnected->queryRootContexts(engineId, this);
689 QCOMPARE(q_context->state(), QDeclarativeDebugQuery::Error);
693 q_context = m_dbg->queryRootContexts(engineId, this);
696 q_context = m_dbg->queryRootContexts(engineId, this);
697 waitForQuery(q_context);
699 QDeclarativeContext *actualContext = m_engine->rootContext();
700 QDeclarativeDebugContextReference context = q_context->rootContext();
701 QCOMPARE(context.debugId(), QDeclarativeDebugService::idForObject(actualContext));
702 QCOMPARE(context.name(), actualContext->objectName());
704 QCOMPARE(context.objects().count(), 4); // 4 qml component objects created for context in main()
706 // root context query sends only root object data - it doesn't fill in
707 // the children or property info
708 QCOMPARE(context.objects()[0].properties().count(), 0);
709 QCOMPARE(context.objects()[0].children().count(), 0);
711 QCOMPARE(context.contexts().count(), 5);
712 QVERIFY(context.contexts()[0].debugId() >= 0);
713 QCOMPARE(context.contexts()[0].name(), QString("tst_QDeclarativeDebug_childContext"));
715 // Make query invalid by deleting client
716 q_context = m_dbg->queryRootContexts(engineId, this);
717 QCOMPARE(q_context->state(), QDeclarativeDebugQuery::Waiting);
719 QCOMPARE(q_context->state(), QDeclarativeDebugQuery::Error);
721 m_dbg = new QDeclarativeEngineDebug(m_conn, this);
724 void tst_QDeclarativeEngineDebug::queryObject()
726 QFETCH(bool, recursive);
728 QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this);
729 waitForQuery(q_engines);
731 QDeclarativeDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this);
732 waitForQuery(q_context);
733 QDeclarativeDebugObjectReference rootObject = q_context->rootContext().objects()[0];
735 QDeclarativeDebugObjectQuery *q_obj = 0;
737 QDeclarativeEngineDebug *unconnected = new QDeclarativeEngineDebug(0);
738 q_obj = recursive ? unconnected->queryObjectRecursive(rootObject, this) : unconnected->queryObject(rootObject, this);
739 QCOMPARE(q_obj->state(), QDeclarativeDebugQuery::Error);
743 q_obj = recursive ? m_dbg->queryObjectRecursive(rootObject, this) : m_dbg->queryObject(rootObject, this);
746 q_obj = recursive ? m_dbg->queryObjectRecursive(rootObject, this) : m_dbg->queryObject(rootObject, this);
749 QDeclarativeDebugObjectReference obj = q_obj->object();
754 // Make query invalid by deleting client
755 q_obj = recursive ? m_dbg->queryObjectRecursive(rootObject, this) : m_dbg->queryObject(rootObject, this);
756 QCOMPARE(q_obj->state(), QDeclarativeDebugQuery::Waiting);
758 QCOMPARE(q_obj->state(), QDeclarativeDebugQuery::Error);
760 m_dbg = new QDeclarativeEngineDebug(m_conn, this);
762 // check source as defined in main()
763 QDeclarativeDebugFileReference source = obj.source();
764 QCOMPARE(source.url(), QUrl::fromLocalFile(""));
765 QCOMPARE(source.lineNumber(), 3);
766 QCOMPARE(source.columnNumber(), 1);
768 // generically test all properties, children and childrens' properties
769 recursiveObjectTest(m_rootItem, obj, recursive);
772 foreach(const QDeclarativeDebugObjectReference &child, obj.children())
773 QVERIFY(child.properties().count() > 0);
775 QDeclarativeDebugObjectReference rect;
776 QDeclarativeDebugObjectReference text;
777 foreach (const QDeclarativeDebugObjectReference &child, obj.children()) {
778 if (child.className() == "Rectangle")
780 else if (child.className() == "Text")
784 // test specific property values
785 QCOMPARE(findProperty(rect.properties(), "width").value(), qVariantFromValue(500));
786 QCOMPARE(findProperty(rect.properties(), "height").value(), qVariantFromValue(600));
787 QCOMPARE(findProperty(rect.properties(), "color").value(), qVariantFromValue(QColor("blue")));
789 QCOMPARE(findProperty(text.properties(), "color").value(), qVariantFromValue(QColor("blue")));
791 foreach(const QDeclarativeDebugObjectReference &child, obj.children())
792 QCOMPARE(child.properties().count(), 0);
796 void tst_QDeclarativeEngineDebug::queryObject_data()
798 QTest::addColumn<bool>("recursive");
800 QTest::newRow("non-recursive") << false;
801 QTest::newRow("recursive") << true;
804 void tst_QDeclarativeEngineDebug::queryExpressionResult()
806 QFETCH(QString, expr);
807 QFETCH(QVariant, result);
809 QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this);
810 waitForQuery(q_engines); // check immediate deletion is ok
812 QDeclarativeDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this);
813 waitForQuery(q_context);
814 int objectId = q_context->rootContext().objects()[0].debugId();
816 QDeclarativeDebugExpressionQuery *q_expr;
818 QDeclarativeEngineDebug *unconnected = new QDeclarativeEngineDebug(0);
819 q_expr = unconnected->queryExpressionResult(objectId, expr, this);
820 QCOMPARE(q_expr->state(), QDeclarativeDebugQuery::Error);
824 q_expr = m_dbg->queryExpressionResult(objectId, expr, this);
827 q_expr = m_dbg->queryExpressionResult(objectId, expr, this);
828 QCOMPARE(q_expr->expression().toString(), expr);
829 waitForQuery(q_expr);
831 QCOMPARE(q_expr->result(), result);
836 // Make query invalid by deleting client
837 q_expr = m_dbg->queryExpressionResult(objectId, expr, this);
838 QCOMPARE(q_expr->state(), QDeclarativeDebugQuery::Waiting);
840 QCOMPARE(q_expr->state(), QDeclarativeDebugQuery::Error);
842 m_dbg = new QDeclarativeEngineDebug(m_conn, this);
845 void tst_QDeclarativeEngineDebug::queryExpressionResult_data()
847 QTest::addColumn<QString>("expr");
848 QTest::addColumn<QVariant>("result");
850 QTest::newRow("width + 50") << "width + 50" << qVariantFromValue(60);
851 QTest::newRow("blueRect.width") << "blueRect.width" << qVariantFromValue(500);
852 QTest::newRow("bad expr") << "aeaef" << qVariantFromValue(QString("<undefined>"));
853 QTest::newRow("QObject*") << "varObj" << qVariantFromValue(QString("<unnamed object>"));
854 QTest::newRow("list of QObject*") << "varObjList" << qVariantFromValue(QString("<unknown value>"));
857 void tst_QDeclarativeEngineDebug::tst_QDeclarativeDebugFileReference()
859 QDeclarativeDebugFileReference ref;
860 QVERIFY(ref.url().isEmpty());
861 QCOMPARE(ref.lineNumber(), -1);
862 QCOMPARE(ref.columnNumber(), -1);
864 ref.setUrl(QUrl("http://test"));
865 QCOMPARE(ref.url(), QUrl("http://test"));
866 ref.setLineNumber(1);
867 QCOMPARE(ref.lineNumber(), 1);
868 ref.setColumnNumber(1);
869 QCOMPARE(ref.columnNumber(), 1);
871 QDeclarativeDebugFileReference copy(ref);
872 QDeclarativeDebugFileReference copyAssign;
874 foreach (const QDeclarativeDebugFileReference &r, (QList<QDeclarativeDebugFileReference>() << copy << copyAssign)) {
875 QCOMPARE(r.url(), ref.url());
876 QCOMPARE(r.lineNumber(), ref.lineNumber());
877 QCOMPARE(r.columnNumber(), ref.columnNumber());
881 void tst_QDeclarativeEngineDebug::tst_QDeclarativeDebugEngineReference()
883 QDeclarativeDebugEngineReference ref;
884 QCOMPARE(ref.debugId(), -1);
885 QVERIFY(ref.name().isEmpty());
887 ref = QDeclarativeDebugEngineReference(1);
888 QCOMPARE(ref.debugId(), 1);
889 QVERIFY(ref.name().isEmpty());
891 QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this);
892 waitForQuery(q_engines);
893 ref = q_engines->engines()[0];
896 QDeclarativeDebugEngineReference copy(ref);
897 QDeclarativeDebugEngineReference copyAssign;
899 foreach (const QDeclarativeDebugEngineReference &r, (QList<QDeclarativeDebugEngineReference>() << copy << copyAssign)) {
900 QCOMPARE(r.debugId(), ref.debugId());
901 QCOMPARE(r.name(), ref.name());
905 void tst_QDeclarativeEngineDebug::tst_QDeclarativeDebugObjectReference()
907 QDeclarativeDebugObjectReference ref;
908 QCOMPARE(ref.debugId(), -1);
909 QCOMPARE(ref.className(), QString());
910 QCOMPARE(ref.name(), QString());
911 QCOMPARE(ref.contextDebugId(), -1);
912 QVERIFY(ref.properties().isEmpty());
913 QVERIFY(ref.children().isEmpty());
915 QDeclarativeDebugFileReference source = ref.source();
916 QVERIFY(source.url().isEmpty());
917 QVERIFY(source.lineNumber() < 0);
918 QVERIFY(source.columnNumber() < 0);
920 ref = QDeclarativeDebugObjectReference(1);
921 QCOMPARE(ref.debugId(), 1);
923 QDeclarativeDebugObjectReference rootObject = findRootObject();
924 QDeclarativeDebugObjectQuery *query = m_dbg->queryObjectRecursive(rootObject, this);
926 ref = query->object();
929 QVERIFY(ref.debugId() >= 0);
931 QDeclarativeDebugObjectReference copy(ref);
932 QDeclarativeDebugObjectReference copyAssign;
934 foreach (const QDeclarativeDebugObjectReference &r, (QList<QDeclarativeDebugObjectReference>() << copy << copyAssign))
935 recursiveCompareObjects(r, ref);
938 void tst_QDeclarativeEngineDebug::tst_QDeclarativeDebugContextReference()
940 QDeclarativeDebugContextReference ref;
941 QCOMPARE(ref.debugId(), -1);
942 QVERIFY(ref.name().isEmpty());
943 QVERIFY(ref.objects().isEmpty());
944 QVERIFY(ref.contexts().isEmpty());
946 QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this);
947 waitForQuery(q_engines);
948 QDeclarativeDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this);
949 waitForQuery(q_context);
951 ref = q_context->rootContext();
954 QVERIFY(ref.debugId() >= 0);
956 QDeclarativeDebugContextReference copy(ref);
957 QDeclarativeDebugContextReference copyAssign;
959 foreach (const QDeclarativeDebugContextReference &r, (QList<QDeclarativeDebugContextReference>() << copy << copyAssign))
960 recursiveCompareContexts(r, ref);
963 void tst_QDeclarativeEngineDebug::tst_QDeclarativeDebugPropertyReference()
965 QDeclarativeDebugObjectReference rootObject = findRootObject();
966 QDeclarativeDebugObjectQuery *query = m_dbg->queryObject(rootObject, this);
968 QDeclarativeDebugObjectReference obj = query->object();
971 QDeclarativeDebugPropertyReference ref = findProperty(obj.properties(), "scale");
972 QVERIFY(ref.objectDebugId() > 0);
973 QVERIFY(!ref.name().isEmpty());
974 QVERIFY(!ref.value().isNull());
975 QVERIFY(!ref.valueTypeName().isEmpty());
976 QVERIFY(!ref.binding().isEmpty());
977 QVERIFY(ref.hasNotifySignal());
979 QDeclarativeDebugPropertyReference copy(ref);
980 QDeclarativeDebugPropertyReference copyAssign;
982 foreach (const QDeclarativeDebugPropertyReference &r, (QList<QDeclarativeDebugPropertyReference>() << copy << copyAssign))
983 compareProperties(r, ref);
986 void tst_QDeclarativeEngineDebug::setBindingForObject()
988 QDeclarativeDebugObjectReference rootObject = findRootObject();
989 QVERIFY(rootObject.debugId() != -1);
990 QDeclarativeDebugPropertyReference widthPropertyRef = findProperty(rootObject.properties(), "width");
992 QCOMPARE(widthPropertyRef.value(), QVariant(10));
993 QCOMPARE(widthPropertyRef.binding(), QString());
998 m_dbg->setBindingForObject(rootObject.debugId(), "width", "15", true);
1000 rootObject = findRootObject();
1001 widthPropertyRef = findProperty(rootObject.properties(), "width");
1003 QCOMPARE(widthPropertyRef.value(), QVariant(15));
1004 QCOMPARE(widthPropertyRef.binding(), QString());
1009 m_dbg->setBindingForObject(rootObject.debugId(), "width", "height", false);
1011 rootObject = findRootObject();
1012 widthPropertyRef = findProperty(rootObject.properties(), "width");
1014 QCOMPARE(widthPropertyRef.value(), QVariant(20));
1015 QCOMPARE(widthPropertyRef.binding(), QString("height"));
1020 m_dbg->resetBindingForObject(rootObject.debugId(), "width");
1022 rootObject = findRootObject();
1023 widthPropertyRef = findProperty(rootObject.properties(), "width");
1025 // QCOMPARE(widthPropertyRef.value(), QVariant(0)); // TODO: Shouldn't this work?
1026 QCOMPARE(widthPropertyRef.binding(), QString());
1031 rootObject = findRootObject();
1032 QCOMPARE(rootObject.children().size(), 5); // Rectangle, Text, MouseArea, Component.onCompleted, NonScriptPropertyElement
1033 QDeclarativeDebugObjectReference mouseAreaObject = rootObject.children().at(2);
1034 QDeclarativeDebugObjectQuery *q_obj = m_dbg->queryObjectRecursive(mouseAreaObject, this);
1035 waitForQuery(q_obj);
1036 mouseAreaObject = q_obj->object();
1038 QCOMPARE(mouseAreaObject.className(), QString("MouseArea"));
1040 QDeclarativeDebugPropertyReference onEnteredRef = findProperty(mouseAreaObject.properties(), "onEntered");
1042 QCOMPARE(onEnteredRef.name(), QString("onEntered"));
1043 QCOMPARE(onEnteredRef.value(), QVariant("(function onEntered() { { console.log('hello') } })"));
1045 m_dbg->setBindingForObject(mouseAreaObject.debugId(), "onEntered", "{console.log('hello, world') }", false) ;
1047 rootObject = findRootObject();
1048 mouseAreaObject = rootObject.children().at(2);
1049 q_obj = m_dbg->queryObjectRecursive(mouseAreaObject, this);
1050 waitForQuery(q_obj);
1051 mouseAreaObject = q_obj->object();
1052 onEnteredRef = findProperty(mouseAreaObject.properties(), "onEntered");
1053 QCOMPARE(onEnteredRef.name(), QString("onEntered"));
1054 QCOMPARE(onEnteredRef.value(), QVariant("{console.log('hello, world') }"));
1057 void tst_QDeclarativeEngineDebug::setBindingInStates()
1059 // Check if changing bindings of propertychanges works
1061 const int sourceIndex = 3;
1063 QDeclarativeDebugObjectReference obj = findRootObject(sourceIndex);
1065 QVERIFY(obj.debugId() != -1);
1066 QVERIFY(obj.children().count() >= 2);
1068 // We are going to switch state a couple of times, we need to get rid of the transition before
1069 QDeclarativeDebugExpressionQuery *q_deleteTransition = m_dbg->queryExpressionResult(obj.debugId(),QString("transitions = []"),this);
1070 waitForQuery(q_deleteTransition);
1071 delete q_deleteTransition;
1074 // check initial value of the property that is changing
1075 QDeclarativeDebugExpressionQuery *q_setState;
1076 q_setState = m_dbg->queryExpressionResult(obj.debugId(),QString("state=\"state1\""),this);
1077 waitForQuery(q_setState);
1080 obj = findRootObject(sourceIndex);
1081 QCOMPARE(findProperty(obj.properties(),"width").value().toInt(),200);
1084 q_setState = m_dbg->queryExpressionResult(obj.debugId(),QString("state=\"\""),this);
1085 waitForQuery(q_setState);
1089 obj = findRootObject(sourceIndex, true);
1090 QCOMPARE(findProperty(obj.properties(),"width").value().toInt(),100);
1093 // change the binding
1094 QDeclarativeDebugObjectReference state = obj.children()[1];
1095 QCOMPARE(state.className(), QString("State"));
1096 QVERIFY(state.children().count() > 0);
1098 QDeclarativeDebugObjectReference propertyChange = state.children()[0];
1099 QVERIFY(propertyChange.debugId() != -1);
1101 QVERIFY( m_dbg->setBindingForObject(propertyChange.debugId(), "width",QVariant(300),true) );
1103 // check properties changed in state
1104 obj = findRootObject(sourceIndex);
1105 QCOMPARE(findProperty(obj.properties(),"width").value().toInt(),100);
1108 q_setState = m_dbg->queryExpressionResult(obj.debugId(),QString("state=\"state1\""),this);
1109 waitForQuery(q_setState);
1112 obj = findRootObject(sourceIndex);
1113 QCOMPARE(findProperty(obj.properties(),"width").value().toInt(),300);
1115 // check changing properties of base state from within a state
1116 QVERIFY(m_dbg->setBindingForObject(obj.debugId(),"width","height*2",false));
1117 QVERIFY(m_dbg->setBindingForObject(obj.debugId(),"height","200",true));
1119 obj = findRootObject(sourceIndex);
1120 QCOMPARE(findProperty(obj.properties(),"width").value().toInt(),300);
1122 q_setState = m_dbg->queryExpressionResult(obj.debugId(),QString("state=\"\""),this);
1123 waitForQuery(q_setState);
1126 obj = findRootObject(sourceIndex);
1127 QCOMPARE(findProperty(obj.properties(),"width").value().toInt(), 400);
1129 // reset binding while in a state
1130 q_setState = m_dbg->queryExpressionResult(obj.debugId(),QString("state=\"state1\""),this);
1131 waitForQuery(q_setState);
1134 obj = findRootObject(sourceIndex);
1135 QCOMPARE(findProperty(obj.properties(),"width").value().toInt(), 300);
1137 m_dbg->resetBindingForObject(propertyChange.debugId(), "width");
1139 obj = findRootObject(sourceIndex);
1140 QCOMPARE(findProperty(obj.properties(),"width").value().toInt(), 400);
1143 m_dbg->setBindingForObject(propertyChange.debugId(), "width", "300", true);
1145 obj = findRootObject(sourceIndex);
1146 QCOMPARE(findProperty(obj.properties(),"width").value().toInt(), 300);
1149 void tst_QDeclarativeEngineDebug::queryObjectTree()
1151 const int sourceIndex = 3;
1153 // Check if states/transitions are initialized when fetching root item
1154 QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this);
1155 waitForQuery(q_engines);
1157 QDeclarativeDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this);
1158 waitForQuery(q_context);
1160 QVERIFY(q_context->rootContext().objects().count() > sourceIndex);
1161 QDeclarativeDebugObjectReference rootObject = q_context->rootContext().objects()[sourceIndex];
1163 QDeclarativeDebugObjectQuery *q_obj = m_dbg->queryObjectRecursive(rootObject, this);
1164 waitForQuery(q_obj);
1166 QDeclarativeDebugObjectReference obj = q_obj->object();
1172 QVERIFY(obj.debugId() != -1);
1173 QVERIFY(obj.children().count() >= 2);
1178 QDeclarativeDebugObjectReference state = obj.children()[1];
1179 QCOMPARE(state.className(), QString("State"));
1180 QVERIFY(state.children().count() > 0);
1182 QDeclarativeDebugObjectReference propertyChange = state.children()[0];
1183 QVERIFY(propertyChange.debugId() != -1);
1185 QDeclarativeDebugPropertyReference propertyChangeTarget = findProperty(propertyChange.properties(),"target");
1186 QCOMPARE(propertyChangeTarget.objectDebugId(), propertyChange.debugId());
1188 QDeclarativeDebugObjectReference targetReference = qvariant_cast<QDeclarativeDebugObjectReference>(propertyChangeTarget.value());
1189 QVERIFY(targetReference.debugId() != -1);
1194 QDeclarativeDebugObjectReference transition = obj.children()[0];
1195 QCOMPARE(transition.className(), QString("Transition"));
1196 QCOMPARE(findProperty(transition.properties(),"from").value().toString(), QString("*"));
1197 QCOMPARE(findProperty(transition.properties(),"to").value(), findProperty(state.properties(),"name").value());
1198 QVERIFY(transition.children().count() > 0);
1200 QDeclarativeDebugObjectReference animation = transition.children()[0];
1201 QVERIFY(animation.debugId() != -1);
1203 QDeclarativeDebugPropertyReference animationTarget = findProperty(animation.properties(),"target");
1204 QCOMPARE(animationTarget.objectDebugId(), animation.debugId());
1206 targetReference = qvariant_cast<QDeclarativeDebugObjectReference>(animationTarget.value());
1207 QVERIFY(targetReference.debugId() != -1);
1209 QCOMPARE(findProperty(animation.properties(),"property").value().toString(), QString("width"));
1210 QCOMPARE(findProperty(animation.properties(),"duration").value().toInt(), 100);
1213 int main(int argc, char *argv[])
1215 int _argc = argc + 1;
1216 char **_argv = new char*[_argc];
1217 for (int i = 0; i < argc; ++i)
1219 char arg[] = "-qmljsdebugger=port:3768";
1220 _argv[_argc - 1] = arg;
1222 QGuiApplication app(_argc, _argv);
1223 tst_QDeclarativeEngineDebug tc;
1224 return QTest::qExec(&tc, _argc, _argv);
1228 #include "tst_qdeclarativeenginedebug.moc"