| 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 |  | 
| 67 | class NonScriptProperty : public QObject { | 
| 68 |     Q_OBJECT | 
| 69 |     Q_PROPERTY(int nonScriptProp READ nonScriptProp WRITE setNonScriptProp NOTIFY nonScriptPropChanged SCRIPTABLE false) | 
| 70 | public: | 
| 71 |     int nonScriptProp() const { return 0; } | 
| 72 |     void setNonScriptProp(int) {} | 
| 73 | signals: | 
| 74 |     void nonScriptPropChanged(); | 
| 75 | }; | 
| 76 | QML_DECLARE_TYPE(NonScriptProperty) | 
| 77 |  | 
| 78 | class CustomTypes : public QObject | 
| 79 | { | 
| 80 |     Q_OBJECT | 
| 81 |     Q_PROPERTY(QModelIndex modelIndex READ modelIndex) | 
| 82 | public: | 
| 83 |     CustomTypes(QObject *parent = nullptr) : QObject(parent) {} | 
| 84 |  | 
| 85 |     QModelIndex modelIndex() { return QModelIndex(); } | 
| 86 | }; | 
| 87 |  | 
| 88 | class JsonTest : public QObject | 
| 89 | { | 
| 90 |    Q_OBJECT | 
| 91 |    Q_PROPERTY(QJsonObject data READ data WRITE setData NOTIFY dataChanged) | 
| 92 |  | 
| 93 | public: | 
| 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 |  | 
| 105 | signals: | 
| 106 |    void dataChanged(const QJsonObject &data); | 
| 107 |  | 
| 108 | public slots: | 
| 109 |    void setData(const QJsonObject &data) | 
| 110 |    { | 
| 111 |        if (data != m_data) { | 
| 112 |            m_data = data; | 
| 113 |            emit dataChanged(data); | 
| 114 |        } | 
| 115 |    } | 
| 116 |  | 
| 117 | private: | 
| 118 |    QJsonObject m_data; | 
| 119 | }; | 
| 120 |  | 
| 121 |  | 
| 122 | class tst_QQmlEngineDebugService : public QObject | 
| 123 | { | 
| 124 |     Q_OBJECT | 
| 125 | public: | 
| 126 |     tst_QQmlEngineDebugService() : m_conn(nullptr), m_dbg(nullptr), m_engine(nullptr), m_rootItem(nullptr) {} | 
| 127 |  | 
| 128 | private: | 
| 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 |  | 
| 148 | private 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 |  | 
| 185 | QQmlEngineDebugObjectReference 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 |  | 
| 210 | QQmlEngineDebugPropertyReference 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 |  | 
| 220 | void 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 |  | 
| 308 | void 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 |  | 
| 324 | void 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 |  | 
| 435 | void tst_QQmlEngineDebugService::cleanupTestCase() | 
| 436 | { | 
| 437 |     delete m_conn; | 
| 438 |     qDeleteAll(c: m_components); | 
| 439 |     delete m_engine; | 
| 440 | } | 
| 441 |  | 
| 442 | void 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 |  | 
| 485 | void 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 |  | 
| 530 | void 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 |  | 
| 590 | void 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 |  | 
| 646 | void 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 |  | 
| 656 | void 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 |  | 
| 665 | void 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 |  | 
| 674 | void 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 |  | 
| 697 | void 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 |  | 
| 728 | void 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 |  | 
| 788 | void 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 |  | 
| 796 | void 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 |  | 
| 867 | void 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 |  | 
| 875 | void 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 |  | 
| 939 | void 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 |  | 
| 962 | void 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 |  | 
| 981 | void 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 |  | 
| 1002 | void 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 |  | 
| 1018 | void 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 |  | 
| 1029 | void 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 |  | 
| 1050 | void 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 |  | 
| 1066 | void 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 |  | 
| 1151 | void 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 |  | 
| 1198 | void 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 |  | 
| 1316 | void 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 |  | 
| 1365 | void 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 |  | 
| 1391 | void 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 |  | 
| 1411 | void 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 |  | 
| 1434 | int 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 |  |