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 <QtQml/qqmlengine.h>
49 #include <QtQml/qqmlcontext.h>
50 #include <QtQml/qqmlcomponent.h>
51 #include <QtQml/qqmlexpression.h>
52 #include <QtQml/qqmlproperty.h>
53 #include <QtQuick/qquickitem.h>
55 #include <private/qqmlbinding_p.h>
56 #include <private/qqmlboundsignal_p.h>
57 #include <private/qqmldebugservice_p.h>
58 #include <private/qqmlmetatype_p.h>
59 #include <private/qqmlproperty_p.h>
61 #include "debugutil_p.h"
62 #include "qqmlenginedebugclient.h"
64 #include "../../../shared/util.h"
66 #define QVERIFYOBJECT(statement) \
68 if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__)) {\
69 return QmlDebugObjectReference();\
73 class NonScriptProperty : public QObject {
75 Q_PROPERTY(int nonScriptProp READ nonScriptProp WRITE setNonScriptProp NOTIFY nonScriptPropChanged SCRIPTABLE false)
77 int nonScriptProp() const { return 0; }
78 void setNonScriptProp(int) {}
80 void nonScriptPropChanged();
82 QML_DECLARE_TYPE(NonScriptProperty)
84 class tst_QQmlEngineDebugService : public QObject
89 QmlDebugObjectReference findRootObject(int context = 0,
90 bool recursive = false);
91 QmlDebugPropertyReference findProperty(
92 const QList<QmlDebugPropertyReference> &props,
93 const QString &name) const;
95 void recursiveObjectTest(QObject *o,
96 const QmlDebugObjectReference &oref,
97 bool recursive) const;
99 QQmlDebugConnection *m_conn;
100 QQmlEngineDebugClient *m_dbg;
101 QQmlEngine *m_engine;
102 QQuickItem *m_rootItem;
104 QObjectList m_components;
108 void cleanupTestCase();
110 void watch_property();
112 void watch_expression();
113 void watch_expression_data();
114 void watch_context();
117 void queryAvailableEngines();
118 void queryRootContexts();
120 void queryObject_data();
121 void queryObjectsForLocation();
122 void queryObjectsForLocation_data();
123 void queryExpressionResult();
124 void queryExpressionResult_data();
125 void queryExpressionResultInRootContext();
126 void queryExpressionResultBC();
127 void queryExpressionResultBC_data();
129 void setBindingForObject();
130 void resetBindingForObject();
131 void setMethodBody();
132 void queryObjectTree();
133 void setBindingInStates();
135 void regression_QTCREATORBUG_7451();
138 QmlDebugObjectReference tst_QQmlEngineDebugService::findRootObject(
139 int context, bool recursive)
141 bool success = false;
142 m_dbg->queryAvailableEngines(&success);
143 QVERIFYOBJECT(success);
144 QVERIFYOBJECT(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
146 QVERIFYOBJECT(m_dbg->engines().count());
147 m_dbg->queryRootContexts(m_dbg->engines()[0].debugId, &success);
148 QVERIFYOBJECT(success);
149 QVERIFYOBJECT(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
151 QVERIFYOBJECT(m_dbg->rootContext().contexts.count());
152 QVERIFYOBJECT(m_dbg->rootContext().contexts.last().objects.count());
153 int count = m_dbg->rootContext().contexts.count();
154 recursive ? m_dbg->queryObjectRecursive(m_dbg->rootContext().contexts[count - context - 1].objects[0],
156 m_dbg->queryObject(m_dbg->rootContext().contexts[count - context - 1].objects[0], &success);
157 QVERIFYOBJECT(success);
158 QVERIFYOBJECT(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
160 return m_dbg->object();
163 QmlDebugPropertyReference tst_QQmlEngineDebugService::findProperty(
164 const QList<QmlDebugPropertyReference> &props, const QString &name) const
166 foreach (const QmlDebugPropertyReference &p, props) {
170 return QmlDebugPropertyReference();
173 void tst_QQmlEngineDebugService::recursiveObjectTest(
174 QObject *o, const QmlDebugObjectReference &oref, bool recursive) const
176 const QMetaObject *meta = o->metaObject();
178 QQmlType *type = QQmlMetaType::qmlType(meta);
179 QString className = type ? QString(type->qmlTypeName())
180 : QString(meta->className());
181 className = className.mid(className.lastIndexOf(QLatin1Char('/'))+1);
183 QCOMPARE(oref.debugId, QQmlDebugService::idForObject(o));
184 QCOMPARE(oref.name, o->objectName());
185 QCOMPARE(oref.className, className);
186 QCOMPARE(oref.contextDebugId, QQmlDebugService::idForObject(
189 const QObjectList &children = o->children();
190 for (int i=0; i<children.count(); i++) {
191 QObject *child = children[i];
192 if (!qmlContext(child))
194 int debugId = QQmlDebugService::idForObject(child);
195 QVERIFY(debugId >= 0);
197 QmlDebugObjectReference cref;
198 foreach (const QmlDebugObjectReference &ref, oref.children) {
199 if (ref.debugId == debugId) {
204 QVERIFY(cref.debugId >= 0);
207 recursiveObjectTest(child, cref, true);
210 foreach (const QmlDebugPropertyReference &p, oref.properties) {
211 QCOMPARE(p.objectDebugId, QQmlDebugService::idForObject(o));
213 // signal properties are fake - they are generated from QQmlAbstractBoundSignal children
214 if (p.name.startsWith("on") && p.name.length() > 2 && p.name[2].isUpper()) {
215 QString signal = p.value.toString();
216 QQmlBoundSignalExpression *expr = QQmlPropertyPrivate::signalExpression(QQmlProperty(o, p.name));
217 QVERIFY(expr && expr->expression() == signal);
218 QVERIFY(p.valueTypeName.isEmpty());
219 QVERIFY(p.binding.isEmpty());
220 QVERIFY(!p.hasNotifySignal);
224 QMetaProperty pmeta = meta->property(meta->indexOfProperty(p.name.toUtf8().constData()));
226 QCOMPARE(p.name, QString::fromUtf8(pmeta.name()));
228 if (pmeta.type() < QVariant::UserType && pmeta.userType() !=
229 QMetaType::QVariant) // TODO test complex types
230 QCOMPARE(p.value , pmeta.read(o));
232 if (p.name == "parent")
233 QVERIFY(p.valueTypeName == "QGraphicsObject*" ||
234 p.valueTypeName == "QQuickItem*");
236 QCOMPARE(p.valueTypeName, QString::fromUtf8(pmeta.typeName()));
238 QQmlAbstractBinding *binding =
239 QQmlPropertyPrivate::binding(
240 QQmlProperty(o, p.name));
242 QCOMPARE(binding->expression(), p.binding);
244 QCOMPARE(p.hasNotifySignal, pmeta.hasNotifySignal());
246 QVERIFY(pmeta.isValid());
250 void tst_QQmlEngineDebugService::initTestCase()
252 qmlRegisterType<NonScriptProperty>("Test", 1, 0, "NonScriptPropertyElement");
254 QTest::ignoreMessage(QtDebugMsg, "QML Debugger: Waiting for connection on port 3768...");
255 m_engine = new QQmlEngine(this);
257 QList<QByteArray> qml;
258 qml << "import QtQuick 2.0\n"
262 "width: 10; height: 20; scale: blueRect.scale;"
263 "Rectangle { id: blueRect; width: 500; height: 600; color: \"blue\"; }"
264 "Text { font.bold: true; color: blueRect.color; }"
266 "onEntered: { console.log('hello') }"
268 "property variant varObj\n"
269 "property variant varObjList: []\n"
270 "property variant varObjMap\n"
271 "property variant simpleVar: 10.05\n"
272 "Component.onCompleted: {\n"
273 "varObj = blueRect;\n"
274 "var list = varObjList;\n"
275 "list[0] = blueRect;\n"
276 "varObjList = list;\n"
277 "var map = new Object;\n"
278 "map.rect = blueRect;\n"
281 "NonScriptPropertyElement {\n"
285 // add second component to test multiple root contexts
286 qml << "import QtQuick 2.0\n"
289 // and a third to test methods
290 qml << "import QtQuick 2.0\n"
292 "function myMethodNoArgs() { return 3; }\n"
293 "function myMethod(a) { return a + 9; }\n"
294 "function myMethodIndirect() { myMethod(3); }\n"
297 // and a fourth to test states
298 qml << "import QtQuick 2.0\n"
305 "PropertyChanges {\n"
315 "PropertyAnimation {\n"
317 "property:\"width\"\n"
325 for (int i=0; i<qml.count(); i++) {
326 QQmlComponent component(m_engine);
327 component.setData(qml[i], QUrl::fromLocalFile(""));
328 QVERIFY(component.isReady()); // fails if bad syntax
329 m_components << qobject_cast<QQuickItem*>(component.create());
331 m_rootItem = qobject_cast<QQuickItem*>(m_components.first());
333 // add an extra context to test for multiple contexts
334 QQmlContext *context = new QQmlContext(m_engine->rootContext(), this);
335 context->setObjectName("tst_QQmlDebug_childContext");
337 m_conn = new QQmlDebugConnection(this);
338 m_conn->connectToHost("127.0.0.1", 3768);
340 bool ok = m_conn->waitForConnected();
342 QTRY_VERIFY(QQmlDebugService::hasDebuggingClient());
343 m_dbg = new QQmlEngineDebugClient(m_conn);
344 QTRY_VERIFY(m_dbg->state() == QQmlEngineDebugClient::Enabled);
347 void tst_QQmlEngineDebugService::cleanupTestCase()
350 qDeleteAll(m_components);
354 void tst_QQmlEngineDebugService::setMethodBody()
357 QmlDebugObjectReference obj = findRootObject(2);
359 QObject *root = m_components.at(2);
363 QVERIFY(QMetaObject::invokeMethod(root, "myMethodNoArgs", Qt::DirectConnection,
364 Q_RETURN_ARG(QVariant, rv)));
365 QVERIFY(rv == QVariant(qreal(3)));
368 QVERIFY(m_dbg->setMethodBody(obj.debugId, "myMethodNoArgs", "return 7",
371 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
373 QVERIFY(QMetaObject::invokeMethod(root, "myMethodNoArgs", Qt::DirectConnection,
374 Q_RETURN_ARG(QVariant, rv)));
375 QVERIFY(rv == QVariant(qreal(7)));
381 QVERIFY(QMetaObject::invokeMethod(root, "myMethod", Qt::DirectConnection,
382 Q_RETURN_ARG(QVariant, rv), Q_ARG(QVariant, QVariant(19))));
383 QVERIFY(rv == QVariant(qreal(28)));
385 QVERIFY(m_dbg->setMethodBody(obj.debugId, "myMethod", "return a + 7",
388 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
390 QVERIFY(QMetaObject::invokeMethod(root, "myMethod", Qt::DirectConnection,
391 Q_RETURN_ARG(QVariant, rv), Q_ARG(QVariant, QVariant(19))));
392 QVERIFY(rv == QVariant(qreal(26)));
396 void tst_QQmlEngineDebugService::watch_property()
398 QmlDebugObjectReference obj = findRootObject();
399 QmlDebugPropertyReference prop = findProperty(obj.properties, "width");
403 QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(0);
404 unconnected->addWatch(prop, &success);
408 m_dbg->addWatch(QmlDebugPropertyReference(), &success);
410 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
411 QCOMPARE(m_dbg->valid(), false);
413 quint32 id = m_dbg->addWatch(prop, &success);
415 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
416 QCOMPARE(m_dbg->valid(), true);
418 QSignalSpy spy(m_dbg, SIGNAL(valueChanged(QByteArray,QVariant)));
420 int origWidth = m_rootItem->property("width").toInt();
421 m_rootItem->setProperty("width", origWidth*2);
423 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(valueChanged(QByteArray,QVariant))));
424 QCOMPARE(spy.count(), 1);
426 m_dbg->removeWatch(id, &success);
428 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
429 QCOMPARE(m_dbg->valid(), true);
431 // restore original value and verify spy doesn't get additional signal since watch has been removed
432 m_rootItem->setProperty("width", origWidth);
434 QCOMPARE(spy.count(), 1);
436 QCOMPARE(spy.at(0).at(0).value<QByteArray>(), prop.name.toUtf8());
437 QCOMPARE(spy.at(0).at(1).value<QVariant>(), qVariantFromValue(origWidth*2));
440 void tst_QQmlEngineDebugService::watch_object()
442 QmlDebugObjectReference obj = findRootObject();
446 QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(0);
447 unconnected->addWatch(obj, &success);
451 m_dbg->addWatch(QmlDebugObjectReference(), &success);
453 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
454 QCOMPARE(m_dbg->valid(), false);
456 quint32 id = m_dbg->addWatch(obj, &success);
458 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
459 QCOMPARE(m_dbg->valid(), true);
461 QSignalSpy spy(m_dbg, SIGNAL(valueChanged(QByteArray,QVariant)));
463 int origWidth = m_rootItem->property("width").toInt();
464 int origHeight = m_rootItem->property("height").toInt();
465 m_rootItem->setProperty("width", origWidth*2);
466 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(valueChanged(QByteArray,QVariant))));
467 m_rootItem->setProperty("height", origHeight*2);
468 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(valueChanged(QByteArray,QVariant))));
470 QVERIFY(spy.count() > 0);
474 for (int i=0; i<spy.count(); i++) {
475 const QVariantList &values = spy[i];
476 if (values[0].value<QByteArray>() == "width")
477 newWidth = values[1].value<QVariant>().toInt();
478 else if (values[0].value<QByteArray>() == "height")
479 newHeight = values[1].value<QVariant>().toInt();
483 m_dbg->removeWatch(id, &success);
485 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
486 QCOMPARE(m_dbg->valid(), true);
488 // since watch has been removed, restoring the original values should not trigger a valueChanged()
490 m_rootItem->setProperty("width", origWidth);
491 m_rootItem->setProperty("height", origHeight);
493 QCOMPARE(spy.count(), 0);
495 QCOMPARE(newWidth, origWidth * 2);
496 QCOMPARE(newHeight, origHeight * 2);
499 void tst_QQmlEngineDebugService::watch_expression()
501 QFETCH(QString, expr);
502 QFETCH(int, increment);
503 QFETCH(int, incrementCount);
505 int origWidth = m_rootItem->property("width").toInt();
507 QmlDebugObjectReference obj = findRootObject();
511 QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(0);
512 unconnected->addWatch(obj, expr, &success);
516 m_dbg->addWatch(QmlDebugObjectReference(), expr, &success);
518 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
519 QCOMPARE(m_dbg->valid(), false);
521 quint32 id = m_dbg->addWatch(obj, expr, &success);
523 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
524 QCOMPARE(m_dbg->valid(), true);
526 QSignalSpy spy(m_dbg, SIGNAL(valueChanged(QByteArray,QVariant)));
528 int width = origWidth;
529 for (int i=0; i<incrementCount+1; i++) {
532 m_rootItem->setProperty("width", width);
533 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(valueChanged(QByteArray,QVariant))));
537 m_dbg->removeWatch(id, &success);
539 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
540 QCOMPARE(m_dbg->valid(), true);
542 // restore original value and verify spy doesn't get a signal since watch has been removed
543 m_rootItem->setProperty("width", origWidth);
545 QCOMPARE(spy.count(), incrementCount);
547 width = origWidth + increment;
548 for (int i=0; i<spy.count(); i++) {
550 QCOMPARE(spy.at(i).at(1).value<QVariant>().toInt(), width);
554 void tst_QQmlEngineDebugService::watch_expression_data()
556 QTest::addColumn<QString>("expr");
557 QTest::addColumn<int>("increment");
558 QTest::addColumn<int>("incrementCount");
560 QTest::newRow("width") << "width" << 0 << 0;
561 QTest::newRow("width+10") << "width + 10" << 10 << 5;
564 void tst_QQmlEngineDebugService::watch_context()
566 QmlDebugContextReference c;
567 QTest::ignoreMessage(QtWarningMsg, "QQmlEngineDebugClient::addWatch(): Not implemented");
569 m_dbg->addWatch(c, QString(), &success);
573 void tst_QQmlEngineDebugService::watch_file()
575 QmlDebugFileReference f;
576 QTest::ignoreMessage(QtWarningMsg, "QQmlEngineDebugClient::addWatch(): Not implemented");
578 m_dbg->addWatch(f, &success);
582 void tst_QQmlEngineDebugService::queryAvailableEngines()
586 QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(0);
587 unconnected->queryAvailableEngines(&success);
591 m_dbg->queryAvailableEngines(&success);
593 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
595 // TODO test multiple engines
596 QList<QmlDebugEngineReference> engines = m_dbg->engines();
597 QCOMPARE(engines.count(), 1);
599 foreach (const QmlDebugEngineReference &e, engines) {
600 QCOMPARE(e.debugId, QQmlDebugService::idForObject(m_engine));
601 QCOMPARE(e.name, m_engine->objectName());
605 void tst_QQmlEngineDebugService::queryRootContexts()
608 m_dbg->queryAvailableEngines(&success);
610 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
611 QVERIFY(m_dbg->engines().count());
612 int engineId = m_dbg->engines()[0].debugId;
614 QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(0);
615 unconnected->queryRootContexts(engineId, &success);
619 m_dbg->queryRootContexts(engineId, &success);
621 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
623 QQmlContext *actualContext = m_engine->rootContext();
624 QmlDebugContextReference context = m_dbg->rootContext();
625 QCOMPARE(context.debugId, QQmlDebugService::idForObject(actualContext));
626 QCOMPARE(context.name, actualContext->objectName());
628 // root context query sends only root object data - it doesn't fill in
629 // the children or property info
630 QCOMPARE(context.objects.count(), 0);
631 QCOMPARE(context.contexts.count(), 5);
632 QVERIFY(context.contexts[0].debugId >= 0);
633 QCOMPARE(context.contexts[0].name, QString("tst_QQmlDebug_childContext"));
636 void tst_QQmlEngineDebugService::queryObject()
638 QFETCH(bool, recursive);
642 QmlDebugObjectReference rootObject = findRootObject();
644 QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(0);
645 recursive ? unconnected->queryObjectRecursive(rootObject, &success) : unconnected->queryObject(rootObject, &success);
649 recursive ? m_dbg->queryObjectRecursive(rootObject, &success) : m_dbg->queryObject(rootObject, &success);
651 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
653 QmlDebugObjectReference obj = m_dbg->object();
655 // check source as defined in main()
656 QmlDebugFileReference source = obj.source;
657 QCOMPARE(source.url, QUrl::fromLocalFile(""));
658 QCOMPARE(source.lineNumber, 3);
659 QCOMPARE(source.columnNumber, 1);
661 // generically test all properties, children and childrens' properties
662 recursiveObjectTest(m_rootItem, obj, recursive);
665 foreach (const QmlDebugObjectReference &child, obj.children)
666 QVERIFY(child.properties.count() > 0);
668 QmlDebugObjectReference rect;
669 QmlDebugObjectReference text;
670 foreach (const QmlDebugObjectReference &child, obj.children) {
671 if (child.className == "Rectangle")
673 else if (child.className == "Text")
677 // test specific property values
678 QCOMPARE(findProperty(rect.properties, "width").value, qVariantFromValue(500));
679 QCOMPARE(findProperty(rect.properties, "height").value, qVariantFromValue(600));
680 QCOMPARE(findProperty(rect.properties, "color").value, qVariantFromValue(QColor("blue")));
682 QCOMPARE(findProperty(text.properties, "color").value, qVariantFromValue(QColor("blue")));
684 foreach (const QmlDebugObjectReference &child, obj.children)
685 QCOMPARE(child.properties.count(), 0);
689 void tst_QQmlEngineDebugService::queryObject_data()
691 QTest::addColumn<bool>("recursive");
693 QTest::newRow("non-recursive") << false;
694 QTest::newRow("recursive") << true;
697 void tst_QQmlEngineDebugService::queryObjectsForLocation()
699 QFETCH(bool, recursive);
703 QmlDebugObjectReference rootObject = findRootObject();
705 const QString fileName = QFileInfo(rootObject.source.url.toString()).fileName();
706 int lineNumber = rootObject.source.lineNumber;
707 int columnNumber = rootObject.source.columnNumber;
709 QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(0);
710 recursive ? unconnected->queryObjectsForLocationRecursive(fileName, lineNumber,
711 columnNumber, &success)
712 : unconnected->queryObjectsForLocation(fileName, lineNumber,
713 columnNumber, &success);
717 recursive ? m_dbg->queryObjectsForLocationRecursive(fileName, lineNumber,
718 columnNumber, &success)
719 : m_dbg->queryObjectsForLocation(fileName, lineNumber,
720 columnNumber, &success);
722 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
724 QVERIFY(m_dbg->objects().count() == 1);
725 QmlDebugObjectReference obj = m_dbg->objects().first();
727 // check source as defined in main()
728 QmlDebugFileReference source = obj.source;
729 QCOMPARE(source.url, QUrl(fileName));
730 QCOMPARE(source.lineNumber, lineNumber);
731 QCOMPARE(source.columnNumber, columnNumber);
733 // generically test all properties, children and childrens' properties
734 recursiveObjectTest(m_rootItem, obj, recursive);
737 foreach (const QmlDebugObjectReference &child, obj.children)
738 QVERIFY(child.properties.count() > 0);
740 QmlDebugObjectReference rect;
741 QmlDebugObjectReference text;
742 foreach (const QmlDebugObjectReference &child, obj.children) {
743 if (child.className == "Rectangle")
745 else if (child.className == "Text")
749 // test specific property values
750 QCOMPARE(findProperty(rect.properties, "width").value, qVariantFromValue(500));
751 QCOMPARE(findProperty(rect.properties, "height").value, qVariantFromValue(600));
752 QCOMPARE(findProperty(rect.properties, "color").value, qVariantFromValue(QColor("blue")));
754 QCOMPARE(findProperty(text.properties, "color").value, qVariantFromValue(QColor("blue")));
756 foreach (const QmlDebugObjectReference &child, obj.children)
757 QCOMPARE(child.properties.count(), 0);
761 void tst_QQmlEngineDebugService::queryObjectsForLocation_data()
763 QTest::addColumn<bool>("recursive");
765 QTest::newRow("non-recursive") << false;
766 QTest::newRow("recursive") << true;
769 void tst_QQmlEngineDebugService::regression_QTCREATORBUG_7451()
771 QmlDebugObjectReference rootObject = findRootObject();
772 int contextId = rootObject.contextDebugId;
773 QQmlContext *context = qobject_cast<QQmlContext *>(QQmlDebugService::objectForId(contextId));
774 QQmlComponent component(context->engine());
776 content.append("import QtQuick 2.0\n"
781 component.setData(content, rootObject.source.url);
782 QObject *object = component.create(context);
784 int idNew = QQmlDebugService::idForObject(object);
787 const QString fileName = QFileInfo(rootObject.source.url.toString()).fileName();
788 int lineNumber = rootObject.source.lineNumber;
789 int columnNumber = rootObject.source.columnNumber;
790 bool success = false;
792 m_dbg->queryObjectsForLocation(fileName, lineNumber,
793 columnNumber, &success);
795 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
797 foreach (QmlDebugObjectReference child, rootObject.children) {
799 lineNumber = child.source.lineNumber;
800 columnNumber = child.source.columnNumber;
801 m_dbg->queryObjectsForLocation(fileName, lineNumber,
802 columnNumber, &success);
804 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
808 QObject *deleted = QQmlDebugService::objectForId(idNew);
811 lineNumber = rootObject.source.lineNumber;
812 columnNumber = rootObject.source.columnNumber;
814 m_dbg->queryObjectsForLocation(fileName, lineNumber,
815 columnNumber, &success);
817 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
819 foreach (QmlDebugObjectReference child, rootObject.children) {
821 lineNumber = child.source.lineNumber;
822 columnNumber = child.source.columnNumber;
823 m_dbg->queryObjectsForLocation(fileName, lineNumber,
824 columnNumber, &success);
826 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
831 void tst_QQmlEngineDebugService::queryExpressionResult()
833 QFETCH(QString, expr);
834 QFETCH(QVariant, result);
836 int objectId = findRootObject().debugId;
840 QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(0);
841 unconnected->queryExpressionResult(objectId, expr, &success);
845 m_dbg->queryExpressionResult(objectId, expr, &success);
847 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
849 QCOMPARE(m_dbg->resultExpr(), result);
852 void tst_QQmlEngineDebugService::queryExpressionResult_data()
854 QTest::addColumn<QString>("expr");
855 QTest::addColumn<QVariant>("result");
857 QTest::newRow("width + 50") << "width + 50" << qVariantFromValue(60);
858 QTest::newRow("blueRect.width") << "blueRect.width" << qVariantFromValue(500);
859 QTest::newRow("bad expr") << "aeaef" << qVariantFromValue(QString("<undefined>"));
860 QTest::newRow("QObject*") << "varObj" << qVariantFromValue(QString("<unnamed object>"));
861 QTest::newRow("list of QObject*") << "varObjList" << qVariantFromValue(QString("<unknown value>"));
863 map.insert(QLatin1String("rect"), QVariant(QLatin1String("<unnamed object>")));
864 QTest::newRow("varObjMap") << "varObjMap" << qVariantFromValue(map);
865 QTest::newRow("simpleVar") << "simpleVar" << qVariantFromValue(10.05);
868 void tst_QQmlEngineDebugService::queryExpressionResultInRootContext()
871 const QString exp = QLatin1String("1");
872 m_dbg->queryExpressionResult(-1, exp, &success);
874 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
876 QCOMPARE(m_dbg->resultExpr().toString(), exp);
879 void tst_QQmlEngineDebugService::queryExpressionResultBC()
881 QFETCH(QString, expr);
882 QFETCH(QVariant, result);
884 int objectId = findRootObject().debugId;
888 QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(0);
889 unconnected->queryExpressionResultBC(objectId, expr, &success);
893 m_dbg->queryExpressionResultBC(objectId, expr, &success);
895 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
897 QCOMPARE(m_dbg->resultExpr(), result);
900 void tst_QQmlEngineDebugService::queryExpressionResultBC_data()
902 QTest::addColumn<QString>("expr");
903 QTest::addColumn<QVariant>("result");
905 QTest::newRow("width + 50") << "width + 50" << qVariantFromValue(60);
906 QTest::newRow("blueRect.width") << "blueRect.width" << qVariantFromValue(500);
907 QTest::newRow("bad expr") << "aeaef" << qVariantFromValue(QString("<undefined>"));
908 QTest::newRow("QObject*") << "varObj" << qVariantFromValue(QString("<unnamed object>"));
909 QTest::newRow("list of QObject*") << "varObjList" << qVariantFromValue(QString("<unknown value>"));
911 map.insert(QLatin1String("rect"), QVariant(QLatin1String("<unnamed object>")));
912 QTest::newRow("varObjMap") << "varObjMap" << qVariantFromValue(map);
913 QTest::newRow("simpleVar") << "simpleVar" << qVariantFromValue(10.05);
916 void tst_QQmlEngineDebugService::setBindingForObject()
918 QmlDebugObjectReference rootObject = findRootObject();
919 QVERIFY(rootObject.debugId != -1);
920 QmlDebugPropertyReference widthPropertyRef = findProperty(rootObject.properties, "width");
922 QCOMPARE(widthPropertyRef.value, QVariant(10));
923 QCOMPARE(widthPropertyRef.binding, QString());
929 m_dbg->setBindingForObject(rootObject.debugId, "width", "15", true,
930 QString(), -1, &success);
932 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
933 QCOMPARE(m_dbg->valid(), true);
935 rootObject = findRootObject();
936 widthPropertyRef = findProperty(rootObject.properties, "width");
938 QCOMPARE(widthPropertyRef.value, QVariant(15));
939 QCOMPARE(widthPropertyRef.binding, QString());
944 m_dbg->setBindingForObject(rootObject.debugId, "width", "height", false,
945 QString(), -1, &success);
947 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
948 QCOMPARE(m_dbg->valid(), true);
950 rootObject = findRootObject();
951 widthPropertyRef = findProperty(rootObject.properties, "width");
953 QCOMPARE(widthPropertyRef.value, QVariant(20));
954 QCOMPARE(widthPropertyRef.binding, QString("height"));
959 rootObject = findRootObject();
960 QCOMPARE(rootObject.children.size(), 5); // Rectangle, Text, MouseArea, Component.onCompleted, NonScriptPropertyElement
961 QmlDebugObjectReference mouseAreaObject = rootObject.children.at(2);
962 m_dbg->queryObjectRecursive(mouseAreaObject, &success);
964 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
965 mouseAreaObject = m_dbg->object();
967 QCOMPARE(mouseAreaObject.className, QString("MouseArea"));
969 QmlDebugPropertyReference onEnteredRef = findProperty(mouseAreaObject.properties, "onEntered");
971 QCOMPARE(onEnteredRef.name, QString("onEntered"));
972 QCOMPARE(onEnteredRef.value, QVariant("(function onEntered() { { console.log('hello') } })"));
974 m_dbg->setBindingForObject(mouseAreaObject.debugId, "onEntered",
975 "{console.log('hello, world') }", false,
976 QString(), -1, &success);
978 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
979 QCOMPARE(m_dbg->valid(), true);
981 rootObject = findRootObject();
982 mouseAreaObject = rootObject.children.at(2);
983 m_dbg->queryObjectRecursive(mouseAreaObject, &success);
985 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
986 mouseAreaObject = m_dbg->object();
987 onEnteredRef = findProperty(mouseAreaObject.properties, "onEntered");
988 QCOMPARE(onEnteredRef.name, QString("onEntered"));
989 QCOMPARE(onEnteredRef.value, QVariant("{console.log('hello, world') }"));
992 void tst_QQmlEngineDebugService::resetBindingForObject()
994 QmlDebugObjectReference rootObject = findRootObject();
995 QVERIFY(rootObject.debugId != -1);
996 QmlDebugPropertyReference widthPropertyRef = findProperty(rootObject.properties, "width");
998 bool success = false;
1000 m_dbg->setBindingForObject(rootObject.debugId, "width", "15", true,
1001 QString(), -1, &success);
1003 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1004 QCOMPARE(m_dbg->valid(), true);
1009 m_dbg->resetBindingForObject(rootObject.debugId, "width", &success);
1011 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1012 QCOMPARE(m_dbg->valid(), true);
1014 rootObject = findRootObject();
1015 widthPropertyRef = findProperty(rootObject.properties, "width");
1017 QCOMPARE(widthPropertyRef.value, QVariant(0));
1018 QCOMPARE(widthPropertyRef.binding, QString());
1021 // reset nested property
1024 m_dbg->resetBindingForObject(rootObject.debugId, "font.bold", &success);
1026 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1027 QCOMPARE(m_dbg->valid(), true);
1029 rootObject = findRootObject();
1030 QmlDebugPropertyReference boldPropertyRef = findProperty(rootObject.properties, "font.bold");
1032 QCOMPARE(boldPropertyRef.value.toBool(), false);
1033 QCOMPARE(boldPropertyRef.binding, QString());
1036 void tst_QQmlEngineDebugService::setBindingInStates()
1038 // Check if changing bindings of propertychanges works
1040 const int sourceIndex = 3;
1042 QmlDebugObjectReference obj = findRootObject(sourceIndex);
1044 QVERIFY(obj.debugId != -1);
1045 QVERIFY(obj.children.count() >= 2);
1047 // We are going to switch state a couple of times, we need to get rid of the transition before
1048 m_dbg->queryExpressionResult(obj.debugId,QString("transitions = []"), &success);
1050 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1053 // check initial value of the property that is changing
1054 m_dbg->queryExpressionResult(obj.debugId,QString("state=\"state1\""), &success);
1056 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1058 obj = findRootObject(sourceIndex);
1059 QCOMPARE(findProperty(obj.properties,"width").value.toInt(),200);
1062 m_dbg->queryExpressionResult(obj.debugId,QString("state=\"\""),
1065 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1068 obj = findRootObject(sourceIndex, true);
1069 QCOMPARE(findProperty(obj.properties,"width").value.toInt(),100);
1072 // change the binding
1073 QmlDebugObjectReference state = obj.children[1];
1074 QCOMPARE(state.className, QString("State"));
1075 QVERIFY(state.children.count() > 0);
1077 QmlDebugObjectReference propertyChange = state.children[0];
1078 QVERIFY(propertyChange.debugId != -1);
1080 m_dbg->setBindingForObject(propertyChange.debugId, "width",QVariant(300),true,
1081 QString(), -1, &success);
1083 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1085 // check properties changed in state
1086 obj = findRootObject(sourceIndex);
1087 QCOMPARE(findProperty(obj.properties,"width").value.toInt(),100);
1090 m_dbg->queryExpressionResult(obj.debugId,QString("state=\"state1\""), &success);
1092 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1094 obj = findRootObject(sourceIndex);
1095 QCOMPARE(findProperty(obj.properties,"width").value.toInt(),300);
1097 // check changing properties of base state from within a state
1098 m_dbg->setBindingForObject(obj.debugId,"width","height*2",false,
1099 QString(), -1, &success);
1101 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1102 m_dbg->setBindingForObject(obj.debugId,"height","200",true,
1103 QString(), -1, &success);
1105 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1107 obj = findRootObject(sourceIndex);
1108 QCOMPARE(findProperty(obj.properties,"width").value.toInt(),300);
1110 m_dbg->queryExpressionResult(obj.debugId,QString("state=\"\""), &success);
1112 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1114 obj = findRootObject(sourceIndex);
1115 QCOMPARE(findProperty(obj.properties,"width").value.toInt(), 400);
1117 // reset binding while in a state
1118 m_dbg->queryExpressionResult(obj.debugId,QString("state=\"state1\""), &success);
1120 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1122 obj = findRootObject(sourceIndex);
1123 QCOMPARE(findProperty(obj.properties,"width").value.toInt(), 300);
1125 m_dbg->resetBindingForObject(propertyChange.debugId, "width", &success);
1127 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1128 QCOMPARE(m_dbg->valid(), true);
1130 obj = findRootObject(sourceIndex);
1131 QCOMPARE(findProperty(obj.properties,"width").value.toInt(), 400);
1134 m_dbg->setBindingForObject(propertyChange.debugId, "width", "300", true,
1135 QString(), -1, &success);
1137 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1138 QCOMPARE(m_dbg->valid(), true);
1140 obj = findRootObject(sourceIndex);
1141 QCOMPARE(findProperty(obj.properties,"width").value.toInt(), 300);
1144 void tst_QQmlEngineDebugService::queryObjectTree()
1146 const int sourceIndex = 3;
1148 QmlDebugObjectReference obj = findRootObject(sourceIndex, true);
1150 QVERIFY(obj.debugId != -1);
1151 QVERIFY(obj.children.count() >= 2);
1154 QmlDebugObjectReference state = obj.children[1];
1155 QCOMPARE(state.className, QString("State"));
1156 QVERIFY(state.children.count() > 0);
1158 QmlDebugObjectReference propertyChange = state.children[0];
1159 QVERIFY(propertyChange.debugId != -1);
1161 QmlDebugPropertyReference propertyChangeTarget = findProperty(propertyChange.properties,"target");
1162 QCOMPARE(propertyChangeTarget.objectDebugId, propertyChange.debugId);
1164 QmlDebugObjectReference targetReference = qvariant_cast<QmlDebugObjectReference>(propertyChangeTarget.value);
1165 QVERIFY(targetReference.debugId != -1);
1170 QmlDebugObjectReference transition = obj.children[0];
1171 QCOMPARE(transition.className, QString("Transition"));
1172 QCOMPARE(findProperty(transition.properties,"from").value.toString(), QString("*"));
1173 QCOMPARE(findProperty(transition.properties,"to").value, findProperty(state.properties,"name").value);
1174 QVERIFY(transition.children.count() > 0);
1176 QmlDebugObjectReference animation = transition.children[0];
1177 QVERIFY(animation.debugId != -1);
1179 QmlDebugPropertyReference animationTarget = findProperty(animation.properties,"target");
1180 QCOMPARE(animationTarget.objectDebugId, animation.debugId);
1182 targetReference = qvariant_cast<QmlDebugObjectReference>(animationTarget.value);
1183 QVERIFY(targetReference.debugId != -1);
1185 QCOMPARE(findProperty(animation.properties,"property").value.toString(), QString("width"));
1186 QCOMPARE(findProperty(animation.properties,"duration").value.toInt(), 100);
1189 int main(int argc, char *argv[])
1191 int _argc = argc + 1;
1192 char **_argv = new char*[_argc];
1193 for (int i = 0; i < argc; ++i)
1195 char arg[] = "-qmljsdebugger=port:3768";
1196 _argv[_argc - 1] = arg;
1198 QGuiApplication app(_argc, _argv);
1199 tst_QQmlEngineDebugService tc;
1200 return QTest::qExec(&tc, _argc, _argv);
1204 #include "tst_qqmlenginedebugservice.moc"