1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include "debugutil_p.h"
30#include "../../../shared/util.h"
31
32#include <private/qqmlbinding_p.h>
33#include <private/qqmlboundsignal_p.h>
34#include <private/qqmldebugservice_p.h>
35#include <private/qqmlmetatype_p.h>
36#include <private/qqmlproperty_p.h>
37#include <private/qqmldebugconnection_p.h>
38#include <private/qqmlenginedebugclient_p.h>
39
40#include <QtTest/qtest.h>
41#include <QtTest/qsignalspy.h>
42
43#include <QtQml/qqmlengine.h>
44#include <QtQml/qqmlcontext.h>
45#include <QtQml/qqmlcomponent.h>
46#include <QtQml/qqmlexpression.h>
47#include <QtQml/qqmlproperty.h>
48#include <QtQml/qqmlincubator.h>
49#include <QtQuick/qquickitem.h>
50
51#include <QtNetwork/qhostaddress.h>
52#include <QtCore/qtimer.h>
53#include <QtCore/qdebug.h>
54#include <QtCore/qthread.h>
55#include <QtCore/qabstractitemmodel.h>
56#include <QtCore/qjsonobject.h>
57#include <QtCore/qjsonarray.h>
58#include <QtCore/qjsondocument.h>
59
60#define QVERIFYOBJECT(statement) \
61 do {\
62 if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__)) {\
63 return QQmlEngineDebugObjectReference();\
64 }\
65 } while (0)
66
67class NonScriptProperty : public QObject {
68 Q_OBJECT
69 Q_PROPERTY(int nonScriptProp READ nonScriptProp WRITE setNonScriptProp NOTIFY nonScriptPropChanged SCRIPTABLE false)
70public:
71 int nonScriptProp() const { return 0; }
72 void setNonScriptProp(int) {}
73signals:
74 void nonScriptPropChanged();
75};
76QML_DECLARE_TYPE(NonScriptProperty)
77
78class CustomTypes : public QObject
79{
80 Q_OBJECT
81 Q_PROPERTY(QModelIndex modelIndex READ modelIndex)
82public:
83 CustomTypes(QObject *parent = nullptr) : QObject(parent) {}
84
85 QModelIndex modelIndex() { return QModelIndex(); }
86};
87
88class JsonTest : public QObject
89{
90 Q_OBJECT
91 Q_PROPERTY(QJsonObject data READ data WRITE setData NOTIFY dataChanged)
92
93public:
94 JsonTest(QObject *parent = 0) : QObject(parent)
95 {
96 m_data["foo"] = QJsonValue(12);
97 m_data["ttt"] = QJsonArray({4, 5, 4, 3, 2});
98 m_data["a"] = QJsonValue(QJsonValue::Null);
99 m_data["b"] = QJsonValue(QJsonValue::Undefined);
100 m_data["c"] = QJsonValue("fffff");
101 }
102
103 QJsonObject data() const { return m_data; }
104
105signals:
106 void dataChanged(const QJsonObject &data);
107
108public slots:
109 void setData(const QJsonObject &data)
110 {
111 if (data != m_data) {
112 m_data = data;
113 emit dataChanged(data);
114 }
115 }
116
117private:
118 QJsonObject m_data;
119};
120
121
122class tst_QQmlEngineDebugService : public QObject
123{
124 Q_OBJECT
125public:
126 tst_QQmlEngineDebugService() : m_conn(nullptr), m_dbg(nullptr), m_engine(nullptr), m_rootItem(nullptr) {}
127
128private:
129 QQmlEngineDebugObjectReference findRootObject(int context = 0,
130 bool recursive = false);
131 QQmlEngineDebugPropertyReference findProperty(
132 const QList<QQmlEngineDebugPropertyReference> &props,
133 const QString &name) const;
134
135 void recursiveObjectTest(QObject *o,
136 const QQmlEngineDebugObjectReference &oref,
137 bool recursive) const;
138
139 void getContexts();
140
141 QQmlDebugConnection *m_conn;
142 QQmlEngineDebugClient *m_dbg;
143 QQmlEngine *m_engine;
144 QQuickItem *m_rootItem;
145
146 QObjectList m_components;
147
148private slots:
149 void initTestCase();
150 void cleanupTestCase();
151
152 void watch_property();
153 void watch_object();
154 void watch_expression();
155 void watch_expression_data();
156 void watch_context();
157 void watch_file();
158
159 void queryAvailableEngines();
160 void queryRootContexts();
161 void queryObject();
162 void queryObject_data();
163 void queryObjectsForLocation();
164 void queryObjectsForLocation_data();
165 void queryExpressionResult();
166 void queryExpressionResult_data();
167 void queryExpressionResultInRootContext();
168 void queryExpressionResultBC();
169 void queryExpressionResultBC_data();
170
171 void setBindingForObject();
172 void resetBindingForObject();
173 void setMethodBody();
174 void queryObjectTree();
175 void setBindingInStates();
176
177 void regression_QTCREATORBUG_7451();
178 void queryObjectWithNonStreamableTypes();
179 void jsonData();
180 void asynchronousCreate();
181 void invalidContexts();
182 void createObjectOnDestruction();
183};
184
185QQmlEngineDebugObjectReference tst_QQmlEngineDebugService::findRootObject(
186 int context, bool recursive)
187{
188 bool success = false;
189 m_dbg->queryAvailableEngines(success: &success);
190 QVERIFYOBJECT(success);
191 QVERIFYOBJECT(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
192
193 QVERIFYOBJECT(m_dbg->engines().count());
194 m_dbg->queryRootContexts(m_dbg->engines()[0], success: &success);
195 QVERIFYOBJECT(success);
196 QVERIFYOBJECT(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
197
198 QVERIFYOBJECT(m_dbg->rootContext().contexts.count());
199 QVERIFYOBJECT(m_dbg->rootContext().contexts.last().objects.count());
200 int count = m_dbg->rootContext().contexts.count();
201 recursive ? m_dbg->queryObjectRecursive(m_dbg->rootContext().contexts[count - context - 1].objects[0],
202 success: &success) :
203 m_dbg->queryObject(m_dbg->rootContext().contexts[count - context - 1].objects[0], success: &success);
204 QVERIFYOBJECT(success);
205 QVERIFYOBJECT(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
206
207 return m_dbg->object();
208}
209
210QQmlEngineDebugPropertyReference tst_QQmlEngineDebugService::findProperty(
211 const QList<QQmlEngineDebugPropertyReference> &props, const QString &name) const
212{
213 foreach (const QQmlEngineDebugPropertyReference &p, props) {
214 if (p.name == name)
215 return p;
216 }
217 return QQmlEngineDebugPropertyReference();
218}
219
220void tst_QQmlEngineDebugService::recursiveObjectTest(
221 QObject *o, const QQmlEngineDebugObjectReference &oref, bool recursive) const
222{
223 const QMetaObject *meta = o->metaObject();
224
225 QCOMPARE(oref.debugId, QQmlDebugService::idForObject(o));
226 QCOMPARE(oref.name, o->objectName());
227 QCOMPARE(oref.className, QQmlMetaType::prettyTypeName(o));
228 QCOMPARE(oref.contextDebugId, QQmlDebugService::idForObject(
229 qmlContext(o)));
230
231 const QObjectList &children = o->children();
232 for (int i=0; i<children.count(); i++) {
233 QObject *child = children[i];
234 if (!qmlContext(child))
235 continue;
236 int debugId = QQmlDebugService::idForObject(child);
237 QVERIFY(debugId >= 0);
238
239 QQmlEngineDebugObjectReference cref;
240 foreach (const QQmlEngineDebugObjectReference &ref, oref.children) {
241 QVERIFY(!ref.className.isEmpty());
242 if (ref.debugId == debugId) {
243 cref = ref;
244 break;
245 }
246 }
247 QVERIFY(cref.debugId >= 0);
248
249 if (recursive)
250 recursiveObjectTest(o: child, oref: cref, recursive: true);
251 }
252
253 foreach (const QQmlEngineDebugPropertyReference &p, oref.properties) {
254 QCOMPARE(p.objectDebugId, QQmlDebugService::idForObject(o));
255
256 // signal properties are fake - they are generated from QQmlAbstractBoundSignal children
257 if (p.name.startsWith(s: "on") && p.name.length() > 2 && p.name[2].isUpper()) {
258 QString signal = p.value.toString();
259 QQmlBoundSignalExpression *expr = QQmlPropertyPrivate::signalExpression(that: QQmlProperty(o, p.name));
260 QVERIFY(expr && expr->expression() == signal);
261 QVERIFY(p.valueTypeName.isEmpty());
262 QVERIFY(p.binding.isEmpty());
263 QVERIFY(!p.hasNotifySignal);
264 continue;
265 }
266
267 QMetaProperty pmeta = meta->property(index: meta->indexOfProperty(name: p.name.toUtf8().constData()));
268
269 QCOMPARE(p.name, QString::fromUtf8(pmeta.name()));
270
271 if (pmeta.userType() == QMetaType::QObjectStar) {
272 const QQmlEngineDebugObjectReference ref
273 = qvariant_cast<QQmlEngineDebugObjectReference>(v: p.value);
274 QObject *pobj = qvariant_cast<QObject *>(v: pmeta.read(obj: o));
275 if (pobj) {
276 if (pobj->objectName().isEmpty())
277 QCOMPARE(ref.name, QString("<unnamed object>"));
278 else
279 QCOMPARE(ref.name, pobj->objectName());
280 } else {
281 QCOMPARE(ref.name, QString("<unknown value>"));
282 }
283 } else if (pmeta.userType() < QMetaType::User && pmeta.userType() != QMetaType::QVariant) {
284 const QVariant expected = pmeta.read(obj: o);
285 QVERIFY2(p.value == expected, QString::fromLatin1("%1 != %2. Details: %3/%4/%5/%6")
286 .arg(QTest::toString(p.value)).arg(QTest::toString(expected)).arg(p.name)
287 .arg(p.valueTypeName).arg(pmeta.userType()).arg(pmeta.userType()).toUtf8());
288 }
289
290 if (p.name == "parent")
291 QVERIFY(p.valueTypeName == "QGraphicsObject*" ||
292 p.valueTypeName == "QQuickItem*");
293 else
294 QCOMPARE(p.valueTypeName, QString::fromUtf8(pmeta.typeName()));
295
296 QQmlAbstractBinding *binding =
297 QQmlPropertyPrivate::binding(
298 that: QQmlProperty(o, p.name));
299 if (binding)
300 QCOMPARE(binding->expression(), p.binding);
301
302 QCOMPARE(p.hasNotifySignal, pmeta.hasNotifySignal());
303
304 QVERIFY(pmeta.isValid());
305 }
306}
307
308void tst_QQmlEngineDebugService::getContexts()
309{
310 bool success = false;
311
312 m_dbg->queryAvailableEngines(success: &success);
313 QVERIFY(success);
314 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
315
316 QList<QQmlEngineDebugEngineReference> engines = m_dbg->engines();
317 QCOMPARE(engines.count(), 1);
318 m_dbg->queryRootContexts(engines.first(), success: &success);
319
320 QVERIFY(success);
321 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
322}
323
324void tst_QQmlEngineDebugService::initTestCase()
325{
326 qmlRegisterType<NonScriptProperty>(uri: "Test", versionMajor: 1, versionMinor: 0, qmlName: "NonScriptPropertyElement");
327
328 QTest::ignoreMessage(type: QtDebugMsg, message: "QML Debugger: Waiting for connection on port 3768...");
329 m_engine = new QQmlEngine(this);
330
331 QList<QByteArray> qml;
332 qml << "import QtQuick 2.0\n"
333 "import Test 1.0\n"
334 "Item {"
335 "id: root\n"
336 "width: 10; height: 20; scale: blueRect.scale;"
337 "Rectangle { id: blueRect; width: 500; height: 600; color: \"blue\"; }"
338 "Text { font.bold: true; color: blueRect.color; }"
339 "MouseArea {"
340 "onEntered: { console.log('hello') }"
341 "}"
342 "property variant varObj\n"
343 "property variant varObjList: []\n"
344 "property variant varObjMap\n"
345 "property variant simpleVar: 10.05\n"
346 "Component.onCompleted: {\n"
347 "varObj = blueRect;\n"
348 "var list = varObjList;\n"
349 "list[0] = blueRect;\n"
350 "varObjList = list;\n"
351 "var map = new Object;\n"
352 "map.rect = blueRect;\n"
353 "varObjMap = map;\n"
354 "}\n"
355 "NonScriptPropertyElement {\n"
356 "}\n"
357 "}";
358
359 // add second component to test multiple root contexts
360 qml << "import QtQuick 2.0\n"
361 "Item {}";
362
363 // and a third to test methods
364 qml << "import QtQuick 2.0\n"
365 "Item {"
366 "function myMethodNoArgs() { return 3; }\n"
367 "function myMethod(a) { return a + 9; }\n"
368 "function myMethodIndirect() { myMethod(3); }\n"
369 "}";
370
371 // and a fourth to test states
372 qml << "import QtQuick 2.0\n"
373 "Rectangle {\n"
374 "id:rootRect\n"
375 "width:100\n"
376 "states: [\n"
377 "State {\n"
378 "name:\"state1\"\n"
379 "PropertyChanges {\n"
380 "target:rootRect\n"
381 "width:200\n"
382 "}\n"
383 "}\n"
384 "]\n"
385 "transitions: [\n"
386 "Transition {\n"
387 "from:\"*\"\n"
388 "to:\"state1\"\n"
389 "PropertyAnimation {\n"
390 "target:rootRect\n"
391 "property:\"width\"\n"
392 "duration:100\n"
393 "}\n"
394 "}\n"
395 "]\n"
396 "}\n"
397 ;
398
399 // test non-streamable properties
400 qmlRegisterType<CustomTypes>(uri: "Backend", versionMajor: 1, versionMinor: 0, qmlName: "CustomTypes");
401 qml << "import Backend 1.0\n"
402 "CustomTypes {}"
403 ;
404
405 qmlRegisterType<JsonTest>(uri: "JsonTest", versionMajor: 1, versionMinor: 0, qmlName: "JsonTest");
406 qml << "import JsonTest 1.0\n"
407 "JsonTest {}"
408 ;
409
410 for (int i=0; i<qml.count(); i++) {
411 QQmlComponent component(m_engine);
412 component.setData(qml[i], baseUrl: QUrl::fromLocalFile(localfile: ""));
413 QVERIFY(component.isReady()); // fails if bad syntax
414 m_components << qobject_cast<QQuickItem*>(object: component.create());
415 }
416 m_rootItem = qobject_cast<QQuickItem*>(object: m_components.first());
417
418 // add an extra context to test for multiple contexts
419 QQmlContext *context = new QQmlContext(m_engine->rootContext(), this);
420 context->setObjectName("tst_QQmlDebug_childContext");
421
422 m_conn = new QQmlDebugConnection(this);
423 m_conn->connectToHost(hostName: "127.0.0.1", port: 3768);
424
425 bool ok = m_conn->waitForConnected();
426 QVERIFY(ok);
427 m_dbg = new QQmlEngineDebugClient(m_conn);
428 QList<QQmlDebugClient *> others = QQmlDebugTest::createOtherClients(connection: m_conn);
429 QTRY_COMPARE(m_dbg->state(), QQmlEngineDebugClient::Enabled);
430 foreach (QQmlDebugClient *other, others)
431 QCOMPARE(other->state(), QQmlDebugClient::Unavailable);
432 qDeleteAll(c: others);
433}
434
435void tst_QQmlEngineDebugService::cleanupTestCase()
436{
437 delete m_conn;
438 qDeleteAll(c: m_components);
439 delete m_engine;
440}
441
442void tst_QQmlEngineDebugService::setMethodBody()
443{
444 bool success;
445 QQmlEngineDebugObjectReference obj = findRootObject(context: 2);
446 QVERIFY(!obj.className.isEmpty());
447
448 QObject *root = m_components.at(i: 2);
449 // Without args
450 {
451 QVariant rv;
452 QVERIFY(QMetaObject::invokeMethod(root, "myMethodNoArgs", Qt::DirectConnection,
453 Q_RETURN_ARG(QVariant, rv)));
454 QCOMPARE(rv, QVariant(qreal(3)));
455
456
457 QVERIFY(m_dbg->setMethodBody(obj.debugId, "myMethodNoArgs", "return 7",
458 &success));
459 QVERIFY(success);
460 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
461
462 QVERIFY(QMetaObject::invokeMethod(root, "myMethodNoArgs", Qt::DirectConnection,
463 Q_RETURN_ARG(QVariant, rv)));
464 QCOMPARE(rv, QVariant(qreal(7)));
465 }
466
467 // With args
468 {
469 QVariant rv;
470 QVERIFY(QMetaObject::invokeMethod(root, "myMethod", Qt::DirectConnection,
471 Q_RETURN_ARG(QVariant, rv), Q_ARG(QVariant, QVariant(19))));
472 QCOMPARE(rv, QVariant(qreal(28)));
473
474 QVERIFY(m_dbg->setMethodBody(obj.debugId, "myMethod", "return a + 7",
475 &success));
476 QVERIFY(success);
477 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
478
479 QVERIFY(QMetaObject::invokeMethod(root, "myMethod", Qt::DirectConnection,
480 Q_RETURN_ARG(QVariant, rv), Q_ARG(QVariant, QVariant(19))));
481 QCOMPARE(rv, QVariant(qreal(26)));
482 }
483}
484
485void tst_QQmlEngineDebugService::watch_property()
486{
487 QQmlEngineDebugObjectReference obj = findRootObject();
488 QVERIFY(!obj.className.isEmpty());
489 QQmlEngineDebugPropertyReference prop = findProperty(props: obj.properties, name: "width");
490
491 bool success;
492
493 QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr);
494 unconnected->addWatch(prop, success: &success);
495 QVERIFY(!success);
496 delete unconnected;
497
498 m_dbg->addWatch(QQmlEngineDebugPropertyReference(), success: &success);
499 QVERIFY(success);
500 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
501 QCOMPARE(m_dbg->valid(), false);
502
503 quint32 id = m_dbg->addWatch(prop, success: &success);
504 QVERIFY(success);
505 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
506 QCOMPARE(m_dbg->valid(), true);
507
508 QSignalSpy spy(m_dbg, SIGNAL(valueChanged(QByteArray,QVariant)));
509
510 int origWidth = m_rootItem->property(name: "width").toInt();
511 m_rootItem->setProperty(name: "width", value: origWidth*2);
512
513 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(valueChanged(QByteArray,QVariant))));
514 QCOMPARE(spy.count(), 1);
515
516 m_dbg->removeWatch(watch: id, success: &success);
517 QVERIFY(success);
518 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
519 QCOMPARE(m_dbg->valid(), true);
520
521 // restore original value and verify spy doesn't get additional signal since watch has been removed
522 m_rootItem->setProperty(name: "width", value: origWidth);
523 QTest::qWait(ms: 100);
524 QCOMPARE(spy.count(), 1);
525
526 QCOMPARE(spy.at(0).at(0).value<QByteArray>(), prop.name.toUtf8());
527 QCOMPARE(spy.at(0).at(1).value<QVariant>(), QVariant::fromValue(origWidth*2));
528}
529
530void tst_QQmlEngineDebugService::watch_object()
531{
532 QQmlEngineDebugObjectReference obj = findRootObject();
533 QVERIFY(!obj.className.isEmpty());
534
535 bool success;
536
537 QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr);
538 unconnected->addWatch(obj, success: &success);
539 QVERIFY(!success);
540 delete unconnected;
541
542 m_dbg->addWatch(QQmlEngineDebugObjectReference(), success: &success);
543 QVERIFY(success);
544 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
545 QCOMPARE(m_dbg->valid(), false);
546
547 quint32 id = m_dbg->addWatch(obj, success: &success);
548 QVERIFY(success);
549 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
550 QCOMPARE(m_dbg->valid(), true);
551
552 QSignalSpy spy(m_dbg, SIGNAL(valueChanged(QByteArray,QVariant)));
553
554 int origWidth = m_rootItem->property(name: "width").toInt();
555 int origHeight = m_rootItem->property(name: "height").toInt();
556 m_rootItem->setProperty(name: "width", value: origWidth*2);
557 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(valueChanged(QByteArray,QVariant))));
558 m_rootItem->setProperty(name: "height", value: origHeight*2);
559 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(valueChanged(QByteArray,QVariant))));
560
561 QVERIFY(spy.count() > 0);
562
563 int newWidth = -1;
564 int newHeight = -1;
565 for (int i=0; i<spy.count(); i++) {
566 const QVariantList &values = spy[i];
567 if (values[0].value<QByteArray>() == "width")
568 newWidth = values[1].value<QVariant>().toInt();
569 else if (values[0].value<QByteArray>() == "height")
570 newHeight = values[1].value<QVariant>().toInt();
571
572 }
573
574 m_dbg->removeWatch(watch: id, success: &success);
575 QVERIFY(success);
576 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
577 QCOMPARE(m_dbg->valid(), true);
578
579 // since watch has been removed, restoring the original values should not trigger a valueChanged()
580 spy.clear();
581 m_rootItem->setProperty(name: "width", value: origWidth);
582 m_rootItem->setProperty(name: "height", value: origHeight);
583 QTest::qWait(ms: 100);
584 QCOMPARE(spy.count(), 0);
585
586 QCOMPARE(newWidth, origWidth * 2);
587 QCOMPARE(newHeight, origHeight * 2);
588}
589
590void tst_QQmlEngineDebugService::watch_expression()
591{
592 QFETCH(QString, expr);
593 QFETCH(int, increment);
594 QFETCH(int, incrementCount);
595
596 int origWidth = m_rootItem->property(name: "width").toInt();
597
598 QQmlEngineDebugObjectReference obj = findRootObject();
599 QVERIFY(!obj.className.isEmpty());
600
601 bool success;
602
603 QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr);
604 unconnected->addWatch(obj, expr, success: &success);
605 QVERIFY(!success);
606 delete unconnected;
607
608 m_dbg->addWatch(QQmlEngineDebugObjectReference(), expr, success: &success);
609 QVERIFY(success);
610 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
611 QCOMPARE(m_dbg->valid(), false);
612
613 quint32 id = m_dbg->addWatch(obj, expr, success: &success);
614 QVERIFY(success);
615 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
616 QCOMPARE(m_dbg->valid(), true);
617
618 QSignalSpy spy(m_dbg, SIGNAL(valueChanged(QByteArray,QVariant)));
619
620 int width = origWidth;
621 for (int i=0; i<incrementCount+1; i++) {
622 if (i > 0) {
623 width += increment;
624 m_rootItem->setProperty(name: "width", value: width);
625 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(valueChanged(QByteArray,QVariant))));
626 }
627 }
628
629 m_dbg->removeWatch(watch: id, success: &success);
630 QVERIFY(success);
631 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
632 QCOMPARE(m_dbg->valid(), true);
633
634 // restore original value and verify spy doesn't get a signal since watch has been removed
635 m_rootItem->setProperty(name: "width", value: origWidth);
636 QTest::qWait(ms: 100);
637 QCOMPARE(spy.count(), incrementCount);
638
639 width = origWidth + increment;
640 for (int i=0; i<spy.count(); i++) {
641 width += increment;
642 QCOMPARE(spy.at(i).at(1).value<QVariant>().toInt(), width);
643 }
644}
645
646void tst_QQmlEngineDebugService::watch_expression_data()
647{
648 QTest::addColumn<QString>(name: "expr");
649 QTest::addColumn<int>(name: "increment");
650 QTest::addColumn<int>(name: "incrementCount");
651
652 QTest::newRow(dataTag: "width") << "width" << 0 << 0;
653 QTest::newRow(dataTag: "width+10") << "width + 10" << 10 << 5;
654}
655
656void tst_QQmlEngineDebugService::watch_context()
657{
658 QQmlEngineDebugContextReference c;
659 QTest::ignoreMessage(type: QtWarningMsg, message: "QQmlEngineDebugClient::addWatch(): Not implemented");
660 bool success;
661 m_dbg->addWatch(c, QString(), success: &success);
662 QVERIFY(!success);
663}
664
665void tst_QQmlEngineDebugService::watch_file()
666{
667 QQmlEngineDebugFileReference f;
668 QTest::ignoreMessage(type: QtWarningMsg, message: "QQmlEngineDebugClient::addWatch(): Not implemented");
669 bool success;
670 m_dbg->addWatch(f, success: &success);
671 QVERIFY(!success);
672}
673
674void tst_QQmlEngineDebugService::queryAvailableEngines()
675{
676 bool success;
677
678 QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr);
679 unconnected->queryAvailableEngines(success: &success);
680 QVERIFY(!success);
681 delete unconnected;
682
683 m_dbg->queryAvailableEngines(success: &success);
684 QVERIFY(success);
685 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
686
687 // TODO test multiple engines
688 QList<QQmlEngineDebugEngineReference> engines = m_dbg->engines();
689 QCOMPARE(engines.count(), 1);
690
691 foreach (const QQmlEngineDebugEngineReference &e, engines) {
692 QCOMPARE(e.debugId, QQmlDebugService::idForObject(m_engine));
693 QCOMPARE(e.name, m_engine->objectName());
694 }
695}
696
697void tst_QQmlEngineDebugService::queryRootContexts()
698{
699 bool success;
700 m_dbg->queryAvailableEngines(success: &success);
701 QVERIFY(success);
702 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
703 QVERIFY(m_dbg->engines().count());
704 const QQmlEngineDebugEngineReference engine = m_dbg->engines()[0];
705
706 QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr);
707 unconnected->queryRootContexts(engine, success: &success);
708 QVERIFY(!success);
709 delete unconnected;
710
711 m_dbg->queryRootContexts(engine, success: &success);
712 QVERIFY(success);
713 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
714
715 QQmlContext *actualContext = m_engine->rootContext();
716 QQmlEngineDebugContextReference context = m_dbg->rootContext();
717 QCOMPARE(context.debugId, QQmlDebugService::idForObject(actualContext));
718 QCOMPARE(context.name, actualContext->objectName());
719
720 // root context query sends only root object data - it doesn't fill in
721 // the children or property info
722 QCOMPARE(context.objects.count(), 0);
723 QCOMPARE(context.contexts.count(), 7);
724 QVERIFY(context.contexts[0].debugId >= 0);
725 QCOMPARE(context.contexts[0].name, QString("tst_QQmlDebug_childContext"));
726}
727
728void tst_QQmlEngineDebugService::queryObject()
729{
730 QFETCH(bool, recursive);
731
732 bool success;
733
734 QQmlEngineDebugObjectReference rootObject = findRootObject();
735 QVERIFY(!rootObject.className.isEmpty());
736
737 QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr);
738 recursive ? unconnected->queryObjectRecursive(rootObject, success: &success) : unconnected->queryObject(rootObject, success: &success);
739 QVERIFY(!success);
740 delete unconnected;
741
742 recursive ? m_dbg->queryObjectRecursive(rootObject, success: &success) : m_dbg->queryObject(rootObject, success: &success);
743 QVERIFY(success);
744 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
745
746 QQmlEngineDebugObjectReference obj = m_dbg->object();
747 QVERIFY(!obj.className.isEmpty());
748
749 // check source as defined in main()
750 QQmlEngineDebugFileReference source = obj.source;
751 QCOMPARE(source.url, QUrl::fromLocalFile(""));
752 QCOMPARE(source.lineNumber, 3);
753 QCOMPARE(source.columnNumber, 1);
754
755 // generically test all properties, children and childrens' properties
756 recursiveObjectTest(o: m_rootItem, oref: obj, recursive);
757
758 if (recursive) {
759 foreach (const QQmlEngineDebugObjectReference &child, obj.children) {
760 QVERIFY(!child.className.isEmpty());
761 QVERIFY(child.properties.count() > 0);
762 }
763
764 QQmlEngineDebugObjectReference rect;
765 QQmlEngineDebugObjectReference text;
766 foreach (const QQmlEngineDebugObjectReference &child, obj.children) {
767 QVERIFY(!child.className.isEmpty());
768 if (child.className == "Rectangle")
769 rect = child;
770 else if (child.className == "Text")
771 text = child;
772 }
773
774 // test specific property values
775 QCOMPARE(findProperty(rect.properties, "width").value, QVariant::fromValue(500));
776 QCOMPARE(findProperty(rect.properties, "height").value, QVariant::fromValue(600));
777 QCOMPARE(findProperty(rect.properties, "color").value, QVariant::fromValue(QColor("blue")));
778
779 QCOMPARE(findProperty(text.properties, "color").value, QVariant::fromValue(QColor("blue")));
780 } else {
781 foreach (const QQmlEngineDebugObjectReference &child, obj.children) {
782 QVERIFY(!child.className.isEmpty());
783 QCOMPARE(child.properties.count(), 0);
784 }
785 }
786}
787
788void tst_QQmlEngineDebugService::queryObject_data()
789{
790 QTest::addColumn<bool>(name: "recursive");
791
792 QTest::newRow(dataTag: "non-recursive") << false;
793 QTest::newRow(dataTag: "recursive") << true;
794}
795
796void tst_QQmlEngineDebugService::queryObjectsForLocation()
797{
798 QFETCH(bool, recursive);
799
800 bool success;
801
802 QQmlEngineDebugObjectReference rootObject = findRootObject();
803 QVERIFY(!rootObject.className.isEmpty());
804
805 const QString fileName = QFileInfo(rootObject.source.url.toString()).fileName();
806 int lineNumber = rootObject.source.lineNumber;
807 int columnNumber = rootObject.source.columnNumber;
808
809 QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr);
810 recursive ? unconnected->queryObjectsForLocationRecursive(file: fileName, lineNumber,
811 columnNumber, success: &success)
812 : unconnected->queryObjectsForLocation(file: fileName, lineNumber,
813 columnNumber, success: &success);
814 QVERIFY(!success);
815 delete unconnected;
816
817 recursive ? m_dbg->queryObjectsForLocationRecursive(file: fileName, lineNumber,
818 columnNumber, success: &success)
819 : m_dbg->queryObjectsForLocation(file: fileName, lineNumber,
820 columnNumber, success: &success);
821 QVERIFY(success);
822 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
823
824 QCOMPARE(m_dbg->objects().count(), 1);
825 QQmlEngineDebugObjectReference obj = m_dbg->objects().first();
826 QVERIFY(!obj.className.isEmpty());
827
828 // check source as defined in main()
829 QQmlEngineDebugFileReference source = obj.source;
830 QCOMPARE(source.url, QUrl(fileName));
831 QCOMPARE(source.lineNumber, lineNumber);
832 QCOMPARE(source.columnNumber, columnNumber);
833
834 // generically test all properties, children and childrens' properties
835 recursiveObjectTest(o: m_rootItem, oref: obj, recursive);
836
837 if (recursive) {
838 foreach (const QQmlEngineDebugObjectReference &child, obj.children) {
839 QVERIFY(!child.className.isEmpty());
840 QVERIFY(child.properties.count() > 0);
841 }
842
843 QQmlEngineDebugObjectReference rect;
844 QQmlEngineDebugObjectReference text;
845 foreach (const QQmlEngineDebugObjectReference &child, obj.children) {
846 QVERIFY(!child.className.isEmpty());
847 if (child.className == "Rectangle")
848 rect = child;
849 else if (child.className == "Text")
850 text = child;
851 }
852
853 // test specific property values
854 QCOMPARE(findProperty(rect.properties, "width").value, QVariant::fromValue(500));
855 QCOMPARE(findProperty(rect.properties, "height").value, QVariant::fromValue(600));
856 QCOMPARE(findProperty(rect.properties, "color").value, QVariant::fromValue(QColor("blue")));
857
858 QCOMPARE(findProperty(text.properties, "color").value, QVariant::fromValue(QColor("blue")));
859 } else {
860 foreach (const QQmlEngineDebugObjectReference &child, obj.children) {
861 QVERIFY(!child.className.isEmpty());
862 QCOMPARE(child.properties.count(), 0);
863 }
864 }
865}
866
867void tst_QQmlEngineDebugService::queryObjectsForLocation_data()
868{
869 QTest::addColumn<bool>(name: "recursive");
870
871 QTest::newRow(dataTag: "non-recursive") << false;
872 QTest::newRow(dataTag: "recursive") << true;
873}
874
875void tst_QQmlEngineDebugService::regression_QTCREATORBUG_7451()
876{
877 QQmlEngineDebugObjectReference rootObject = findRootObject();
878 QVERIFY(!rootObject.className.isEmpty());
879 int contextId = rootObject.contextDebugId;
880 QQmlContext *context = qobject_cast<QQmlContext *>(object: QQmlDebugService::objectForId(id: contextId));
881 QQmlComponent component(context->engine());
882 QByteArray content;
883 content.append(s: "import QtQuick 2.0\n"
884 "Text {"
885 "y: 10\n"
886 "text: \"test\"\n"
887 "}");
888 component.setData(content, baseUrl: rootObject.source.url);
889 QObject *object = component.create(context);
890 QVERIFY(object);
891 int idNew = QQmlDebugService::idForObject(object);
892 QVERIFY(idNew >= 0);
893
894 const QString fileName = QFileInfo(rootObject.source.url.toString()).fileName();
895 int lineNumber = rootObject.source.lineNumber;
896 int columnNumber = rootObject.source.columnNumber;
897 bool success = false;
898
899 m_dbg->queryObjectsForLocation(file: fileName, lineNumber,
900 columnNumber, success: &success);
901 QVERIFY(success);
902 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
903
904 foreach (QQmlEngineDebugObjectReference child, rootObject.children) {
905 QVERIFY(!child.className.isEmpty());
906 success = false;
907 lineNumber = child.source.lineNumber;
908 columnNumber = child.source.columnNumber;
909 m_dbg->queryObjectsForLocation(file: fileName, lineNumber,
910 columnNumber, success: &success);
911 QVERIFY(success);
912 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
913 }
914
915 delete object;
916 QObject *deleted = QQmlDebugService::objectForId(id: idNew);
917 QVERIFY(!deleted);
918
919 lineNumber = rootObject.source.lineNumber;
920 columnNumber = rootObject.source.columnNumber;
921 success = false;
922 m_dbg->queryObjectsForLocation(file: fileName, lineNumber,
923 columnNumber, success: &success);
924 QVERIFY(success);
925 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
926
927 foreach (QQmlEngineDebugObjectReference child, rootObject.children) {
928 QVERIFY(!child.className.isEmpty());
929 success = false;
930 lineNumber = child.source.lineNumber;
931 columnNumber = child.source.columnNumber;
932 m_dbg->queryObjectsForLocation(file: fileName, lineNumber,
933 columnNumber, success: &success);
934 QVERIFY(success);
935 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
936 }
937}
938
939void tst_QQmlEngineDebugService::queryObjectWithNonStreamableTypes()
940{
941 bool success;
942
943 QQmlEngineDebugObjectReference rootObject = findRootObject(context: 4, recursive: true);
944 QVERIFY(!rootObject.className.isEmpty());
945
946 QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr);
947 unconnected->queryObject(rootObject, success: &success);
948 QVERIFY(!success);
949 delete unconnected;
950
951 m_dbg->queryObject(rootObject, success: &success);
952 QVERIFY(success);
953 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
954
955 QQmlEngineDebugObjectReference obj = m_dbg->object();
956 QVERIFY(!obj.className.isEmpty());
957
958 QCOMPARE(findProperty(obj.properties, "modelIndex").value,
959 QVariant(QLatin1String("QModelIndex()")));
960}
961
962void tst_QQmlEngineDebugService::jsonData()
963{
964 bool success;
965
966 QQmlEngineDebugObjectReference rootObject = findRootObject(context: 5, recursive: true);
967 QVERIFY(!rootObject.className.isEmpty());
968
969 m_dbg->queryObject(rootObject, success: &success);
970 QVERIFY(success);
971 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
972
973 QQmlEngineDebugObjectReference obj = m_dbg->object();
974 QVERIFY(!obj.className.isEmpty());
975
976 QCOMPARE(findProperty(obj.properties, "data").value,
977 QJsonDocument::fromJson("{\"a\":null,\"c\":\"fffff\",\"foo\":12,\"ttt\":[4,5,4,3,2]}")
978 .toVariant());
979}
980
981void tst_QQmlEngineDebugService::queryExpressionResult()
982{
983 QFETCH(QString, expr);
984 QFETCH(QVariant, result);
985
986 int objectId = findRootObject().debugId;
987
988 bool success;
989
990 QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr);
991 unconnected->queryExpressionResult(objectDebugId: objectId, expr, success: &success);
992 QVERIFY(!success);
993 delete unconnected;
994
995 m_dbg->queryExpressionResult(objectDebugId: objectId, expr, success: &success);
996 QVERIFY(success);
997 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
998
999 QCOMPARE(m_dbg->resultExpr(), result);
1000}
1001
1002void tst_QQmlEngineDebugService::queryExpressionResult_data()
1003{
1004 QTest::addColumn<QString>(name: "expr");
1005 QTest::addColumn<QVariant>(name: "result");
1006
1007 QTest::newRow(dataTag: "width + 50") << "width + 50" << QVariant::fromValue(value: 60);
1008 QTest::newRow(dataTag: "blueRect.width") << "blueRect.width" << QVariant::fromValue(value: 500);
1009 QTest::newRow(dataTag: "bad expr") << "aeaef" << QVariant::fromValue(value: QString("<undefined>"));
1010 QTest::newRow(dataTag: "QObject*") << "varObj" << QVariant::fromValue(value: QString("<unnamed object>"));
1011 QTest::newRow(dataTag: "list of QObject*") << "varObjList" << QVariant::fromValue(value: QVariantList() << QVariant(QString("<unnamed object>")));
1012 QVariantMap map;
1013 map.insert(key: QLatin1String("rect"), value: QVariant(QLatin1String("<unnamed object>")));
1014 QTest::newRow(dataTag: "varObjMap") << "varObjMap" << QVariant::fromValue(value: map);
1015 QTest::newRow(dataTag: "simpleVar") << "simpleVar" << QVariant::fromValue(value: 10.05);
1016}
1017
1018void tst_QQmlEngineDebugService::queryExpressionResultInRootContext()
1019{
1020 bool success;
1021 const QString exp = QLatin1String("1");
1022 m_dbg->queryExpressionResult(objectDebugId: -1, expr: exp, success: &success);
1023 QVERIFY(success);
1024 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1025
1026 QCOMPARE(m_dbg->resultExpr().toString(), exp);
1027}
1028
1029void tst_QQmlEngineDebugService::queryExpressionResultBC()
1030{
1031 QFETCH(QString, expr);
1032 QFETCH(QVariant, result);
1033
1034 int objectId = findRootObject().debugId;
1035
1036 bool success;
1037
1038 QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr);
1039 unconnected->queryExpressionResultBC(objectDebugId: objectId, expr, success: &success);
1040 QVERIFY(!success);
1041 delete unconnected;
1042
1043 m_dbg->queryExpressionResultBC(objectDebugId: objectId, expr, success: &success);
1044 QVERIFY(success);
1045 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1046
1047 QCOMPARE(m_dbg->resultExpr(), result);
1048}
1049
1050void tst_QQmlEngineDebugService::queryExpressionResultBC_data()
1051{
1052 QTest::addColumn<QString>(name: "expr");
1053 QTest::addColumn<QVariant>(name: "result");
1054
1055 QTest::newRow(dataTag: "width + 50") << "width + 50" << QVariant::fromValue(value: 60);
1056 QTest::newRow(dataTag: "blueRect.width") << "blueRect.width" << QVariant::fromValue(value: 500);
1057 QTest::newRow(dataTag: "bad expr") << "aeaef" << QVariant::fromValue(value: QString("<undefined>"));
1058 QTest::newRow(dataTag: "QObject*") << "varObj" << QVariant::fromValue(value: QString("<unnamed object>"));
1059 QTest::newRow(dataTag: "list of QObject*") << "varObjList" << QVariant::fromValue(value: QVariantList() << QVariant(QString("<unnamed object>")));
1060 QVariantMap map;
1061 map.insert(key: QLatin1String("rect"), value: QVariant(QLatin1String("<unnamed object>")));
1062 QTest::newRow(dataTag: "varObjMap") << "varObjMap" << QVariant::fromValue(value: map);
1063 QTest::newRow(dataTag: "simpleVar") << "simpleVar" << QVariant::fromValue(value: 10.05);
1064}
1065
1066void tst_QQmlEngineDebugService::setBindingForObject()
1067{
1068 QQmlEngineDebugObjectReference rootObject = findRootObject();
1069 QVERIFY(!rootObject.className.isEmpty());
1070 QVERIFY(rootObject.debugId != -1);
1071 QQmlEngineDebugPropertyReference widthPropertyRef = findProperty(props: rootObject.properties, name: "width");
1072
1073 QCOMPARE(widthPropertyRef.value, QVariant(10));
1074 QCOMPARE(widthPropertyRef.binding, QString());
1075
1076 bool success;
1077 //
1078 // set literal
1079 //
1080 m_dbg->setBindingForObject(objectDebugId: rootObject.debugId, propertyName: "width", bindingExpression: "15", isLiteralValue: true,
1081 source: QString(), line: -1, success: &success);
1082 QVERIFY(success);
1083 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1084 QCOMPARE(m_dbg->valid(), true);
1085
1086 rootObject = findRootObject();
1087 QVERIFY(!rootObject.className.isEmpty());
1088 widthPropertyRef = findProperty(props: rootObject.properties, name: "width");
1089
1090 QCOMPARE(widthPropertyRef.value, QVariant(15));
1091 QCOMPARE(widthPropertyRef.binding, QString());
1092
1093 //
1094 // set expression
1095 //
1096 m_dbg->setBindingForObject(objectDebugId: rootObject.debugId, propertyName: "width", bindingExpression: "height", isLiteralValue: false,
1097 source: QString(), line: -1, success: &success);
1098 QVERIFY(success);
1099 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1100 QCOMPARE(m_dbg->valid(), true);
1101
1102 rootObject = findRootObject();
1103 QVERIFY(!rootObject.className.isEmpty());
1104 widthPropertyRef = findProperty(props: rootObject.properties, name: "width");
1105
1106 QCOMPARE(widthPropertyRef.value, QVariant(20));
1107 QEXPECT_FAIL("", "Cannot retrieve text for a binding (QTBUG-37273)", Continue);
1108 QCOMPARE(widthPropertyRef.binding, QString("height"));
1109
1110 //
1111 // set handler
1112 //
1113 rootObject = findRootObject();
1114 QVERIFY(!rootObject.className.isEmpty());
1115 QCOMPARE(rootObject.children.size(), 5); // Rectangle, Text, MouseArea, Component.onCompleted, NonScriptPropertyElement
1116 QQmlEngineDebugObjectReference mouseAreaObject = rootObject.children.at(i: 2);
1117 QVERIFY(!mouseAreaObject.className.isEmpty());
1118 m_dbg->queryObjectRecursive(mouseAreaObject, success: &success);
1119 QVERIFY(success);
1120 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1121 mouseAreaObject = m_dbg->object();
1122 QCOMPARE(mouseAreaObject.className, QString("MouseArea"));
1123
1124 QQmlEngineDebugPropertyReference onEnteredRef = findProperty(props: mouseAreaObject.properties, name: "onEntered");
1125
1126 QCOMPARE(onEnteredRef.name, QString("onEntered"));
1127 // Sorry, can't do that anymore: QCOMPARE(onEnteredRef.value, QVariant("{ console.log('hello') }"));
1128 QCOMPARE(onEnteredRef.value, QVariant("function() { [native code] }"));
1129
1130 m_dbg->setBindingForObject(objectDebugId: mouseAreaObject.debugId, propertyName: "onEntered",
1131 bindingExpression: "{console.log('hello, world') }", isLiteralValue: false,
1132 source: QString(), line: -1, success: &success);
1133 QVERIFY(success);
1134 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1135 QCOMPARE(m_dbg->valid(), true);
1136
1137 rootObject = findRootObject();
1138 QVERIFY(!rootObject.className.isEmpty());
1139 mouseAreaObject = rootObject.children.at(i: 2);
1140 QVERIFY(!mouseAreaObject.className.isEmpty());
1141 m_dbg->queryObjectRecursive(mouseAreaObject, success: &success);
1142 QVERIFY(success);
1143 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1144 mouseAreaObject = m_dbg->object();
1145 QVERIFY(!mouseAreaObject.className.isEmpty());
1146 onEnteredRef = findProperty(props: mouseAreaObject.properties, name: "onEntered");
1147 QCOMPARE(onEnteredRef.name, QString("onEntered"));
1148 QCOMPARE(onEnteredRef.value, QVariant("function() { [native code] }"));
1149}
1150
1151void tst_QQmlEngineDebugService::resetBindingForObject()
1152{
1153 QQmlEngineDebugObjectReference rootObject = findRootObject();
1154 QVERIFY(!rootObject.className.isEmpty());
1155 QVERIFY(rootObject.debugId != -1);
1156 QQmlEngineDebugPropertyReference widthPropertyRef = findProperty(props: rootObject.properties, name: "width");
1157
1158 bool success = false;
1159
1160 m_dbg->setBindingForObject(objectDebugId: rootObject.debugId, propertyName: "width", bindingExpression: "15", isLiteralValue: true,
1161 source: QString(), line: -1, success: &success);
1162 QVERIFY(success);
1163 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1164 QCOMPARE(m_dbg->valid(), true);
1165
1166 //
1167 // reset
1168 //
1169 m_dbg->resetBindingForObject(objectDebugId: rootObject.debugId, propertyName: "width", success: &success);
1170 QVERIFY(success);
1171 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1172 QCOMPARE(m_dbg->valid(), true);
1173
1174 rootObject = findRootObject();
1175 QVERIFY(!rootObject.className.isEmpty());
1176 widthPropertyRef = findProperty(props: rootObject.properties, name: "width");
1177
1178 QCOMPARE(widthPropertyRef.value, QVariant(0));
1179 QCOMPARE(widthPropertyRef.binding, QString());
1180
1181 //
1182 // reset nested property
1183 //
1184 success = false;
1185 m_dbg->resetBindingForObject(objectDebugId: rootObject.debugId, propertyName: "font.bold", success: &success);
1186 QVERIFY(success);
1187 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1188 QCOMPARE(m_dbg->valid(), true);
1189
1190 rootObject = findRootObject();
1191 QVERIFY(!rootObject.className.isEmpty());
1192 QQmlEngineDebugPropertyReference boldPropertyRef = findProperty(props: rootObject.properties, name: "font.bold");
1193
1194 QCOMPARE(boldPropertyRef.value.toBool(), false);
1195 QCOMPARE(boldPropertyRef.binding, QString());
1196}
1197
1198void tst_QQmlEngineDebugService::setBindingInStates()
1199{
1200 // Check if changing bindings of propertychanges works
1201
1202 const int sourceIndex = 3;
1203
1204 QQmlEngineDebugObjectReference obj = findRootObject(context: sourceIndex);
1205 QVERIFY(!obj.className.isEmpty());
1206 QVERIFY(obj.debugId != -1);
1207 QVERIFY(obj.children.count() >= 2);
1208 bool success;
1209 // We are going to switch state a couple of times, we need to get rid of the transition before
1210 m_dbg->queryExpressionResult(objectDebugId: obj.debugId,expr: QString("transitions = []"), success: &success);
1211 QVERIFY(success);
1212 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1213
1214
1215 // check initial value of the property that is changing
1216 m_dbg->queryExpressionResult(objectDebugId: obj.debugId,expr: QString("state=\"state1\""), success: &success);
1217 QVERIFY(success);
1218 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1219
1220 obj = findRootObject(context: sourceIndex);
1221 QVERIFY(!obj.className.isEmpty());
1222 QCOMPARE(findProperty(obj.properties,"width").value.toInt(),200);
1223
1224
1225 m_dbg->queryExpressionResult(objectDebugId: obj.debugId,expr: QString("state=\"\""),
1226 success: &success);
1227 QVERIFY(success);
1228 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1229
1230
1231 obj = findRootObject(context: sourceIndex, recursive: true);
1232 QVERIFY(!obj.className.isEmpty());
1233 QCOMPARE(findProperty(obj.properties,"width").value.toInt(),100);
1234
1235
1236 // change the binding
1237 QQmlEngineDebugObjectReference state = obj.children[1];
1238 QCOMPARE(state.className, QString("State"));
1239 QVERIFY(state.children.count() > 0);
1240
1241 QQmlEngineDebugObjectReference propertyChange = state.children[0];
1242 QVERIFY(!propertyChange.className.isEmpty());
1243 QVERIFY(propertyChange.debugId != -1);
1244
1245 m_dbg->setBindingForObject(objectDebugId: propertyChange.debugId, propertyName: "width",bindingExpression: QVariant(300),isLiteralValue: true,
1246 source: QString(), line: -1, success: &success);
1247 QVERIFY(success);
1248 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1249
1250 // check properties changed in state
1251 obj = findRootObject(context: sourceIndex);
1252 QVERIFY(!obj.className.isEmpty());
1253 QCOMPARE(findProperty(obj.properties,"width").value.toInt(),100);
1254
1255
1256 m_dbg->queryExpressionResult(objectDebugId: obj.debugId,expr: QString("state=\"state1\""), success: &success);
1257 QVERIFY(success);
1258 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1259
1260 obj = findRootObject(context: sourceIndex);
1261 QVERIFY(!obj.className.isEmpty());
1262 QCOMPARE(findProperty(obj.properties,"width").value.toInt(),300);
1263
1264 // check changing properties of base state from within a state
1265 m_dbg->setBindingForObject(objectDebugId: obj.debugId,propertyName: "width",bindingExpression: "height*2",isLiteralValue: false,
1266 source: QString(), line: -1, success: &success);
1267 QVERIFY(success);
1268 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1269 m_dbg->setBindingForObject(objectDebugId: obj.debugId,propertyName: "height",bindingExpression: "200",isLiteralValue: true,
1270 source: QString(), line: -1, success: &success);
1271 QVERIFY(success);
1272 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1273
1274 obj = findRootObject(context: sourceIndex);
1275 QVERIFY(!obj.className.isEmpty());
1276 QCOMPARE(findProperty(obj.properties,"width").value.toInt(),300);
1277
1278 m_dbg->queryExpressionResult(objectDebugId: obj.debugId,expr: QString("state=\"\""), success: &success);
1279 QVERIFY(success);
1280 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1281
1282 obj = findRootObject(context: sourceIndex);
1283 QVERIFY(!obj.className.isEmpty());
1284 QCOMPARE(findProperty(obj.properties,"width").value.toInt(), 400);
1285
1286 // reset binding while in a state
1287 m_dbg->queryExpressionResult(objectDebugId: obj.debugId,expr: QString("state=\"state1\""), success: &success);
1288 QVERIFY(success);
1289 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1290
1291 obj = findRootObject(context: sourceIndex);
1292 QVERIFY(!obj.className.isEmpty());
1293 QCOMPARE(findProperty(obj.properties,"width").value.toInt(), 300);
1294
1295 m_dbg->resetBindingForObject(objectDebugId: propertyChange.debugId, propertyName: "width", success: &success);
1296 QVERIFY(success);
1297 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1298 QCOMPARE(m_dbg->valid(), true);
1299
1300 obj = findRootObject(context: sourceIndex);
1301 QVERIFY(!obj.className.isEmpty());
1302 QCOMPARE(findProperty(obj.properties,"width").value.toInt(), 400);
1303
1304 // re-add binding
1305 m_dbg->setBindingForObject(objectDebugId: propertyChange.debugId, propertyName: "width", bindingExpression: "300", isLiteralValue: true,
1306 source: QString(), line: -1, success: &success);
1307 QVERIFY(success);
1308 QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result())));
1309 QCOMPARE(m_dbg->valid(), true);
1310
1311 obj = findRootObject(context: sourceIndex);
1312 QVERIFY(!obj.className.isEmpty());
1313 QCOMPARE(findProperty(obj.properties,"width").value.toInt(), 300);
1314}
1315
1316void tst_QQmlEngineDebugService::queryObjectTree()
1317{
1318 const int sourceIndex = 3;
1319
1320 QQmlEngineDebugObjectReference obj = findRootObject(context: sourceIndex, recursive: true);
1321 QVERIFY(!obj.className.isEmpty());
1322 QVERIFY(obj.debugId != -1);
1323 QVERIFY(obj.children.count() >= 2);
1324
1325 // check state
1326 QQmlEngineDebugObjectReference state = obj.children[1];
1327 QCOMPARE(state.className, QString("State"));
1328 QVERIFY(state.children.count() > 0);
1329
1330 QQmlEngineDebugObjectReference propertyChange = state.children[0];
1331 QVERIFY(!propertyChange.className.isEmpty());
1332 QVERIFY(propertyChange.debugId != -1);
1333
1334 QQmlEngineDebugPropertyReference propertyChangeTarget = findProperty(props: propertyChange.properties,name: "target");
1335 QCOMPARE(propertyChangeTarget.objectDebugId, propertyChange.debugId);
1336
1337 QQmlEngineDebugObjectReference targetReference = qvariant_cast<QQmlEngineDebugObjectReference>(v: propertyChangeTarget.value);
1338 QVERIFY(!targetReference.className.isEmpty());
1339 QCOMPARE(targetReference.debugId, -1);
1340 QCOMPARE(targetReference.name, QString("<unnamed object>"));
1341
1342 // check transition
1343 QQmlEngineDebugObjectReference transition = obj.children[0];
1344 QCOMPARE(transition.className, QString("Transition"));
1345 QCOMPARE(findProperty(transition.properties,"from").value.toString(), QString("*"));
1346 QCOMPARE(findProperty(transition.properties,"to").value, findProperty(state.properties,"name").value);
1347 QVERIFY(transition.children.count() > 0);
1348
1349 QQmlEngineDebugObjectReference animation = transition.children[0];
1350 QVERIFY(!animation.className.isEmpty());
1351 QVERIFY(animation.debugId != -1);
1352
1353 QQmlEngineDebugPropertyReference animationTarget = findProperty(props: animation.properties,name: "target");
1354 QCOMPARE(animationTarget.objectDebugId, animation.debugId);
1355
1356 targetReference = qvariant_cast<QQmlEngineDebugObjectReference>(v: animationTarget.value);
1357 QVERIFY(!targetReference.className.isEmpty());
1358 QCOMPARE(targetReference.debugId, -1);
1359 QCOMPARE(targetReference.name, QString("<unnamed object>"));
1360
1361 QCOMPARE(findProperty(animation.properties,"property").value.toString(), QString("width"));
1362 QCOMPARE(findProperty(animation.properties,"duration").value.toInt(), 100);
1363}
1364
1365void tst_QQmlEngineDebugService::asynchronousCreate() {
1366 QQmlEngineDebugObjectReference object;
1367 auto connection = connect(sender: m_dbg, signal: &QQmlEngineDebugClient::newObject, context: this, slot: [&](int objectId) {
1368 object.debugId = objectId;
1369 });
1370
1371 QByteArray asynchronousComponent = "import QtQuick 2.5\n"
1372 "Rectangle { id: asyncRect }";
1373 QQmlComponent component(m_engine);
1374 component.setData(asynchronousComponent, baseUrl: QUrl::fromLocalFile(localfile: ""));
1375 QVERIFY(component.isReady()); // fails if bad syntax
1376 QQmlIncubator incubator(QQmlIncubator::Asynchronous);
1377 component.create(incubator);
1378
1379 QVERIFY(m_dbg->object().idString != QLatin1String("asyncRect"));
1380
1381 QTRY_VERIFY(object.debugId != -1);
1382 disconnect(connection);
1383
1384 bool success = false;
1385 m_dbg->queryObject(object, success: &success);
1386 QVERIFY(success);
1387
1388 QTRY_COMPARE(m_dbg->object().idString, QLatin1String("asyncRect"));
1389}
1390
1391void tst_QQmlEngineDebugService::invalidContexts()
1392{
1393 getContexts();
1394 const int base = m_dbg->rootContext().contexts.count();
1395 QQmlContext context(m_engine);
1396 getContexts();
1397 QCOMPARE(m_dbg->rootContext().contexts.count(), base + 1);
1398 QQmlContextData *contextData = QQmlContextData::get(context: &context);
1399 contextData->invalidate();
1400 getContexts();
1401 QCOMPARE(m_dbg->rootContext().contexts.count(), base);
1402 QQmlContextData *rootData = QQmlContextData::get(context: m_engine->rootContext());
1403 rootData->invalidate();
1404 getContexts();
1405 QCOMPARE(m_dbg->rootContext().contexts.count(), 0);
1406 contextData->setParent(rootData); // makes context valid again, but not root.
1407 getContexts();
1408 QCOMPARE(m_dbg->rootContext().contexts.count(), 0);
1409}
1410
1411void tst_QQmlEngineDebugService::createObjectOnDestruction()
1412{
1413 QSignalSpy spy(m_dbg, SIGNAL(newObject(int)));
1414 {
1415 QQmlEngine engine;
1416 QQmlComponent component(&engine);
1417 component.setData(
1418 "import QtQml 2.0;"
1419 "QtObject {"
1420 "property Component x:"
1421 "Qt.createQmlObject('import QtQml 2.0; Component { QtObject { } }',"
1422 "this, 'x.qml');"
1423 "Component.onDestruction: x.createObject(this, {});"
1424 "}", baseUrl: QUrl::fromLocalFile(localfile: "x.qml"));
1425 QVERIFY(component.isReady());
1426 QVERIFY(component.create());
1427 QTRY_COMPARE(spy.count(), 2);
1428 }
1429 // Doesn't crash and doesn't give us another signal for the object created on destruction.
1430 QTest::qWait(ms: 500);
1431 QCOMPARE(spy.count(), 2);
1432}
1433
1434int main(int argc, char *argv[])
1435{
1436 int _argc = argc + 1;
1437 QScopedArrayPointer<char *>_argv(new char*[_argc]);
1438 for (int i = 0; i < argc; ++i)
1439 _argv[i] = argv[i];
1440 char arg[] = "-qmljsdebugger=port:3768,services:QmlDebugger";
1441 _argv[_argc - 1] = arg;
1442
1443 QGuiApplication app(_argc, _argv.data());
1444 tst_QQmlEngineDebugService tc;
1445 return QTest::qExec(testObject: &tc, argc: _argc, argv: _argv.data());
1446}
1447
1448#include "tst_qqmlenginedebugservice.moc"
1449

source code of qtdeclarative/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp