tests/auto/declarative/qdeclarativedebug/tst_qdeclarativedebug.cpp
changeset 30 5dc02b23752f
child 33 3e2da88830cd
equal deleted inserted replaced
29:b72c6db6890b 30:5dc02b23752f
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the test suite of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 #include <qtest.h>
       
    42 #include <QSignalSpy>
       
    43 #include <QTimer>
       
    44 #include <QHostAddress>
       
    45 #include <QDebug>
       
    46 #include <QThread>
       
    47 
       
    48 #include <QtDeclarative/qdeclarativeengine.h>
       
    49 #include <QtDeclarative/qdeclarativecontext.h>
       
    50 #include <QtDeclarative/qdeclarativecomponent.h>
       
    51 #include <QtDeclarative/qdeclarativeexpression.h>
       
    52 #include <QtDeclarative/qdeclarativeproperty.h>
       
    53 
       
    54 #include <private/qdeclarativebinding_p.h>
       
    55 #include <private/qdeclarativedebug_p.h>
       
    56 #include <private/qdeclarativeenginedebug_p.h>
       
    57 #include <private/qdeclarativedebugclient_p.h>
       
    58 #include <private/qdeclarativedebugservice_p.h>
       
    59 #include <private/qdeclarativerectangle_p.h>
       
    60 #include <private/qdeclarativemetatype_p.h>
       
    61 #include <private/qdeclarativeproperty_p.h>
       
    62 
       
    63 #include "../../../shared/util.h"
       
    64 #include "../shared/debugutil_p.h"
       
    65 
       
    66 Q_DECLARE_METATYPE(QDeclarativeDebugWatch::State)
       
    67 
       
    68 
       
    69 class tst_QDeclarativeDebug : public QObject
       
    70 {
       
    71     Q_OBJECT
       
    72 
       
    73 private:
       
    74     QDeclarativeDebugObjectReference findRootObject();
       
    75     QDeclarativeDebugPropertyReference findProperty(const QList<QDeclarativeDebugPropertyReference> &props, const QString &name) const;
       
    76     void waitForQuery(QDeclarativeDebugQuery *query);
       
    77 
       
    78     void recursiveObjectTest(QObject *o, const QDeclarativeDebugObjectReference &oref, bool recursive) const;
       
    79 
       
    80     void recursiveCompareObjects(const QDeclarativeDebugObjectReference &a, const QDeclarativeDebugObjectReference &b) const;
       
    81     void recursiveCompareContexts(const QDeclarativeDebugContextReference &a, const QDeclarativeDebugContextReference &b) const;
       
    82     void compareProperties(const QDeclarativeDebugPropertyReference &a, const QDeclarativeDebugPropertyReference &b) const;
       
    83     
       
    84     QDeclarativeDebugConnection *m_conn;
       
    85     QDeclarativeEngineDebug *m_dbg;
       
    86     QDeclarativeEngine *m_engine;
       
    87     QDeclarativeItem *m_rootItem;
       
    88 
       
    89     QObjectList m_components;
       
    90 
       
    91 private slots:
       
    92     void initTestCase();
       
    93     void cleanupTestCase();
       
    94 
       
    95     void watch_property();
       
    96     void watch_object();
       
    97     void watch_expression();
       
    98     void watch_expression_data();
       
    99     void watch_context();
       
   100     void watch_file();
       
   101 
       
   102     void queryAvailableEngines();
       
   103     void queryRootContexts();
       
   104     void queryObject();
       
   105     void queryObject_data();
       
   106     void queryExpressionResult();
       
   107     void queryExpressionResult_data();
       
   108 
       
   109     void tst_QDeclarativeDebugFileReference();
       
   110     void tst_QDeclarativeDebugEngineReference();
       
   111     void tst_QDeclarativeDebugObjectReference();
       
   112     void tst_QDeclarativeDebugContextReference();
       
   113     void tst_QDeclarativeDebugPropertyReference();
       
   114 };
       
   115 
       
   116 QDeclarativeDebugObjectReference tst_QDeclarativeDebug::findRootObject()
       
   117 {
       
   118     QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this);
       
   119     waitForQuery(q_engines);
       
   120     
       
   121     if (q_engines->engines().count() == 0)
       
   122         return QDeclarativeDebugObjectReference();
       
   123     QDeclarativeDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this);
       
   124     waitForQuery(q_context);
       
   125 
       
   126     if (q_context->rootContext().objects().count() == 0)
       
   127         return QDeclarativeDebugObjectReference();
       
   128     QDeclarativeDebugObjectQuery *q_obj = m_dbg->queryObject(q_context->rootContext().objects()[0], this);
       
   129     waitForQuery(q_obj);
       
   130 
       
   131     QDeclarativeDebugObjectReference result = q_obj->object();
       
   132 
       
   133     delete q_engines;
       
   134     delete q_context;
       
   135     delete q_obj;
       
   136 
       
   137     return result;
       
   138 }
       
   139 
       
   140 QDeclarativeDebugPropertyReference tst_QDeclarativeDebug::findProperty(const QList<QDeclarativeDebugPropertyReference> &props, const QString &name) const
       
   141 {
       
   142     foreach(const QDeclarativeDebugPropertyReference &p, props) {
       
   143         if (p.name() == name)
       
   144             return p;
       
   145     }
       
   146     return QDeclarativeDebugPropertyReference();
       
   147 }
       
   148 
       
   149 void tst_QDeclarativeDebug::waitForQuery(QDeclarativeDebugQuery *query)
       
   150 {
       
   151     QVERIFY(query);
       
   152     QCOMPARE(query->parent(), qobject_cast<QObject*>(this));
       
   153     QVERIFY(query->state() == QDeclarativeDebugQuery::Waiting);
       
   154     if (!QDeclarativeDebugTest::waitForSignal(query, SIGNAL(stateChanged(QDeclarativeDebugQuery::State))))
       
   155         QFAIL("query timed out");
       
   156 }
       
   157 
       
   158 void tst_QDeclarativeDebug::recursiveObjectTest(QObject *o, const QDeclarativeDebugObjectReference &oref, bool recursive) const
       
   159 {
       
   160     const QMetaObject *meta = o->metaObject();
       
   161 
       
   162     QDeclarativeType *type = QDeclarativeMetaType::qmlType(o->metaObject());
       
   163     QString className = type ? type->qmlTypeName() : QString();
       
   164     className = className.mid(className.lastIndexOf(QLatin1Char('/'))+1);
       
   165 
       
   166     QCOMPARE(oref.debugId(), QDeclarativeDebugService::idForObject(o));
       
   167     QCOMPARE(oref.name(), o->objectName());
       
   168     QCOMPARE(oref.className(), className);
       
   169     QCOMPARE(oref.contextDebugId(), QDeclarativeDebugService::idForObject(qmlContext(o)));
       
   170 
       
   171     const QObjectList &children = o->children();
       
   172     for (int i=0; i<children.count(); i++) {
       
   173         QObject *child = children[i];
       
   174         if (!qmlContext(child))
       
   175             continue;
       
   176         int debugId = QDeclarativeDebugService::idForObject(child);
       
   177         QVERIFY(debugId >= 0);
       
   178 
       
   179         QDeclarativeDebugObjectReference cref;
       
   180         foreach (const QDeclarativeDebugObjectReference &ref, oref.children()) {
       
   181             if (ref.debugId() == debugId) {
       
   182                 cref = ref;
       
   183                 break;
       
   184             }
       
   185         }
       
   186         QVERIFY(cref.debugId() >= 0);
       
   187 
       
   188         if (recursive)
       
   189             recursiveObjectTest(child, cref, true);
       
   190     }
       
   191 
       
   192     foreach (const QDeclarativeDebugPropertyReference &p, oref.properties()) {
       
   193         QCOMPARE(p.objectDebugId(), QDeclarativeDebugService::idForObject(o));
       
   194 
       
   195         // signal properties are fake - they are generated from QDeclarativeBoundSignal children
       
   196         if (p.name().startsWith("on") && p.name().length() > 2 && p.name()[2].isUpper()) {
       
   197             QVERIFY(p.value().toString().startsWith('{') && p.value().toString().endsWith('}'));
       
   198             QVERIFY(p.valueTypeName().isEmpty());
       
   199             QVERIFY(p.binding().isEmpty());
       
   200             QVERIFY(!p.hasNotifySignal());
       
   201             continue;
       
   202         }
       
   203 
       
   204         QMetaProperty pmeta = meta->property(meta->indexOfProperty(p.name().toUtf8().constData()));
       
   205 
       
   206         QCOMPARE(p.name(), QString::fromUtf8(pmeta.name()));
       
   207 
       
   208         if (pmeta.type() < QVariant::UserType) // TODO test complex types
       
   209             QCOMPARE(p.value(), pmeta.read(o));
       
   210 
       
   211         if (p.name() == "parent")
       
   212             QVERIFY(p.valueTypeName() == "QGraphicsObject*" || p.valueTypeName() == "QDeclarativeItem*");
       
   213         else
       
   214             QCOMPARE(p.valueTypeName(), QString::fromUtf8(pmeta.typeName()));
       
   215 
       
   216         QDeclarativeAbstractBinding *binding = 
       
   217             QDeclarativePropertyPrivate::binding(QDeclarativeProperty(o, p.name()));
       
   218         if (binding)
       
   219             QCOMPARE(binding->expression(), p.binding());
       
   220 
       
   221         QCOMPARE(p.hasNotifySignal(), pmeta.hasNotifySignal());
       
   222 
       
   223         QVERIFY(pmeta.isValid());
       
   224     }
       
   225 }
       
   226 
       
   227 void tst_QDeclarativeDebug::recursiveCompareObjects(const QDeclarativeDebugObjectReference &a, const QDeclarativeDebugObjectReference &b) const
       
   228 {
       
   229     QCOMPARE(a.debugId(), b.debugId());
       
   230     QCOMPARE(a.className(), b.className());
       
   231     QCOMPARE(a.name(), b.name());
       
   232     QCOMPARE(a.contextDebugId(), b.contextDebugId());
       
   233 
       
   234     QCOMPARE(a.source().url(), b.source().url());
       
   235     QCOMPARE(a.source().lineNumber(), b.source().lineNumber());
       
   236     QCOMPARE(a.source().columnNumber(), b.source().columnNumber());
       
   237 
       
   238     QCOMPARE(a.properties().count(), b.properties().count());
       
   239     QCOMPARE(a.children().count(), b.children().count());
       
   240 
       
   241     QList<QDeclarativeDebugPropertyReference> aprops = a.properties();
       
   242     QList<QDeclarativeDebugPropertyReference> bprops = b.properties();
       
   243 
       
   244     for (int i=0; i<aprops.count(); i++)
       
   245         compareProperties(aprops[i], bprops[i]);
       
   246 
       
   247     for (int i=0; i<a.children().count(); i++)
       
   248         recursiveCompareObjects(a.children()[i], b.children()[i]);
       
   249 }
       
   250 
       
   251 void tst_QDeclarativeDebug::recursiveCompareContexts(const QDeclarativeDebugContextReference &a, const QDeclarativeDebugContextReference &b) const
       
   252 {
       
   253     QCOMPARE(a.debugId(), b.debugId());
       
   254     QCOMPARE(a.name(), b.name());
       
   255     QCOMPARE(a.objects().count(), b.objects().count());
       
   256     QCOMPARE(a.contexts().count(), b.contexts().count());
       
   257 
       
   258     for (int i=0; i<a.objects().count(); i++)
       
   259         recursiveCompareObjects(a.objects()[i], b.objects()[i]);
       
   260 
       
   261     for (int i=0; i<a.contexts().count(); i++)
       
   262         recursiveCompareContexts(a.contexts()[i], b.contexts()[i]);
       
   263 }
       
   264 
       
   265 void tst_QDeclarativeDebug::compareProperties(const QDeclarativeDebugPropertyReference &a, const QDeclarativeDebugPropertyReference &b) const
       
   266 {
       
   267     QCOMPARE(a.objectDebugId(), b.objectDebugId());
       
   268     QCOMPARE(a.name(), b.name());
       
   269     QCOMPARE(a.value(), b.value());
       
   270     QCOMPARE(a.valueTypeName(), b.valueTypeName());
       
   271     QCOMPARE(a.binding(), b.binding());
       
   272     QCOMPARE(a.hasNotifySignal(), b.hasNotifySignal());
       
   273 }
       
   274 
       
   275 void tst_QDeclarativeDebug::initTestCase()
       
   276 {
       
   277     qRegisterMetaType<QDeclarativeDebugWatch::State>();
       
   278 
       
   279     QTest::ignoreMessage(QtWarningMsg, "QDeclarativeDebugServer: Waiting for connection on port 3768...");
       
   280     qputenv("QML_DEBUG_SERVER_PORT", "3768");
       
   281     m_engine = new QDeclarativeEngine(this);
       
   282 
       
   283     QList<QByteArray> qml;
       
   284     qml << "import Qt 4.7\n"
       
   285             "Item {"
       
   286                 "width: 10; height: 20; scale: blueRect.scale;"
       
   287                 "Rectangle { id: blueRect; width: 500; height: 600; color: \"blue\"; }"
       
   288                 "Text { color: blueRect.color; }"
       
   289                 "MouseArea {"
       
   290                     "onEntered: { console.log('hello') }"
       
   291                 "}"
       
   292             "}";
       
   293     // add second component to test multiple root contexts
       
   294     qml << "import Qt 4.7\n"
       
   295             "Item {}";
       
   296 
       
   297     for (int i=0; i<qml.count(); i++) {
       
   298         QDeclarativeComponent component(m_engine);
       
   299         component.setData(qml[i], QUrl::fromLocalFile(""));
       
   300         Q_ASSERT(component.isReady());  // fails if bad syntax
       
   301         m_components << qobject_cast<QDeclarativeItem*>(component.create());
       
   302     }
       
   303     m_rootItem = qobject_cast<QDeclarativeItem*>(m_components.first());
       
   304 
       
   305     // add an extra context to test for multiple contexts
       
   306     QDeclarativeContext *context = new QDeclarativeContext(m_engine->rootContext(), this);
       
   307     context->setObjectName("tst_QDeclarativeDebug_childContext");
       
   308 
       
   309     m_conn = new QDeclarativeDebugConnection(this);
       
   310     m_conn->connectToHost("127.0.0.1", 3768);
       
   311 
       
   312     QTest::ignoreMessage(QtWarningMsg, "QDeclarativeDebugServer: Connection established");
       
   313     bool ok = m_conn->waitForConnected();
       
   314     Q_ASSERT(ok);
       
   315     QTRY_VERIFY(QDeclarativeDebugService::hasDebuggingClient());
       
   316 
       
   317     m_dbg = new QDeclarativeEngineDebug(m_conn, this);
       
   318 }
       
   319 
       
   320 void tst_QDeclarativeDebug::cleanupTestCase()
       
   321 {
       
   322     qDeleteAll(m_components);
       
   323 }
       
   324 
       
   325 void tst_QDeclarativeDebug::watch_property()
       
   326 {
       
   327     QDeclarativeDebugObjectReference obj = findRootObject();
       
   328     QDeclarativeDebugPropertyReference prop = findProperty(obj.properties(), "width");
       
   329 
       
   330     QDeclarativeDebugPropertyWatch *watch;
       
   331     
       
   332     QDeclarativeEngineDebug *unconnected = new QDeclarativeEngineDebug(0);
       
   333     watch = unconnected->addWatch(prop, this);
       
   334     QCOMPARE(watch->state(), QDeclarativeDebugWatch::Dead);
       
   335     delete watch;
       
   336     delete unconnected;
       
   337 
       
   338     watch = m_dbg->addWatch(QDeclarativeDebugPropertyReference(), this);
       
   339     QVERIFY(QDeclarativeDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QDeclarativeDebugWatch::State))));
       
   340     QCOMPARE(watch->state(), QDeclarativeDebugWatch::Inactive);
       
   341     delete watch;
       
   342     
       
   343     watch = m_dbg->addWatch(prop, this);
       
   344     QCOMPARE(watch->state(), QDeclarativeDebugWatch::Waiting);
       
   345     QCOMPARE(watch->objectDebugId(), obj.debugId());
       
   346     QCOMPARE(watch->name(), prop.name());
       
   347 
       
   348     QSignalSpy spy(watch, SIGNAL(valueChanged(QByteArray,QVariant)));
       
   349 
       
   350     int origWidth = m_rootItem->property("width").toInt();
       
   351     m_rootItem->setProperty("width", origWidth*2);
       
   352 
       
   353     // stateChanged() is received before valueChanged()
       
   354     QVERIFY(QDeclarativeDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QDeclarativeDebugWatch::State))));
       
   355     QCOMPARE(watch->state(), QDeclarativeDebugWatch::Active);
       
   356     QCOMPARE(spy.count(), 1);
       
   357 
       
   358     m_dbg->removeWatch(watch);
       
   359     delete watch;
       
   360 
       
   361     // restore original value and verify spy doesn't get additional signal since watch has been removed
       
   362     m_rootItem->setProperty("width", origWidth);
       
   363     QTest::qWait(100);
       
   364     QCOMPARE(spy.count(), 1);
       
   365 
       
   366     QCOMPARE(spy.at(0).at(0).value<QByteArray>(), prop.name().toUtf8());
       
   367     QCOMPARE(spy.at(0).at(1).value<QVariant>(), qVariantFromValue(origWidth*2));
       
   368 }
       
   369 
       
   370 void tst_QDeclarativeDebug::watch_object()
       
   371 {
       
   372     QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this);
       
   373     waitForQuery(q_engines);
       
   374     
       
   375     Q_ASSERT(q_engines->engines().count() > 0);
       
   376     QDeclarativeDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this);
       
   377     waitForQuery(q_context);
       
   378 
       
   379     Q_ASSERT(q_context->rootContext().objects().count() > 0);
       
   380     QDeclarativeDebugObjectQuery *q_obj = m_dbg->queryObject(q_context->rootContext().objects()[0], this);
       
   381     waitForQuery(q_obj);
       
   382 
       
   383     QDeclarativeDebugObjectReference obj = q_obj->object();
       
   384 
       
   385     delete q_engines;
       
   386     delete q_context;
       
   387     delete q_obj;
       
   388 
       
   389     QDeclarativeDebugWatch *watch;
       
   390 
       
   391     QDeclarativeEngineDebug *unconnected = new QDeclarativeEngineDebug(0);
       
   392     watch = unconnected->addWatch(obj, this);
       
   393     QCOMPARE(watch->state(), QDeclarativeDebugWatch::Dead);
       
   394     delete watch;
       
   395     delete unconnected;
       
   396     
       
   397     watch = m_dbg->addWatch(QDeclarativeDebugObjectReference(), this);
       
   398     QVERIFY(QDeclarativeDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QDeclarativeDebugWatch::State))));
       
   399     QCOMPARE(watch->state(), QDeclarativeDebugWatch::Inactive);
       
   400     delete watch;
       
   401 
       
   402     watch = m_dbg->addWatch(obj, this);
       
   403     QCOMPARE(watch->state(), QDeclarativeDebugWatch::Waiting);
       
   404     QCOMPARE(watch->objectDebugId(), obj.debugId());
       
   405 
       
   406     QSignalSpy spy(watch, SIGNAL(valueChanged(QByteArray,QVariant)));
       
   407 
       
   408     int origWidth = m_rootItem->property("width").toInt();
       
   409     int origHeight = m_rootItem->property("height").toInt();
       
   410     m_rootItem->setProperty("width", origWidth*2);
       
   411     m_rootItem->setProperty("height", origHeight*2);
       
   412 
       
   413     // stateChanged() is received before any valueChanged() signals
       
   414     QVERIFY(QDeclarativeDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QDeclarativeDebugWatch::State))));
       
   415     QCOMPARE(watch->state(), QDeclarativeDebugWatch::Active);
       
   416     QVERIFY(spy.count() > 0);
       
   417 
       
   418     int newWidth = -1;
       
   419     int newHeight = -1;
       
   420     for (int i=0; i<spy.count(); i++) {
       
   421         const QVariantList &values = spy[i];
       
   422         if (values[0].value<QByteArray>() == "width")
       
   423             newWidth = values[1].value<QVariant>().toInt();
       
   424         else if (values[0].value<QByteArray>() == "height")
       
   425             newHeight = values[1].value<QVariant>().toInt();
       
   426 
       
   427     }
       
   428 
       
   429     m_dbg->removeWatch(watch);
       
   430     delete watch;
       
   431 
       
   432     // since watch has been removed, restoring the original values should not trigger a valueChanged()
       
   433     spy.clear();
       
   434     m_rootItem->setProperty("width", origWidth);
       
   435     m_rootItem->setProperty("height", origHeight);
       
   436     QTest::qWait(100);
       
   437     QCOMPARE(spy.count(), 0);
       
   438 
       
   439     QCOMPARE(newWidth, origWidth * 2);
       
   440     QCOMPARE(newHeight, origHeight * 2);
       
   441 }
       
   442 
       
   443 void tst_QDeclarativeDebug::watch_expression()
       
   444 {
       
   445     QFETCH(QString, expr);
       
   446     QFETCH(int, increment);
       
   447     QFETCH(int, incrementCount);
       
   448 
       
   449     int origWidth = m_rootItem->property("width").toInt();
       
   450     
       
   451     QDeclarativeDebugObjectReference obj = findRootObject();
       
   452 
       
   453     QDeclarativeDebugObjectExpressionWatch *watch;
       
   454 
       
   455     QDeclarativeEngineDebug *unconnected = new QDeclarativeEngineDebug(0);
       
   456     watch = unconnected->addWatch(obj, expr, this);
       
   457     QCOMPARE(watch->state(), QDeclarativeDebugWatch::Dead);
       
   458     delete watch;
       
   459     delete unconnected;
       
   460     
       
   461     watch = m_dbg->addWatch(QDeclarativeDebugObjectReference(), expr, this);
       
   462     QVERIFY(QDeclarativeDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QDeclarativeDebugWatch::State))));
       
   463     QCOMPARE(watch->state(), QDeclarativeDebugWatch::Inactive);
       
   464     delete watch;
       
   465     
       
   466     watch = m_dbg->addWatch(obj, expr, this);
       
   467     QCOMPARE(watch->state(), QDeclarativeDebugWatch::Waiting);
       
   468     QCOMPARE(watch->objectDebugId(), obj.debugId());
       
   469     QCOMPARE(watch->expression(), expr);
       
   470 
       
   471     QSignalSpy spyState(watch, SIGNAL(stateChanged(QDeclarativeDebugWatch::State)));
       
   472 
       
   473     QSignalSpy spy(watch, SIGNAL(valueChanged(QByteArray,QVariant)));
       
   474     int expectedSpyCount = incrementCount + 1;  // should also get signal with expression's initial value
       
   475 
       
   476     int width = origWidth;
       
   477     for (int i=0; i<incrementCount+1; i++) {
       
   478         if (i > 0) {
       
   479             width += increment;
       
   480             m_rootItem->setProperty("width", width);
       
   481         }
       
   482         if (!QDeclarativeDebugTest::waitForSignal(watch, SIGNAL(valueChanged(QByteArray,QVariant))))
       
   483             QFAIL("Did not receive valueChanged() for expression");
       
   484     }
       
   485 
       
   486     if (spyState.count() == 0)
       
   487         QVERIFY(QDeclarativeDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QDeclarativeDebugWatch::State))));
       
   488     QCOMPARE(spyState.count(), 1);
       
   489     QCOMPARE(watch->state(), QDeclarativeDebugWatch::Active);
       
   490 
       
   491     m_dbg->removeWatch(watch);
       
   492     delete watch;
       
   493 
       
   494     // restore original value and verify spy doesn't get a signal since watch has been removed
       
   495     m_rootItem->setProperty("width", origWidth); 
       
   496     QTest::qWait(100);
       
   497     QCOMPARE(spy.count(), expectedSpyCount);
       
   498 
       
   499     width = origWidth + increment;
       
   500     for (int i=0; i<spy.count(); i++) {
       
   501         QCOMPARE(spy.at(i).at(1).value<QVariant>().toInt(), width);
       
   502         width += increment;
       
   503     }
       
   504 }
       
   505 
       
   506 void tst_QDeclarativeDebug::watch_expression_data()
       
   507 {
       
   508     QTest::addColumn<QString>("expr");
       
   509     QTest::addColumn<int>("increment");
       
   510     QTest::addColumn<int>("incrementCount");
       
   511 
       
   512     QTest::newRow("width") << "width" << 0 << 0;
       
   513     QTest::newRow("width+10") << "width + 10" << 10 << 5;
       
   514 }
       
   515 
       
   516 void tst_QDeclarativeDebug::watch_context()
       
   517 {
       
   518     QDeclarativeDebugContextReference c;
       
   519     QTest::ignoreMessage(QtWarningMsg, "QDeclarativeEngineDebug::addWatch(): Not implemented");
       
   520     QVERIFY(!m_dbg->addWatch(c, QString(), this));
       
   521 }
       
   522 
       
   523 void tst_QDeclarativeDebug::watch_file()
       
   524 {
       
   525     QDeclarativeDebugFileReference f;
       
   526     QTest::ignoreMessage(QtWarningMsg, "QDeclarativeEngineDebug::addWatch(): Not implemented");
       
   527     QVERIFY(!m_dbg->addWatch(f, this));
       
   528 }
       
   529 
       
   530 void tst_QDeclarativeDebug::queryAvailableEngines()
       
   531 {
       
   532     QDeclarativeDebugEnginesQuery *q_engines;
       
   533 
       
   534     QDeclarativeEngineDebug *unconnected = new QDeclarativeEngineDebug(0);
       
   535     q_engines = unconnected->queryAvailableEngines(0);
       
   536     QCOMPARE(q_engines->state(), QDeclarativeDebugQuery::Error);
       
   537     delete q_engines;
       
   538     delete unconnected;
       
   539 
       
   540     q_engines = m_dbg->queryAvailableEngines(this);
       
   541     delete q_engines;
       
   542 
       
   543     q_engines = m_dbg->queryAvailableEngines(this);
       
   544     QVERIFY(q_engines->engines().isEmpty());
       
   545     waitForQuery(q_engines);
       
   546 
       
   547     // TODO test multiple engines
       
   548     QList<QDeclarativeDebugEngineReference> engines = q_engines->engines();
       
   549     QCOMPARE(engines.count(), 1);
       
   550 
       
   551     foreach(const QDeclarativeDebugEngineReference &e, engines) {
       
   552         QCOMPARE(e.debugId(), QDeclarativeDebugService::idForObject(m_engine));
       
   553         QCOMPARE(e.name(), m_engine->objectName());
       
   554     }
       
   555 
       
   556     delete q_engines;
       
   557 }
       
   558 
       
   559 void tst_QDeclarativeDebug::queryRootContexts()
       
   560 {
       
   561     QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this);
       
   562     waitForQuery(q_engines);
       
   563     int engineId = q_engines->engines()[0].debugId();
       
   564 
       
   565     QDeclarativeDebugRootContextQuery *q_context;
       
   566     
       
   567     QDeclarativeEngineDebug *unconnected = new QDeclarativeEngineDebug(0);
       
   568     q_context = unconnected->queryRootContexts(engineId, this);
       
   569     QCOMPARE(q_context->state(), QDeclarativeDebugQuery::Error);
       
   570     delete q_context;
       
   571     delete unconnected;
       
   572 
       
   573     q_context = m_dbg->queryRootContexts(engineId, this);
       
   574     delete q_context;
       
   575 
       
   576     q_context = m_dbg->queryRootContexts(engineId, this);
       
   577     waitForQuery(q_context);
       
   578 
       
   579     QDeclarativeContext *actualContext = m_engine->rootContext();
       
   580     QDeclarativeDebugContextReference context = q_context->rootContext();
       
   581     QCOMPARE(context.debugId(), QDeclarativeDebugService::idForObject(actualContext));
       
   582     QCOMPARE(context.name(), actualContext->objectName());
       
   583 
       
   584     QCOMPARE(context.objects().count(), 2); // 2 qml component objects created for context in main()
       
   585 
       
   586     // root context query sends only root object data - it doesn't fill in
       
   587     // the children or property info
       
   588     QCOMPARE(context.objects()[0].properties().count(), 0);
       
   589     QCOMPARE(context.objects()[0].children().count(), 0);
       
   590 
       
   591     QCOMPARE(context.contexts().count(), 1);
       
   592     QVERIFY(context.contexts()[0].debugId() >= 0);
       
   593     QCOMPARE(context.contexts()[0].name(), QString("tst_QDeclarativeDebug_childContext"));
       
   594 
       
   595     delete q_engines;
       
   596     delete q_context;
       
   597 }
       
   598 
       
   599 void tst_QDeclarativeDebug::queryObject()
       
   600 {
       
   601     QFETCH(bool, recursive);
       
   602 
       
   603     QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this);
       
   604     waitForQuery(q_engines);
       
   605     
       
   606     QDeclarativeDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this);
       
   607     waitForQuery(q_context);
       
   608     QDeclarativeDebugObjectReference rootObject = q_context->rootContext().objects()[0];
       
   609 
       
   610     QDeclarativeDebugObjectQuery *q_obj = 0;
       
   611 
       
   612     QDeclarativeEngineDebug *unconnected = new QDeclarativeEngineDebug(0);
       
   613     q_obj = recursive ? unconnected->queryObjectRecursive(rootObject, this) : unconnected->queryObject(rootObject, this);
       
   614     QCOMPARE(q_obj->state(), QDeclarativeDebugQuery::Error);
       
   615     delete q_obj;
       
   616     delete unconnected;
       
   617 
       
   618     q_obj = recursive ? m_dbg->queryObjectRecursive(rootObject, this) : m_dbg->queryObject(rootObject, this);
       
   619     delete q_obj;
       
   620 
       
   621     q_obj = recursive ? m_dbg->queryObjectRecursive(rootObject, this) : m_dbg->queryObject(rootObject, this);
       
   622     waitForQuery(q_obj);
       
   623 
       
   624     QDeclarativeDebugObjectReference obj = q_obj->object();
       
   625 
       
   626     delete q_engines;
       
   627     delete q_context;
       
   628     delete q_obj;
       
   629 
       
   630     // check source as defined in main()
       
   631     QDeclarativeDebugFileReference source = obj.source();
       
   632     QCOMPARE(source.url(), QUrl::fromLocalFile(""));
       
   633     QCOMPARE(source.lineNumber(), 2);
       
   634     QCOMPARE(source.columnNumber(), 1);
       
   635 
       
   636     // generically test all properties, children and childrens' properties
       
   637     recursiveObjectTest(m_rootItem, obj, recursive);
       
   638 
       
   639     if (recursive) {
       
   640         foreach(const QDeclarativeDebugObjectReference &child, obj.children())
       
   641             QVERIFY(child.properties().count() > 0);
       
   642 
       
   643         QDeclarativeDebugObjectReference rect;
       
   644         QDeclarativeDebugObjectReference text;
       
   645         foreach (const QDeclarativeDebugObjectReference &child, obj.children()) {
       
   646             if (child.className() == "Rectangle")
       
   647                 rect = child;
       
   648             else if (child.className() == "Text")
       
   649                 text = child;
       
   650         }
       
   651 
       
   652         // test specific property values
       
   653         QCOMPARE(findProperty(rect.properties(), "width").value(), qVariantFromValue(500));
       
   654         QCOMPARE(findProperty(rect.properties(), "height").value(), qVariantFromValue(600));
       
   655         QCOMPARE(findProperty(rect.properties(), "color").value(), qVariantFromValue(QColor("blue")));
       
   656 
       
   657         QCOMPARE(findProperty(text.properties(), "color").value(), qVariantFromValue(QColor("blue")));
       
   658 
       
   659     } else {
       
   660         foreach(const QDeclarativeDebugObjectReference &child, obj.children())
       
   661             QCOMPARE(child.properties().count(), 0);
       
   662     }
       
   663 }
       
   664 
       
   665 void tst_QDeclarativeDebug::queryObject_data()
       
   666 {
       
   667     QTest::addColumn<bool>("recursive");
       
   668 
       
   669     QTest::newRow("non-recursive") << false;
       
   670     QTest::newRow("recursive") << true;
       
   671 }
       
   672 
       
   673 void tst_QDeclarativeDebug::queryExpressionResult()
       
   674 {
       
   675     QFETCH(QString, expr);
       
   676     QFETCH(QVariant, result);
       
   677 
       
   678     QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this);
       
   679     waitForQuery(q_engines);    // check immediate deletion is ok
       
   680     
       
   681     QDeclarativeDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this);
       
   682     waitForQuery(q_context);
       
   683     int objectId = q_context->rootContext().objects()[0].debugId();
       
   684 
       
   685     QDeclarativeDebugExpressionQuery *q_expr;
       
   686 
       
   687     QDeclarativeEngineDebug *unconnected = new QDeclarativeEngineDebug(0);
       
   688     q_expr = unconnected->queryExpressionResult(objectId, expr, this);
       
   689     QCOMPARE(q_expr->state(), QDeclarativeDebugQuery::Error);
       
   690     delete q_expr;
       
   691     delete unconnected;
       
   692     
       
   693     q_expr = m_dbg->queryExpressionResult(objectId, expr, this);
       
   694     delete q_expr;
       
   695 
       
   696     q_expr = m_dbg->queryExpressionResult(objectId, expr, this);
       
   697     QCOMPARE(q_expr->expression(), expr);
       
   698     waitForQuery(q_expr);
       
   699 
       
   700     QCOMPARE(q_expr->result(), result);
       
   701 
       
   702     delete q_engines;
       
   703     delete q_context;
       
   704     delete q_expr;
       
   705 }
       
   706 
       
   707 void tst_QDeclarativeDebug::queryExpressionResult_data()
       
   708 {
       
   709     QTest::addColumn<QString>("expr");
       
   710     QTest::addColumn<QVariant>("result");
       
   711 
       
   712     QTest::newRow("width + 50") << "width + 50" << qVariantFromValue(60);
       
   713     QTest::newRow("blueRect.width") << "blueRect.width" << qVariantFromValue(500);
       
   714     QTest::newRow("bad expr") << "aeaef" << qVariantFromValue(QString("<undefined>"));
       
   715 }
       
   716 
       
   717 void tst_QDeclarativeDebug::tst_QDeclarativeDebugFileReference()
       
   718 {
       
   719     QDeclarativeDebugFileReference ref;
       
   720     QVERIFY(ref.url().isEmpty());
       
   721     QCOMPARE(ref.lineNumber(), -1);
       
   722     QCOMPARE(ref.columnNumber(), -1);
       
   723 
       
   724     ref.setUrl(QUrl("http://test"));
       
   725     QCOMPARE(ref.url(), QUrl("http://test"));
       
   726     ref.setLineNumber(1);
       
   727     QCOMPARE(ref.lineNumber(), 1);
       
   728     ref.setColumnNumber(1);
       
   729     QCOMPARE(ref.columnNumber(), 1);
       
   730 
       
   731     QDeclarativeDebugFileReference copy(ref);
       
   732     QDeclarativeDebugFileReference copyAssign;
       
   733     copyAssign = ref;
       
   734     foreach (const QDeclarativeDebugFileReference &r, (QList<QDeclarativeDebugFileReference>() << copy << copyAssign)) {
       
   735         QCOMPARE(r.url(), ref.url());
       
   736         QCOMPARE(r.lineNumber(), ref.lineNumber());
       
   737         QCOMPARE(r.columnNumber(), ref.columnNumber());
       
   738     }
       
   739 }
       
   740 
       
   741 void tst_QDeclarativeDebug::tst_QDeclarativeDebugEngineReference()
       
   742 {
       
   743     QDeclarativeDebugEngineReference ref;
       
   744     QCOMPARE(ref.debugId(), -1);
       
   745     QVERIFY(ref.name().isEmpty());
       
   746 
       
   747     ref = QDeclarativeDebugEngineReference(1);
       
   748     QCOMPARE(ref.debugId(), 1);
       
   749     QVERIFY(ref.name().isEmpty());
       
   750 
       
   751     QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this);
       
   752     waitForQuery(q_engines);
       
   753     ref = q_engines->engines()[0];
       
   754     delete q_engines;
       
   755 
       
   756     QDeclarativeDebugEngineReference copy(ref);
       
   757     QDeclarativeDebugEngineReference copyAssign;
       
   758     copyAssign = ref;
       
   759     foreach (const QDeclarativeDebugEngineReference &r, (QList<QDeclarativeDebugEngineReference>() << copy << copyAssign)) {
       
   760         QCOMPARE(r.debugId(), ref.debugId());
       
   761         QCOMPARE(r.name(), ref.name());
       
   762     }
       
   763 }
       
   764 
       
   765 void tst_QDeclarativeDebug::tst_QDeclarativeDebugObjectReference()
       
   766 {
       
   767     QDeclarativeDebugObjectReference ref;
       
   768     QCOMPARE(ref.debugId(), -1);
       
   769     QCOMPARE(ref.className(), QString());
       
   770     QCOMPARE(ref.name(), QString());
       
   771     QCOMPARE(ref.contextDebugId(), -1);
       
   772     QVERIFY(ref.properties().isEmpty());
       
   773     QVERIFY(ref.children().isEmpty());
       
   774 
       
   775     QDeclarativeDebugFileReference source = ref.source();
       
   776     QVERIFY(source.url().isEmpty());
       
   777     QVERIFY(source.lineNumber() < 0);
       
   778     QVERIFY(source.columnNumber() < 0);
       
   779 
       
   780     ref = QDeclarativeDebugObjectReference(1);
       
   781     QCOMPARE(ref.debugId(), 1);
       
   782 
       
   783     QDeclarativeDebugObjectReference rootObject = findRootObject();
       
   784     QDeclarativeDebugObjectQuery *query = m_dbg->queryObjectRecursive(rootObject, this);
       
   785     waitForQuery(query);
       
   786     ref = query->object();
       
   787     delete query;
       
   788 
       
   789     QVERIFY(ref.debugId() >= 0);
       
   790 
       
   791     QDeclarativeDebugObjectReference copy(ref);
       
   792     QDeclarativeDebugObjectReference copyAssign;
       
   793     copyAssign = ref;
       
   794     foreach (const QDeclarativeDebugObjectReference &r, (QList<QDeclarativeDebugObjectReference>() << copy << copyAssign))
       
   795         recursiveCompareObjects(r, ref);
       
   796 }
       
   797 
       
   798 void tst_QDeclarativeDebug::tst_QDeclarativeDebugContextReference()
       
   799 {
       
   800     QDeclarativeDebugContextReference ref;
       
   801     QCOMPARE(ref.debugId(), -1);
       
   802     QVERIFY(ref.name().isEmpty());
       
   803     QVERIFY(ref.objects().isEmpty());
       
   804     QVERIFY(ref.contexts().isEmpty());
       
   805 
       
   806     QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this);
       
   807     waitForQuery(q_engines);
       
   808     QDeclarativeDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this);
       
   809     waitForQuery(q_context);
       
   810 
       
   811     ref = q_context->rootContext();
       
   812     delete q_engines;
       
   813     delete q_context;
       
   814     QVERIFY(ref.debugId() >= 0);
       
   815 
       
   816     QDeclarativeDebugContextReference copy(ref);
       
   817     QDeclarativeDebugContextReference copyAssign;
       
   818     copyAssign = ref;
       
   819     foreach (const QDeclarativeDebugContextReference &r, (QList<QDeclarativeDebugContextReference>() << copy << copyAssign))
       
   820         recursiveCompareContexts(r, ref);
       
   821 }
       
   822 
       
   823 void tst_QDeclarativeDebug::tst_QDeclarativeDebugPropertyReference()
       
   824 {
       
   825     QDeclarativeDebugObjectReference rootObject = findRootObject();
       
   826     QDeclarativeDebugObjectQuery *query = m_dbg->queryObject(rootObject, this);
       
   827     waitForQuery(query);
       
   828     QDeclarativeDebugObjectReference obj = query->object();
       
   829     delete query;   
       
   830 
       
   831     QDeclarativeDebugPropertyReference ref = findProperty(obj.properties(), "scale");
       
   832     QVERIFY(ref.objectDebugId() > 0);
       
   833     QVERIFY(!ref.name().isEmpty());
       
   834     QVERIFY(!ref.value().isNull());
       
   835     QVERIFY(!ref.valueTypeName().isEmpty());
       
   836     QVERIFY(!ref.binding().isEmpty());
       
   837     QVERIFY(ref.hasNotifySignal());
       
   838   
       
   839     QDeclarativeDebugPropertyReference copy(ref);
       
   840     QDeclarativeDebugPropertyReference copyAssign;
       
   841     copyAssign = ref;
       
   842     foreach (const QDeclarativeDebugPropertyReference &r, (QList<QDeclarativeDebugPropertyReference>() << copy << copyAssign))
       
   843         compareProperties(r, ref);
       
   844 }
       
   845 
       
   846 QTEST_MAIN(tst_QDeclarativeDebug)
       
   847 
       
   848 #include "tst_qdeclarativedebug.moc"