/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <qtest.h>
#include <QSignalSpy>
#include <QTimer>
#include <QHostAddress>
#include <QDebug>
#include <QThread>
#include <QtDeclarative/qdeclarativeengine.h>
#include <QtDeclarative/qdeclarativecontext.h>
#include <QtDeclarative/qdeclarativecomponent.h>
#include <QtDeclarative/qdeclarativeexpression.h>
#include <QtDeclarative/qdeclarativeproperty.h>
#include <private/qdeclarativebinding_p.h>
#include <private/qdeclarativedebug_p.h>
#include <private/qdeclarativeenginedebug_p.h>
#include <private/qdeclarativedebugclient_p.h>
#include <private/qdeclarativedebugservice_p.h>
#include <private/qdeclarativerectangle_p.h>
#include <private/qdeclarativemetatype_p.h>
#include <private/qdeclarativeproperty_p.h>
#include "../../../shared/util.h"
#include "../shared/debugutil_p.h"
Q_DECLARE_METATYPE(QDeclarativeDebugWatch::State)
class tst_QDeclarativeDebug : public QObject
{
Q_OBJECT
private:
QDeclarativeDebugObjectReference findRootObject(int context = 0);
QDeclarativeDebugPropertyReference findProperty(const QList<QDeclarativeDebugPropertyReference> &props, const QString &name) const;
void waitForQuery(QDeclarativeDebugQuery *query);
void recursiveObjectTest(QObject *o, const QDeclarativeDebugObjectReference &oref, bool recursive) const;
void recursiveCompareObjects(const QDeclarativeDebugObjectReference &a, const QDeclarativeDebugObjectReference &b) const;
void recursiveCompareContexts(const QDeclarativeDebugContextReference &a, const QDeclarativeDebugContextReference &b) const;
void compareProperties(const QDeclarativeDebugPropertyReference &a, const QDeclarativeDebugPropertyReference &b) const;
QDeclarativeDebugConnection *m_conn;
QDeclarativeEngineDebug *m_dbg;
QDeclarativeEngine *m_engine;
QDeclarativeItem *m_rootItem;
QObjectList m_components;
private slots:
void initTestCase();
void cleanupTestCase();
void watch_property();
void watch_object();
void watch_expression();
void watch_expression_data();
void watch_context();
void watch_file();
void queryAvailableEngines();
void queryRootContexts();
void queryObject();
void queryObject_data();
void queryExpressionResult();
void queryExpressionResult_data();
void tst_QDeclarativeDebugFileReference();
void tst_QDeclarativeDebugEngineReference();
void tst_QDeclarativeDebugObjectReference();
void tst_QDeclarativeDebugContextReference();
void tst_QDeclarativeDebugPropertyReference();
void setMethodBody();
};
QDeclarativeDebugObjectReference tst_QDeclarativeDebug::findRootObject(int context)
{
QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this);
waitForQuery(q_engines);
if (q_engines->engines().count() == 0)
return QDeclarativeDebugObjectReference();
QDeclarativeDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this);
waitForQuery(q_context);
if (q_context->rootContext().objects().count() == 0)
return QDeclarativeDebugObjectReference();
QDeclarativeDebugObjectQuery *q_obj = m_dbg->queryObject(q_context->rootContext().objects()[context], this);
waitForQuery(q_obj);
QDeclarativeDebugObjectReference result = q_obj->object();
delete q_engines;
delete q_context;
delete q_obj;
return result;
}
QDeclarativeDebugPropertyReference tst_QDeclarativeDebug::findProperty(const QList<QDeclarativeDebugPropertyReference> &props, const QString &name) const
{
foreach(const QDeclarativeDebugPropertyReference &p, props) {
if (p.name() == name)
return p;
}
return QDeclarativeDebugPropertyReference();
}
void tst_QDeclarativeDebug::waitForQuery(QDeclarativeDebugQuery *query)
{
QVERIFY(query);
QCOMPARE(query->parent(), qobject_cast<QObject*>(this));
QVERIFY(query->state() == QDeclarativeDebugQuery::Waiting);
if (!QDeclarativeDebugTest::waitForSignal(query, SIGNAL(stateChanged(QDeclarativeDebugQuery::State))))
QFAIL("query timed out");
}
void tst_QDeclarativeDebug::recursiveObjectTest(QObject *o, const QDeclarativeDebugObjectReference &oref, bool recursive) const
{
const QMetaObject *meta = o->metaObject();
QDeclarativeType *type = QDeclarativeMetaType::qmlType(o->metaObject());
QString className = type ? type->qmlTypeName() : QString();
className = className.mid(className.lastIndexOf(QLatin1Char('/'))+1);
QCOMPARE(oref.debugId(), QDeclarativeDebugService::idForObject(o));
QCOMPARE(oref.name(), o->objectName());
QCOMPARE(oref.className(), className);
QCOMPARE(oref.contextDebugId(), QDeclarativeDebugService::idForObject(qmlContext(o)));
const QObjectList &children = o->children();
for (int i=0; i<children.count(); i++) {
QObject *child = children[i];
if (!qmlContext(child))
continue;
int debugId = QDeclarativeDebugService::idForObject(child);
QVERIFY(debugId >= 0);
QDeclarativeDebugObjectReference cref;
foreach (const QDeclarativeDebugObjectReference &ref, oref.children()) {
if (ref.debugId() == debugId) {
cref = ref;
break;
}
}
QVERIFY(cref.debugId() >= 0);
if (recursive)
recursiveObjectTest(child, cref, true);
}
foreach (const QDeclarativeDebugPropertyReference &p, oref.properties()) {
QCOMPARE(p.objectDebugId(), QDeclarativeDebugService::idForObject(o));
// signal properties are fake - they are generated from QDeclarativeBoundSignal children
if (p.name().startsWith("on") && p.name().length() > 2 && p.name()[2].isUpper()) {
QVERIFY(p.value().toString().startsWith('{') && p.value().toString().endsWith('}'));
QVERIFY(p.valueTypeName().isEmpty());
QVERIFY(p.binding().isEmpty());
QVERIFY(!p.hasNotifySignal());
continue;
}
QMetaProperty pmeta = meta->property(meta->indexOfProperty(p.name().toUtf8().constData()));
QCOMPARE(p.name(), QString::fromUtf8(pmeta.name()));
if (pmeta.type() < QVariant::UserType) // TODO test complex types
QCOMPARE(p.value(), pmeta.read(o));
if (p.name() == "parent")
QVERIFY(p.valueTypeName() == "QGraphicsObject*" || p.valueTypeName() == "QDeclarativeItem*");
else
QCOMPARE(p.valueTypeName(), QString::fromUtf8(pmeta.typeName()));
QDeclarativeAbstractBinding *binding =
QDeclarativePropertyPrivate::binding(QDeclarativeProperty(o, p.name()));
if (binding)
QCOMPARE(binding->expression(), p.binding());
QCOMPARE(p.hasNotifySignal(), pmeta.hasNotifySignal());
QVERIFY(pmeta.isValid());
}
}
void tst_QDeclarativeDebug::recursiveCompareObjects(const QDeclarativeDebugObjectReference &a, const QDeclarativeDebugObjectReference &b) const
{
QCOMPARE(a.debugId(), b.debugId());
QCOMPARE(a.className(), b.className());
QCOMPARE(a.name(), b.name());
QCOMPARE(a.contextDebugId(), b.contextDebugId());
QCOMPARE(a.source().url(), b.source().url());
QCOMPARE(a.source().lineNumber(), b.source().lineNumber());
QCOMPARE(a.source().columnNumber(), b.source().columnNumber());
QCOMPARE(a.properties().count(), b.properties().count());
QCOMPARE(a.children().count(), b.children().count());
QList<QDeclarativeDebugPropertyReference> aprops = a.properties();
QList<QDeclarativeDebugPropertyReference> bprops = b.properties();
for (int i=0; i<aprops.count(); i++)
compareProperties(aprops[i], bprops[i]);
for (int i=0; i<a.children().count(); i++)
recursiveCompareObjects(a.children()[i], b.children()[i]);
}
void tst_QDeclarativeDebug::recursiveCompareContexts(const QDeclarativeDebugContextReference &a, const QDeclarativeDebugContextReference &b) const
{
QCOMPARE(a.debugId(), b.debugId());
QCOMPARE(a.name(), b.name());
QCOMPARE(a.objects().count(), b.objects().count());
QCOMPARE(a.contexts().count(), b.contexts().count());
for (int i=0; i<a.objects().count(); i++)
recursiveCompareObjects(a.objects()[i], b.objects()[i]);
for (int i=0; i<a.contexts().count(); i++)
recursiveCompareContexts(a.contexts()[i], b.contexts()[i]);
}
void tst_QDeclarativeDebug::compareProperties(const QDeclarativeDebugPropertyReference &a, const QDeclarativeDebugPropertyReference &b) const
{
QCOMPARE(a.objectDebugId(), b.objectDebugId());
QCOMPARE(a.name(), b.name());
QCOMPARE(a.value(), b.value());
QCOMPARE(a.valueTypeName(), b.valueTypeName());
QCOMPARE(a.binding(), b.binding());
QCOMPARE(a.hasNotifySignal(), b.hasNotifySignal());
}
void tst_QDeclarativeDebug::initTestCase()
{
qRegisterMetaType<QDeclarativeDebugWatch::State>();
QTest::ignoreMessage(QtWarningMsg, "QDeclarativeDebugServer: Waiting for connection on port 3768...");
qputenv("QML_DEBUG_SERVER_PORT", "3768");
m_engine = new QDeclarativeEngine(this);
QList<QByteArray> qml;
qml << "import Qt 4.7\n"
"Item {"
"width: 10; height: 20; scale: blueRect.scale;"
"Rectangle { id: blueRect; width: 500; height: 600; color: \"blue\"; }"
"Text { color: blueRect.color; }"
"MouseArea {"
"onEntered: { console.log('hello') }"
"}"
"}";
// add second component to test multiple root contexts
qml << "import Qt 4.7\n"
"Item {}";
// and a third to test methods
qml << "import Qt 4.7\n"
"Item {"
"function myMethodNoArgs() { return 3; }\n"
"function myMethod(a) { return a + 9; }\n"
"function myMethodIndirect() { myMethod(3); }\n"
"}";
for (int i=0; i<qml.count(); i++) {
QDeclarativeComponent component(m_engine);
component.setData(qml[i], QUrl::fromLocalFile(""));
Q_ASSERT(component.isReady()); // fails if bad syntax
m_components << qobject_cast<QDeclarativeItem*>(component.create());
}
m_rootItem = qobject_cast<QDeclarativeItem*>(m_components.first());
// add an extra context to test for multiple contexts
QDeclarativeContext *context = new QDeclarativeContext(m_engine->rootContext(), this);
context->setObjectName("tst_QDeclarativeDebug_childContext");
m_conn = new QDeclarativeDebugConnection(this);
m_conn->connectToHost("127.0.0.1", 3768);
QTest::ignoreMessage(QtWarningMsg, "QDeclarativeDebugServer: Connection established");
bool ok = m_conn->waitForConnected();
Q_ASSERT(ok);
QTRY_VERIFY(QDeclarativeDebugService::hasDebuggingClient());
m_dbg = new QDeclarativeEngineDebug(m_conn, this);
}
void tst_QDeclarativeDebug::cleanupTestCase()
{
qDeleteAll(m_components);
}
void tst_QDeclarativeDebug::setMethodBody()
{
QDeclarativeDebugObjectReference obj = findRootObject(2);
QObject *root = m_components.at(2);
// Without args
{
QVariant rv;
QVERIFY(QMetaObject::invokeMethod(root, "myMethodNoArgs", Qt::DirectConnection,
Q_RETURN_ARG(QVariant, rv)));
QVERIFY(rv == QVariant(qreal(3)));
QVERIFY(m_dbg->setMethodBody(obj.debugId(), "myMethodNoArgs", "return 7"));
QTest::qWait(100);
QVERIFY(QMetaObject::invokeMethod(root, "myMethodNoArgs", Qt::DirectConnection,
Q_RETURN_ARG(QVariant, rv)));
QVERIFY(rv == QVariant(qreal(7)));
}
// With args
{
QVariant rv;
QVERIFY(QMetaObject::invokeMethod(root, "myMethod", Qt::DirectConnection,
Q_RETURN_ARG(QVariant, rv), Q_ARG(QVariant, QVariant(19))));
QVERIFY(rv == QVariant(qreal(28)));
QVERIFY(m_dbg->setMethodBody(obj.debugId(), "myMethod", "return a + 7"));
QTest::qWait(100);
QVERIFY(QMetaObject::invokeMethod(root, "myMethod", Qt::DirectConnection,
Q_RETURN_ARG(QVariant, rv), Q_ARG(QVariant, QVariant(19))));
QVERIFY(rv == QVariant(qreal(26)));
}
}
void tst_QDeclarativeDebug::watch_property()
{
QDeclarativeDebugObjectReference obj = findRootObject();
QDeclarativeDebugPropertyReference prop = findProperty(obj.properties(), "width");
QDeclarativeDebugPropertyWatch *watch;
QDeclarativeEngineDebug *unconnected = new QDeclarativeEngineDebug(0);
watch = unconnected->addWatch(prop, this);
QCOMPARE(watch->state(), QDeclarativeDebugWatch::Dead);
delete watch;
delete unconnected;
watch = m_dbg->addWatch(QDeclarativeDebugPropertyReference(), this);
QVERIFY(QDeclarativeDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QDeclarativeDebugWatch::State))));
QCOMPARE(watch->state(), QDeclarativeDebugWatch::Inactive);
delete watch;
watch = m_dbg->addWatch(prop, this);
QCOMPARE(watch->state(), QDeclarativeDebugWatch::Waiting);
QCOMPARE(watch->objectDebugId(), obj.debugId());
QCOMPARE(watch->name(), prop.name());
QSignalSpy spy(watch, SIGNAL(valueChanged(QByteArray,QVariant)));
int origWidth = m_rootItem->property("width").toInt();
m_rootItem->setProperty("width", origWidth*2);
// stateChanged() is received before valueChanged()
QVERIFY(QDeclarativeDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QDeclarativeDebugWatch::State))));
QCOMPARE(watch->state(), QDeclarativeDebugWatch::Active);
QCOMPARE(spy.count(), 1);
m_dbg->removeWatch(watch);
delete watch;
// restore original value and verify spy doesn't get additional signal since watch has been removed
m_rootItem->setProperty("width", origWidth);
QTest::qWait(100);
QCOMPARE(spy.count(), 1);
QCOMPARE(spy.at(0).at(0).value<QByteArray>(), prop.name().toUtf8());
QCOMPARE(spy.at(0).at(1).value<QVariant>(), qVariantFromValue(origWidth*2));
}
void tst_QDeclarativeDebug::watch_object()
{
QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this);
waitForQuery(q_engines);
Q_ASSERT(q_engines->engines().count() > 0);
QDeclarativeDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this);
waitForQuery(q_context);
Q_ASSERT(q_context->rootContext().objects().count() > 0);
QDeclarativeDebugObjectQuery *q_obj = m_dbg->queryObject(q_context->rootContext().objects()[0], this);
waitForQuery(q_obj);
QDeclarativeDebugObjectReference obj = q_obj->object();
delete q_engines;
delete q_context;
delete q_obj;
QDeclarativeDebugWatch *watch;
QDeclarativeEngineDebug *unconnected = new QDeclarativeEngineDebug(0);
watch = unconnected->addWatch(obj, this);
QCOMPARE(watch->state(), QDeclarativeDebugWatch::Dead);
delete watch;
delete unconnected;
watch = m_dbg->addWatch(QDeclarativeDebugObjectReference(), this);
QVERIFY(QDeclarativeDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QDeclarativeDebugWatch::State))));
QCOMPARE(watch->state(), QDeclarativeDebugWatch::Inactive);
delete watch;
watch = m_dbg->addWatch(obj, this);
QCOMPARE(watch->state(), QDeclarativeDebugWatch::Waiting);
QCOMPARE(watch->objectDebugId(), obj.debugId());
QSignalSpy spy(watch, SIGNAL(valueChanged(QByteArray,QVariant)));
int origWidth = m_rootItem->property("width").toInt();
int origHeight = m_rootItem->property("height").toInt();
m_rootItem->setProperty("width", origWidth*2);
m_rootItem->setProperty("height", origHeight*2);
// stateChanged() is received before any valueChanged() signals
QVERIFY(QDeclarativeDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QDeclarativeDebugWatch::State))));
QCOMPARE(watch->state(), QDeclarativeDebugWatch::Active);
QVERIFY(spy.count() > 0);
int newWidth = -1;
int newHeight = -1;
for (int i=0; i<spy.count(); i++) {
const QVariantList &values = spy[i];
if (values[0].value<QByteArray>() == "width")
newWidth = values[1].value<QVariant>().toInt();
else if (values[0].value<QByteArray>() == "height")
newHeight = values[1].value<QVariant>().toInt();
}
m_dbg->removeWatch(watch);
delete watch;
// since watch has been removed, restoring the original values should not trigger a valueChanged()
spy.clear();
m_rootItem->setProperty("width", origWidth);
m_rootItem->setProperty("height", origHeight);
QTest::qWait(100);
QCOMPARE(spy.count(), 0);
QCOMPARE(newWidth, origWidth * 2);
QCOMPARE(newHeight, origHeight * 2);
}
void tst_QDeclarativeDebug::watch_expression()
{
QFETCH(QString, expr);
QFETCH(int, increment);
QFETCH(int, incrementCount);
int origWidth = m_rootItem->property("width").toInt();
QDeclarativeDebugObjectReference obj = findRootObject();
QDeclarativeDebugObjectExpressionWatch *watch;
QDeclarativeEngineDebug *unconnected = new QDeclarativeEngineDebug(0);
watch = unconnected->addWatch(obj, expr, this);
QCOMPARE(watch->state(), QDeclarativeDebugWatch::Dead);
delete watch;
delete unconnected;
watch = m_dbg->addWatch(QDeclarativeDebugObjectReference(), expr, this);
QVERIFY(QDeclarativeDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QDeclarativeDebugWatch::State))));
QCOMPARE(watch->state(), QDeclarativeDebugWatch::Inactive);
delete watch;
watch = m_dbg->addWatch(obj, expr, this);
QCOMPARE(watch->state(), QDeclarativeDebugWatch::Waiting);
QCOMPARE(watch->objectDebugId(), obj.debugId());
QCOMPARE(watch->expression(), expr);
QSignalSpy spyState(watch, SIGNAL(stateChanged(QDeclarativeDebugWatch::State)));
QSignalSpy spy(watch, SIGNAL(valueChanged(QByteArray,QVariant)));
int expectedSpyCount = incrementCount + 1; // should also get signal with expression's initial value
int width = origWidth;
for (int i=0; i<incrementCount+1; i++) {
if (i > 0) {
width += increment;
m_rootItem->setProperty("width", width);
}
if (!QDeclarativeDebugTest::waitForSignal(watch, SIGNAL(valueChanged(QByteArray,QVariant))))
QFAIL("Did not receive valueChanged() for expression");
}
if (spyState.count() == 0)
QVERIFY(QDeclarativeDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QDeclarativeDebugWatch::State))));
QCOMPARE(spyState.count(), 1);
QCOMPARE(watch->state(), QDeclarativeDebugWatch::Active);
m_dbg->removeWatch(watch);
delete watch;
// restore original value and verify spy doesn't get a signal since watch has been removed
m_rootItem->setProperty("width", origWidth);
QTest::qWait(100);
QCOMPARE(spy.count(), expectedSpyCount);
width = origWidth + increment;
for (int i=0; i<spy.count(); i++) {
QCOMPARE(spy.at(i).at(1).value<QVariant>().toInt(), width);
width += increment;
}
}
void tst_QDeclarativeDebug::watch_expression_data()
{
QTest::addColumn<QString>("expr");
QTest::addColumn<int>("increment");
QTest::addColumn<int>("incrementCount");
QTest::newRow("width") << "width" << 0 << 0;
QTest::newRow("width+10") << "width + 10" << 10 << 5;
}
void tst_QDeclarativeDebug::watch_context()
{
QDeclarativeDebugContextReference c;
QTest::ignoreMessage(QtWarningMsg, "QDeclarativeEngineDebug::addWatch(): Not implemented");
QVERIFY(!m_dbg->addWatch(c, QString(), this));
}
void tst_QDeclarativeDebug::watch_file()
{
QDeclarativeDebugFileReference f;
QTest::ignoreMessage(QtWarningMsg, "QDeclarativeEngineDebug::addWatch(): Not implemented");
QVERIFY(!m_dbg->addWatch(f, this));
}
void tst_QDeclarativeDebug::queryAvailableEngines()
{
QDeclarativeDebugEnginesQuery *q_engines;
QDeclarativeEngineDebug *unconnected = new QDeclarativeEngineDebug(0);
q_engines = unconnected->queryAvailableEngines(0);
QCOMPARE(q_engines->state(), QDeclarativeDebugQuery::Error);
delete q_engines;
delete unconnected;
q_engines = m_dbg->queryAvailableEngines(this);
delete q_engines;
q_engines = m_dbg->queryAvailableEngines(this);
QVERIFY(q_engines->engines().isEmpty());
waitForQuery(q_engines);
// TODO test multiple engines
QList<QDeclarativeDebugEngineReference> engines = q_engines->engines();
QCOMPARE(engines.count(), 1);
foreach(const QDeclarativeDebugEngineReference &e, engines) {
QCOMPARE(e.debugId(), QDeclarativeDebugService::idForObject(m_engine));
QCOMPARE(e.name(), m_engine->objectName());
}
delete q_engines;
}
void tst_QDeclarativeDebug::queryRootContexts()
{
QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this);
waitForQuery(q_engines);
int engineId = q_engines->engines()[0].debugId();
QDeclarativeDebugRootContextQuery *q_context;
QDeclarativeEngineDebug *unconnected = new QDeclarativeEngineDebug(0);
q_context = unconnected->queryRootContexts(engineId, this);
QCOMPARE(q_context->state(), QDeclarativeDebugQuery::Error);
delete q_context;
delete unconnected;
q_context = m_dbg->queryRootContexts(engineId, this);
delete q_context;
q_context = m_dbg->queryRootContexts(engineId, this);
waitForQuery(q_context);
QDeclarativeContext *actualContext = m_engine->rootContext();
QDeclarativeDebugContextReference context = q_context->rootContext();
QCOMPARE(context.debugId(), QDeclarativeDebugService::idForObject(actualContext));
QCOMPARE(context.name(), actualContext->objectName());
QCOMPARE(context.objects().count(), 3); // 3 qml component objects created for context in main()
// root context query sends only root object data - it doesn't fill in
// the children or property info
QCOMPARE(context.objects()[0].properties().count(), 0);
QCOMPARE(context.objects()[0].children().count(), 0);
QCOMPARE(context.contexts().count(), 1);
QVERIFY(context.contexts()[0].debugId() >= 0);
QCOMPARE(context.contexts()[0].name(), QString("tst_QDeclarativeDebug_childContext"));
delete q_engines;
delete q_context;
}
void tst_QDeclarativeDebug::queryObject()
{
QFETCH(bool, recursive);
QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this);
waitForQuery(q_engines);
QDeclarativeDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this);
waitForQuery(q_context);
QDeclarativeDebugObjectReference rootObject = q_context->rootContext().objects()[0];
QDeclarativeDebugObjectQuery *q_obj = 0;
QDeclarativeEngineDebug *unconnected = new QDeclarativeEngineDebug(0);
q_obj = recursive ? unconnected->queryObjectRecursive(rootObject, this) : unconnected->queryObject(rootObject, this);
QCOMPARE(q_obj->state(), QDeclarativeDebugQuery::Error);
delete q_obj;
delete unconnected;
q_obj = recursive ? m_dbg->queryObjectRecursive(rootObject, this) : m_dbg->queryObject(rootObject, this);
delete q_obj;
q_obj = recursive ? m_dbg->queryObjectRecursive(rootObject, this) : m_dbg->queryObject(rootObject, this);
waitForQuery(q_obj);
QDeclarativeDebugObjectReference obj = q_obj->object();
delete q_engines;
delete q_context;
delete q_obj;
// check source as defined in main()
QDeclarativeDebugFileReference source = obj.source();
QCOMPARE(source.url(), QUrl::fromLocalFile(""));
QCOMPARE(source.lineNumber(), 2);
QCOMPARE(source.columnNumber(), 1);
// generically test all properties, children and childrens' properties
recursiveObjectTest(m_rootItem, obj, recursive);
if (recursive) {
foreach(const QDeclarativeDebugObjectReference &child, obj.children())
QVERIFY(child.properties().count() > 0);
QDeclarativeDebugObjectReference rect;
QDeclarativeDebugObjectReference text;
foreach (const QDeclarativeDebugObjectReference &child, obj.children()) {
if (child.className() == "Rectangle")
rect = child;
else if (child.className() == "Text")
text = child;
}
// test specific property values
QCOMPARE(findProperty(rect.properties(), "width").value(), qVariantFromValue(500));
QCOMPARE(findProperty(rect.properties(), "height").value(), qVariantFromValue(600));
QCOMPARE(findProperty(rect.properties(), "color").value(), qVariantFromValue(QColor("blue")));
QCOMPARE(findProperty(text.properties(), "color").value(), qVariantFromValue(QColor("blue")));
} else {
foreach(const QDeclarativeDebugObjectReference &child, obj.children())
QCOMPARE(child.properties().count(), 0);
}
}
void tst_QDeclarativeDebug::queryObject_data()
{
QTest::addColumn<bool>("recursive");
QTest::newRow("non-recursive") << false;
QTest::newRow("recursive") << true;
}
void tst_QDeclarativeDebug::queryExpressionResult()
{
QFETCH(QString, expr);
QFETCH(QVariant, result);
QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this);
waitForQuery(q_engines); // check immediate deletion is ok
QDeclarativeDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this);
waitForQuery(q_context);
int objectId = q_context->rootContext().objects()[0].debugId();
QDeclarativeDebugExpressionQuery *q_expr;
QDeclarativeEngineDebug *unconnected = new QDeclarativeEngineDebug(0);
q_expr = unconnected->queryExpressionResult(objectId, expr, this);
QCOMPARE(q_expr->state(), QDeclarativeDebugQuery::Error);
delete q_expr;
delete unconnected;
q_expr = m_dbg->queryExpressionResult(objectId, expr, this);
delete q_expr;
q_expr = m_dbg->queryExpressionResult(objectId, expr, this);
QCOMPARE(q_expr->expression().toString(), expr);
waitForQuery(q_expr);
QCOMPARE(q_expr->result(), result);
delete q_engines;
delete q_context;
delete q_expr;
}
void tst_QDeclarativeDebug::queryExpressionResult_data()
{
QTest::addColumn<QString>("expr");
QTest::addColumn<QVariant>("result");
QTest::newRow("width + 50") << "width + 50" << qVariantFromValue(60);
QTest::newRow("blueRect.width") << "blueRect.width" << qVariantFromValue(500);
QTest::newRow("bad expr") << "aeaef" << qVariantFromValue(QString("<undefined>"));
}
void tst_QDeclarativeDebug::tst_QDeclarativeDebugFileReference()
{
QDeclarativeDebugFileReference ref;
QVERIFY(ref.url().isEmpty());
QCOMPARE(ref.lineNumber(), -1);
QCOMPARE(ref.columnNumber(), -1);
ref.setUrl(QUrl("http://test"));
QCOMPARE(ref.url(), QUrl("http://test"));
ref.setLineNumber(1);
QCOMPARE(ref.lineNumber(), 1);
ref.setColumnNumber(1);
QCOMPARE(ref.columnNumber(), 1);
QDeclarativeDebugFileReference copy(ref);
QDeclarativeDebugFileReference copyAssign;
copyAssign = ref;
foreach (const QDeclarativeDebugFileReference &r, (QList<QDeclarativeDebugFileReference>() << copy << copyAssign)) {
QCOMPARE(r.url(), ref.url());
QCOMPARE(r.lineNumber(), ref.lineNumber());
QCOMPARE(r.columnNumber(), ref.columnNumber());
}
}
void tst_QDeclarativeDebug::tst_QDeclarativeDebugEngineReference()
{
QDeclarativeDebugEngineReference ref;
QCOMPARE(ref.debugId(), -1);
QVERIFY(ref.name().isEmpty());
ref = QDeclarativeDebugEngineReference(1);
QCOMPARE(ref.debugId(), 1);
QVERIFY(ref.name().isEmpty());
QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this);
waitForQuery(q_engines);
ref = q_engines->engines()[0];
delete q_engines;
QDeclarativeDebugEngineReference copy(ref);
QDeclarativeDebugEngineReference copyAssign;
copyAssign = ref;
foreach (const QDeclarativeDebugEngineReference &r, (QList<QDeclarativeDebugEngineReference>() << copy << copyAssign)) {
QCOMPARE(r.debugId(), ref.debugId());
QCOMPARE(r.name(), ref.name());
}
}
void tst_QDeclarativeDebug::tst_QDeclarativeDebugObjectReference()
{
QDeclarativeDebugObjectReference ref;
QCOMPARE(ref.debugId(), -1);
QCOMPARE(ref.className(), QString());
QCOMPARE(ref.name(), QString());
QCOMPARE(ref.contextDebugId(), -1);
QVERIFY(ref.properties().isEmpty());
QVERIFY(ref.children().isEmpty());
QDeclarativeDebugFileReference source = ref.source();
QVERIFY(source.url().isEmpty());
QVERIFY(source.lineNumber() < 0);
QVERIFY(source.columnNumber() < 0);
ref = QDeclarativeDebugObjectReference(1);
QCOMPARE(ref.debugId(), 1);
QDeclarativeDebugObjectReference rootObject = findRootObject();
QDeclarativeDebugObjectQuery *query = m_dbg->queryObjectRecursive(rootObject, this);
waitForQuery(query);
ref = query->object();
delete query;
QVERIFY(ref.debugId() >= 0);
QDeclarativeDebugObjectReference copy(ref);
QDeclarativeDebugObjectReference copyAssign;
copyAssign = ref;
foreach (const QDeclarativeDebugObjectReference &r, (QList<QDeclarativeDebugObjectReference>() << copy << copyAssign))
recursiveCompareObjects(r, ref);
}
void tst_QDeclarativeDebug::tst_QDeclarativeDebugContextReference()
{
QDeclarativeDebugContextReference ref;
QCOMPARE(ref.debugId(), -1);
QVERIFY(ref.name().isEmpty());
QVERIFY(ref.objects().isEmpty());
QVERIFY(ref.contexts().isEmpty());
QDeclarativeDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this);
waitForQuery(q_engines);
QDeclarativeDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this);
waitForQuery(q_context);
ref = q_context->rootContext();
delete q_engines;
delete q_context;
QVERIFY(ref.debugId() >= 0);
QDeclarativeDebugContextReference copy(ref);
QDeclarativeDebugContextReference copyAssign;
copyAssign = ref;
foreach (const QDeclarativeDebugContextReference &r, (QList<QDeclarativeDebugContextReference>() << copy << copyAssign))
recursiveCompareContexts(r, ref);
}
void tst_QDeclarativeDebug::tst_QDeclarativeDebugPropertyReference()
{
QDeclarativeDebugObjectReference rootObject = findRootObject();
QDeclarativeDebugObjectQuery *query = m_dbg->queryObject(rootObject, this);
waitForQuery(query);
QDeclarativeDebugObjectReference obj = query->object();
delete query;
QDeclarativeDebugPropertyReference ref = findProperty(obj.properties(), "scale");
QVERIFY(ref.objectDebugId() > 0);
QVERIFY(!ref.name().isEmpty());
QVERIFY(!ref.value().isNull());
QVERIFY(!ref.valueTypeName().isEmpty());
QVERIFY(!ref.binding().isEmpty());
QVERIFY(ref.hasNotifySignal());
QDeclarativeDebugPropertyReference copy(ref);
QDeclarativeDebugPropertyReference copyAssign;
copyAssign = ref;
foreach (const QDeclarativeDebugPropertyReference &r, (QList<QDeclarativeDebugPropertyReference>() << copy << copyAssign))
compareProperties(r, ref);
}
QTEST_MAIN(tst_QDeclarativeDebug)
#include "tst_qdeclarativedebug.moc"