df8109abfb0013d5d5c5a7ccec7426941089bb86
[profile/ivi/qtdeclarative.git] / tests / auto / qml / debugger / qqmlenginedebug / tst_qqmlenginedebug.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
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.
16 **
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.
20 **
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.
28 **
29 ** Other Usage
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.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 #include <qtest.h>
42 #include <QSignalSpy>
43 #include <QTimer>
44 #include <QHostAddress>
45 #include <QDebug>
46 #include <QThread>
47
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>
54
55 #include <private/qqmlbinding_p.h>
56 #include <private/qqmlboundsignal_p.h>
57 #include <private/qqmlenginedebug_p.h>
58 #include <private/qqmldebugservice_p.h>
59 #include <private/qqmlmetatype_p.h>
60 #include <private/qqmlproperty_p.h>
61
62 #include "../shared/debugutil_p.h"
63
64 Q_DECLARE_METATYPE(QQmlDebugWatch::State)
65
66 class tst_QQmlEngineDebug : public QObject
67 {
68     Q_OBJECT
69
70 private:
71     QQmlDebugObjectReference findRootObject(int context = 0, bool recursive = false);
72     QQmlDebugPropertyReference findProperty(const QList<QQmlDebugPropertyReference> &props, const QString &name) const;
73     void waitForQuery(QQmlDebugQuery *query);
74
75     void recursiveObjectTest(QObject *o, const QQmlDebugObjectReference &oref, bool recursive) const;
76
77     void recursiveCompareObjects(const QQmlDebugObjectReference &a, const QQmlDebugObjectReference &b) const;
78     void recursiveCompareContexts(const QQmlDebugContextReference &a, const QQmlDebugContextReference &b) const;
79     void compareProperties(const QQmlDebugPropertyReference &a, const QQmlDebugPropertyReference &b) const;
80
81     QQmlDebugConnection *m_conn;
82     QQmlEngineDebug *m_dbg;
83     QQmlEngine *m_engine;
84     QQuickItem *m_rootItem;
85
86     QObjectList m_components;
87
88 private slots:
89     void initTestCase();
90     void cleanupTestCase();
91
92     void watch_property();
93     void watch_object();
94     void watch_expression();
95     void watch_expression_data();
96     void watch_context();
97     void watch_file();
98
99     void queryAvailableEngines();
100     void queryRootContexts();
101     void queryObject();
102     void queryObject_data();
103     void queryExpressionResult();
104     void queryExpressionResult_data();
105
106     void tst_QQmlDebugFileReference();
107     void tst_QQmlDebugEngineReference();
108     void tst_QQmlDebugObjectReference();
109     void tst_QQmlDebugContextReference();
110     void tst_QQmlDebugPropertyReference();
111
112     void setBindingForObject();
113     void setMethodBody();
114     void queryObjectTree();
115     void setBindingInStates();
116 };
117
118 class NonScriptProperty : public QObject {
119     Q_OBJECT
120     Q_PROPERTY(int nonScriptProp READ nonScriptProp WRITE setNonScriptProp NOTIFY nonScriptPropChanged SCRIPTABLE false)
121 public:
122     int nonScriptProp() const { return 0; }
123     void setNonScriptProp(int) {}
124 signals:
125     void nonScriptPropChanged();
126 };
127 QML_DECLARE_TYPE(NonScriptProperty)
128
129
130 QQmlDebugObjectReference tst_QQmlEngineDebug::findRootObject(int context, bool recursive)
131 {
132     QQmlDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this);
133     waitForQuery(q_engines);
134
135     if (q_engines->engines().count() == 0)
136         return QQmlDebugObjectReference();
137     QQmlDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this);
138     waitForQuery(q_context);
139
140     if (q_context->rootContext().objects().count() == 0)
141         return QQmlDebugObjectReference();
142     QQmlDebugObjectQuery *q_obj = recursive ?
143                 m_dbg->queryObjectRecursive(q_context->rootContext().objects()[context], this) :
144                 m_dbg->queryObject(q_context->rootContext().objects()[context], this);
145     waitForQuery(q_obj);
146
147     QQmlDebugObjectReference result = q_obj->object();
148
149     delete q_engines;
150     delete q_context;
151     delete q_obj;
152
153     return result;
154 }
155
156 QQmlDebugPropertyReference tst_QQmlEngineDebug::findProperty(const QList<QQmlDebugPropertyReference> &props, const QString &name) const
157 {
158     foreach(const QQmlDebugPropertyReference &p, props) {
159         if (p.name() == name)
160             return p;
161     }
162     return QQmlDebugPropertyReference();
163 }
164
165 void tst_QQmlEngineDebug::waitForQuery(QQmlDebugQuery *query)
166 {
167     QVERIFY(query);
168     QCOMPARE(query->parent(), qobject_cast<QObject*>(this));
169     QVERIFY(query->state() == QQmlDebugQuery::Waiting);
170     if (!QQmlDebugTest::waitForSignal(query, SIGNAL(stateChanged(QQmlDebugQuery::State))))
171         QFAIL("query timed out");
172 }
173
174 void tst_QQmlEngineDebug::recursiveObjectTest(QObject *o, const QQmlDebugObjectReference &oref, bool recursive) const
175 {
176     const QMetaObject *meta = o->metaObject();
177
178     QQmlType *type = QQmlMetaType::qmlType(meta);
179     QString className = type ? QString(type->qmlTypeName()) : QString(meta->className());
180     className = className.mid(className.lastIndexOf(QLatin1Char('/'))+1);
181
182     QCOMPARE(oref.debugId(), QQmlDebugService::idForObject(o));
183     QCOMPARE(oref.name(), o->objectName());
184     QCOMPARE(oref.className(), className);
185     QCOMPARE(oref.contextDebugId(), QQmlDebugService::idForObject(qmlContext(o)));
186
187     const QObjectList &children = o->children();
188     for (int i=0; i<children.count(); i++) {
189         QObject *child = children[i];
190         if (!qmlContext(child))
191             continue;
192         int debugId = QQmlDebugService::idForObject(child);
193         QVERIFY(debugId >= 0);
194
195         QQmlDebugObjectReference cref;
196         foreach (const QQmlDebugObjectReference &ref, oref.children()) {
197             if (ref.debugId() == debugId) {
198                 cref = ref;
199                 break;
200             }
201         }
202         QVERIFY(cref.debugId() >= 0);
203
204         if (recursive)
205             recursiveObjectTest(child, cref, true);
206     }
207
208     foreach (const QQmlDebugPropertyReference &p, oref.properties()) {
209         QCOMPARE(p.objectDebugId(), QQmlDebugService::idForObject(o));
210
211         // signal properties are fake - they are generated from QQmlBoundSignal children
212         if (p.name().startsWith("on") && p.name().length() > 2 && p.name()[2].isUpper()) {
213             QList<QQmlBoundSignal*> signalHandlers = o->findChildren<QQmlBoundSignal*>();
214             QString signal = p.value().toString();
215             bool found = false;
216             for (int i = 0; i < signalHandlers.count(); ++i)
217                 if (signalHandlers.at(i)->expression()->expression() == signal) {
218                     found = true;
219                     break;
220                 }
221             QVERIFY(found);
222             QVERIFY(p.valueTypeName().isEmpty());
223             QVERIFY(p.binding().isEmpty());
224             QVERIFY(!p.hasNotifySignal());
225             continue;
226         }
227
228         QMetaProperty pmeta = meta->property(meta->indexOfProperty(p.name().toUtf8().constData()));
229
230         QCOMPARE(p.name(), QString::fromUtf8(pmeta.name()));
231
232         if (pmeta.type() < QVariant::UserType && pmeta.userType() != QMetaType::QVariant) // TODO test complex types
233             QCOMPARE(p.value(), pmeta.read(o));
234
235         if (p.name() == "parent")
236             QVERIFY(p.valueTypeName() == "QGraphicsObject*" || p.valueTypeName() == "QQuickItem*");
237         else
238             QCOMPARE(p.valueTypeName(), QString::fromUtf8(pmeta.typeName()));
239
240         QQmlAbstractBinding *binding = 
241             QQmlPropertyPrivate::binding(QQmlProperty(o, p.name()));
242         if (binding)
243             QCOMPARE(binding->expression(), p.binding());
244
245         QCOMPARE(p.hasNotifySignal(), pmeta.hasNotifySignal());
246
247         QVERIFY(pmeta.isValid());
248     }
249 }
250
251 void tst_QQmlEngineDebug::recursiveCompareObjects(const QQmlDebugObjectReference &a, const QQmlDebugObjectReference &b) const
252 {
253     QCOMPARE(a.debugId(), b.debugId());
254     QCOMPARE(a.className(), b.className());
255     QCOMPARE(a.name(), b.name());
256     QCOMPARE(a.contextDebugId(), b.contextDebugId());
257
258     QCOMPARE(a.source().url(), b.source().url());
259     QCOMPARE(a.source().lineNumber(), b.source().lineNumber());
260     QCOMPARE(a.source().columnNumber(), b.source().columnNumber());
261
262     QCOMPARE(a.properties().count(), b.properties().count());
263     QCOMPARE(a.children().count(), b.children().count());
264
265     QList<QQmlDebugPropertyReference> aprops = a.properties();
266     QList<QQmlDebugPropertyReference> bprops = b.properties();
267
268     for (int i=0; i<aprops.count(); i++)
269         compareProperties(aprops[i], bprops[i]);
270
271     for (int i=0; i<a.children().count(); i++)
272         recursiveCompareObjects(a.children()[i], b.children()[i]);
273 }
274
275 void tst_QQmlEngineDebug::recursiveCompareContexts(const QQmlDebugContextReference &a, const QQmlDebugContextReference &b) const
276 {
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());
281
282     for (int i=0; i<a.objects().count(); i++)
283         recursiveCompareObjects(a.objects()[i], b.objects()[i]);
284
285     for (int i=0; i<a.contexts().count(); i++)
286         recursiveCompareContexts(a.contexts()[i], b.contexts()[i]);
287 }
288
289 void tst_QQmlEngineDebug::compareProperties(const QQmlDebugPropertyReference &a, const QQmlDebugPropertyReference &b) const
290 {
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());
297 }
298
299 void tst_QQmlEngineDebug::initTestCase()
300 {
301     qRegisterMetaType<QQmlDebugWatch::State>();
302     qmlRegisterType<NonScriptProperty>("Test", 1, 0, "NonScriptPropertyElement");
303
304     QTest::ignoreMessage(QtWarningMsg, "QML Debugger: Waiting for connection on port 3768...");
305     m_engine = new QQmlEngine(this);
306
307     QList<QByteArray> qml;
308     qml << "import QtQuick 2.0\n"
309            "import Test 1.0\n"
310            "Item {"
311                 "id: root\n"
312                 "width: 10; height: 20; scale: blueRect.scale;"
313                 "Rectangle { id: blueRect; width: 500; height: 600; color: \"blue\"; }"
314                 "Text { color: blueRect.color; }"
315                 "MouseArea {"
316                     "onEntered: { console.log('hello') }"
317                 "}"
318                 "property variant varObj\n"
319                 "property variant varObjList: []\n"
320                 "property variant varObjMap\n"
321                 "Component.onCompleted: {\n"
322                     "varObj = blueRect;\n"
323                     "var list = varObjList;\n"
324                     "list[0] = blueRect;\n"
325                     "varObjList = list;\n"
326                     "var map = new Object;\n"
327                     "map.rect = blueRect;\n"
328                     "varObjMap = map;\n"
329                 "}\n"
330                 "NonScriptPropertyElement {\n"
331                 "}\n"
332             "}";
333
334     // add second component to test multiple root contexts
335     qml << "import QtQuick 2.0\n"
336             "Item {}";
337
338     // and a third to test methods
339     qml << "import QtQuick 2.0\n"
340             "Item {"
341                 "function myMethodNoArgs() { return 3; }\n"
342                 "function myMethod(a) { return a + 9; }\n"
343                 "function myMethodIndirect() { myMethod(3); }\n"
344             "}";
345
346     // and a fourth to test states
347     qml << "import QtQuick 2.0\n"
348            "Rectangle {\n"
349                 "id:rootRect\n"
350                 "width:100\n"
351                 "states: [\n"
352                     "State {\n"
353                         "name:\"state1\"\n"
354                         "PropertyChanges {\n"
355                             "target:rootRect\n"
356                             "width:200\n"
357                         "}\n"
358                     "}\n"
359                 "]\n"
360                 "transitions: [\n"
361                     "Transition {\n"
362                         "from:\"*\"\n"
363                         "to:\"state1\"\n"
364                         "PropertyAnimation {\n"
365                             "target:rootRect\n"
366                             "property:\"width\"\n"
367                             "duration:100\n"
368                         "}\n"
369                     "}\n"
370                 "]\n"
371            "}\n"
372            ;
373
374     for (int i=0; i<qml.count(); i++) {
375         QQmlComponent component(m_engine);
376         component.setData(qml[i], QUrl::fromLocalFile(""));
377         QVERIFY(component.isReady());  // fails if bad syntax
378         m_components << qobject_cast<QQuickItem*>(component.create());
379     }
380     m_rootItem = qobject_cast<QQuickItem*>(m_components.first());
381
382     // add an extra context to test for multiple contexts
383     QQmlContext *context = new QQmlContext(m_engine->rootContext(), this);
384     context->setObjectName("tst_QQmlDebug_childContext");
385
386     m_conn = new QQmlDebugConnection(this);
387     m_conn->connectToHost("127.0.0.1", 3768);
388
389     QTest::ignoreMessage(QtWarningMsg, "QML Debugger: Connection established.");
390     bool ok = m_conn->waitForConnected();
391     QVERIFY(ok);
392     QTRY_VERIFY(QQmlDebugService::hasDebuggingClient());
393     m_dbg = new QQmlEngineDebug(m_conn, this);
394     QTRY_VERIFY(m_dbg->state() == QQmlEngineDebug::Enabled);
395 }
396
397 void tst_QQmlEngineDebug::cleanupTestCase()
398 {
399     delete m_dbg;
400     delete m_conn;
401     qDeleteAll(m_components);
402     delete m_engine;
403 }
404
405 void tst_QQmlEngineDebug::setMethodBody()
406 {
407     QQmlDebugObjectReference obj = findRootObject(2);
408
409     QObject *root = m_components.at(2);
410     // Without args
411     {
412     QVariant rv;
413     QVERIFY(QMetaObject::invokeMethod(root, "myMethodNoArgs", Qt::DirectConnection,
414                                       Q_RETURN_ARG(QVariant, rv)));
415     QVERIFY(rv == QVariant(qreal(3)));
416
417
418     QVERIFY(m_dbg->setMethodBody(obj.debugId(), "myMethodNoArgs", "return 7"));
419     QTest::qWait(100);
420
421     QVERIFY(QMetaObject::invokeMethod(root, "myMethodNoArgs", Qt::DirectConnection,
422                                       Q_RETURN_ARG(QVariant, rv)));
423     QVERIFY(rv == QVariant(qreal(7)));
424     }
425
426     // With args
427     {
428     QVariant rv;
429     QVERIFY(QMetaObject::invokeMethod(root, "myMethod", Qt::DirectConnection,
430                                       Q_RETURN_ARG(QVariant, rv), Q_ARG(QVariant, QVariant(19))));
431     QVERIFY(rv == QVariant(qreal(28)));
432
433     QVERIFY(m_dbg->setMethodBody(obj.debugId(), "myMethod", "return a + 7"));
434     QTest::qWait(100);
435
436     QVERIFY(QMetaObject::invokeMethod(root, "myMethod", Qt::DirectConnection,
437                                       Q_RETURN_ARG(QVariant, rv), Q_ARG(QVariant, QVariant(19))));
438     QVERIFY(rv == QVariant(qreal(26)));
439     }
440 }
441
442 void tst_QQmlEngineDebug::watch_property()
443 {
444     QQmlDebugObjectReference obj = findRootObject();
445     QQmlDebugPropertyReference prop = findProperty(obj.properties(), "width");
446
447     QQmlDebugPropertyWatch *watch;
448
449     QQmlEngineDebug *unconnected = new QQmlEngineDebug(0);
450     watch = unconnected->addWatch(prop, this);
451     QCOMPARE(watch->state(), QQmlDebugWatch::Dead);
452     delete watch;
453     delete unconnected;
454
455     watch = m_dbg->addWatch(QQmlDebugPropertyReference(), this);
456     QVERIFY(QQmlDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QQmlDebugWatch::State))));
457     QCOMPARE(watch->state(), QQmlDebugWatch::Inactive);
458     delete watch;
459
460     watch = m_dbg->addWatch(prop, this);
461     QCOMPARE(watch->state(), QQmlDebugWatch::Waiting);
462     QCOMPARE(watch->objectDebugId(), obj.debugId());
463     QCOMPARE(watch->name(), prop.name());
464
465     QSignalSpy spy(watch, SIGNAL(valueChanged(QByteArray,QVariant)));
466
467     int origWidth = m_rootItem->property("width").toInt();
468     m_rootItem->setProperty("width", origWidth*2);
469
470     // stateChanged() is received before valueChanged()
471     QVERIFY(QQmlDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QQmlDebugWatch::State))));
472     QCOMPARE(watch->state(), QQmlDebugWatch::Active);
473     QCOMPARE(spy.count(), 1);
474
475     m_dbg->removeWatch(watch);
476     delete watch;
477
478     // restore original value and verify spy doesn't get additional signal since watch has been removed
479     m_rootItem->setProperty("width", origWidth);
480     QTest::qWait(100);
481     QCOMPARE(spy.count(), 1);
482
483     QCOMPARE(spy.at(0).at(0).value<QByteArray>(), prop.name().toUtf8());
484     QCOMPARE(spy.at(0).at(1).value<QVariant>(), qVariantFromValue(origWidth*2));
485 }
486
487 void tst_QQmlEngineDebug::watch_object()
488 {
489     QQmlDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this);
490     waitForQuery(q_engines);
491
492     QVERIFY(q_engines->engines().count() > 0);
493     QQmlDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this);
494     waitForQuery(q_context);
495
496     QVERIFY(q_context->rootContext().objects().count() > 0);
497     QQmlDebugObjectQuery *q_obj = m_dbg->queryObject(q_context->rootContext().objects()[0], this);
498     waitForQuery(q_obj);
499
500     QQmlDebugObjectReference obj = q_obj->object();
501
502     delete q_engines;
503     delete q_context;
504     delete q_obj;
505
506     QQmlDebugWatch *watch;
507
508     QQmlEngineDebug *unconnected = new QQmlEngineDebug(0);
509     watch = unconnected->addWatch(obj, this);
510     QCOMPARE(watch->state(), QQmlDebugWatch::Dead);
511     delete watch;
512     delete unconnected;
513
514     watch = m_dbg->addWatch(QQmlDebugObjectReference(), this);
515     QVERIFY(QQmlDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QQmlDebugWatch::State))));
516     QCOMPARE(watch->state(), QQmlDebugWatch::Inactive);
517     delete watch;
518
519     watch = m_dbg->addWatch(obj, this);
520     QCOMPARE(watch->state(), QQmlDebugWatch::Waiting);
521     QCOMPARE(watch->objectDebugId(), obj.debugId());
522
523     QSignalSpy spy(watch, SIGNAL(valueChanged(QByteArray,QVariant)));
524
525     int origWidth = m_rootItem->property("width").toInt();
526     int origHeight = m_rootItem->property("height").toInt();
527     m_rootItem->setProperty("width", origWidth*2);
528     m_rootItem->setProperty("height", origHeight*2);
529
530     // stateChanged() is received before any valueChanged() signals
531     QVERIFY(QQmlDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QQmlDebugWatch::State))));
532     QCOMPARE(watch->state(), QQmlDebugWatch::Active);
533     QVERIFY(spy.count() > 0);
534
535     int newWidth = -1;
536     int newHeight = -1;
537     for (int i=0; i<spy.count(); i++) {
538         const QVariantList &values = spy[i];
539         if (values[0].value<QByteArray>() == "width")
540             newWidth = values[1].value<QVariant>().toInt();
541         else if (values[0].value<QByteArray>() == "height")
542             newHeight = values[1].value<QVariant>().toInt();
543
544     }
545
546     m_dbg->removeWatch(watch);
547     delete watch;
548
549     // since watch has been removed, restoring the original values should not trigger a valueChanged()
550     spy.clear();
551     m_rootItem->setProperty("width", origWidth);
552     m_rootItem->setProperty("height", origHeight);
553     QTest::qWait(100);
554     QCOMPARE(spy.count(), 0);
555
556     QCOMPARE(newWidth, origWidth * 2);
557     QCOMPARE(newHeight, origHeight * 2);
558 }
559
560 void tst_QQmlEngineDebug::watch_expression()
561 {
562     QFETCH(QString, expr);
563     QFETCH(int, increment);
564     QFETCH(int, incrementCount);
565
566     int origWidth = m_rootItem->property("width").toInt();
567
568     QQmlDebugObjectReference obj = findRootObject();
569
570     QQmlDebugObjectExpressionWatch *watch;
571
572     QQmlEngineDebug *unconnected = new QQmlEngineDebug(0);
573     watch = unconnected->addWatch(obj, expr, this);
574     QCOMPARE(watch->state(), QQmlDebugWatch::Dead);
575     delete watch;
576     delete unconnected;
577
578     watch = m_dbg->addWatch(QQmlDebugObjectReference(), expr, this);
579     QVERIFY(QQmlDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QQmlDebugWatch::State))));
580     QCOMPARE(watch->state(), QQmlDebugWatch::Inactive);
581     delete watch;
582
583     watch = m_dbg->addWatch(obj, expr, this);
584     QCOMPARE(watch->state(), QQmlDebugWatch::Waiting);
585     QCOMPARE(watch->objectDebugId(), obj.debugId());
586     QCOMPARE(watch->expression(), expr);
587
588     QSignalSpy spyState(watch, SIGNAL(stateChanged(QQmlDebugWatch::State)));
589
590     QSignalSpy spy(watch, SIGNAL(valueChanged(QByteArray,QVariant)));
591     int expectedSpyCount = incrementCount + 1;  // should also get signal with expression's initial value
592
593     int width = origWidth;
594     for (int i=0; i<incrementCount+1; i++) {
595         if (i > 0) {
596             width += increment;
597             m_rootItem->setProperty("width", width);
598         }
599         if (!QQmlDebugTest::waitForSignal(watch, SIGNAL(valueChanged(QByteArray,QVariant))))
600             QFAIL("Did not receive valueChanged() for expression");
601     }
602
603     if (spyState.count() == 0)
604         QVERIFY(QQmlDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QQmlDebugWatch::State))));
605     QCOMPARE(spyState.count(), 1);
606     QCOMPARE(watch->state(), QQmlDebugWatch::Active);
607
608     m_dbg->removeWatch(watch);
609     delete watch;
610
611     // restore original value and verify spy doesn't get a signal since watch has been removed
612     m_rootItem->setProperty("width", origWidth);
613     QTest::qWait(100);
614     QCOMPARE(spy.count(), expectedSpyCount);
615
616     width = origWidth + increment;
617     for (int i=0; i<spy.count(); i++) {
618         QCOMPARE(spy.at(i).at(1).value<QVariant>().toInt(), width);
619         width += increment;
620     }
621 }
622
623 void tst_QQmlEngineDebug::watch_expression_data()
624 {
625     QTest::addColumn<QString>("expr");
626     QTest::addColumn<int>("increment");
627     QTest::addColumn<int>("incrementCount");
628
629     QTest::newRow("width") << "width" << 0 << 0;
630     QTest::newRow("width+10") << "width + 10" << 10 << 5;
631 }
632
633 void tst_QQmlEngineDebug::watch_context()
634 {
635     QQmlDebugContextReference c;
636     QTest::ignoreMessage(QtWarningMsg, "QQmlEngineDebug::addWatch(): Not implemented");
637     QVERIFY(!m_dbg->addWatch(c, QString(), this));
638 }
639
640 void tst_QQmlEngineDebug::watch_file()
641 {
642     QQmlDebugFileReference f;
643     QTest::ignoreMessage(QtWarningMsg, "QQmlEngineDebug::addWatch(): Not implemented");
644     QVERIFY(!m_dbg->addWatch(f, this));
645 }
646
647 void tst_QQmlEngineDebug::queryAvailableEngines()
648 {
649     QQmlDebugEnginesQuery *q_engines;
650
651     QQmlEngineDebug *unconnected = new QQmlEngineDebug(0);
652     q_engines = unconnected->queryAvailableEngines(0);
653     QCOMPARE(q_engines->state(), QQmlDebugQuery::Error);
654     delete q_engines;
655     delete unconnected;
656
657     q_engines = m_dbg->queryAvailableEngines(this);
658     delete q_engines;
659
660     q_engines = m_dbg->queryAvailableEngines(this);
661     QVERIFY(q_engines->engines().isEmpty());
662     waitForQuery(q_engines);
663
664     // TODO test multiple engines
665     QList<QQmlDebugEngineReference> engines = q_engines->engines();
666     QCOMPARE(engines.count(), 1);
667
668     foreach(const QQmlDebugEngineReference &e, engines) {
669         QCOMPARE(e.debugId(), QQmlDebugService::idForObject(m_engine));
670         QCOMPARE(e.name(), m_engine->objectName());
671     }
672
673     // Make query invalid by deleting client
674     q_engines = m_dbg->queryAvailableEngines(this);
675     QCOMPARE(q_engines->state(), QQmlDebugQuery::Waiting);
676     delete m_dbg;
677     QCOMPARE(q_engines->state(), QQmlDebugQuery::Error);
678     delete q_engines;
679     m_dbg = new QQmlEngineDebug(m_conn, this);
680 }
681
682 void tst_QQmlEngineDebug::queryRootContexts()
683 {
684     QQmlDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this);
685     waitForQuery(q_engines);
686     int engineId = q_engines->engines()[0].debugId();
687     delete q_engines;
688
689     QQmlDebugRootContextQuery *q_context;
690
691     QQmlEngineDebug *unconnected = new QQmlEngineDebug(0);
692     q_context = unconnected->queryRootContexts(engineId, this);
693     QCOMPARE(q_context->state(), QQmlDebugQuery::Error);
694     delete q_context;
695     delete unconnected;
696
697     q_context = m_dbg->queryRootContexts(engineId, this);
698     delete q_context;
699
700     q_context = m_dbg->queryRootContexts(engineId, this);
701     waitForQuery(q_context);
702
703     QQmlContext *actualContext = m_engine->rootContext();
704     QQmlDebugContextReference context = q_context->rootContext();
705     QCOMPARE(context.debugId(), QQmlDebugService::idForObject(actualContext));
706     QCOMPARE(context.name(), actualContext->objectName());
707
708     QCOMPARE(context.objects().count(), 4); // 4 qml component objects created for context in main()
709
710     // root context query sends only root object data - it doesn't fill in
711     // the children or property info
712     QCOMPARE(context.objects()[0].properties().count(), 0);
713     QCOMPARE(context.objects()[0].children().count(), 0);
714
715     QCOMPARE(context.contexts().count(), 5);
716     QVERIFY(context.contexts()[0].debugId() >= 0);
717     QCOMPARE(context.contexts()[0].name(), QString("tst_QQmlDebug_childContext"));
718
719     // Make query invalid by deleting client
720     q_context = m_dbg->queryRootContexts(engineId, this);
721     QCOMPARE(q_context->state(), QQmlDebugQuery::Waiting);
722     delete m_dbg;
723     QCOMPARE(q_context->state(), QQmlDebugQuery::Error);
724     delete q_context;
725     m_dbg = new QQmlEngineDebug(m_conn, this);
726 }
727
728 void tst_QQmlEngineDebug::queryObject()
729 {
730     QFETCH(bool, recursive);
731
732     QQmlDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this);
733     waitForQuery(q_engines);
734
735     QQmlDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this);
736     waitForQuery(q_context);
737     QQmlDebugObjectReference rootObject = q_context->rootContext().objects()[0];
738
739     QQmlDebugObjectQuery *q_obj = 0;
740
741     QQmlEngineDebug *unconnected = new QQmlEngineDebug(0);
742     q_obj = recursive ? unconnected->queryObjectRecursive(rootObject, this) : unconnected->queryObject(rootObject, this);
743     QCOMPARE(q_obj->state(), QQmlDebugQuery::Error);
744     delete q_obj;
745     delete unconnected;
746
747     q_obj = recursive ? m_dbg->queryObjectRecursive(rootObject, this) : m_dbg->queryObject(rootObject, this);
748     delete q_obj;
749
750     q_obj = recursive ? m_dbg->queryObjectRecursive(rootObject, this) : m_dbg->queryObject(rootObject, this);
751     waitForQuery(q_obj);
752
753     QQmlDebugObjectReference obj = q_obj->object();
754
755     delete q_engines;
756     delete q_context;
757
758     // Make query invalid by deleting client
759     q_obj = recursive ? m_dbg->queryObjectRecursive(rootObject, this) : m_dbg->queryObject(rootObject, this);
760     QCOMPARE(q_obj->state(), QQmlDebugQuery::Waiting);
761     delete m_dbg;
762     QCOMPARE(q_obj->state(), QQmlDebugQuery::Error);
763     delete q_obj;
764     m_dbg = new QQmlEngineDebug(m_conn, this);
765
766     // check source as defined in main()
767     QQmlDebugFileReference source = obj.source();
768     QCOMPARE(source.url(), QUrl::fromLocalFile(""));
769     QCOMPARE(source.lineNumber(), 3);
770     QCOMPARE(source.columnNumber(), 1);
771
772     // generically test all properties, children and childrens' properties
773     recursiveObjectTest(m_rootItem, obj, recursive);
774
775     if (recursive) {
776         foreach(const QQmlDebugObjectReference &child, obj.children())
777             QVERIFY(child.properties().count() > 0);
778
779         QQmlDebugObjectReference rect;
780         QQmlDebugObjectReference text;
781         foreach (const QQmlDebugObjectReference &child, obj.children()) {
782             if (child.className() == "Rectangle")
783                 rect = child;
784             else if (child.className() == "Text")
785                 text = child;
786         }
787
788         // test specific property values
789         QCOMPARE(findProperty(rect.properties(), "width").value(), qVariantFromValue(500));
790         QCOMPARE(findProperty(rect.properties(), "height").value(), qVariantFromValue(600));
791         QCOMPARE(findProperty(rect.properties(), "color").value(), qVariantFromValue(QColor("blue")));
792
793         QCOMPARE(findProperty(text.properties(), "color").value(), qVariantFromValue(QColor("blue")));
794     } else {
795         foreach(const QQmlDebugObjectReference &child, obj.children())
796             QCOMPARE(child.properties().count(), 0);
797     }
798 }
799
800 void tst_QQmlEngineDebug::queryObject_data()
801 {
802     QTest::addColumn<bool>("recursive");
803
804     QTest::newRow("non-recursive") << false;
805     QTest::newRow("recursive") << true;
806 }
807
808 void tst_QQmlEngineDebug::queryExpressionResult()
809 {
810     QFETCH(QString, expr);
811     QFETCH(QVariant, result);
812
813     QQmlDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this);
814     waitForQuery(q_engines);    // check immediate deletion is ok
815
816     QQmlDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this);
817     waitForQuery(q_context);
818     int objectId = q_context->rootContext().objects()[0].debugId();
819
820     QQmlDebugExpressionQuery *q_expr;
821
822     QQmlEngineDebug *unconnected = new QQmlEngineDebug(0);
823     q_expr = unconnected->queryExpressionResult(objectId, expr, this);
824     QCOMPARE(q_expr->state(), QQmlDebugQuery::Error);
825     delete q_expr;
826     delete unconnected;
827
828     q_expr = m_dbg->queryExpressionResult(objectId, expr, this);
829     delete q_expr;
830
831     q_expr = m_dbg->queryExpressionResult(objectId, expr, this);
832     QCOMPARE(q_expr->expression().toString(), expr);
833     waitForQuery(q_expr);
834
835     QCOMPARE(q_expr->result(), result);
836
837     delete q_engines;
838     delete q_context;
839
840     // Make query invalid by deleting client
841     q_expr = m_dbg->queryExpressionResult(objectId, expr, this);
842     QCOMPARE(q_expr->state(), QQmlDebugQuery::Waiting);
843     delete m_dbg;
844     QCOMPARE(q_expr->state(), QQmlDebugQuery::Error);
845     delete q_expr;
846     m_dbg = new QQmlEngineDebug(m_conn, this);
847 }
848
849 void tst_QQmlEngineDebug::queryExpressionResult_data()
850 {
851     QTest::addColumn<QString>("expr");
852     QTest::addColumn<QVariant>("result");
853
854     QTest::newRow("width + 50") << "width + 50" << qVariantFromValue(60);
855     QTest::newRow("blueRect.width") << "blueRect.width" << qVariantFromValue(500);
856     QTest::newRow("bad expr") << "aeaef" << qVariantFromValue(QString("<undefined>"));
857     QTest::newRow("QObject*") << "varObj" << qVariantFromValue(QString("<unnamed object>"));
858     QTest::newRow("list of QObject*") << "varObjList" << qVariantFromValue(QString("<unknown value>"));
859     QVariantMap map;
860     map.insert(QLatin1String("rect"), QVariant(QLatin1String("<unnamed object>")));
861     QTest::newRow("varObjMap") << "varObjMap" << qVariantFromValue(map);
862 }
863
864 void tst_QQmlEngineDebug::tst_QQmlDebugFileReference()
865 {
866     QQmlDebugFileReference ref;
867     QVERIFY(ref.url().isEmpty());
868     QCOMPARE(ref.lineNumber(), -1);
869     QCOMPARE(ref.columnNumber(), -1);
870
871     ref.setUrl(QUrl("http://test"));
872     QCOMPARE(ref.url(), QUrl("http://test"));
873     ref.setLineNumber(1);
874     QCOMPARE(ref.lineNumber(), 1);
875     ref.setColumnNumber(1);
876     QCOMPARE(ref.columnNumber(), 1);
877
878     QQmlDebugFileReference copy(ref);
879     QQmlDebugFileReference copyAssign;
880     copyAssign = ref;
881     foreach (const QQmlDebugFileReference &r, (QList<QQmlDebugFileReference>() << copy << copyAssign)) {
882         QCOMPARE(r.url(), ref.url());
883         QCOMPARE(r.lineNumber(), ref.lineNumber());
884         QCOMPARE(r.columnNumber(), ref.columnNumber());
885     }
886 }
887
888 void tst_QQmlEngineDebug::tst_QQmlDebugEngineReference()
889 {
890     QQmlDebugEngineReference ref;
891     QCOMPARE(ref.debugId(), -1);
892     QVERIFY(ref.name().isEmpty());
893
894     ref = QQmlDebugEngineReference(1);
895     QCOMPARE(ref.debugId(), 1);
896     QVERIFY(ref.name().isEmpty());
897
898     QQmlDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this);
899     waitForQuery(q_engines);
900     ref = q_engines->engines()[0];
901     delete q_engines;
902
903     QQmlDebugEngineReference copy(ref);
904     QQmlDebugEngineReference copyAssign;
905     copyAssign = ref;
906     foreach (const QQmlDebugEngineReference &r, (QList<QQmlDebugEngineReference>() << copy << copyAssign)) {
907         QCOMPARE(r.debugId(), ref.debugId());
908         QCOMPARE(r.name(), ref.name());
909     }
910 }
911
912 void tst_QQmlEngineDebug::tst_QQmlDebugObjectReference()
913 {
914     QQmlDebugObjectReference ref;
915     QCOMPARE(ref.debugId(), -1);
916     QCOMPARE(ref.className(), QString());
917     QCOMPARE(ref.name(), QString());
918     QCOMPARE(ref.contextDebugId(), -1);
919     QVERIFY(ref.properties().isEmpty());
920     QVERIFY(ref.children().isEmpty());
921
922     QQmlDebugFileReference source = ref.source();
923     QVERIFY(source.url().isEmpty());
924     QVERIFY(source.lineNumber() < 0);
925     QVERIFY(source.columnNumber() < 0);
926
927     ref = QQmlDebugObjectReference(1);
928     QCOMPARE(ref.debugId(), 1);
929
930     QQmlDebugObjectReference rootObject = findRootObject();
931     QQmlDebugObjectQuery *query = m_dbg->queryObjectRecursive(rootObject, this);
932     waitForQuery(query);
933     ref = query->object();
934     delete query;
935
936     QVERIFY(ref.debugId() >= 0);
937
938     QQmlDebugObjectReference copy(ref);
939     QQmlDebugObjectReference copyAssign;
940     copyAssign = ref;
941     foreach (const QQmlDebugObjectReference &r, (QList<QQmlDebugObjectReference>() << copy << copyAssign))
942         recursiveCompareObjects(r, ref);
943 }
944
945 void tst_QQmlEngineDebug::tst_QQmlDebugContextReference()
946 {
947     QQmlDebugContextReference ref;
948     QCOMPARE(ref.debugId(), -1);
949     QVERIFY(ref.name().isEmpty());
950     QVERIFY(ref.objects().isEmpty());
951     QVERIFY(ref.contexts().isEmpty());
952
953     QQmlDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this);
954     waitForQuery(q_engines);
955     QQmlDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this);
956     waitForQuery(q_context);
957
958     ref = q_context->rootContext();
959     delete q_engines;
960     delete q_context;
961     QVERIFY(ref.debugId() >= 0);
962
963     QQmlDebugContextReference copy(ref);
964     QQmlDebugContextReference copyAssign;
965     copyAssign = ref;
966     foreach (const QQmlDebugContextReference &r, (QList<QQmlDebugContextReference>() << copy << copyAssign))
967         recursiveCompareContexts(r, ref);
968 }
969
970 void tst_QQmlEngineDebug::tst_QQmlDebugPropertyReference()
971 {
972     QQmlDebugObjectReference rootObject = findRootObject();
973     QQmlDebugObjectQuery *query = m_dbg->queryObject(rootObject, this);
974     waitForQuery(query);
975     QQmlDebugObjectReference obj = query->object();
976     delete query;
977
978     QQmlDebugPropertyReference ref = findProperty(obj.properties(), "scale");
979     QVERIFY(ref.objectDebugId() > 0);
980     QVERIFY(!ref.name().isEmpty());
981     QVERIFY(!ref.value().isNull());
982     QVERIFY(!ref.valueTypeName().isEmpty());
983     QVERIFY(!ref.binding().isEmpty());
984     QVERIFY(ref.hasNotifySignal());
985
986     QQmlDebugPropertyReference copy(ref);
987     QQmlDebugPropertyReference copyAssign;
988     copyAssign = ref;
989     foreach (const QQmlDebugPropertyReference &r, (QList<QQmlDebugPropertyReference>() << copy << copyAssign))
990         compareProperties(r, ref);
991 }
992
993 void tst_QQmlEngineDebug::setBindingForObject()
994 {
995     QQmlDebugObjectReference rootObject = findRootObject();
996     QVERIFY(rootObject.debugId() != -1);
997     QQmlDebugPropertyReference widthPropertyRef = findProperty(rootObject.properties(), "width");
998
999     QCOMPARE(widthPropertyRef.value(), QVariant(10));
1000     QCOMPARE(widthPropertyRef.binding(), QString());
1001
1002     //
1003     // set literal
1004     //
1005     m_dbg->setBindingForObject(rootObject.debugId(), "width", "15", true);
1006
1007     rootObject = findRootObject();
1008     widthPropertyRef =  findProperty(rootObject.properties(), "width");
1009
1010     QCOMPARE(widthPropertyRef.value(), QVariant(15));
1011     QCOMPARE(widthPropertyRef.binding(), QString());
1012
1013     //
1014     // set expression
1015     //
1016     m_dbg->setBindingForObject(rootObject.debugId(), "width", "height", false);
1017
1018     rootObject = findRootObject();
1019     widthPropertyRef =  findProperty(rootObject.properties(), "width");
1020
1021     QCOMPARE(widthPropertyRef.value(), QVariant(20));
1022     QCOMPARE(widthPropertyRef.binding(), QString("height"));
1023
1024     //
1025     // reset
1026     //
1027     m_dbg->resetBindingForObject(rootObject.debugId(), "width");
1028
1029     rootObject = findRootObject();
1030     widthPropertyRef =  findProperty(rootObject.properties(), "width");
1031
1032    // QCOMPARE(widthPropertyRef.value(), QVariant(0)); // TODO: Shouldn't this work?
1033     QCOMPARE(widthPropertyRef.binding(), QString());
1034
1035     //
1036     // set handler
1037     //
1038     rootObject = findRootObject();
1039     QCOMPARE(rootObject.children().size(), 5); // Rectangle, Text, MouseArea, Component.onCompleted, NonScriptPropertyElement
1040     QQmlDebugObjectReference mouseAreaObject = rootObject.children().at(2);
1041     QQmlDebugObjectQuery *q_obj = m_dbg->queryObjectRecursive(mouseAreaObject, this);
1042     waitForQuery(q_obj);
1043     mouseAreaObject = q_obj->object();
1044
1045     QCOMPARE(mouseAreaObject.className(), QString("MouseArea"));
1046
1047     QQmlDebugPropertyReference onEnteredRef = findProperty(mouseAreaObject.properties(), "onEntered");
1048
1049     QCOMPARE(onEnteredRef.name(), QString("onEntered"));
1050     QCOMPARE(onEnteredRef.value(),  QVariant("(function onEntered() { { console.log('hello') } })"));
1051
1052     m_dbg->setBindingForObject(mouseAreaObject.debugId(), "onEntered", "{console.log('hello, world') }", false) ;
1053
1054     rootObject = findRootObject();
1055     mouseAreaObject = rootObject.children().at(2);
1056     q_obj = m_dbg->queryObjectRecursive(mouseAreaObject, this);
1057     waitForQuery(q_obj);
1058     mouseAreaObject = q_obj->object();
1059     onEnteredRef = findProperty(mouseAreaObject.properties(), "onEntered");
1060     QCOMPARE(onEnteredRef.name(), QString("onEntered"));
1061     QCOMPARE(onEnteredRef.value(),  QVariant("{console.log('hello, world') }"));
1062 }
1063
1064 void tst_QQmlEngineDebug::setBindingInStates()
1065 {
1066     // Check if changing bindings of propertychanges works
1067
1068     const int sourceIndex = 3;
1069
1070     QQmlDebugObjectReference obj = findRootObject(sourceIndex);
1071
1072     QVERIFY(obj.debugId() != -1);
1073     QVERIFY(obj.children().count() >= 2);
1074
1075     // We are going to switch state a couple of times, we need to get rid of the transition before
1076     QQmlDebugExpressionQuery *q_deleteTransition = m_dbg->queryExpressionResult(obj.debugId(),QString("transitions = []"),this);
1077     waitForQuery(q_deleteTransition);
1078     delete q_deleteTransition;
1079
1080
1081     // check initial value of the property that is changing
1082     QQmlDebugExpressionQuery *q_setState;
1083     q_setState = m_dbg->queryExpressionResult(obj.debugId(),QString("state=\"state1\""),this);
1084     waitForQuery(q_setState);
1085     delete q_setState;
1086
1087     obj = findRootObject(sourceIndex);
1088     QCOMPARE(findProperty(obj.properties(),"width").value().toInt(),200);
1089
1090
1091     q_setState = m_dbg->queryExpressionResult(obj.debugId(),QString("state=\"\""),this);
1092     waitForQuery(q_setState);
1093     delete q_setState;
1094
1095
1096     obj = findRootObject(sourceIndex, true);
1097     QCOMPARE(findProperty(obj.properties(),"width").value().toInt(),100);
1098
1099
1100     // change the binding
1101     QQmlDebugObjectReference state = obj.children()[1];
1102     QCOMPARE(state.className(), QString("State"));
1103     QVERIFY(state.children().count() > 0);
1104
1105     QQmlDebugObjectReference propertyChange = state.children()[0];
1106     QVERIFY(propertyChange.debugId() != -1);
1107
1108     QVERIFY( m_dbg->setBindingForObject(propertyChange.debugId(), "width",QVariant(300),true) );
1109
1110     // check properties changed in state
1111     obj = findRootObject(sourceIndex);
1112     QCOMPARE(findProperty(obj.properties(),"width").value().toInt(),100);
1113
1114
1115     q_setState = m_dbg->queryExpressionResult(obj.debugId(),QString("state=\"state1\""),this);
1116     waitForQuery(q_setState);
1117     delete q_setState;
1118
1119     obj = findRootObject(sourceIndex);
1120     QCOMPARE(findProperty(obj.properties(),"width").value().toInt(),300);
1121
1122     // check changing properties of base state from within a state
1123     QVERIFY(m_dbg->setBindingForObject(obj.debugId(),"width","height*2",false));
1124     QVERIFY(m_dbg->setBindingForObject(obj.debugId(),"height","200",true));
1125
1126     obj = findRootObject(sourceIndex);
1127     QCOMPARE(findProperty(obj.properties(),"width").value().toInt(),300);
1128
1129     q_setState = m_dbg->queryExpressionResult(obj.debugId(),QString("state=\"\""),this);
1130     waitForQuery(q_setState);
1131     delete q_setState;
1132
1133     obj = findRootObject(sourceIndex);
1134     QCOMPARE(findProperty(obj.properties(),"width").value().toInt(), 400);
1135
1136     //  reset binding while in a state
1137     q_setState = m_dbg->queryExpressionResult(obj.debugId(),QString("state=\"state1\""),this);
1138     waitForQuery(q_setState);
1139     delete q_setState;
1140
1141     obj = findRootObject(sourceIndex);
1142     QCOMPARE(findProperty(obj.properties(),"width").value().toInt(), 300);
1143
1144     m_dbg->resetBindingForObject(propertyChange.debugId(), "width");
1145
1146     obj = findRootObject(sourceIndex);
1147     QCOMPARE(findProperty(obj.properties(),"width").value().toInt(), 400);
1148
1149     // re-add binding
1150     m_dbg->setBindingForObject(propertyChange.debugId(), "width", "300", true);
1151
1152     obj = findRootObject(sourceIndex);
1153     QCOMPARE(findProperty(obj.properties(),"width").value().toInt(), 300);
1154 }
1155
1156 void tst_QQmlEngineDebug::queryObjectTree()
1157 {
1158     const int sourceIndex = 3;
1159
1160     // Check if states/transitions are initialized when fetching root item
1161     QQmlDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this);
1162     waitForQuery(q_engines);
1163
1164     QQmlDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this);
1165     waitForQuery(q_context);
1166
1167     QVERIFY(q_context->rootContext().objects().count() > sourceIndex);
1168     QQmlDebugObjectReference rootObject = q_context->rootContext().objects()[sourceIndex];
1169
1170     QQmlDebugObjectQuery *q_obj = m_dbg->queryObjectRecursive(rootObject, this);
1171     waitForQuery(q_obj);
1172
1173     QQmlDebugObjectReference obj = q_obj->object();
1174
1175     delete q_engines;
1176     delete q_context;
1177     delete q_obj;
1178
1179     QVERIFY(obj.debugId() != -1);
1180     QVERIFY(obj.children().count() >= 2);
1181
1182
1183
1184     // check state
1185     QQmlDebugObjectReference state = obj.children()[1];
1186     QCOMPARE(state.className(), QString("State"));
1187     QVERIFY(state.children().count() > 0);
1188
1189     QQmlDebugObjectReference propertyChange = state.children()[0];
1190     QVERIFY(propertyChange.debugId() != -1);
1191
1192     QQmlDebugPropertyReference propertyChangeTarget = findProperty(propertyChange.properties(),"target");
1193     QCOMPARE(propertyChangeTarget.objectDebugId(), propertyChange.debugId());
1194
1195     QQmlDebugObjectReference targetReference = qvariant_cast<QQmlDebugObjectReference>(propertyChangeTarget.value());
1196     QVERIFY(targetReference.debugId() != -1);
1197
1198
1199
1200     // check transition
1201     QQmlDebugObjectReference transition = obj.children()[0];
1202     QCOMPARE(transition.className(), QString("Transition"));
1203     QCOMPARE(findProperty(transition.properties(),"from").value().toString(), QString("*"));
1204     QCOMPARE(findProperty(transition.properties(),"to").value(), findProperty(state.properties(),"name").value());
1205     QVERIFY(transition.children().count() > 0);
1206
1207     QQmlDebugObjectReference animation = transition.children()[0];
1208     QVERIFY(animation.debugId() != -1);
1209
1210     QQmlDebugPropertyReference animationTarget = findProperty(animation.properties(),"target");
1211     QCOMPARE(animationTarget.objectDebugId(), animation.debugId());
1212
1213     targetReference = qvariant_cast<QQmlDebugObjectReference>(animationTarget.value());
1214     QVERIFY(targetReference.debugId() != -1);
1215
1216     QCOMPARE(findProperty(animation.properties(),"property").value().toString(), QString("width"));
1217     QCOMPARE(findProperty(animation.properties(),"duration").value().toInt(), 100);
1218 }
1219
1220 int main(int argc, char *argv[])
1221 {
1222     int _argc = argc + 1;
1223     char **_argv = new char*[_argc];
1224     for (int i = 0; i < argc; ++i)
1225         _argv[i] = argv[i];
1226     char arg[] = "-qmljsdebugger=port:3768";
1227     _argv[_argc - 1] = arg;
1228
1229     QGuiApplication app(_argc, _argv);
1230     tst_QQmlEngineDebug tc;
1231     return QTest::qExec(&tc, _argc, _argv);
1232     delete _argv;
1233 }
1234
1235 #include "tst_qqmlenginedebug.moc"