diff options
Diffstat (limited to 'tests/auto/qml/debugger')
42 files changed, 5974 insertions, 0 deletions
diff --git a/tests/auto/qml/debugger/debugger.pro b/tests/auto/qml/debugger/debugger.pro new file mode 100644 index 0000000000..4f9ebbc350 --- /dev/null +++ b/tests/auto/qml/debugger/debugger.pro @@ -0,0 +1,16 @@ +TEMPLATE = subdirs + +PRIVATETESTS += \ + qqmlenginedebug \ + qqmldebugclient \ + qqmldebugservice \ + qqmldebugjs \ + qqmlinspector \ + qqmlprofilerservice \ + qpacketprotocol \ + qv8profilerservice \ + qdebugmessageservice + +contains(QT_CONFIG, private_tests) { + SUBDIRS += $$PRIVATETESTS +} diff --git a/tests/auto/qml/debugger/qdebugmessageservice/data/test.qml b/tests/auto/qml/debugger/qdebugmessageservice/data/test.qml new file mode 100644 index 0000000000..ab86c7d468 --- /dev/null +++ b/tests/auto/qml/debugger/qdebugmessageservice/data/test.qml @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Item { + width: 360 + height: 360 + Component.onCompleted: { + console.log("console.log") + console.count("console.count"); + } +} diff --git a/tests/auto/qml/debugger/qdebugmessageservice/qdebugmessageservice.pro b/tests/auto/qml/debugger/qdebugmessageservice/qdebugmessageservice.pro new file mode 100644 index 0000000000..afda4b23bd --- /dev/null +++ b/tests/auto/qml/debugger/qdebugmessageservice/qdebugmessageservice.pro @@ -0,0 +1,21 @@ +CONFIG += testcase +TARGET = tst_qdebugmessageservice +QT += network qml-private testlib +macx:CONFIG -= app_bundle + +HEADERS += ../shared/debugutil_p.h + +SOURCES += tst_qdebugmessageservice.cpp \ + ../shared/debugutil.cpp + +INCLUDEPATH += ../shared + +include(../../../shared/util.pri) + +testDataFiles.files = data +testDataFiles.path = . +DEPLOYMENT += testDataFiles + +CONFIG += parallel_test + +OTHER_FILES += data/test.qml diff --git a/tests/auto/qml/debugger/qdebugmessageservice/tst_qdebugmessageservice.cpp b/tests/auto/qml/debugger/qdebugmessageservice/tst_qdebugmessageservice.cpp new file mode 100644 index 0000000000..a19fd4b766 --- /dev/null +++ b/tests/auto/qml/debugger/qdebugmessageservice/tst_qdebugmessageservice.cpp @@ -0,0 +1,242 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtQml/private/qqmldebugclient_p.h> + +//QQmlDebugTest +#include "../shared/debugutil_p.h" +#include "../../../shared/util.h" + +#include <QtCore/QString> +#include <QtTest/QtTest> + +const char *NORMALMODE = "-qmljsdebugger=port:3777,block"; +const char *QMLFILE = "test.qml"; + +class QQmlDebugMsgClient; +class tst_QDebugMessageService : public QQmlDataTest +{ + Q_OBJECT + +public: + tst_QDebugMessageService(); + + void init(); + +private slots: + void initTestCase(); + void cleanupTestCase(); + + void cleanup(); + + void retrieveDebugOutput(); + +private: + QQmlDebugProcess *m_process; + QQmlDebugMsgClient *m_client; + QQmlDebugConnection *m_connection; +}; + +struct LogEntry { + LogEntry(QtMsgType _type, QString _message) + : type(_type), message(_message) {} + + QtMsgType type; + QString message; + int line; + QString file; + QString function; + + QString toString() const { return QString::number(type) + ": " + message; } +}; + +bool operator==(const LogEntry &t1, const LogEntry &t2) +{ + return t1.type == t2.type && t1.message == t2.message + && t1.line == t2.line && t1.file == t2.file + && t1.function == t2.function; +} + +class QQmlDebugMsgClient : public QQmlDebugClient +{ + Q_OBJECT +public: + QQmlDebugMsgClient(QQmlDebugConnection *connection) + : QQmlDebugClient(QLatin1String("DebugMessages"), connection) + { + } + + QList<LogEntry> logBuffer; + +protected: + //inherited from QQmlDebugClient + void stateChanged(State state); + void messageReceived(const QByteArray &data); + +signals: + void enabled(); + void debugOutput(); +}; + +void QQmlDebugMsgClient::stateChanged(State state) +{ + if (state == Enabled) { + emit enabled(); + } +} + +void QQmlDebugMsgClient::messageReceived(const QByteArray &data) +{ + QDataStream ds(data); + QByteArray command; + ds >> command; + + if (command == "MESSAGE") { + int type; + QByteArray message; + QByteArray file; + QByteArray function; + int line; + ds >> type >> message >> file >> line >> function; + QVERIFY(ds.atEnd()); + + QVERIFY(type >= QtDebugMsg); + QVERIFY(type <= QtFatalMsg); + + LogEntry entry((QtMsgType)type, QString::fromUtf8(message)); + entry.line = line; + entry.file = QString::fromUtf8(file); + entry.function = QString::fromUtf8(function); + logBuffer << entry; + emit debugOutput(); + } else { + QFAIL("Unknown message"); + } +} + +tst_QDebugMessageService::tst_QDebugMessageService() +{ +} + +void tst_QDebugMessageService::initTestCase() +{ + QQmlDataTest::initTestCase(); + m_process = 0; + m_client = 0; + m_connection = 0; +} + +void tst_QDebugMessageService::cleanupTestCase() +{ + if (m_process) + delete m_process; + + if (m_client) + delete m_client; + + if (m_connection) + delete m_connection; +} + +void tst_QDebugMessageService::init() +{ + m_connection = new QQmlDebugConnection(); + m_process = new QQmlDebugProcess(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene"); + m_client = new QQmlDebugMsgClient(m_connection); + + m_process->start(QStringList() << QLatin1String(NORMALMODE) << QQmlDataTest::instance()->testFile(QMLFILE)); + if (!m_process->waitForSessionStart()) { + QFAIL(QString("Could not launch app. Application output: \n%1").arg(m_process->output()).toAscii()); + } + + m_connection->connectToHost("127.0.0.1", 3777); + QVERIFY(m_connection->waitForConnected()); + + QVERIFY(QQmlDebugTest::waitForSignal(m_client, SIGNAL(enabled()))); +} + +void tst_QDebugMessageService::cleanup() +{ + if (QTest::currentTestFailed()) + qDebug() << m_process->output(); + if (m_process) + delete m_process; + + if (m_client) + delete m_client; + + if (m_connection) + delete m_connection; + + m_process = 0; + m_client = 0; + m_connection = 0; +} + +void tst_QDebugMessageService::retrieveDebugOutput() +{ + init(); + + int maxTries = 2; + while ((m_client->logBuffer.size() < 2) + || (maxTries-- > 0)) + QQmlDebugTest::waitForSignal(m_client, SIGNAL(debugOutput()), 1000); + + QVERIFY(m_client->logBuffer.size() >= 2); + + const QString path = + QUrl::fromLocalFile(QQmlDataTest::instance()->testFile(QMLFILE)).toString(); + LogEntry entry1(QtDebugMsg, QLatin1String("console.log")); + entry1.line = 48; + entry1.file = path; + entry1.function = QLatin1String("onCompleted"); + LogEntry entry2(QtDebugMsg, QLatin1String("console.count: 1")); + entry2.line = 49; + entry2.file = path; + entry2.function = QLatin1String("onCompleted"); + + QVERIFY(m_client->logBuffer.contains(entry1)); + QVERIFY(m_client->logBuffer.contains(entry2)); +} + +QTEST_MAIN(tst_QDebugMessageService) + +#include "tst_qdebugmessageservice.moc" diff --git a/tests/auto/qml/debugger/qpacketprotocol/qpacketprotocol.pro b/tests/auto/qml/debugger/qpacketprotocol/qpacketprotocol.pro new file mode 100644 index 0000000000..88439196a7 --- /dev/null +++ b/tests/auto/qml/debugger/qpacketprotocol/qpacketprotocol.pro @@ -0,0 +1,10 @@ +CONFIG += testcase +TARGET = tst_qpacketprotocol +macx:CONFIG -= app_bundle + +HEADERS += ../shared/debugutil_p.h +SOURCES += tst_qpacketprotocol.cpp \ + ../shared/debugutil.cpp + +CONFIG += parallel_test +QT += qml-private network testlib diff --git a/tests/auto/qml/debugger/qpacketprotocol/tst_qpacketprotocol.cpp b/tests/auto/qml/debugger/qpacketprotocol/tst_qpacketprotocol.cpp new file mode 100644 index 0000000000..c02dd2d8fa --- /dev/null +++ b/tests/auto/qml/debugger/qpacketprotocol/tst_qpacketprotocol.cpp @@ -0,0 +1,263 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <qtest.h> +#include <QSignalSpy> +#include <QTimer> +#include <QTcpSocket> +#include <QTcpServer> +#include <QDebug> +#include <QBuffer> + +#include <private/qpacketprotocol_p.h> + +#include "../shared/debugutil_p.h" + +class tst_QPacketProtocol : public QObject +{ + Q_OBJECT + +private: + QTcpServer *m_server; + QTcpSocket *m_client; + QTcpSocket *m_serverConn; + +private slots: + void init(); + void cleanup(); + + void maximumPacketSize(); + void setMaximumPacketSize(); + void setMaximumPacketSize_data(); + void send(); + void send_data(); + void packetsAvailable(); + void packetsAvailable_data(); + void clear(); + void read(); + void device(); + + void tst_QPacket_clear(); +}; + +void tst_QPacketProtocol::init() +{ + m_server = new QTcpServer(this); + m_serverConn = 0; + QVERIFY(m_server->listen(QHostAddress("127.0.0.1"))); + + m_client = new QTcpSocket(this); + m_client->connectToHost(m_server->serverAddress(), m_server->serverPort()); + + QVERIFY(m_client->waitForConnected()); + QVERIFY(m_server->waitForNewConnection(5000)); + m_serverConn = m_server->nextPendingConnection(); +} + +void tst_QPacketProtocol::cleanup() +{ + delete m_client; + delete m_serverConn; + delete m_server; +} + +void tst_QPacketProtocol::maximumPacketSize() +{ + QPacketProtocol p(m_client); + QCOMPARE(p.maximumPacketSize(), 0x7FFFFFFF); +} + +void tst_QPacketProtocol::setMaximumPacketSize() +{ + QFETCH(qint32, size); + QFETCH(qint32, expected); + + QPacketProtocol out(m_serverConn); + QCOMPARE(out.setMaximumPacketSize(size), expected); +} + +void tst_QPacketProtocol::setMaximumPacketSize_data() +{ + QTest::addColumn<int>("size"); + QTest::addColumn<int>("expected"); + + QTest::newRow("invalid") << qint32(sizeof(qint32) - 1) << qint32(0x7FFFFFFF); + QTest::newRow("still invalid") << qint32(sizeof(qint32)) << qint32(0x7FFFFFFF); + QTest::newRow("valid") << qint32(sizeof(qint32) + 1) << qint32(sizeof(qint32) + 1); +} + +void tst_QPacketProtocol::send() +{ + QFETCH(bool, useAutoSend); + + QPacketProtocol in(m_client); + QPacketProtocol out(m_serverConn); + + QByteArray ba; + int num; + + if (useAutoSend) { + out.send() << "Hello world" << 123; + } else { + QPacket packet; + packet << "Hello world" << 123; + out.send(packet); + } + + QVERIFY(QQmlDebugTest::waitForSignal(&in, SIGNAL(readyRead()))); + + QPacket p = in.read(); + p >> ba >> num; + QCOMPARE(ba, QByteArray("Hello world") + '\0'); + QCOMPARE(num, 123); +} + +void tst_QPacketProtocol::send_data() +{ + QTest::addColumn<bool>("useAutoSend"); + + QTest::newRow("auto send") << true; + QTest::newRow("no auto send") << false; +} + +void tst_QPacketProtocol::packetsAvailable() +{ + QFETCH(int, packetCount); + + QPacketProtocol out(m_client); + QPacketProtocol in(m_serverConn); + + QCOMPARE(out.packetsAvailable(), qint64(0)); + QCOMPARE(in.packetsAvailable(), qint64(0)); + + for (int i=0; i<packetCount; i++) + out.send() << "Hello"; + + QVERIFY(QQmlDebugTest::waitForSignal(&in, SIGNAL(readyRead()))); + QCOMPARE(in.packetsAvailable(), qint64(packetCount)); +} + +void tst_QPacketProtocol::packetsAvailable_data() +{ + QTest::addColumn<int>("packetCount"); + + QTest::newRow("1") << 1; + QTest::newRow("2") << 2; + QTest::newRow("10") << 10; +} + +void tst_QPacketProtocol::clear() +{ + QPacketProtocol in(m_client); + QPacketProtocol out(m_serverConn); + + out.send() << 123; + out.send() << 456; + QVERIFY(QQmlDebugTest::waitForSignal(&in, SIGNAL(readyRead()))); + + in.clear(); + QVERIFY(in.read().isEmpty()); +} + +void tst_QPacketProtocol::read() +{ + QPacketProtocol in(m_client); + QPacketProtocol out(m_serverConn); + + QVERIFY(in.read().isEmpty()); + + out.send() << 123; + out.send() << 456; + QVERIFY(QQmlDebugTest::waitForSignal(&in, SIGNAL(readyRead()))); + + int num; + + QPacket p1 = in.read(); + QVERIFY(!p1.isEmpty()); + p1 >> num; + QCOMPARE(num, 123); + + QPacket p2 = in.read(); + QVERIFY(!p2.isEmpty()); + p2 >> num; + QCOMPARE(num, 456); + + QVERIFY(in.read().isEmpty()); +} + +void tst_QPacketProtocol::device() +{ + QPacketProtocol p(m_client); + QVERIFY(p.device() == m_client); +} + +void tst_QPacketProtocol::tst_QPacket_clear() +{ + QPacketProtocol protocol(m_client); + + QPacket packet; + + packet << "Hello world!" << 123; + protocol.send(packet); + + packet.clear(); + QVERIFY(packet.isEmpty()); + packet << "Goodbyte world!" << 789; + protocol.send(packet); + + QByteArray ba; + int num; + QPacketProtocol in(m_serverConn); + QVERIFY(QQmlDebugTest::waitForSignal(&in, SIGNAL(readyRead()))); + + QPacket p1 = in.read(); + p1 >> ba >> num; + QCOMPARE(ba, QByteArray("Hello world!") + '\0'); + QCOMPARE(num, 123); + + QPacket p2 = in.read(); + p2 >> ba >> num; + QCOMPARE(ba, QByteArray("Goodbyte world!") + '\0'); + QCOMPARE(num, 789); +} + +QTEST_MAIN(tst_QPacketProtocol) + +#include "tst_qpacketprotocol.moc" diff --git a/tests/auto/qml/debugger/qqmldebugclient/qqmldebugclient.pro b/tests/auto/qml/debugger/qqmldebugclient/qqmldebugclient.pro new file mode 100644 index 0000000000..d298b5c087 --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugclient/qqmldebugclient.pro @@ -0,0 +1,11 @@ +CONFIG += testcase +TARGET = tst_qqmldebugclient +macx:CONFIG -= app_bundle + +HEADERS += ../shared/debugutil_p.h +SOURCES += tst_qqmldebugclient.cpp \ + ../shared/debugutil.cpp + +CONFIG += declarative_debug + +QT += qml-private testlib diff --git a/tests/auto/qml/debugger/qqmldebugclient/tst_qqmldebugclient.cpp b/tests/auto/qml/debugger/qqmldebugclient/tst_qqmldebugclient.cpp new file mode 100644 index 0000000000..2891076af6 --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugclient/tst_qqmldebugclient.cpp @@ -0,0 +1,197 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <qtest.h> +#include <QSignalSpy> +#include <QTimer> +#include <QHostAddress> +#include <QDebug> +#include <QThread> + +#include <QtQml/qqmlengine.h> + +#include "../shared/debugutil_p.h" + +#define PORT 13770 +#define STR_PORT "13770" + +class tst_QQmlDebugClient : public QObject +{ + Q_OBJECT + +private: + QQmlDebugConnection *m_conn; + +private slots: + void initTestCase(); + + void name(); + void state(); + void sendMessage(); + void parallelConnect(); + void sequentialConnect(); +}; + +void tst_QQmlDebugClient::initTestCase() +{ + const QString waitingMsg = QString("QQmlDebugServer: Waiting for connection on port %1...").arg(PORT); + QTest::ignoreMessage(QtWarningMsg, waitingMsg.toAscii().constData()); + new QQmlEngine(this); + + m_conn = new QQmlDebugConnection(this); + + QQmlDebugTestClient client("tst_QQmlDebugClient::handshake()", m_conn); + QQmlDebugTestService service("tst_QQmlDebugClient::handshake()"); + + QTest::ignoreMessage(QtWarningMsg, "QQmlDebugServer: Connection established"); + for (int i = 0; i < 50; ++i) { + // try for 5 seconds ... + m_conn->connectToHost("127.0.0.1", PORT); + if (m_conn->waitForConnected()) + break; + QTest::qSleep(100); + } + + QVERIFY(m_conn->isConnected()); + + QTRY_VERIFY(QQmlDebugService::hasDebuggingClient()); + QTRY_COMPARE(client.state(), QQmlDebugClient::Enabled); +} + +void tst_QQmlDebugClient::name() +{ + QString name = "tst_QQmlDebugClient::name()"; + + QQmlDebugClient client(name, m_conn); + QCOMPARE(client.name(), name); +} + +void tst_QQmlDebugClient::state() +{ + { + QQmlDebugConnection dummyConn; + QQmlDebugClient client("tst_QQmlDebugClient::state()", &dummyConn); + QCOMPARE(client.state(), QQmlDebugClient::NotConnected); + QCOMPARE(client.serviceVersion(), -1.0f); + } + + QQmlDebugTestClient client("tst_QQmlDebugClient::state()", m_conn); + QCOMPARE(client.state(), QQmlDebugClient::Unavailable); + + { + QQmlDebugTestService service("tst_QQmlDebugClient::state()", 2); + QTRY_COMPARE(client.state(), QQmlDebugClient::Enabled); + QCOMPARE(client.serviceVersion(), 2.0f); + } + + QTRY_COMPARE(client.state(), QQmlDebugClient::Unavailable); + + // duplicate plugin name + QTest::ignoreMessage(QtWarningMsg, "QQmlDebugClient: Conflicting plugin name \"tst_QQmlDebugClient::state()\" "); + QQmlDebugClient client2("tst_QQmlDebugClient::state()", m_conn); + QCOMPARE(client2.state(), QQmlDebugClient::NotConnected); + + QQmlDebugClient client3("tst_QQmlDebugClient::state3()", 0); + QCOMPARE(client3.state(), QQmlDebugClient::NotConnected); +} + +void tst_QQmlDebugClient::sendMessage() +{ + QQmlDebugTestService service("tst_QQmlDebugClient::sendMessage()"); + QQmlDebugTestClient client("tst_QQmlDebugClient::sendMessage()", m_conn); + + QByteArray msg = "hello!"; + + QTRY_COMPARE(client.state(), QQmlDebugClient::Enabled); + + client.sendMessage(msg); + QByteArray resp = client.waitForResponse(); + QCOMPARE(resp, msg); +} + +void tst_QQmlDebugClient::parallelConnect() +{ + QQmlDebugConnection connection2; + + QTest::ignoreMessage(QtWarningMsg, "QQmlDebugServer: Another client is already connected"); + // will connect & immediately disconnect + connection2.connectToHost("127.0.0.1", PORT); + QVERIFY(connection2.waitForConnected()); + QTRY_COMPARE(connection2.state(), QAbstractSocket::UnconnectedState); + QVERIFY(m_conn->isConnected()); +} + +void tst_QQmlDebugClient::sequentialConnect() +{ + QQmlDebugConnection connection2; + QQmlDebugTestClient client2("tst_QQmlDebugClient::handshake()", &connection2); + QQmlDebugTestService service("tst_QQmlDebugClient::handshake()"); + + m_conn->close(); + QVERIFY(!m_conn->isConnected()); + QCOMPARE(m_conn->state(), QAbstractSocket::UnconnectedState); + + // Make sure that the disconnect is actually delivered to the server + QTest::qWait(100); + + connection2.connectToHost("127.0.0.1", PORT); + QTest::ignoreMessage(QtWarningMsg, "QQmlDebugServer: Connection established"); + QVERIFY(connection2.waitForConnected()); + QVERIFY(connection2.isConnected()); + QTRY_VERIFY(client2.state() == QQmlDebugClient::Enabled); +} + +int main(int argc, char *argv[]) +{ + int _argc = argc + 1; + char **_argv = new char*[_argc]; + for (int i = 0; i < argc; ++i) + _argv[i] = argv[i]; + char arg[] = "-qmljsdebugger=port:" STR_PORT; + _argv[_argc - 1] = arg; + + QGuiApplication app(_argc, _argv); + tst_QQmlDebugClient tc; + return QTest::qExec(&tc, _argc, _argv); + delete _argv; +} + +#include "tst_qqmldebugclient.moc" + diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/breakpointRelocation.qml b/tests/auto/qml/debugger/qqmldebugjs/data/breakpointRelocation.qml new file mode 100644 index 0000000000..1f0f9e22c9 --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugjs/data/breakpointRelocation.qml @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +//DO NOT CHANGE + +Item { + Component.onCompleted: { + //Comment + + var b = 6; + } +} + diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/changeBreakpoint.qml b/tests/auto/qml/debugger/qqmldebugjs/data/changeBreakpoint.qml new file mode 100644 index 0000000000..fd81b3f805 --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugjs/data/changeBreakpoint.qml @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +//DO NOT CHANGE + +Item { + id: item + property int d: 0 + + function doSomething() { + var a = 5; + var b = 6; + } + + Timer { + id: timer; interval: 1; running: true; repeat: true + onTriggered: doSomething(); + } + +} diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/condition.qml b/tests/auto/qml/debugger/qqmldebugjs/data/condition.qml new file mode 100644 index 0000000000..ad4144be11 --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugjs/data/condition.qml @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +//DO NOT CHANGE + +Item { + id: item + property int a: 0 + Timer { + id: timer; interval: 1; repeat: true; running: true + onTriggered: a++ + } +} + diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/createComponent.qml b/tests/auto/qml/debugger/qqmldebugjs/data/createComponent.qml new file mode 100644 index 0000000000..993f33a661 --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugjs/data/createComponent.qml @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +//DO NOT CHANGE +Item { + Component.onCompleted: { + var component = Qt.createComponent("oncompleted.qml") + if (component.status === Component.Ready) { + component.createObject(); + } + } +} + + diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/exception.qml b/tests/auto/qml/debugger/qqmldebugjs/data/exception.qml new file mode 100644 index 0000000000..b491087a02 --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugjs/data/exception.qml @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +//DO NOT CHANGE + +Item { + id: root + + Component.onCompleted: dummy() +} + diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/loadjsfile.qml b/tests/auto/qml/debugger/qqmldebugjs/data/loadjsfile.qml new file mode 100644 index 0000000000..4fff66a325 --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugjs/data/loadjsfile.qml @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import "test.js" as Script + +//DO NOT CHANGE +Item { + Component.onCompleted: Script.printMessage("onCompleted"); +} diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/oncompleted.qml b/tests/auto/qml/debugger/qqmldebugjs/data/oncompleted.qml new file mode 100644 index 0000000000..e03ba2ca79 --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugjs/data/oncompleted.qml @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +//DO NOT CHANGE + +Item { + Component.onCompleted: { + console.log("Hello world") + } + id: root + property int a: 10 +} + diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/stepAction.qml b/tests/auto/qml/debugger/qqmldebugjs/data/stepAction.qml new file mode 100644 index 0000000000..690f9fd446 --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugjs/data/stepAction.qml @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +//DO NOT CHANGE + +Item { + id: item + property int d: 0 + + function doSomething() { + var a = 5; + var b = 6; + } + + Component.onCompleted: doSomething() + +} diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/test.js b/tests/auto/qml/debugger/qqmldebugjs/data/test.js new file mode 100644 index 0000000000..7de138bdf6 --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugjs/data/test.js @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +function printMessage(msg) +{ + print(msg); +} + +function add(a,b) +{ + //This is a comment and below is an empty line + + var out = a + b; + return out; +} diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/test.qml b/tests/auto/qml/debugger/qqmldebugjs/data/test.qml new file mode 100644 index 0000000000..200f26b1c3 --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugjs/data/test.qml @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +//DO NOT CHANGE + +Item { + Component.onCompleted: { + var a = [1, 2] + var b = {a: "hello", d: 1 } + var c + var d = 12 + } +} + diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/timer.qml b/tests/auto/qml/debugger/qqmldebugjs/data/timer.qml new file mode 100644 index 0000000000..d9440415d2 --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugjs/data/timer.qml @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +//DO NOT CHANGE +Item { + Timer { + id: timer; interval: 1; running: true; repeat: true; triggeredOnStart: true + onTriggered: { + console.log("timer"); + } + } +} diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs.pro b/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs.pro new file mode 100644 index 0000000000..72b0e77f71 --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs.pro @@ -0,0 +1,29 @@ +CONFIG += testcase +TARGET = tst_qqmldebugjs +QT += qml-private testlib +macx:CONFIG -= app_bundle + +HEADERS += ../shared/debugutil_p.h +SOURCES += tst_qqmldebugjs.cpp \ + ../shared/debugutil.cpp + +INCLUDEPATH += ../shared + +include (../../../shared/util.pri) + +testDataFiles.files = data +testDataFiles.path = . +DEPLOYMENT += testDataFiles + +CONFIG += parallel_test + +OTHER_FILES += data/test.qml data/test.js \ + data/timer.qml \ + data/exception.qml \ + data/oncompleted.qml \ + data/loadjsfile.qml \ + data/condition.qml \ + data/changeBreakpoint.qml \ + data/stepAction.qml \ + data/breakpointRelocation.qml \ + data/createComponent.qml diff --git a/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp b/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp new file mode 100644 index 0000000000..9a8b00bb6c --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp @@ -0,0 +1,1790 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qtest.h> +#include <QtCore/QProcess> +#include <QtCore/QTimer> +#include <QtCore/QFileInfo> +#include <QtCore/QDir> +#include <QtCore/QMutex> +#include <QtCore/QLibraryInfo> +#include <QtQml/private/qqmldebugclient_p.h> +#include <QtQml/QJSEngine> + +//QQmlDebugTest +#include "../shared/debugutil_p.h" +#include "../../../shared/util.h" + +const char *V8REQUEST = "v8request"; +const char *V8MESSAGE = "v8message"; +const char *SEQ = "seq"; +const char *TYPE = "type"; +const char *COMMAND = "command"; +const char *ARGUMENTS = "arguments"; +const char *STEPACTION = "stepaction"; +const char *STEPCOUNT = "stepcount"; +const char *EXPRESSION = "expression"; +const char *FRAME = "frame"; +const char *GLOBAL = "global"; +const char *DISABLEBREAK = "disable_break"; +const char *HANDLES = "handles"; +const char *INCLUDESOURCE = "includeSource"; +const char *FROMFRAME = "fromFrame"; +const char *TOFRAME = "toFrame"; +const char *BOTTOM = "bottom"; +const char *NUMBER = "number"; +const char *FRAMENUMBER = "frameNumber"; +const char *TYPES = "types"; +const char *IDS = "ids"; +const char *FILTER = "filter"; +const char *FROMLINE = "fromLine"; +const char *TOLINE = "toLine"; +const char *TARGET = "target"; +const char *LINE = "line"; +const char *COLUMN = "column"; +const char *ENABLED = "enabled"; +const char *CONDITION = "condition"; +const char *IGNORECOUNT = "ignoreCount"; +const char *BREAKPOINT = "breakpoint"; +const char *FLAGS = "flags"; + +const char *CONTINEDEBUGGING = "continue"; +const char *EVALUATE = "evaluate"; +const char *LOOKUP = "lookup"; +const char *BACKTRACE = "backtrace"; +const char *SCOPE = "scope"; +const char *SCOPES = "scopes"; +const char *SCRIPTS = "scripts"; +const char *SOURCE = "source"; +const char *SETBREAKPOINT = "setbreakpoint"; +const char *CHANGEBREAKPOINT = "changebreakpoint"; +const char *CLEARBREAKPOINT = "clearbreakpoint"; +const char *SETEXCEPTIONBREAK = "setexceptionbreak"; +const char *V8FLAGS = "v8flags"; +const char *VERSION = "version"; +const char *DISCONNECT = "disconnect"; +const char *LISTBREAKPOINTS = "listbreakpoints"; +const char *GARBAGECOLLECTOR = "gc"; +//const char *PROFILE = "profile"; + +const char *CONNECT = "connect"; +const char *INTERRUPT = "interrupt"; +const char *BREAKAFTERCOMPILE = "breakaftercompile"; + +const char *REQUEST = "request"; +const char *IN = "in"; +const char *NEXT = "next"; +const char *OUT = "out"; + +const char *FUNCTION = "function"; +const char *SCRIPT = "script"; +const char *SCRIPTREGEXP = "scriptRegExp"; +const char *EVENT = "event"; + +const char *ALL = "all"; +const char *UNCAUGHT = "uncaught"; + +//const char *PAUSE = "pause"; +//const char *RESUME = "resume"; + +const char *BLOCKMODE = "-qmljsdebugger=port:3771,block"; +const char *NORMALMODE = "-qmljsdebugger=port:3771"; +const char *TEST_QMLFILE = "test.qml"; +const char *TEST_JSFILE = "test.js"; +const char *TIMER_QMLFILE = "timer.qml"; +const char *LOADJSFILE_QMLFILE = "loadjsfile.qml"; +const char *EXCEPTION_QMLFILE = "exception.qml"; +const char *ONCOMPLETED_QMLFILE = "oncompleted.qml"; +const char *CREATECOMPONENT_QMLFILE = "createComponent.qml"; +const char *CONDITION_QMLFILE = "condition.qml"; +const char *CHANGEBREAKPOINT_QMLFILE = "changeBreakpoint.qml"; +const char *STEPACTION_QMLFILE = "stepAction.qml"; +const char *BREAKPOINTRELOCATION_QMLFILE = "breakpointRelocation.qml"; + +#define VARIANTMAPINIT \ + QString obj("{}"); \ + QJSValue jsonVal = parser.call(QJSValueList() << obj); \ + jsonVal.setProperty(SEQ,QJSValue(seq++)); \ + jsonVal.setProperty(TYPE,REQUEST); + + +#undef QVERIFY +#define QVERIFY(statement) \ +do {\ + if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__)) {\ + if (QTest::currentTestFailed()) \ + qDebug().nospace() << "\nDEBUGGEE OUTPUT:\n" << process->output();\ + return;\ + }\ +} while (0) + + +class QJSDebugClient; + +class tst_QQmlDebugJS : public QQmlDataTest +{ + Q_OBJECT + + bool init(const QString &qmlFile = QString(TEST_QMLFILE), bool blockMode = true); + +private slots: + void initTestCase(); + void cleanupTestCase(); + + void cleanup(); + + void connect(); + void interrupt(); + void breakAfterCompile(); + void getVersion(); + void getVersionWhenAttaching(); + + void applyV8Flags(); + + void disconnect(); + + void gc(); + + void listBreakpoints(); + + void setBreakpointInScriptOnCompleted(); + void setBreakpointInScriptOnComponentCreated(); + void setBreakpointInScriptOnTimerCallback(); + void setBreakpointInScriptInDifferentFile(); + void setBreakpointInScriptOnComment(); + void setBreakpointInScriptOnEmptyLine(); + void setBreakpointInScriptWithCondition(); + //void setBreakpointInFunction(); //NOT SUPPORTED + void setBreakpointOnEvent(); + void setBreakpointWhenAttaching(); + + void changeBreakpoint(); + void changeBreakpointOnCondition(); + + void clearBreakpoint(); + + void setExceptionBreak(); + + void stepNext(); + void stepNextWithCount(); + void stepIn(); + void stepOut(); + void continueDebugging(); + + void backtrace(); + + void getFrameDetails(); + + void getScopeDetails(); + + void evaluateInGlobalScope(); + void evaluateInLocalScope(); + + void getScopes(); + + void getScripts(); + + void getSource(); + + // void profile(); //NOT SUPPORTED + + // void verifyQMLOptimizerDisabled(); + +private: + QQmlDebugProcess *process; + QJSDebugClient *client; + QQmlDebugConnection *connection; + QTime t; +}; + +class QJSDebugClient : public QQmlDebugClient +{ + Q_OBJECT +public: + enum StepAction + { + Continue, + In, + Out, + Next + }; + + enum Exception + { + All, + Uncaught + }; + +// enum ProfileCommand +// { +// Pause, +// Resume +// }; + + QJSDebugClient(QQmlDebugConnection *connection) + : QQmlDebugClient(QLatin1String("V8Debugger"), connection), + seq(0) + { + parser = jsEngine.evaluate(QLatin1String("JSON.parse")); + stringify = jsEngine.evaluate(QLatin1String("JSON.stringify")); + } + + void connect(); + void interrupt(); + void breakAfterCompile(bool enabled); + + void continueDebugging(StepAction stepAction, int stepCount = 1); + void evaluate(QString expr, bool global = false, bool disableBreak = false, int frame = -1, const QVariantMap &addContext = QVariantMap()); + void lookup(QList<int> handles, bool includeSource = false); + void backtrace(int fromFrame = -1, int toFrame = -1, bool bottom = false); + void frame(int number = -1); + void scope(int number = -1, int frameNumber = -1); + void scopes(int frameNumber = -1); + void scripts(int types = 4, QList<int> ids = QList<int>(), bool includeSource = false, QVariant filter = QVariant()); + void source(int frame = -1, int fromLine = -1, int toLine = -1); + void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = true, QString condition = QString(), int ignoreCount = -1); + void changeBreakpoint(int breakpoint, bool enabled = true, QString condition = QString(), int ignoreCount = -1); + void clearBreakpoint(int breakpoint); + void setExceptionBreak(Exception type, bool enabled = false); + void v8flags(QString flags); + void version(); + //void profile(ProfileCommand command); //NOT SUPPORTED + void disconnect(); + void gc(); + void listBreakpoints(); + +protected: + //inherited from QQmlDebugClient + void stateChanged(State state); + void messageReceived(const QByteArray &data); + +signals: + void enabled(); + void connected(); + void interruptRequested(); + void breakAfterCompileRequested(); + void result(); + void stopped(); + +private: + void sendMessage(const QByteArray &); + void flushSendBuffer(); + QByteArray packMessage(const QByteArray &type, const QByteArray &message = QByteArray()); + +private: + QJSEngine jsEngine; + int seq; + + QList<QByteArray> sendBuffer; +public: + QJSValue parser; + QJSValue stringify; + QByteArray response; + +}; + +void QJSDebugClient::connect() +{ + sendMessage(packMessage(CONNECT)); +} + +void QJSDebugClient::interrupt() +{ + sendMessage(packMessage(INTERRUPT)); +} + +void QJSDebugClient::breakAfterCompile(bool enabled) +{ + QByteArray request; + QDataStream rs(&request, QIODevice::WriteOnly); + rs << enabled; + sendMessage(packMessage(BREAKAFTERCOMPILE, request)); +} + +void QJSDebugClient::continueDebugging(StepAction action, int count) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "continue", + // "arguments" : { "stepaction" : <"in", "next" or "out">, + // "stepcount" : <number of steps (default 1)> + // } + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(CONTINEDEBUGGING))); + + if (action != Continue) { + QJSValue args = parser.call(QJSValueList() << obj); + switch (action) { + case In: args.setProperty(QLatin1String(STEPACTION),QJSValue(QLatin1String(IN))); + break; + case Out: args.setProperty(QLatin1String(STEPACTION),QJSValue(QLatin1String(OUT))); + break; + case Next: args.setProperty(QLatin1String(STEPACTION),QJSValue(QLatin1String(NEXT))); + break; + default:break; + } + if (!args.isUndefined()) { + if (count != 1) + args.setProperty(QLatin1String(STEPCOUNT),QJSValue(count)); + jsonVal.setProperty(QLatin1String(ARGUMENTS),args); + } + } + QJSValue json = stringify.call(QJSValueList() << jsonVal); + sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); +} + +void QJSDebugClient::evaluate(QString expr, bool global, bool disableBreak, int frame, const QVariantMap &/*addContext*/) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "evaluate", + // "arguments" : { "expression" : <expression to evaluate>, + // "frame" : <number>, + // "global" : <boolean>, + // "disable_break" : <boolean>, + // "additional_context" : [ + // { "name" : <name1>, "handle" : <handle1> }, + // { "name" : <name2>, "handle" : <handle2> }, + // ... + // ] + // } + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(EVALUATE))); + + QJSValue args = parser.call(QJSValueList() << obj); + args.setProperty(QLatin1String(EXPRESSION),QJSValue(expr)); + + if (frame != -1) + args.setProperty(QLatin1String(FRAME),QJSValue(frame)); + + if (global) + args.setProperty(QLatin1String(GLOBAL),QJSValue(global)); + + if (disableBreak) + args.setProperty(QLatin1String(DISABLEBREAK),QJSValue(disableBreak)); + + if (!args.isUndefined()) { + jsonVal.setProperty(QLatin1String(ARGUMENTS),args); + } + + QJSValue json = stringify.call(QJSValueList() << jsonVal); + sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); +} + +void QJSDebugClient::lookup(QList<int> handles, bool includeSource) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "lookup", + // "arguments" : { "handles" : <array of handles>, + // "includeSource" : <boolean indicating whether the source will be included when script objects are returned>, + // } + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(LOOKUP))); + + QJSValue args = parser.call(QJSValueList() << obj); + + QString arr("[]"); + QJSValue array = parser.call(QJSValueList() << arr); + int index = 0; + foreach (int handle, handles) { + array.setProperty(index++,QJSValue(handle)); + } + args.setProperty(QLatin1String(HANDLES),array); + + if (includeSource) + args.setProperty(QLatin1String(INCLUDESOURCE),QJSValue(includeSource)); + + if (!args.isUndefined()) { + jsonVal.setProperty(QLatin1String(ARGUMENTS),args); + } + + QJSValue json = stringify.call(QJSValueList() << jsonVal); + sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); +} + +void QJSDebugClient::backtrace(int fromFrame, int toFrame, bool bottom) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "backtrace", + // "arguments" : { "fromFrame" : <number> + // "toFrame" : <number> + // "bottom" : <boolean, set to true if the bottom of the stack is requested> + // } + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(BACKTRACE))); + + QJSValue args = parser.call(QJSValueList() << obj); + + if (fromFrame != -1) + args.setProperty(QLatin1String(FROMFRAME),QJSValue(fromFrame)); + + if (toFrame != -1) + args.setProperty(QLatin1String(TOFRAME),QJSValue(toFrame)); + + if (bottom) + args.setProperty(QLatin1String(BOTTOM),QJSValue(bottom)); + + if (!args.isUndefined()) { + jsonVal.setProperty(QLatin1String(ARGUMENTS),args); + } + + QJSValue json = stringify.call(QJSValueList() << jsonVal); + sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); +} + +void QJSDebugClient::frame(int number) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "frame", + // "arguments" : { "number" : <frame number> + // } + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(FRAME))); + + if (number != -1) { + QJSValue args = parser.call(QJSValueList() << obj); + args.setProperty(QLatin1String(NUMBER),QJSValue(number)); + + if (!args.isUndefined()) { + jsonVal.setProperty(QLatin1String(ARGUMENTS),args); + } + } + + QJSValue json = stringify.call(QJSValueList() << jsonVal); + sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); +} + +void QJSDebugClient::scope(int number, int frameNumber) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "scope", + // "arguments" : { "number" : <scope number> + // "frameNumber" : <frame number, optional uses selected frame if missing> + // } + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(SCOPE))); + + if (number != -1) { + QJSValue args = parser.call(QJSValueList() << obj); + args.setProperty(QLatin1String(NUMBER),QJSValue(number)); + + if (frameNumber != -1) + args.setProperty(QLatin1String(FRAMENUMBER),QJSValue(frameNumber)); + + if (!args.isUndefined()) { + jsonVal.setProperty(QLatin1String(ARGUMENTS),args); + } + } + + QJSValue json = stringify.call(QJSValueList() << jsonVal); + sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); +} + +void QJSDebugClient::scopes(int frameNumber) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "scopes", + // "arguments" : { "frameNumber" : <frame number, optional uses selected frame if missing> + // } + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(SCOPES))); + + if (frameNumber != -1) { + QJSValue args = parser.call(QJSValueList() << obj); + args.setProperty(QLatin1String(FRAMENUMBER),QJSValue(frameNumber)); + + if (!args.isUndefined()) { + jsonVal.setProperty(QLatin1String(ARGUMENTS),args); + } + } + + QJSValue json = stringify.call(QJSValueList() << jsonVal); + sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); +} + +void QJSDebugClient::scripts(int types, QList<int> ids, bool includeSource, QVariant /*filter*/) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "scripts", + // "arguments" : { "types" : <types of scripts to retrieve + // set bit 0 for native scripts + // set bit 1 for extension scripts + // set bit 2 for normal scripts + // (default is 4 for normal scripts)> + // "ids" : <array of id's of scripts to return. If this is not specified all scripts are requrned> + // "includeSource" : <boolean indicating whether the source code should be included for the scripts returned> + // "filter" : <string or number: filter string or script id. + // If a number is specified, then only the script with the same number as its script id will be retrieved. + // If a string is specified, then only scripts whose names contain the filter string will be retrieved.> + // } + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(SCRIPTS))); + + QJSValue args = parser.call(QJSValueList() << obj); + args.setProperty(QLatin1String(TYPES),QJSValue(types)); + + if (ids.count()) { + QString arr("[]"); + QJSValue array = parser.call(QJSValueList() << arr); + int index = 0; + foreach (int id, ids) { + array.setProperty(index++,QJSValue(id)); + } + args.setProperty(QLatin1String(IDS),array); + } + + if (includeSource) + args.setProperty(QLatin1String(INCLUDESOURCE),QJSValue(includeSource)); + + if (!args.isUndefined()) { + jsonVal.setProperty(QLatin1String(ARGUMENTS),args); + } + + QJSValue json = stringify.call(QJSValueList() << jsonVal); + sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); +} + +void QJSDebugClient::source(int frame, int fromLine, int toLine) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "source", + // "arguments" : { "frame" : <frame number (default selected frame)> + // "fromLine" : <from line within the source default is line 0> + // "toLine" : <to line within the source this line is not included in + // the result default is the number of lines in the script> + // } + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(SOURCE))); + + QJSValue args = parser.call(QJSValueList() << obj); + + if (frame != -1) + args.setProperty(QLatin1String(FRAME),QJSValue(frame)); + + if (fromLine != -1) + args.setProperty(QLatin1String(FROMLINE),QJSValue(fromLine)); + + if (toLine != -1) + args.setProperty(QLatin1String(TOLINE),QJSValue(toLine)); + + if (!args.isUndefined()) { + jsonVal.setProperty(QLatin1String(ARGUMENTS),args); + } + + QJSValue json = stringify.call(QJSValueList() << jsonVal); + sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); +} + +void QJSDebugClient::setBreakpoint(QString type, QString target, int line, int column, bool enabled, QString condition, int ignoreCount) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "setbreakpoint", + // "arguments" : { "type" : <"function" or "script" or "scriptId" or "scriptRegExp"> + // "target" : <function expression or script identification> + // "line" : <line in script or function> + // "column" : <character position within the line> + // "enabled" : <initial enabled state. True or false, default is true> + // "condition" : <string with break point condition> + // "ignoreCount" : <number specifying the number of break point hits to ignore, default value is 0> + // } + // } + + if (type == QLatin1String(EVENT)) { + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + rs << target.toUtf8() << enabled; + sendMessage(packMessage(QByteArray("breakonsignal"), reply)); + + } else { + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(SETBREAKPOINT))); + + QJSValue args = parser.call(QJSValueList() << obj); + + args.setProperty(QLatin1String(TYPE),QJSValue(type)); + args.setProperty(QLatin1String(TARGET),QJSValue(target)); + + if (line != -1) + args.setProperty(QLatin1String(LINE),QJSValue(line)); + + if (column != -1) + args.setProperty(QLatin1String(COLUMN),QJSValue(column)); + + args.setProperty(QLatin1String(ENABLED),QJSValue(enabled)); + + if (!condition.isEmpty()) + args.setProperty(QLatin1String(CONDITION),QJSValue(condition)); + + if (ignoreCount != -1) + args.setProperty(QLatin1String(IGNORECOUNT),QJSValue(ignoreCount)); + + if (!args.isUndefined()) { + jsonVal.setProperty(QLatin1String(ARGUMENTS),args); + } + + QJSValue json = stringify.call(QJSValueList() << jsonVal); + sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); + } +} + +void QJSDebugClient::changeBreakpoint(int breakpoint, bool enabled, QString condition, int ignoreCount) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "changebreakpoint", + // "arguments" : { "breakpoint" : <number of the break point to clear> + // "enabled" : <initial enabled state. True or false, default is true> + // "condition" : <string with break point condition> + // "ignoreCount" : <number specifying the number of break point hits } + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(CHANGEBREAKPOINT))); + + QJSValue args = parser.call(QJSValueList() << obj); + + args.setProperty(QLatin1String(BREAKPOINT),QJSValue(breakpoint)); + + args.setProperty(QLatin1String(ENABLED),QJSValue(enabled)); + + if (!condition.isEmpty()) + args.setProperty(QLatin1String(CONDITION),QJSValue(condition)); + + if (ignoreCount != -1) + args.setProperty(QLatin1String(IGNORECOUNT),QJSValue(ignoreCount)); + + if (!args.isUndefined()) { + jsonVal.setProperty(QLatin1String(ARGUMENTS),args); + } + + QJSValue json = stringify.call(QJSValueList() << jsonVal); + sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); +} + +void QJSDebugClient::clearBreakpoint(int breakpoint) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "clearbreakpoint", + // "arguments" : { "breakpoint" : <number of the break point to clear> + // } + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(CLEARBREAKPOINT))); + + QJSValue args = parser.call(QJSValueList() << obj); + + args.setProperty(QLatin1String(BREAKPOINT),QJSValue(breakpoint)); + + if (!args.isUndefined()) { + jsonVal.setProperty(QLatin1String(ARGUMENTS),args); + } + + QJSValue json = stringify.call(QJSValueList() << jsonVal); + sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); +} + +void QJSDebugClient::setExceptionBreak(Exception type, bool enabled) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "setexceptionbreak", + // "arguments" : { "type" : <string: "all", or "uncaught">, + // "enabled" : <optional bool: enables the break type if true> + // } + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(SETEXCEPTIONBREAK))); + + QJSValue args = parser.call(QJSValueList() << obj); + + if (type == All) + args.setProperty(QLatin1String(TYPE),QJSValue(QLatin1String(ALL))); + else if (type == Uncaught) + args.setProperty(QLatin1String(TYPE),QJSValue(QLatin1String(UNCAUGHT))); + + if (enabled) + args.setProperty(QLatin1String(ENABLED),QJSValue(enabled)); + + if (!args.isUndefined()) { + jsonVal.setProperty(QLatin1String(ARGUMENTS),args); + } + + QJSValue json = stringify.call(QJSValueList() << jsonVal); + sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); +} + +void QJSDebugClient::v8flags(QString flags) +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "v8flags", + // "arguments" : { "flags" : <string: a sequence of v8 flags just like those used on the command line> + // } + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(V8FLAGS))); + + QJSValue args = parser.call(QJSValueList() << obj); + + args.setProperty(QLatin1String(FLAGS),QJSValue(flags)); + + if (!args.isUndefined()) { + jsonVal.setProperty(QLatin1String(ARGUMENTS),args); + } + + QJSValue json = stringify.call(QJSValueList() << jsonVal); + sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); +} + +void QJSDebugClient::version() +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "version", + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(VERSION))); + + QJSValue json = stringify.call(QJSValueList() << jsonVal); + sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); +} + +//void QJSDebugClient::profile(ProfileCommand command) +//{ +//// { "seq" : <number>, +//// "type" : "request", +//// "command" : "profile", +//// "arguments" : { "command" : "resume" or "pause" } +//// } +// VARIANTMAPINIT; +// jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(PROFILE))); + +// QJSValue args = parser.call(QJSValueList() << obj); + +// if (command == Resume) +// args.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(RESUME))); +// else +// args.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(PAUSE))); + +// args.setProperty(QLatin1String("modules"),QJSValue(1)); +// if (!args.isUndefined()) { +// jsonVal.setProperty(QLatin1String(ARGUMENTS),args); +// } + +// QJSValue json = stringify.call(QJSValueList() << jsonVal); +// sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); +//} + +void QJSDebugClient::disconnect() +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "disconnect", + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(DISCONNECT))); + + QJSValue json = stringify.call(QJSValueList() << jsonVal); + sendMessage(packMessage(DISCONNECT, json.toString().toUtf8())); +} + +void QJSDebugClient::gc() +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "gc", + // "arguments" : { "type" : <string: "all">, + // } + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(GARBAGECOLLECTOR))); + + QJSValue args = parser.call(QJSValueList() << obj); + + args.setProperty(QLatin1String(TYPE),QJSValue(QLatin1String(ALL))); + + if (!args.isUndefined()) { + jsonVal.setProperty(QLatin1String(ARGUMENTS),args); + } + + QJSValue json = stringify.call(QJSValueList() << jsonVal); + sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); +} + +void QJSDebugClient::listBreakpoints() +{ + // { "seq" : <number>, + // "type" : "request", + // "command" : "listbreakpoints", + // } + VARIANTMAPINIT; + jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(LISTBREAKPOINTS))); + + QJSValue json = stringify.call(QJSValueList() << jsonVal); + sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); +} + +void QJSDebugClient::stateChanged(State state) +{ + if (state == Enabled) { + flushSendBuffer(); + emit enabled(); + } +} + +void QJSDebugClient::messageReceived(const QByteArray &data) +{ + QDataStream ds(data); + QByteArray command; + ds >> command; + + if (command == "V8DEBUG") { + QByteArray type; + ds >> type >> response; + + if (type == CONNECT) { + emit connected(); + + } else if (type == INTERRUPT) { + emit interruptRequested(); + + } else if (type == V8MESSAGE) { + QString jsonString(response); + QVariantMap value = parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + QString type = value.value("type").toString(); + + if (type == "response") { + + if (!value.value("success").toBool()) { +// qDebug() << "Error: The test case will fail since no signal is emitted"; + return; + } + + QString debugCommand(value.value("command").toString()); + if (debugCommand == "backtrace" || + debugCommand == "lookup" || + debugCommand == "setbreakpoint" || + debugCommand == "evaluate" || + debugCommand == "listbreakpoints" || + debugCommand == "version" || + debugCommand == "v8flags" || + debugCommand == "disconnect" || + debugCommand == "gc" || + debugCommand == "changebreakpoint" || + debugCommand == "clearbreakpoint" || + debugCommand == "frame" || + debugCommand == "scope" || + debugCommand == "scopes" || + debugCommand == "scripts" || + debugCommand == "source" || + debugCommand == "setexceptionbreak" /*|| + debugCommand == "profile"*/) { + emit result(); + + } else { + // DO NOTHING + } + + } else if (type == QLatin1String(EVENT)) { + QString event(value.value(QLatin1String(EVENT)).toString()); + + if (event == "break" || + event == "exception") + emit stopped(); + } + + } else if (type == BREAKAFTERCOMPILE) { + emit breakAfterCompileRequested(); + + } + } +} + +void QJSDebugClient::sendMessage(const QByteArray &msg) +{ + if (state() == Enabled) { + QQmlDebugClient::sendMessage(msg); + } else { + sendBuffer.append(msg); + } +} + +void QJSDebugClient::flushSendBuffer() +{ + foreach (const QByteArray &msg, sendBuffer) + QQmlDebugClient::sendMessage(msg); + sendBuffer.clear(); +} + +QByteArray QJSDebugClient::packMessage(const QByteArray &type, const QByteArray &message) +{ + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + QByteArray cmd = "V8DEBUG"; + rs << cmd << type << message; + return reply; +} + +void tst_QQmlDebugJS::initTestCase() +{ + QQmlDataTest::initTestCase(); + t.start(); + process = 0; + client = 0; + connection = 0; +} + +void tst_QQmlDebugJS::cleanupTestCase() +{ + if (process) { + process->stop(); + delete process; + } + + if (client) + delete client; + + if (connection) + delete connection; + +// qDebug() << "Time Elapsed:" << t.elapsed(); +} + +bool tst_QQmlDebugJS::init(const QString &qmlFile, bool blockMode) +{ + connection = new QQmlDebugConnection(); + process = new QQmlDebugProcess(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene"); + client = new QJSDebugClient(connection); + + QStringList systemEnvironment = QProcess::systemEnvironment(); + systemEnvironment << "QML_DISABLE_OPTIMIZER=1"; + process->setEnvironment(systemEnvironment); + if (blockMode) + process->start(QStringList() << QLatin1String(BLOCKMODE) << testFile(qmlFile)); + else + process->start(QStringList() << QLatin1String(NORMALMODE) << testFile(qmlFile)); + + if (!process->waitForSessionStart()) { + return false; + } + + connection->connectToHost("127.0.0.1", 3771); + if (!connection->waitForConnected()) + return false; + + return QQmlDebugTest::waitForSignal(client, SIGNAL(enabled())); +} + +void tst_QQmlDebugJS::cleanup() +{ + if (process) { + process->stop(); + delete process; + } + + if (client) + delete client; + + if (connection) + delete connection; + + process = 0; + client = 0; + connection = 0; +} + +void tst_QQmlDebugJS::connect() +{ + //void connect() + + QVERIFY(init()); + client->connect(); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(connected()))); +} + +void tst_QQmlDebugJS::interrupt() +{ + //void connect() + + QVERIFY(init()); + client->connect(); + + client->interrupt(); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(interruptRequested()))); +} + +void tst_QQmlDebugJS::breakAfterCompile() +{ + //void breakAfterCompile(bool enabled) + + QVERIFY(init()); + client->breakAfterCompile(true); + client->connect(); + + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(breakAfterCompileRequested()))); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); +} + +void tst_QQmlDebugJS::getVersion() +{ + //void version() + + QVERIFY(init()); + client->connect(); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(connected()))); + + client->version(); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); +} + +void tst_QQmlDebugJS::getVersionWhenAttaching() +{ + //void version() + + QVERIFY(init(QLatin1String(TIMER_QMLFILE), false)); + client->connect(); + + client->version(); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); +} + +void tst_QQmlDebugJS::applyV8Flags() +{ + //void v8flags(QString flags) + + QVERIFY(init()); + client->connect(); + + client->v8flags(QString()); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); +} + +void tst_QQmlDebugJS::disconnect() +{ + //void disconnect() + + QVERIFY(init()); + client->connect(); + + client->disconnect(); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); +} + +void tst_QQmlDebugJS::gc() +{ + //void gc() + + QVERIFY(init()); + client->connect(); + + client->gc(); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); +} + +void tst_QQmlDebugJS::listBreakpoints() +{ + //void listBreakpoints() + + int sourceLine1 = 47; + int sourceLine2 = 48; + int sourceLine3 = 49; + + QVERIFY(init()); + client->connect(); + + client->setBreakpoint(QLatin1String(SCRIPTREGEXP), QLatin1String(TEST_QMLFILE), sourceLine1, -1, true); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); + client->setBreakpoint(QLatin1String(SCRIPTREGEXP), QLatin1String(TEST_QMLFILE), sourceLine2, -1, true); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); + client->setBreakpoint(QLatin1String(SCRIPTREGEXP), QLatin1String(TEST_QMLFILE), sourceLine3, -1, true); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); + client->listBreakpoints(); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); + + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QList<QVariant> breakpoints = value.value("body").toMap().value("breakpoints").toList(); + + QCOMPARE(breakpoints.count(), 3); +} + +void tst_QQmlDebugJS::setBreakpointInScriptOnCompleted() +{ + //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) + + int sourceLine = 47; + QVERIFY(init(ONCOMPLETED_QMLFILE)); + + client->setBreakpoint(QLatin1String(SCRIPTREGEXP), QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); + client->connect(); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine); + QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(ONCOMPLETED_QMLFILE)); +} + +void tst_QQmlDebugJS::setBreakpointInScriptOnComponentCreated() +{ + //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) + + int sourceLine = 47; + QVERIFY(init(CREATECOMPONENT_QMLFILE)); + + client->setBreakpoint(QLatin1String(SCRIPTREGEXP), QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); + client->connect(); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine); + QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(ONCOMPLETED_QMLFILE)); +} + +void tst_QQmlDebugJS::setBreakpointInScriptOnTimerCallback() +{ + int sourceLine = 48; + QVERIFY(init(TIMER_QMLFILE)); + + client->connect(); + + client->setBreakpoint(QLatin1String(SCRIPTREGEXP), QLatin1String(TIMER_QMLFILE), sourceLine, -1, true); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine); + QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(TIMER_QMLFILE)); +} + +void tst_QQmlDebugJS::setBreakpointInScriptInDifferentFile() +{ + //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) + + int sourceLine = 43; + QVERIFY(init(LOADJSFILE_QMLFILE)); + + client->connect(); + client->setBreakpoint(QLatin1String(SCRIPTREGEXP), QLatin1String(TEST_JSFILE), sourceLine, -1, true); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine); + QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(TEST_JSFILE)); +} + +void tst_QQmlDebugJS::setBreakpointInScriptOnComment() +{ + //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) + + int sourceLine = 47; + int actualLine = 49; + QVERIFY(init(BREAKPOINTRELOCATION_QMLFILE)); + + client->connect(); + client->setBreakpoint(QLatin1String(SCRIPTREGEXP), QLatin1String(BREAKPOINTRELOCATION_QMLFILE), sourceLine, -1, true); + QEXPECT_FAIL("", "Relocation of breakpoints is disabled right now", Abort); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()), 1)); + + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + + QCOMPARE(body.value("sourceLine").toInt(), actualLine); + QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(BREAKPOINTRELOCATION_QMLFILE)); +} + +void tst_QQmlDebugJS::setBreakpointInScriptOnEmptyLine() +{ + //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) + + int sourceLine = 48; + int actualLine = 49; + QVERIFY(init(BREAKPOINTRELOCATION_QMLFILE)); + + client->connect(); + client->setBreakpoint(QLatin1String(SCRIPTREGEXP), QLatin1String(BREAKPOINTRELOCATION_QMLFILE), sourceLine, -1, true); + QEXPECT_FAIL("", "Relocation of breakpoints is disabled right now", Abort); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()), 1)); + + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + + QCOMPARE(body.value("sourceLine").toInt(), actualLine); + QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(BREAKPOINTRELOCATION_QMLFILE)); +} + +void tst_QQmlDebugJS::setBreakpointInScriptWithCondition() +{ + //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) + + int out = 10; + int sourceLine = 50; + QVERIFY(init(CONDITION_QMLFILE)); + + client->connect(); + client->setBreakpoint(QLatin1String(SCRIPTREGEXP), QLatin1String(CONDITION_QMLFILE), sourceLine, 1, true, QLatin1String("a > 10")); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + //Get the frame index + QString jsonString = client->response; + QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + + int frameIndex = body.value("index").toInt(); + + //Verify the value of 'result' + client->evaluate(QLatin1String("a"),frameIndex); + + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); + + jsonString = client->response; + value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + body = value.value("body").toMap(); + + QVERIFY(body.value("value").toInt() > out); +} + +void tst_QQmlDebugJS::setBreakpointWhenAttaching() +{ + int sourceLine = 49; + QVERIFY(init(QLatin1String(TIMER_QMLFILE), false)); + + client->connect(); + client->setBreakpoint(QLatin1String(SCRIPTREGEXP), QLatin1String(TIMER_QMLFILE), sourceLine); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); +} + +//void tst_QQmlDebugJS::setBreakpointInFunction() +//{ +// //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) + +// int actualLine = 31; + +// client->connect(); +// client->setBreakpoint(QLatin1String(FUNCTION), QLatin1String("doSomethingElse"), -1, -1, true); + +// QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); + +// QString jsonString(client->response); +// QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + +// QVariantMap body = value.value("body").toMap(); + +// QCOMPARE(body.value("sourceLine").toInt(), actualLine); +// QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(QMLFILE)); +//} + +void tst_QQmlDebugJS::setBreakpointOnEvent() +{ + //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) + + QVERIFY(init(TIMER_QMLFILE)); + + client->connect(); + + client->setBreakpoint(QLatin1String(EVENT), QLatin1String("triggered"), -1, -1, true); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + + QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(TIMER_QMLFILE)); +} + + +void tst_QQmlDebugJS::changeBreakpoint() +{ + //void changeBreakpoint(int breakpoint, bool enabled = false, QString condition = QString(), int ignoreCount = -1) + + int sourceLine1 = 50; + int sourceLine2 = 51; + QVERIFY(init(CHANGEBREAKPOINT_QMLFILE)); + + client->connect(); + client->setBreakpoint(QLatin1String(SCRIPTREGEXP), QLatin1String(CHANGEBREAKPOINT_QMLFILE), sourceLine2, -1, true); + client->setBreakpoint(QLatin1String(SCRIPTREGEXP), QLatin1String(CHANGEBREAKPOINT_QMLFILE), sourceLine1, -1, true); + + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + //Will hit 1st brakpoint, change this breakpoint enable = false + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + QList<QVariant> breakpointsHit = body.value("breakpoints").toList(); + + int breakpoint = breakpointsHit.at(0).toInt(); + client->changeBreakpoint(breakpoint,false); + + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); + + //Continue with debugging + client->continueDebugging(QJSDebugClient::Continue); + //Hit 2nd breakpoint + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + //Continue with debugging + client->continueDebugging(QJSDebugClient::Continue); + //Should stop at 2nd breakpoint + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + jsonString = client->response; + value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + body = value.value("body").toMap(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine2); +} + +void tst_QQmlDebugJS::changeBreakpointOnCondition() +{ + //void changeBreakpoint(int breakpoint, bool enabled = false, QString condition = QString(), int ignoreCount = -1) + + int sourceLine1 = 50; + int sourceLine2 = 51; + + QVERIFY(init(CHANGEBREAKPOINT_QMLFILE)); + + client->connect(); + client->setBreakpoint(QLatin1String(SCRIPTREGEXP), QLatin1String(CHANGEBREAKPOINT_QMLFILE), sourceLine1, -1, true); + client->setBreakpoint(QLatin1String(SCRIPTREGEXP), QLatin1String(CHANGEBREAKPOINT_QMLFILE), sourceLine2, -1, true); + + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + //Will hit 1st brakpoint, change this breakpoint enable = false + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + QList<QVariant> breakpointsHit = body.value("breakpoints").toList(); + + int breakpoint = breakpointsHit.at(0).toInt(); + client->changeBreakpoint(breakpoint, false, QLatin1String("d == 0")); + + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); + + //Continue with debugging + client->continueDebugging(QJSDebugClient::Continue); + //Hit 2nd breakpoint + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + //Continue with debugging + client->continueDebugging(QJSDebugClient::Continue); + //Should stop at 2nd breakpoint + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + jsonString = client->response; + value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + body = value.value("body").toMap(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine2); + +} + +void tst_QQmlDebugJS::clearBreakpoint() +{ + //void clearBreakpoint(int breakpoint); + + int sourceLine1 = 50; + int sourceLine2 = 51; + QVERIFY(init(CHANGEBREAKPOINT_QMLFILE)); + + client->connect(); + client->setBreakpoint(QLatin1String(SCRIPTREGEXP), QLatin1String(CHANGEBREAKPOINT_QMLFILE), sourceLine1, -1, true); + client->setBreakpoint(QLatin1String(SCRIPTREGEXP), QLatin1String(CHANGEBREAKPOINT_QMLFILE), sourceLine2, -1, true); + + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + //Will hit 1st brakpoint, change this breakpoint enable = false + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + QList<QVariant> breakpointsHit = body.value("breakpoints").toList(); + + int breakpoint = breakpointsHit.at(0).toInt(); + client->clearBreakpoint(breakpoint); + + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); + + //Continue with debugging + client->continueDebugging(QJSDebugClient::Continue); + //Hit 2nd breakpoint + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + //Continue with debugging + client->continueDebugging(QJSDebugClient::Continue); + //Should stop at 2nd breakpoint + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + jsonString = client->response; + value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + body = value.value("body").toMap(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine2); +} + +void tst_QQmlDebugJS::setExceptionBreak() +{ + //void setExceptionBreak(QString type, bool enabled = false); + + QVERIFY(init(EXCEPTION_QMLFILE)); + client->setExceptionBreak(QJSDebugClient::All,true); + client->connect(); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); +} + +void tst_QQmlDebugJS::stepNext() +{ + //void continueDebugging(StepAction stepAction, int stepCount = 1); + + int sourceLine = 50; + QVERIFY(init(STEPACTION_QMLFILE)); + + client->setBreakpoint(QLatin1String(SCRIPTREGEXP), QLatin1String(STEPACTION_QMLFILE), sourceLine, -1, true); + client->connect(); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + client->continueDebugging(QJSDebugClient::Next); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine + 1); + QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(STEPACTION_QMLFILE)); +} + +void tst_QQmlDebugJS::stepNextWithCount() +{ + //void continueDebugging(StepAction stepAction, int stepCount = 1); + + int sourceLine = 50; + QVERIFY(init(STEPACTION_QMLFILE)); + + client->connect(); + client->setBreakpoint(QLatin1String(SCRIPTREGEXP), QLatin1String(STEPACTION_QMLFILE), sourceLine, -1, true); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + client->continueDebugging(QJSDebugClient::Next, 2); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine + 2); + QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(STEPACTION_QMLFILE)); +} + +void tst_QQmlDebugJS::stepIn() +{ + //void continueDebugging(StepAction stepAction, int stepCount = 1); + + int sourceLine = 54; + int actualLine = 50; + QVERIFY(init(STEPACTION_QMLFILE)); + + client->connect(); + client->setBreakpoint(QLatin1String(SCRIPTREGEXP), QLatin1String(STEPACTION_QMLFILE), sourceLine, 1, true); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + client->continueDebugging(QJSDebugClient::In); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + + QCOMPARE(body.value("sourceLine").toInt(), actualLine); + QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(STEPACTION_QMLFILE)); +} + +void tst_QQmlDebugJS::stepOut() +{ + //void continueDebugging(StepAction stepAction, int stepCount = 1); + + int sourceLine = 50; + int actualLine = 54; + QVERIFY(init(STEPACTION_QMLFILE)); + + client->connect(); + client->setBreakpoint(QLatin1String(SCRIPTREGEXP), QLatin1String(STEPACTION_QMLFILE), sourceLine, -1, true); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + client->continueDebugging(QJSDebugClient::Out); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + + QCOMPARE(body.value("sourceLine").toInt(), actualLine); + QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(STEPACTION_QMLFILE)); +} + +void tst_QQmlDebugJS::continueDebugging() +{ + //void continueDebugging(StepAction stepAction, int stepCount = 1); + + int sourceLine1 = 54; + int sourceLine2 = 51; + QVERIFY(init(STEPACTION_QMLFILE)); + + client->connect(); + client->setBreakpoint(QLatin1String(SCRIPTREGEXP), QLatin1String(STEPACTION_QMLFILE), sourceLine1, -1, true); + client->setBreakpoint(QLatin1String(SCRIPTREGEXP), QLatin1String(STEPACTION_QMLFILE), sourceLine2, -1, true); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + client->continueDebugging(QJSDebugClient::Continue); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine2); + QCOMPARE(QFileInfo(body.value("script").toMap().value("name").toString()).fileName(), QLatin1String(STEPACTION_QMLFILE)); +} + +void tst_QQmlDebugJS::backtrace() +{ + //void backtrace(int fromFrame = -1, int toFrame = -1, bool bottom = false); + + int sourceLine = 47; + QVERIFY(init(ONCOMPLETED_QMLFILE)); + + client->setBreakpoint(QLatin1String(SCRIPTREGEXP), QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); + client->connect(); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + client->backtrace(); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); +} + +void tst_QQmlDebugJS::getFrameDetails() +{ + //void frame(int number = -1); + + int sourceLine = 47; + QVERIFY(init(ONCOMPLETED_QMLFILE)); + + client->setBreakpoint(QLatin1String(SCRIPTREGEXP), QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); + client->connect(); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + client->frame(); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); +} + +void tst_QQmlDebugJS::getScopeDetails() +{ + //void scope(int number = -1, int frameNumber = -1); + + int sourceLine = 47; + QVERIFY(init(ONCOMPLETED_QMLFILE)); + + client->setBreakpoint(QLatin1String(SCRIPTREGEXP), QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); + client->connect(); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + client->scope(); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); +} + +void tst_QQmlDebugJS::evaluateInGlobalScope() +{ + //void evaluate(QString expr, bool global = false, bool disableBreak = false, int frame = -1, const QVariantMap &addContext = QVariantMap()); + + QVERIFY(init()); + + client->connect(); + client->evaluate(QLatin1String("print('Hello World')"), true); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); + + //Verify the value of 'print' + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + + QCOMPARE(body.value("text").toString(),QLatin1String("undefined")); +} + +void tst_QQmlDebugJS::evaluateInLocalScope() +{ + //void evaluate(QString expr, bool global = false, bool disableBreak = false, int frame = -1, const QVariantMap &addContext = QVariantMap()); + + int sourceLine = 47; + QVERIFY(init(ONCOMPLETED_QMLFILE)); + + client->setBreakpoint(QLatin1String(SCRIPTREGEXP), QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); + client->connect(); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + client->frame(); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); + + //Get the frame index + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + QVariantMap body = value.value("body").toMap(); + + int frameIndex = body.value("index").toInt(); + + client->evaluate(QLatin1String("root.a"), frameIndex); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); + + //Verify the value of 'timer.interval' + jsonString = client->response; + value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); + + body = value.value("body").toMap(); + + QCOMPARE(body.value("value").toInt(),10); +} + +void tst_QQmlDebugJS::getScopes() +{ + //void scopes(int frameNumber = -1); + + int sourceLine = 47; + QVERIFY(init(ONCOMPLETED_QMLFILE)); + + client->setBreakpoint(QLatin1String(SCRIPTREGEXP), QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); + client->connect(); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + client->scopes(); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); +} + +void tst_QQmlDebugJS::getScripts() +{ + //void scripts(int types = -1, QList<int> ids = QList<int>(), bool includeSource = false, QVariant filter = QVariant()); + +#ifdef Q_OS_MAC + QSKIP("QTBUG-23475 - Unreliable test on Mac OS X"); +#endif + + QVERIFY(init()); + + client->connect(); + + client->scripts(); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); + QString jsonString(client->response); + QVariantMap value = client->parser.call(QJSValueList() + << QJSValue(jsonString)).toVariant().toMap(); + + QList<QVariant> scripts = value.value("body").toList(); + + QCOMPARE(scripts.count(), 2); +} + +void tst_QQmlDebugJS::getSource() +{ + //void source(int frame = -1, int fromLine = -1, int toLine = -1); + + int sourceLine = 47; + QVERIFY(init(ONCOMPLETED_QMLFILE)); + + client->setBreakpoint(QLatin1String(SCRIPTREGEXP), QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); + client->connect(); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); + + client->source(); + QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); +} + +QTEST_MAIN(tst_QQmlDebugJS) + +#include "tst_qqmldebugjs.moc" + diff --git a/tests/auto/qml/debugger/qqmldebugservice/qqmldebugservice.pro b/tests/auto/qml/debugger/qqmldebugservice/qqmldebugservice.pro new file mode 100644 index 0000000000..ecb3b2ca2c --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugservice/qqmldebugservice.pro @@ -0,0 +1,11 @@ +CONFIG += testcase +TARGET = tst_qqmldebugservice +macx:CONFIG -= app_bundle + +HEADERS += ../shared/debugutil_p.h +SOURCES += tst_qqmldebugservice.cpp \ + ../shared/debugutil.cpp + +CONFIG += parallel_test declarative_debug + +QT += qml-private testlib diff --git a/tests/auto/qml/debugger/qqmldebugservice/tst_qqmldebugservice.cpp b/tests/auto/qml/debugger/qqmldebugservice/tst_qqmldebugservice.cpp new file mode 100644 index 0000000000..5af38c2af0 --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugservice/tst_qqmldebugservice.cpp @@ -0,0 +1,216 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <qtest.h> +#include <QSignalSpy> +#include <QTimer> +#include <QHostAddress> +#include <QDebug> +#include <QThread> + +#include <QtQml/qqmlengine.h> + +#include <private/qqmldebugclient_p.h> +#include <private/qqmldebugservice_p.h> + +#include "../../../shared/util.h" +#include "../shared/debugutil_p.h" + +#define PORT 13769 +#define STR_PORT "13769" + +class tst_QQmlDebugService : public QObject +{ + Q_OBJECT +private: + QQmlDebugConnection *m_conn; + +private slots: + void initTestCase(); + + void name(); + void version(); + void state(); + void sendMessage(); + void idForObject(); + void objectForId(); + void objectToString(); +}; + +void tst_QQmlDebugService::initTestCase() +{ + const QString waitingMsg = QString("QQmlDebugServer: Waiting for connection on port %1...").arg(PORT); + QTest::ignoreMessage(QtWarningMsg, waitingMsg.toAscii().constData()); + new QQmlEngine(this); + + m_conn = new QQmlDebugConnection(this); + + + QTest::ignoreMessage(QtWarningMsg, "QQmlDebugServer: Connection established"); + for (int i = 0; i < 50; ++i) { + // try for 5 seconds ... + m_conn->connectToHost("127.0.0.1", PORT); + if (m_conn->waitForConnected()) + break; + QTest::qSleep(100); + } + QVERIFY(m_conn->isConnected()); + + QTRY_VERIFY(QQmlDebugService::hasDebuggingClient()); +} + +void tst_QQmlDebugService::name() +{ + QString name = "tst_QQmlDebugService::name()"; + + QQmlDebugService service(name, 1); + QCOMPARE(service.name(), name); +} + +void tst_QQmlDebugService::version() +{ + QString name = "tst_QQmlDebugService::name()"; + + QQmlDebugService service(name, 2); + QCOMPARE(service.version(), 2.0f); +} + +void tst_QQmlDebugService::state() +{ + QQmlDebugTestService service("tst_QQmlDebugService::state()"); + QCOMPARE(service.state(), QQmlDebugService::Unavailable); + + { + QQmlDebugTestClient client("tst_QQmlDebugService::state()", m_conn); + QTRY_COMPARE(client.state(), QQmlDebugClient::Enabled); + QTRY_COMPARE(service.state(), QQmlDebugService::Enabled); + } + + + QTRY_COMPARE(service.state(), QQmlDebugService::Unavailable); + + QTest::ignoreMessage(QtWarningMsg, "QQmlDebugService: Conflicting plugin name \"tst_QQmlDebugService::state()\" "); + QQmlDebugTestService duplicate("tst_QQmlDebugService::state()"); + QCOMPARE(duplicate.state(), QQmlDebugService::NotConnected); +} + +void tst_QQmlDebugService::sendMessage() +{ + QQmlDebugTestService service("tst_QQmlDebugService::sendMessage()"); + QQmlDebugTestClient client("tst_QQmlDebugService::sendMessage()", m_conn); + + QByteArray msg = "hello!"; + + QTRY_COMPARE(client.state(), QQmlDebugClient::Enabled); + QTRY_COMPARE(service.state(), QQmlDebugService::Enabled); + + client.sendMessage(msg); + QByteArray resp = client.waitForResponse(); + QCOMPARE(resp, msg); + + QTest::ignoreMessage(QtWarningMsg, "QQmlDebugService: Conflicting plugin name \"tst_QQmlDebugService::sendMessage()\" "); + QQmlDebugTestService duplicate("tst_QQmlDebugService::sendMessage()"); + duplicate.sendMessage("msg"); +} + +void tst_QQmlDebugService::idForObject() +{ + QCOMPARE(QQmlDebugService::idForObject(0), -1); + + QObject *objA = new QObject; + + int idA = QQmlDebugService::idForObject(objA); + QVERIFY(idA >= 0); + QCOMPARE(QQmlDebugService::objectForId(idA), objA); + + int idAA = QQmlDebugService::idForObject(objA); + QCOMPARE(idAA, idA); + + QObject *objB = new QObject; + int idB = QQmlDebugService::idForObject(objB); + QVERIFY(idB != idA); + QCOMPARE(QQmlDebugService::objectForId(idB), objB); + + delete objA; + delete objB; +} + +void tst_QQmlDebugService::objectForId() +{ + QCOMPARE(QQmlDebugService::objectForId(-1), static_cast<QObject*>(0)); + QCOMPARE(QQmlDebugService::objectForId(1), static_cast<QObject*>(0)); + + QObject *obj = new QObject; + int id = QQmlDebugService::idForObject(obj); + QCOMPARE(QQmlDebugService::objectForId(id), obj); + + delete obj; + QCOMPARE(QQmlDebugService::objectForId(id), static_cast<QObject*>(0)); +} + +void tst_QQmlDebugService::objectToString() +{ + QCOMPARE(QQmlDebugService::objectToString(0), QString("NULL")); + + QObject *obj = new QObject; + QCOMPARE(QQmlDebugService::objectToString(obj), QString("QObject: <unnamed>")); + + obj->setObjectName("Hello"); + QCOMPARE(QQmlDebugService::objectToString(obj), QString("QObject: Hello")); + delete obj; +} + + +int main(int argc, char *argv[]) +{ + int _argc = argc + 1; + char **_argv = new char*[_argc]; + for (int i = 0; i < argc; ++i) + _argv[i] = argv[i]; + char arg[] = "-qmljsdebugger=port:" STR_PORT; + _argv[_argc - 1] = arg; + + QGuiApplication app(_argc, _argv); + tst_QQmlDebugService tc; + return QTest::qExec(&tc, _argc, _argv); + delete _argv; +} + +#include "tst_qqmldebugservice.moc" diff --git a/tests/auto/qml/debugger/qqmlenginedebug/qqmlenginedebug.pro b/tests/auto/qml/debugger/qqmlenginedebug/qqmlenginedebug.pro new file mode 100644 index 0000000000..529c4667cf --- /dev/null +++ b/tests/auto/qml/debugger/qqmlenginedebug/qqmlenginedebug.pro @@ -0,0 +1,11 @@ +CONFIG += testcase +TARGET = tst_qqmlenginedebug +macx:CONFIG -= app_bundle + +HEADERS += ../shared/debugutil_p.h +SOURCES += tst_qqmlenginedebug.cpp \ + ../shared/debugutil.cpp + +CONFIG += parallel_test declarative_debug + +QT += core-private qml-private quick-private v8-private testlib diff --git a/tests/auto/qml/debugger/qqmlenginedebug/tst_qqmlenginedebug.cpp b/tests/auto/qml/debugger/qqmlenginedebug/tst_qqmlenginedebug.cpp new file mode 100644 index 0000000000..21ed2f40ff --- /dev/null +++ b/tests/auto/qml/debugger/qqmlenginedebug/tst_qqmlenginedebug.cpp @@ -0,0 +1,1235 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <qtest.h> +#include <QSignalSpy> +#include <QTimer> +#include <QHostAddress> +#include <QDebug> +#include <QThread> + +#include <QtQml/qqmlengine.h> +#include <QtQml/qqmlcontext.h> +#include <QtQml/qqmlcomponent.h> +#include <QtQml/qqmlexpression.h> +#include <QtQml/qqmlproperty.h> +#include <QtQuick/qquickitem.h> + +#include <private/qqmlbinding_p.h> +#include <private/qqmlboundsignal_p.h> +#include <private/qqmlenginedebug_p.h> +#include <private/qqmldebugservice_p.h> +#include <private/qqmlmetatype_p.h> +#include <private/qqmlproperty_p.h> + +#include "../shared/debugutil_p.h" + +Q_DECLARE_METATYPE(QQmlDebugWatch::State) + +class tst_QQmlEngineDebug : public QObject +{ + Q_OBJECT + +private: + QQmlDebugObjectReference findRootObject(int context = 0, bool recursive = false); + QQmlDebugPropertyReference findProperty(const QList<QQmlDebugPropertyReference> &props, const QString &name) const; + void waitForQuery(QQmlDebugQuery *query); + + void recursiveObjectTest(QObject *o, const QQmlDebugObjectReference &oref, bool recursive) const; + + void recursiveCompareObjects(const QQmlDebugObjectReference &a, const QQmlDebugObjectReference &b) const; + void recursiveCompareContexts(const QQmlDebugContextReference &a, const QQmlDebugContextReference &b) const; + void compareProperties(const QQmlDebugPropertyReference &a, const QQmlDebugPropertyReference &b) const; + + QQmlDebugConnection *m_conn; + QQmlEngineDebug *m_dbg; + QQmlEngine *m_engine; + QQuickItem *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_QQmlDebugFileReference(); + void tst_QQmlDebugEngineReference(); + void tst_QQmlDebugObjectReference(); + void tst_QQmlDebugContextReference(); + void tst_QQmlDebugPropertyReference(); + + void setBindingForObject(); + void setMethodBody(); + void queryObjectTree(); + void setBindingInStates(); +}; + +class NonScriptProperty : public QObject { + Q_OBJECT + Q_PROPERTY(int nonScriptProp READ nonScriptProp WRITE setNonScriptProp NOTIFY nonScriptPropChanged SCRIPTABLE false) +public: + int nonScriptProp() const { return 0; } + void setNonScriptProp(int) {} +signals: + void nonScriptPropChanged(); +}; +QML_DECLARE_TYPE(NonScriptProperty) + + +QQmlDebugObjectReference tst_QQmlEngineDebug::findRootObject(int context, bool recursive) +{ + QQmlDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this); + waitForQuery(q_engines); + + if (q_engines->engines().count() == 0) + return QQmlDebugObjectReference(); + QQmlDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this); + waitForQuery(q_context); + + if (q_context->rootContext().objects().count() == 0) + return QQmlDebugObjectReference(); + QQmlDebugObjectQuery *q_obj = recursive ? + m_dbg->queryObjectRecursive(q_context->rootContext().objects()[context], this) : + m_dbg->queryObject(q_context->rootContext().objects()[context], this); + waitForQuery(q_obj); + + QQmlDebugObjectReference result = q_obj->object(); + + delete q_engines; + delete q_context; + delete q_obj; + + return result; +} + +QQmlDebugPropertyReference tst_QQmlEngineDebug::findProperty(const QList<QQmlDebugPropertyReference> &props, const QString &name) const +{ + foreach(const QQmlDebugPropertyReference &p, props) { + if (p.name() == name) + return p; + } + return QQmlDebugPropertyReference(); +} + +void tst_QQmlEngineDebug::waitForQuery(QQmlDebugQuery *query) +{ + QVERIFY(query); + QCOMPARE(query->parent(), qobject_cast<QObject*>(this)); + QVERIFY(query->state() == QQmlDebugQuery::Waiting); + if (!QQmlDebugTest::waitForSignal(query, SIGNAL(stateChanged(QQmlDebugQuery::State)))) + QFAIL("query timed out"); +} + +void tst_QQmlEngineDebug::recursiveObjectTest(QObject *o, const QQmlDebugObjectReference &oref, bool recursive) const +{ + const QMetaObject *meta = o->metaObject(); + + QQmlType *type = QQmlMetaType::qmlType(meta); + QString className = type ? QString(type->qmlTypeName()) : QString(meta->className()); + className = className.mid(className.lastIndexOf(QLatin1Char('/'))+1); + + QCOMPARE(oref.debugId(), QQmlDebugService::idForObject(o)); + QCOMPARE(oref.name(), o->objectName()); + QCOMPARE(oref.className(), className); + QCOMPARE(oref.contextDebugId(), QQmlDebugService::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 = QQmlDebugService::idForObject(child); + QVERIFY(debugId >= 0); + + QQmlDebugObjectReference cref; + foreach (const QQmlDebugObjectReference &ref, oref.children()) { + if (ref.debugId() == debugId) { + cref = ref; + break; + } + } + QVERIFY(cref.debugId() >= 0); + + if (recursive) + recursiveObjectTest(child, cref, true); + } + + foreach (const QQmlDebugPropertyReference &p, oref.properties()) { + QCOMPARE(p.objectDebugId(), QQmlDebugService::idForObject(o)); + + // signal properties are fake - they are generated from QQmlBoundSignal children + if (p.name().startsWith("on") && p.name().length() > 2 && p.name()[2].isUpper()) { + QList<QQmlBoundSignal*> signalHandlers = o->findChildren<QQmlBoundSignal*>(); + QString signal = p.value().toString(); + bool found = false; + for (int i = 0; i < signalHandlers.count(); ++i) + if (signalHandlers.at(i)->expression()->expression() == signal) { + found = true; + break; + } + QVERIFY(found); + 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 && pmeta.userType() != QMetaType::QVariant) // TODO test complex types + QCOMPARE(p.value(), pmeta.read(o)); + + if (p.name() == "parent") + QVERIFY(p.valueTypeName() == "QGraphicsObject*" || p.valueTypeName() == "QQuickItem*"); + else + QCOMPARE(p.valueTypeName(), QString::fromUtf8(pmeta.typeName())); + + QQmlAbstractBinding *binding = + QQmlPropertyPrivate::binding(QQmlProperty(o, p.name())); + if (binding) + QCOMPARE(binding->expression(), p.binding()); + + QCOMPARE(p.hasNotifySignal(), pmeta.hasNotifySignal()); + + QVERIFY(pmeta.isValid()); + } +} + +void tst_QQmlEngineDebug::recursiveCompareObjects(const QQmlDebugObjectReference &a, const QQmlDebugObjectReference &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<QQmlDebugPropertyReference> aprops = a.properties(); + QList<QQmlDebugPropertyReference> 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_QQmlEngineDebug::recursiveCompareContexts(const QQmlDebugContextReference &a, const QQmlDebugContextReference &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_QQmlEngineDebug::compareProperties(const QQmlDebugPropertyReference &a, const QQmlDebugPropertyReference &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_QQmlEngineDebug::initTestCase() +{ + qRegisterMetaType<QQmlDebugWatch::State>(); + qmlRegisterType<NonScriptProperty>("Test", 1, 0, "NonScriptPropertyElement"); + + QTest::ignoreMessage(QtWarningMsg, "QQmlDebugServer: Waiting for connection on port 3768..."); + m_engine = new QQmlEngine(this); + + QList<QByteArray> qml; + qml << "import QtQuick 2.0\n" + "import Test 1.0\n" + "Item {" + "id: root\n" + "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') }" + "}" + "property variant varObj\n" + "property variant varObjList: []\n" + "property variant varObjMap\n" + "Component.onCompleted: {\n" + "varObj = blueRect;\n" + "var list = varObjList;\n" + "list[0] = blueRect;\n" + "varObjList = list;\n" + "var map = new Object;\n" + "map.rect = blueRect;\n" + "varObjMap = map;\n" + "}\n" + "NonScriptPropertyElement {\n" + "}\n" + "}"; + + // add second component to test multiple root contexts + qml << "import QtQuick 2.0\n" + "Item {}"; + + // and a third to test methods + qml << "import QtQuick 2.0\n" + "Item {" + "function myMethodNoArgs() { return 3; }\n" + "function myMethod(a) { return a + 9; }\n" + "function myMethodIndirect() { myMethod(3); }\n" + "}"; + + // and a fourth to test states + qml << "import QtQuick 2.0\n" + "Rectangle {\n" + "id:rootRect\n" + "width:100\n" + "states: [\n" + "State {\n" + "name:\"state1\"\n" + "PropertyChanges {\n" + "target:rootRect\n" + "width:200\n" + "}\n" + "}\n" + "]\n" + "transitions: [\n" + "Transition {\n" + "from:\"*\"\n" + "to:\"state1\"\n" + "PropertyAnimation {\n" + "target:rootRect\n" + "property:\"width\"\n" + "duration:100\n" + "}\n" + "}\n" + "]\n" + "}\n" + ; + + for (int i=0; i<qml.count(); i++) { + QQmlComponent component(m_engine); + component.setData(qml[i], QUrl::fromLocalFile("")); + QVERIFY(component.isReady()); // fails if bad syntax + m_components << qobject_cast<QQuickItem*>(component.create()); + } + m_rootItem = qobject_cast<QQuickItem*>(m_components.first()); + + // add an extra context to test for multiple contexts + QQmlContext *context = new QQmlContext(m_engine->rootContext(), this); + context->setObjectName("tst_QQmlDebug_childContext"); + + m_conn = new QQmlDebugConnection(this); + m_conn->connectToHost("127.0.0.1", 3768); + + QTest::ignoreMessage(QtWarningMsg, "QQmlDebugServer: Connection established"); + bool ok = m_conn->waitForConnected(); + QVERIFY(ok); + QTRY_VERIFY(QQmlDebugService::hasDebuggingClient()); + m_dbg = new QQmlEngineDebug(m_conn, this); + QTRY_VERIFY(m_dbg->state() == QQmlEngineDebug::Enabled); +} + +void tst_QQmlEngineDebug::cleanupTestCase() +{ + delete m_dbg; + delete m_conn; + qDeleteAll(m_components); + delete m_engine; +} + +void tst_QQmlEngineDebug::setMethodBody() +{ + QQmlDebugObjectReference 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_QQmlEngineDebug::watch_property() +{ + QQmlDebugObjectReference obj = findRootObject(); + QQmlDebugPropertyReference prop = findProperty(obj.properties(), "width"); + + QQmlDebugPropertyWatch *watch; + + QQmlEngineDebug *unconnected = new QQmlEngineDebug(0); + watch = unconnected->addWatch(prop, this); + QCOMPARE(watch->state(), QQmlDebugWatch::Dead); + delete watch; + delete unconnected; + + watch = m_dbg->addWatch(QQmlDebugPropertyReference(), this); + QVERIFY(QQmlDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QQmlDebugWatch::State)))); + QCOMPARE(watch->state(), QQmlDebugWatch::Inactive); + delete watch; + + watch = m_dbg->addWatch(prop, this); + QCOMPARE(watch->state(), QQmlDebugWatch::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(QQmlDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QQmlDebugWatch::State)))); + QCOMPARE(watch->state(), QQmlDebugWatch::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_QQmlEngineDebug::watch_object() +{ + QQmlDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this); + waitForQuery(q_engines); + + QVERIFY(q_engines->engines().count() > 0); + QQmlDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this); + waitForQuery(q_context); + + QVERIFY(q_context->rootContext().objects().count() > 0); + QQmlDebugObjectQuery *q_obj = m_dbg->queryObject(q_context->rootContext().objects()[0], this); + waitForQuery(q_obj); + + QQmlDebugObjectReference obj = q_obj->object(); + + delete q_engines; + delete q_context; + delete q_obj; + + QQmlDebugWatch *watch; + + QQmlEngineDebug *unconnected = new QQmlEngineDebug(0); + watch = unconnected->addWatch(obj, this); + QCOMPARE(watch->state(), QQmlDebugWatch::Dead); + delete watch; + delete unconnected; + + watch = m_dbg->addWatch(QQmlDebugObjectReference(), this); + QVERIFY(QQmlDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QQmlDebugWatch::State)))); + QCOMPARE(watch->state(), QQmlDebugWatch::Inactive); + delete watch; + + watch = m_dbg->addWatch(obj, this); + QCOMPARE(watch->state(), QQmlDebugWatch::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(QQmlDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QQmlDebugWatch::State)))); + QCOMPARE(watch->state(), QQmlDebugWatch::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_QQmlEngineDebug::watch_expression() +{ + QFETCH(QString, expr); + QFETCH(int, increment); + QFETCH(int, incrementCount); + + int origWidth = m_rootItem->property("width").toInt(); + + QQmlDebugObjectReference obj = findRootObject(); + + QQmlDebugObjectExpressionWatch *watch; + + QQmlEngineDebug *unconnected = new QQmlEngineDebug(0); + watch = unconnected->addWatch(obj, expr, this); + QCOMPARE(watch->state(), QQmlDebugWatch::Dead); + delete watch; + delete unconnected; + + watch = m_dbg->addWatch(QQmlDebugObjectReference(), expr, this); + QVERIFY(QQmlDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QQmlDebugWatch::State)))); + QCOMPARE(watch->state(), QQmlDebugWatch::Inactive); + delete watch; + + watch = m_dbg->addWatch(obj, expr, this); + QCOMPARE(watch->state(), QQmlDebugWatch::Waiting); + QCOMPARE(watch->objectDebugId(), obj.debugId()); + QCOMPARE(watch->expression(), expr); + + QSignalSpy spyState(watch, SIGNAL(stateChanged(QQmlDebugWatch::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 (!QQmlDebugTest::waitForSignal(watch, SIGNAL(valueChanged(QByteArray,QVariant)))) + QFAIL("Did not receive valueChanged() for expression"); + } + + if (spyState.count() == 0) + QVERIFY(QQmlDebugTest::waitForSignal(watch, SIGNAL(stateChanged(QQmlDebugWatch::State)))); + QCOMPARE(spyState.count(), 1); + QCOMPARE(watch->state(), QQmlDebugWatch::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_QQmlEngineDebug::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_QQmlEngineDebug::watch_context() +{ + QQmlDebugContextReference c; + QTest::ignoreMessage(QtWarningMsg, "QQmlEngineDebug::addWatch(): Not implemented"); + QVERIFY(!m_dbg->addWatch(c, QString(), this)); +} + +void tst_QQmlEngineDebug::watch_file() +{ + QQmlDebugFileReference f; + QTest::ignoreMessage(QtWarningMsg, "QQmlEngineDebug::addWatch(): Not implemented"); + QVERIFY(!m_dbg->addWatch(f, this)); +} + +void tst_QQmlEngineDebug::queryAvailableEngines() +{ + QQmlDebugEnginesQuery *q_engines; + + QQmlEngineDebug *unconnected = new QQmlEngineDebug(0); + q_engines = unconnected->queryAvailableEngines(0); + QCOMPARE(q_engines->state(), QQmlDebugQuery::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<QQmlDebugEngineReference> engines = q_engines->engines(); + QCOMPARE(engines.count(), 1); + + foreach(const QQmlDebugEngineReference &e, engines) { + QCOMPARE(e.debugId(), QQmlDebugService::idForObject(m_engine)); + QCOMPARE(e.name(), m_engine->objectName()); + } + + // Make query invalid by deleting client + q_engines = m_dbg->queryAvailableEngines(this); + QCOMPARE(q_engines->state(), QQmlDebugQuery::Waiting); + delete m_dbg; + QCOMPARE(q_engines->state(), QQmlDebugQuery::Error); + delete q_engines; + m_dbg = new QQmlEngineDebug(m_conn, this); +} + +void tst_QQmlEngineDebug::queryRootContexts() +{ + QQmlDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this); + waitForQuery(q_engines); + int engineId = q_engines->engines()[0].debugId(); + delete q_engines; + + QQmlDebugRootContextQuery *q_context; + + QQmlEngineDebug *unconnected = new QQmlEngineDebug(0); + q_context = unconnected->queryRootContexts(engineId, this); + QCOMPARE(q_context->state(), QQmlDebugQuery::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); + + QQmlContext *actualContext = m_engine->rootContext(); + QQmlDebugContextReference context = q_context->rootContext(); + QCOMPARE(context.debugId(), QQmlDebugService::idForObject(actualContext)); + QCOMPARE(context.name(), actualContext->objectName()); + + QCOMPARE(context.objects().count(), 4); // 4 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(), 5); + QVERIFY(context.contexts()[0].debugId() >= 0); + QCOMPARE(context.contexts()[0].name(), QString("tst_QQmlDebug_childContext")); + + // Make query invalid by deleting client + q_context = m_dbg->queryRootContexts(engineId, this); + QCOMPARE(q_context->state(), QQmlDebugQuery::Waiting); + delete m_dbg; + QCOMPARE(q_context->state(), QQmlDebugQuery::Error); + delete q_context; + m_dbg = new QQmlEngineDebug(m_conn, this); +} + +void tst_QQmlEngineDebug::queryObject() +{ + QFETCH(bool, recursive); + + QQmlDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this); + waitForQuery(q_engines); + + QQmlDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this); + waitForQuery(q_context); + QQmlDebugObjectReference rootObject = q_context->rootContext().objects()[0]; + + QQmlDebugObjectQuery *q_obj = 0; + + QQmlEngineDebug *unconnected = new QQmlEngineDebug(0); + q_obj = recursive ? unconnected->queryObjectRecursive(rootObject, this) : unconnected->queryObject(rootObject, this); + QCOMPARE(q_obj->state(), QQmlDebugQuery::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); + + QQmlDebugObjectReference obj = q_obj->object(); + + delete q_engines; + delete q_context; + + // Make query invalid by deleting client + q_obj = recursive ? m_dbg->queryObjectRecursive(rootObject, this) : m_dbg->queryObject(rootObject, this); + QCOMPARE(q_obj->state(), QQmlDebugQuery::Waiting); + delete m_dbg; + QCOMPARE(q_obj->state(), QQmlDebugQuery::Error); + delete q_obj; + m_dbg = new QQmlEngineDebug(m_conn, this); + + // check source as defined in main() + QQmlDebugFileReference source = obj.source(); + QCOMPARE(source.url(), QUrl::fromLocalFile("")); + QCOMPARE(source.lineNumber(), 3); + QCOMPARE(source.columnNumber(), 1); + + // generically test all properties, children and childrens' properties + recursiveObjectTest(m_rootItem, obj, recursive); + + if (recursive) { + foreach(const QQmlDebugObjectReference &child, obj.children()) + QVERIFY(child.properties().count() > 0); + + QQmlDebugObjectReference rect; + QQmlDebugObjectReference text; + foreach (const QQmlDebugObjectReference &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 QQmlDebugObjectReference &child, obj.children()) + QCOMPARE(child.properties().count(), 0); + } +} + +void tst_QQmlEngineDebug::queryObject_data() +{ + QTest::addColumn<bool>("recursive"); + + QTest::newRow("non-recursive") << false; + QTest::newRow("recursive") << true; +} + +void tst_QQmlEngineDebug::queryExpressionResult() +{ + QFETCH(QString, expr); + QFETCH(QVariant, result); + + QQmlDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this); + waitForQuery(q_engines); // check immediate deletion is ok + + QQmlDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this); + waitForQuery(q_context); + int objectId = q_context->rootContext().objects()[0].debugId(); + + QQmlDebugExpressionQuery *q_expr; + + QQmlEngineDebug *unconnected = new QQmlEngineDebug(0); + q_expr = unconnected->queryExpressionResult(objectId, expr, this); + QCOMPARE(q_expr->state(), QQmlDebugQuery::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; + + // Make query invalid by deleting client + q_expr = m_dbg->queryExpressionResult(objectId, expr, this); + QCOMPARE(q_expr->state(), QQmlDebugQuery::Waiting); + delete m_dbg; + QCOMPARE(q_expr->state(), QQmlDebugQuery::Error); + delete q_expr; + m_dbg = new QQmlEngineDebug(m_conn, this); +} + +void tst_QQmlEngineDebug::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>")); + QTest::newRow("QObject*") << "varObj" << qVariantFromValue(QString("<unnamed object>")); + QTest::newRow("list of QObject*") << "varObjList" << qVariantFromValue(QString("<unknown value>")); + QVariantMap map; + map.insert(QLatin1String("rect"), QVariant(QLatin1String("<unnamed object>"))); + QTest::newRow("varObjMap") << "varObjMap" << qVariantFromValue(map); +} + +void tst_QQmlEngineDebug::tst_QQmlDebugFileReference() +{ + QQmlDebugFileReference 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); + + QQmlDebugFileReference copy(ref); + QQmlDebugFileReference copyAssign; + copyAssign = ref; + foreach (const QQmlDebugFileReference &r, (QList<QQmlDebugFileReference>() << copy << copyAssign)) { + QCOMPARE(r.url(), ref.url()); + QCOMPARE(r.lineNumber(), ref.lineNumber()); + QCOMPARE(r.columnNumber(), ref.columnNumber()); + } +} + +void tst_QQmlEngineDebug::tst_QQmlDebugEngineReference() +{ + QQmlDebugEngineReference ref; + QCOMPARE(ref.debugId(), -1); + QVERIFY(ref.name().isEmpty()); + + ref = QQmlDebugEngineReference(1); + QCOMPARE(ref.debugId(), 1); + QVERIFY(ref.name().isEmpty()); + + QQmlDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this); + waitForQuery(q_engines); + ref = q_engines->engines()[0]; + delete q_engines; + + QQmlDebugEngineReference copy(ref); + QQmlDebugEngineReference copyAssign; + copyAssign = ref; + foreach (const QQmlDebugEngineReference &r, (QList<QQmlDebugEngineReference>() << copy << copyAssign)) { + QCOMPARE(r.debugId(), ref.debugId()); + QCOMPARE(r.name(), ref.name()); + } +} + +void tst_QQmlEngineDebug::tst_QQmlDebugObjectReference() +{ + QQmlDebugObjectReference 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()); + + QQmlDebugFileReference source = ref.source(); + QVERIFY(source.url().isEmpty()); + QVERIFY(source.lineNumber() < 0); + QVERIFY(source.columnNumber() < 0); + + ref = QQmlDebugObjectReference(1); + QCOMPARE(ref.debugId(), 1); + + QQmlDebugObjectReference rootObject = findRootObject(); + QQmlDebugObjectQuery *query = m_dbg->queryObjectRecursive(rootObject, this); + waitForQuery(query); + ref = query->object(); + delete query; + + QVERIFY(ref.debugId() >= 0); + + QQmlDebugObjectReference copy(ref); + QQmlDebugObjectReference copyAssign; + copyAssign = ref; + foreach (const QQmlDebugObjectReference &r, (QList<QQmlDebugObjectReference>() << copy << copyAssign)) + recursiveCompareObjects(r, ref); +} + +void tst_QQmlEngineDebug::tst_QQmlDebugContextReference() +{ + QQmlDebugContextReference ref; + QCOMPARE(ref.debugId(), -1); + QVERIFY(ref.name().isEmpty()); + QVERIFY(ref.objects().isEmpty()); + QVERIFY(ref.contexts().isEmpty()); + + QQmlDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this); + waitForQuery(q_engines); + QQmlDebugRootContextQuery *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); + + QQmlDebugContextReference copy(ref); + QQmlDebugContextReference copyAssign; + copyAssign = ref; + foreach (const QQmlDebugContextReference &r, (QList<QQmlDebugContextReference>() << copy << copyAssign)) + recursiveCompareContexts(r, ref); +} + +void tst_QQmlEngineDebug::tst_QQmlDebugPropertyReference() +{ + QQmlDebugObjectReference rootObject = findRootObject(); + QQmlDebugObjectQuery *query = m_dbg->queryObject(rootObject, this); + waitForQuery(query); + QQmlDebugObjectReference obj = query->object(); + delete query; + + QQmlDebugPropertyReference 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()); + + QQmlDebugPropertyReference copy(ref); + QQmlDebugPropertyReference copyAssign; + copyAssign = ref; + foreach (const QQmlDebugPropertyReference &r, (QList<QQmlDebugPropertyReference>() << copy << copyAssign)) + compareProperties(r, ref); +} + +void tst_QQmlEngineDebug::setBindingForObject() +{ + QQmlDebugObjectReference rootObject = findRootObject(); + QVERIFY(rootObject.debugId() != -1); + QQmlDebugPropertyReference widthPropertyRef = findProperty(rootObject.properties(), "width"); + + QCOMPARE(widthPropertyRef.value(), QVariant(10)); + QCOMPARE(widthPropertyRef.binding(), QString()); + + // + // set literal + // + m_dbg->setBindingForObject(rootObject.debugId(), "width", "15", true); + + rootObject = findRootObject(); + widthPropertyRef = findProperty(rootObject.properties(), "width"); + + QCOMPARE(widthPropertyRef.value(), QVariant(15)); + QCOMPARE(widthPropertyRef.binding(), QString()); + + // + // set expression + // + m_dbg->setBindingForObject(rootObject.debugId(), "width", "height", false); + + rootObject = findRootObject(); + widthPropertyRef = findProperty(rootObject.properties(), "width"); + + QCOMPARE(widthPropertyRef.value(), QVariant(20)); + QCOMPARE(widthPropertyRef.binding(), QString("height")); + + // + // reset + // + m_dbg->resetBindingForObject(rootObject.debugId(), "width"); + + rootObject = findRootObject(); + widthPropertyRef = findProperty(rootObject.properties(), "width"); + + // QCOMPARE(widthPropertyRef.value(), QVariant(0)); // TODO: Shouldn't this work? + QCOMPARE(widthPropertyRef.binding(), QString()); + + // + // set handler + // + rootObject = findRootObject(); + QCOMPARE(rootObject.children().size(), 5); // Rectangle, Text, MouseArea, Component.onCompleted, NonScriptPropertyElement + QQmlDebugObjectReference mouseAreaObject = rootObject.children().at(2); + QQmlDebugObjectQuery *q_obj = m_dbg->queryObjectRecursive(mouseAreaObject, this); + waitForQuery(q_obj); + mouseAreaObject = q_obj->object(); + + QCOMPARE(mouseAreaObject.className(), QString("MouseArea")); + + QQmlDebugPropertyReference onEnteredRef = findProperty(mouseAreaObject.properties(), "onEntered"); + + QCOMPARE(onEnteredRef.name(), QString("onEntered")); + QCOMPARE(onEnteredRef.value(), QVariant("(function onEntered() { { console.log('hello') } })")); + + m_dbg->setBindingForObject(mouseAreaObject.debugId(), "onEntered", "{console.log('hello, world') }", false) ; + + rootObject = findRootObject(); + mouseAreaObject = rootObject.children().at(2); + q_obj = m_dbg->queryObjectRecursive(mouseAreaObject, this); + waitForQuery(q_obj); + mouseAreaObject = q_obj->object(); + onEnteredRef = findProperty(mouseAreaObject.properties(), "onEntered"); + QCOMPARE(onEnteredRef.name(), QString("onEntered")); + QCOMPARE(onEnteredRef.value(), QVariant("{console.log('hello, world') }")); +} + +void tst_QQmlEngineDebug::setBindingInStates() +{ + // Check if changing bindings of propertychanges works + + const int sourceIndex = 3; + + QQmlDebugObjectReference obj = findRootObject(sourceIndex); + + QVERIFY(obj.debugId() != -1); + QVERIFY(obj.children().count() >= 2); + + // We are going to switch state a couple of times, we need to get rid of the transition before + QQmlDebugExpressionQuery *q_deleteTransition = m_dbg->queryExpressionResult(obj.debugId(),QString("transitions = []"),this); + waitForQuery(q_deleteTransition); + delete q_deleteTransition; + + + // check initial value of the property that is changing + QQmlDebugExpressionQuery *q_setState; + q_setState = m_dbg->queryExpressionResult(obj.debugId(),QString("state=\"state1\""),this); + waitForQuery(q_setState); + delete q_setState; + + obj = findRootObject(sourceIndex); + QCOMPARE(findProperty(obj.properties(),"width").value().toInt(),200); + + + q_setState = m_dbg->queryExpressionResult(obj.debugId(),QString("state=\"\""),this); + waitForQuery(q_setState); + delete q_setState; + + + obj = findRootObject(sourceIndex, true); + QCOMPARE(findProperty(obj.properties(),"width").value().toInt(),100); + + + // change the binding + QQmlDebugObjectReference state = obj.children()[1]; + QCOMPARE(state.className(), QString("State")); + QVERIFY(state.children().count() > 0); + + QQmlDebugObjectReference propertyChange = state.children()[0]; + QVERIFY(propertyChange.debugId() != -1); + + QVERIFY( m_dbg->setBindingForObject(propertyChange.debugId(), "width",QVariant(300),true) ); + + // check properties changed in state + obj = findRootObject(sourceIndex); + QCOMPARE(findProperty(obj.properties(),"width").value().toInt(),100); + + + q_setState = m_dbg->queryExpressionResult(obj.debugId(),QString("state=\"state1\""),this); + waitForQuery(q_setState); + delete q_setState; + + obj = findRootObject(sourceIndex); + QCOMPARE(findProperty(obj.properties(),"width").value().toInt(),300); + + // check changing properties of base state from within a state + QVERIFY(m_dbg->setBindingForObject(obj.debugId(),"width","height*2",false)); + QVERIFY(m_dbg->setBindingForObject(obj.debugId(),"height","200",true)); + + obj = findRootObject(sourceIndex); + QCOMPARE(findProperty(obj.properties(),"width").value().toInt(),300); + + q_setState = m_dbg->queryExpressionResult(obj.debugId(),QString("state=\"\""),this); + waitForQuery(q_setState); + delete q_setState; + + obj = findRootObject(sourceIndex); + QCOMPARE(findProperty(obj.properties(),"width").value().toInt(), 400); + + // reset binding while in a state + q_setState = m_dbg->queryExpressionResult(obj.debugId(),QString("state=\"state1\""),this); + waitForQuery(q_setState); + delete q_setState; + + obj = findRootObject(sourceIndex); + QCOMPARE(findProperty(obj.properties(),"width").value().toInt(), 300); + + m_dbg->resetBindingForObject(propertyChange.debugId(), "width"); + + obj = findRootObject(sourceIndex); + QCOMPARE(findProperty(obj.properties(),"width").value().toInt(), 400); + + // re-add binding + m_dbg->setBindingForObject(propertyChange.debugId(), "width", "300", true); + + obj = findRootObject(sourceIndex); + QCOMPARE(findProperty(obj.properties(),"width").value().toInt(), 300); +} + +void tst_QQmlEngineDebug::queryObjectTree() +{ + const int sourceIndex = 3; + + // Check if states/transitions are initialized when fetching root item + QQmlDebugEnginesQuery *q_engines = m_dbg->queryAvailableEngines(this); + waitForQuery(q_engines); + + QQmlDebugRootContextQuery *q_context = m_dbg->queryRootContexts(q_engines->engines()[0].debugId(), this); + waitForQuery(q_context); + + QVERIFY(q_context->rootContext().objects().count() > sourceIndex); + QQmlDebugObjectReference rootObject = q_context->rootContext().objects()[sourceIndex]; + + QQmlDebugObjectQuery *q_obj = m_dbg->queryObjectRecursive(rootObject, this); + waitForQuery(q_obj); + + QQmlDebugObjectReference obj = q_obj->object(); + + delete q_engines; + delete q_context; + delete q_obj; + + QVERIFY(obj.debugId() != -1); + QVERIFY(obj.children().count() >= 2); + + + + // check state + QQmlDebugObjectReference state = obj.children()[1]; + QCOMPARE(state.className(), QString("State")); + QVERIFY(state.children().count() > 0); + + QQmlDebugObjectReference propertyChange = state.children()[0]; + QVERIFY(propertyChange.debugId() != -1); + + QQmlDebugPropertyReference propertyChangeTarget = findProperty(propertyChange.properties(),"target"); + QCOMPARE(propertyChangeTarget.objectDebugId(), propertyChange.debugId()); + + QQmlDebugObjectReference targetReference = qvariant_cast<QQmlDebugObjectReference>(propertyChangeTarget.value()); + QVERIFY(targetReference.debugId() != -1); + + + + // check transition + QQmlDebugObjectReference transition = obj.children()[0]; + QCOMPARE(transition.className(), QString("Transition")); + QCOMPARE(findProperty(transition.properties(),"from").value().toString(), QString("*")); + QCOMPARE(findProperty(transition.properties(),"to").value(), findProperty(state.properties(),"name").value()); + QVERIFY(transition.children().count() > 0); + + QQmlDebugObjectReference animation = transition.children()[0]; + QVERIFY(animation.debugId() != -1); + + QQmlDebugPropertyReference animationTarget = findProperty(animation.properties(),"target"); + QCOMPARE(animationTarget.objectDebugId(), animation.debugId()); + + targetReference = qvariant_cast<QQmlDebugObjectReference>(animationTarget.value()); + QVERIFY(targetReference.debugId() != -1); + + QCOMPARE(findProperty(animation.properties(),"property").value().toString(), QString("width")); + QCOMPARE(findProperty(animation.properties(),"duration").value().toInt(), 100); +} + +int main(int argc, char *argv[]) +{ + int _argc = argc + 1; + char **_argv = new char*[_argc]; + for (int i = 0; i < argc; ++i) + _argv[i] = argv[i]; + char arg[] = "-qmljsdebugger=port:3768"; + _argv[_argc - 1] = arg; + + QGuiApplication app(_argc, _argv); + tst_QQmlEngineDebug tc; + return QTest::qExec(&tc, _argc, _argv); + delete _argv; +} + +#include "tst_qqmlenginedebug.moc" diff --git a/tests/auto/qml/debugger/qqmlinspector/app/app.pro b/tests/auto/qml/debugger/qqmlinspector/app/app.pro new file mode 100644 index 0000000000..f6ef983735 --- /dev/null +++ b/tests/auto/qml/debugger/qqmlinspector/app/app.pro @@ -0,0 +1,9 @@ +TARGET = app +QT += qml quick widgets + +CONFIG += declarative_debug +macx:CONFIG -= app_bundle + +SOURCES += main.cpp + +OTHER_FILES += qtquick1.qml qtquick2.qml diff --git a/tests/auto/qml/debugger/qqmlinspector/app/main.cpp b/tests/auto/qml/debugger/qqmlinspector/app/main.cpp new file mode 100644 index 0000000000..a7ef09c283 --- /dev/null +++ b/tests/auto/qml/debugger/qqmlinspector/app/main.cpp @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/QDebug> +#include <QtCore/QStringList> +#include <QtQuick/QQuickView> +#include <QtGui/QGuiApplication> + +int main(int argc, char *argv[]) +{ + QGuiApplication app(argc, argv); + + bool qtquick2 = true; + for (int i = 1; i < app.arguments().size(); ++i) { + const QString arg = app.arguments().at(i); + if (arg == "-qtquick1") { + qtquick2 = false; + } else if (arg == "-qtquick2") { + qtquick2 = true; + } else { + qWarning() << "Usage: " << app.arguments().at(0) << "[-qtquick1|-qtquick2]"; + return -1; + } + } + + if (qtquick2) { + QQuickView *view = new QQuickView(); + view->setSource(QUrl::fromLocalFile("app/qtquick2.qml")); + } else { + qWarning("No suitable QtQuick1 implementation is available!"); + } + return app.exec(); +} diff --git a/tests/auto/qml/debugger/qqmlinspector/app/qtquick2.qml b/tests/auto/qml/debugger/qqmlinspector/app/qtquick2.qml new file mode 100644 index 0000000000..9c36e13c5b --- /dev/null +++ b/tests/auto/qml/debugger/qqmlinspector/app/qtquick2.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +Item { + +} diff --git a/tests/auto/qml/debugger/qqmlinspector/qqmlinspector.pro b/tests/auto/qml/debugger/qqmlinspector/qqmlinspector.pro new file mode 100644 index 0000000000..5544f76581 --- /dev/null +++ b/tests/auto/qml/debugger/qqmlinspector/qqmlinspector.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs + +SUBDIRS += tst_qqmlinspector.pro app diff --git a/tests/auto/qml/debugger/qqmlinspector/tst_qqmlinspector.cpp b/tests/auto/qml/debugger/qqmlinspector/tst_qqmlinspector.cpp new file mode 100644 index 0000000000..4f74a036d6 --- /dev/null +++ b/tests/auto/qml/debugger/qqmlinspector/tst_qqmlinspector.cpp @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <qtest.h> +#include <QSignalSpy> +#include <QTimer> +#include <QHostAddress> +#include <QDebug> +#include <QThread> + +#include "../../../../../src/plugins/qmltooling/shared/qqmlinspectorprotocol.h" +#include "../shared/debugutil_p.h" + +using namespace QmlJSDebugger; + +#define PORT 13772 +#define STR_PORT "13772" + +class QQmlInspectorClient : public QQmlDebugClient +{ + Q_OBJECT + +public: + QQmlInspectorClient(QQmlDebugConnection *connection) + : QQmlDebugClient(QLatin1String("QQmlObserverMode"), connection) + , m_showAppOnTop(false) + { + } + + bool showAppOnTop() const { return m_showAppOnTop; } + void setShowAppOnTop(bool showOnTop); + +signals: + void showAppOnTopChanged(); + +protected: + void messageReceived(const QByteArray &message); + +private: + bool m_showAppOnTop; +}; + +class tst_QQmlInspector : public QObject +{ + Q_OBJECT + +public: + tst_QQmlInspector() + : m_process(0) + , m_connection(0) + , m_client(0) + { + } + + +private: + QQmlDebugProcess *m_process; + QQmlDebugConnection *m_connection; + QQmlInspectorClient *m_client; + +private slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + void connect(); + void showAppOnTop(); +}; + + +void QQmlInspectorClient::setShowAppOnTop(bool showOnTop) +{ + QByteArray message; + QDataStream ds(&message, QIODevice::WriteOnly); + ds << InspectorProtocol::ShowAppOnTop << showOnTop; + + sendMessage(message); +} + +void QQmlInspectorClient::messageReceived(const QByteArray &message) +{ + QDataStream ds(message); + InspectorProtocol::Message type; + ds >> type; + + switch (type) { + case InspectorProtocol::ShowAppOnTop: + ds >> m_showAppOnTop; + emit showAppOnTopChanged(); + break; + default: + qDebug() << "Unhandled message " << (int)type; + } +} + +void tst_QQmlInspector::initTestCase() +{ +} + +void tst_QQmlInspector::cleanupTestCase() +{ +} + +void tst_QQmlInspector::init() +{ + const QString executable = SRCDIR"/app/app"; + const QString argument = "-qmljsdebugger=port:"STR_PORT",block"; + + m_process = new QQmlDebugProcess(executable); + m_process->start(QStringList() << argument); + if (!m_process->waitForSessionStart()) { + QFAIL(QString("Could not launch app '%1'.\nApplication output:\n%2").arg(executable, m_process->output()).toAscii()); + } + + QQmlDebugConnection *m_connection = new QQmlDebugConnection(); + m_client = new QQmlInspectorClient(m_connection); + + m_connection->connectToHost(QLatin1String("127.0.0.1"), PORT); +} + +void tst_QQmlInspector::cleanup() +{ + delete m_process; + delete m_connection; + delete m_client; +} + +void tst_QQmlInspector::connect() +{ + QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled); +} + +void tst_QQmlInspector::showAppOnTop() +{ + QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled); + + m_client->setShowAppOnTop(true); + QVERIFY(QQmlDebugTest::waitForSignal(m_client, SIGNAL(showAppOnTopChanged()))); + QCOMPARE(m_client->showAppOnTop(), true); + + m_client->setShowAppOnTop(false); + QVERIFY(QQmlDebugTest::waitForSignal(m_client, SIGNAL(showAppOnTopChanged()))); + QCOMPARE(m_client->showAppOnTop(), false); +} + +QTEST_MAIN(tst_QQmlInspector) + +#include "tst_qqmlinspector.moc" diff --git a/tests/auto/qml/debugger/qqmlinspector/tst_qqmlinspector.pro b/tests/auto/qml/debugger/qqmlinspector/tst_qqmlinspector.pro new file mode 100644 index 0000000000..010e97cef7 --- /dev/null +++ b/tests/auto/qml/debugger/qqmlinspector/tst_qqmlinspector.pro @@ -0,0 +1,12 @@ +CONFIG += testcase +TARGET = tst_qqmlinspector +macx:CONFIG -= app_bundle + +HEADERS += ../shared/debugutil_p.h +SOURCES += tst_qqmlinspector.cpp \ + ../shared/debugutil.cpp + +DEFINES += SRCDIR=\\\"$$PWD\\\" +CONFIG += parallel_test declarative_debug + +QT += qml-private testlib diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/data/exit.qml b/tests/auto/qml/debugger/qqmlprofilerservice/data/exit.qml new file mode 100644 index 0000000000..b250524caa --- /dev/null +++ b/tests/auto/qml/debugger/qqmlprofilerservice/data/exit.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 + +Item { + Timer { + running: true + interval: 1 + onTriggered: Qt.quit(); + } +} diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/data/test.qml b/tests/auto/qml/debugger/qqmlprofilerservice/data/test.qml new file mode 100644 index 0000000000..9c36e13c5b --- /dev/null +++ b/tests/auto/qml/debugger/qqmlprofilerservice/data/test.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +Item { + +} diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/qqmlprofilerservice.pro b/tests/auto/qml/debugger/qqmlprofilerservice/qqmlprofilerservice.pro new file mode 100644 index 0000000000..81fe3d5943 --- /dev/null +++ b/tests/auto/qml/debugger/qqmlprofilerservice/qqmlprofilerservice.pro @@ -0,0 +1,15 @@ +CONFIG += testcase +TARGET = tst_qqmlprofilerservice +macx:CONFIG -= app_bundle + +HEADERS += ../shared/debugutil_p.h + +SOURCES += tst_qqmlprofilerservice.cpp \ + ../shared/debugutil.cpp +OTHER_FILES += data/test.qml + +include (../../../shared/util.pri) + +CONFIG += parallel_test declarative_debug + +QT += core-private v8-private qml-private testlib diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp new file mode 100644 index 0000000000..edc120ba85 --- /dev/null +++ b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp @@ -0,0 +1,310 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qtest.h> +#include <QLibraryInfo> + +#include "QtQml/private/qqmlprofilerservice_p.h" +#include "../shared/debugutil_p.h" +#include "../../../shared/util.h" + +#define PORT 13773 +#define STR_PORT "13773" + +class QQmlProfilerClient : public QQmlDebugClient +{ + Q_OBJECT + +public: + QQmlProfilerClient(QQmlDebugConnection *connection) + : QQmlDebugClient(QLatin1String("CanvasFrameRate"), connection) + { + } + + QList<QQmlProfilerData> traceMessages; + + void setTraceState(bool enabled) { + QByteArray message; + QDataStream stream(&message, QIODevice::WriteOnly); + stream << enabled; + sendMessage(message); + } + +signals: + void complete(); + +protected: + void messageReceived(const QByteArray &message); +}; + +class tst_QQmlProfilerService : public QQmlDataTest +{ + Q_OBJECT + +public: + tst_QQmlProfilerService() + : m_process(0) + , m_connection(0) + , m_client(0) + { + } + +private: + QQmlDebugProcess *m_process; + QQmlDebugConnection *m_connection; + QQmlProfilerClient *m_client; + + void connect(bool block, const QString &testFile); + +private slots: + void cleanup(); + + void blockingConnectWithTraceEnabled(); + void blockingConnectWithTraceDisabled(); + void nonBlockingConnect(); + void profileOnExit(); +}; + +void QQmlProfilerClient::messageReceived(const QByteArray &message) +{ + QByteArray msg = message; + QDataStream stream(&msg, QIODevice::ReadOnly); + + + QQmlProfilerData data; + data.time = -2; + data.messageType = -1; + data.detailType = -1; + data.line = -1; + data.framerate = -1; + data.animationcount = -1; + + stream >> data.time >> data.messageType; + + QVERIFY(data.time >= -1); + + switch (data.messageType) { + case (QQmlProfilerService::Event): { + stream >> data.detailType; + + switch (data.detailType) { + case QQmlProfilerService::AnimationFrame: { + stream >> data.framerate >> data.animationcount; + QVERIFY(data.framerate != -1); + QVERIFY(data.animationcount != -1); + break; + } + case QQmlProfilerService::FramePaint: + case QQmlProfilerService::Mouse: + case QQmlProfilerService::Key: + case QQmlProfilerService::StartTrace: + case QQmlProfilerService::EndTrace: + break; + default: { + QString failMsg = QString("Unknown event type:") + data.detailType; + QFAIL(qPrintable(failMsg)); + break; + } + } + break; + } + case QQmlProfilerService::Complete: { + emit complete(); + return; + } + case QQmlProfilerService::RangeStart: { + stream >> data.detailType; + QVERIFY(data.detailType >= 0 && data.detailType < QQmlProfilerService::MaximumRangeType); + break; + } + case QQmlProfilerService::RangeEnd: { + stream >> data.detailType; + QVERIFY(data.detailType >= 0 && data.detailType < QQmlProfilerService::MaximumRangeType); + break; + } + case QQmlProfilerService::RangeData: { + stream >> data.detailType >> data.detailData; + QVERIFY(data.detailType >= 0 && data.detailType < QQmlProfilerService::MaximumRangeType); + break; + } + case QQmlProfilerService::RangeLocation: { + stream >> data.detailType >> data.detailData >> data.line >> data.column; + QVERIFY(data.detailType >= 0 && data.detailType < QQmlProfilerService::MaximumRangeType); + QVERIFY(data.line >= -2); + break; + } + default: + QString failMsg = QString("Unknown message type:") + data.messageType; + QFAIL(qPrintable(failMsg)); + break; + } + QVERIFY(stream.atEnd()); + traceMessages.append(data); +} + +void tst_QQmlProfilerService::connect(bool block, const QString &testFile) +{ + const QString executable = QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene"; + QStringList arguments; + + if (block) + arguments << QString("-qmljsdebugger=port:"STR_PORT",block"); + else + arguments << QString("-qmljsdebugger=port:"STR_PORT); + + arguments << QQmlDataTest::instance()->testFile(testFile); + + m_process = new QQmlDebugProcess(executable); + m_process->start(QStringList() << arguments); + if (!m_process->waitForSessionStart()) { + QString failMsg = QString("Could not launch app '%1'.\nApplication output:\n%2").arg( + executable, m_process->output()); + QFAIL(qPrintable(failMsg)); + } + + QQmlDebugConnection *m_connection = new QQmlDebugConnection(); + m_client = new QQmlProfilerClient(m_connection); + + m_connection->connectToHost(QLatin1String("127.0.0.1"), PORT); +} + +void tst_QQmlProfilerService::cleanup() +{ + delete m_process; + delete m_connection; + delete m_client; +} + +void tst_QQmlProfilerService::blockingConnectWithTraceEnabled() +{ + connect(true, "test.qml"); + QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled); + + m_client->setTraceState(true); + m_client->setTraceState(false); + if (!QQmlDebugTest::waitForSignal(m_client, SIGNAL(complete()))) { + QString failMsg + = QString("No trace received in time. App output: \n%1\n").arg(m_process->output()); + QFAIL(qPrintable(failMsg)); + } + + QVERIFY(m_client->traceMessages.count()); + // must start with "StartTrace" + QCOMPARE(m_client->traceMessages.first().messageType, (int)QQmlProfilerService::Event); + QCOMPARE(m_client->traceMessages.first().detailType, (int)QQmlProfilerService::StartTrace); + + // must end with "EndTrace" + QCOMPARE(m_client->traceMessages.last().messageType, (int)QQmlProfilerService::Event); + QCOMPARE(m_client->traceMessages.last().detailType, (int)QQmlProfilerService::EndTrace); +} + +void tst_QQmlProfilerService::blockingConnectWithTraceDisabled() +{ + connect(true, "test.qml"); + QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled); + + m_client->setTraceState(false); + m_client->setTraceState(true); + m_client->setTraceState(false); + if (!QQmlDebugTest::waitForSignal(m_client, SIGNAL(complete()))) { + QString failMsg + = QString("No trace received in time. App output: \n%1\n").arg(m_process->output()); + QFAIL(qPrintable(failMsg)); + } + + QVERIFY(m_client->traceMessages.count()); + + // must start with "StartTrace" + QCOMPARE(m_client->traceMessages.first().messageType, (int)QQmlProfilerService::Event); + QCOMPARE(m_client->traceMessages.first().detailType, (int)QQmlProfilerService::StartTrace); + + // must end with "EndTrace" + QCOMPARE(m_client->traceMessages.last().messageType, (int)QQmlProfilerService::Event); + QCOMPARE(m_client->traceMessages.last().detailType, (int)QQmlProfilerService::EndTrace); +} + +void tst_QQmlProfilerService::nonBlockingConnect() +{ + connect(false, "test.qml"); + QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled); + + m_client->setTraceState(true); + m_client->setTraceState(false); + if (!QQmlDebugTest::waitForSignal(m_client, SIGNAL(complete()))) { + QString failMsg + = QString("No trace received in time. App output: \n%1\n").arg(m_process->output()); + QFAIL(qPrintable(failMsg)); + } + + // must start with "StartTrace" + QCOMPARE(m_client->traceMessages.first().messageType, (int)QQmlProfilerService::Event); + QCOMPARE(m_client->traceMessages.first().detailType, (int)QQmlProfilerService::StartTrace); + + // must end with "EndTrace" + QCOMPARE(m_client->traceMessages.last().messageType, (int)QQmlProfilerService::Event); + QCOMPARE(m_client->traceMessages.last().detailType, (int)QQmlProfilerService::EndTrace); +} + +void tst_QQmlProfilerService::profileOnExit() +{ + connect(true, "exit.qml"); + QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled); + + m_client->setTraceState(true); + + if (!QQmlDebugTest::waitForSignal(m_client, SIGNAL(complete()))) { + QString failMsg + = QString("No trace received in time. App output: \n%1\n").arg(m_process->output()); + QFAIL(qPrintable(failMsg)); + } + + // must start with "StartTrace" + QCOMPARE(m_client->traceMessages.first().messageType, (int)QQmlProfilerService::Event); + QCOMPARE(m_client->traceMessages.first().detailType, (int)QQmlProfilerService::StartTrace); + + // must end with "EndTrace" + QCOMPARE(m_client->traceMessages.last().messageType, (int)QQmlProfilerService::Event); + QCOMPARE(m_client->traceMessages.last().detailType, (int)QQmlProfilerService::EndTrace); +} + +QTEST_MAIN(tst_QQmlProfilerService) + +#include "tst_qqmlprofilerservice.moc" diff --git a/tests/auto/qml/debugger/qv8profilerservice/data/console.qml b/tests/auto/qml/debugger/qv8profilerservice/data/console.qml new file mode 100644 index 0000000000..c23c820216 --- /dev/null +++ b/tests/auto/qml/debugger/qv8profilerservice/data/console.qml @@ -0,0 +1,14 @@ +import QtQuick 2.0 + + +Item { + function f() + { + } + + Component.onCompleted: { + console.profile(); + f(); + console.profileEnd(); + } +} diff --git a/tests/auto/qml/debugger/qv8profilerservice/data/exit.qml b/tests/auto/qml/debugger/qv8profilerservice/data/exit.qml new file mode 100644 index 0000000000..604265354c --- /dev/null +++ b/tests/auto/qml/debugger/qv8profilerservice/data/exit.qml @@ -0,0 +1,11 @@ +import QtQuick 2.0 + +Item { + Timer { + running: true + interval: 1 + onTriggered: { + Qt.quit(); + } + } +} diff --git a/tests/auto/qml/debugger/qv8profilerservice/data/test.qml b/tests/auto/qml/debugger/qv8profilerservice/data/test.qml new file mode 100644 index 0000000000..9c36e13c5b --- /dev/null +++ b/tests/auto/qml/debugger/qv8profilerservice/data/test.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +Item { + +} diff --git a/tests/auto/qml/debugger/qv8profilerservice/qv8profilerservice.pro b/tests/auto/qml/debugger/qv8profilerservice/qv8profilerservice.pro new file mode 100644 index 0000000000..e980d6594d --- /dev/null +++ b/tests/auto/qml/debugger/qv8profilerservice/qv8profilerservice.pro @@ -0,0 +1,16 @@ +CONFIG += testcase +TARGET = tst_qv8profilerservice +macx:CONFIG -= app_bundle + +HEADERS += ../shared/debugutil_p.h + +SOURCES += tst_qv8profilerservice.cpp \ + ../shared/debugutil.cpp + +include (../../../shared/util.pri) + +OTHER_FILES += data/test.qml + +CONFIG += parallel_test declarative_debug + +QT += qml-private testlib diff --git a/tests/auto/qml/debugger/qv8profilerservice/tst_qv8profilerservice.cpp b/tests/auto/qml/debugger/qv8profilerservice/tst_qv8profilerservice.cpp new file mode 100644 index 0000000000..c5992a4403 --- /dev/null +++ b/tests/auto/qml/debugger/qv8profilerservice/tst_qv8profilerservice.cpp @@ -0,0 +1,294 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qtest.h> +#include <QLibraryInfo> + +#include "QtQml/private/qv8profilerservice_p.h" +#include "../shared/debugutil_p.h" +#include "../../../shared/util.h" + +#define PORT 13774 +#define STR_PORT "13774" + +class QV8ProfilerClient : public QQmlDebugClient +{ + Q_OBJECT + +public: + QV8ProfilerClient(QQmlDebugConnection *connection) + : QQmlDebugClient(QLatin1String("V8Profiler"), connection) + { + } + + void startProfiling(const QString &name) { + QByteArray message; + QDataStream stream(&message, QIODevice::WriteOnly); + stream << QByteArray("V8PROFILER") << QByteArray("start") << name; + sendMessage(message); + } + + void stopProfiling(const QString &name) { + QByteArray message; + QDataStream stream(&message, QIODevice::WriteOnly); + stream << QByteArray("V8PROFILER") << QByteArray("stop") << name; + sendMessage(message); + } + + void takeSnapshot() { + QByteArray message; + QDataStream stream(&message, QIODevice::WriteOnly); + stream << QByteArray("V8SNAPSHOT") << QByteArray("full"); + sendMessage(message); + } + + void deleteSnapshots() { + QByteArray message; + QDataStream stream(&message, QIODevice::WriteOnly); + stream << QByteArray("V8SNAPSHOT") << QByteArray("delete"); + sendMessage(message); + } + + QList<QV8ProfilerData> traceMessages; + QList<QByteArray> snapshotMessages; + +signals: + void started(); + void complete(); + void snapshot(); + +protected: + void messageReceived(const QByteArray &message); +}; + +class tst_QV8ProfilerService : public QQmlDataTest +{ + Q_OBJECT + +public: + tst_QV8ProfilerService() + : m_process(0) + , m_connection(0) + , m_client(0) + { + } + +private: + QQmlDebugProcess *m_process; + QQmlDebugConnection *m_connection; + QV8ProfilerClient *m_client; + + void connect(bool block, const QString &testFile); + +private slots: + void cleanup(); + + void blockingConnectWithTraceEnabled(); + void blockingConnectWithTraceDisabled(); + void nonBlockingConnect(); + void snapshot(); + void profileOnExit(); + void console(); +}; + +void QV8ProfilerClient::messageReceived(const QByteArray &message) +{ + QByteArray msg = message; + QDataStream stream(&msg, QIODevice::ReadOnly); + + int messageType; + stream >> messageType; + + QVERIFY(messageType >= 0); + QVERIFY(messageType < QV8ProfilerService::V8MaximumMessage); + + switch (messageType) { + case QV8ProfilerService::V8Entry: { + QV8ProfilerData entry; + stream >> entry.filename >> entry.functionname >> entry.lineNumber >> entry.totalTime >> entry.selfTime >> entry.treeLevel; + traceMessages.append(entry); + break; + } + case QV8ProfilerService::V8Complete: + emit complete(); + break; + case QV8ProfilerService::V8SnapshotChunk: { + QByteArray json; + stream >> json; + snapshotMessages.append(json); + break; + } + case QV8ProfilerService::V8SnapshotComplete: + emit snapshot(); + break; + case QV8ProfilerService::V8Started: + emit started(); + break; + default: + QString failMessage = QString("Unknown message type: %1").arg(messageType); + QFAIL(qPrintable(failMessage)); + } + + QVERIFY(stream.atEnd()); +} + +void tst_QV8ProfilerService::connect(bool block, const QString &testFile) +{ + const QString executable = QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene"; + QStringList arguments; + + if (block) + arguments << QString("-qmljsdebugger=port:"STR_PORT",block"); + else + arguments << QString("-qmljsdebugger=port:"STR_PORT); + + arguments << QQmlDataTest::instance()->testFile(testFile); + + m_process = new QQmlDebugProcess(executable); + m_process->start(QStringList() << arguments); + if (!m_process->waitForSessionStart()) { + QString failMsg = QString("Could not launch app '%1'.\nApplication output:\n%2").arg( + executable, m_process->output()); + QFAIL(qPrintable(failMsg)); + } + + QQmlDebugConnection *m_connection = new QQmlDebugConnection(); + m_client = new QV8ProfilerClient(m_connection); + + m_connection->connectToHost(QLatin1String("127.0.0.1"), PORT); +} + +void tst_QV8ProfilerService::cleanup() +{ + if (QTest::currentTestFailed()) + qDebug() << "Application Output:" << m_process->output(); + + delete m_process; + delete m_connection; + delete m_client; +} + +void tst_QV8ProfilerService::blockingConnectWithTraceEnabled() +{ + connect(true, "test.qml"); + QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled); + + m_client->startProfiling(""); + QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(started())), + "No start signal received in time."); + m_client->stopProfiling(""); + QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(complete())), + "No trace received in time."); +} + +void tst_QV8ProfilerService::blockingConnectWithTraceDisabled() +{ + connect(true, "test.qml"); + QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled); + + m_client->stopProfiling(""); + if (QQmlDebugTest::waitForSignal(m_client, SIGNAL(complete()), 1000)) { + QString failMsg + = QString("Unexpected trace received! App output: %1\n\n").arg(m_process->output()); + QFAIL(qPrintable(failMsg)); + } + m_client->startProfiling(""); + QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(started())), + "No start signal received in time."); + m_client->stopProfiling(""); + QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(complete())), + "No trace received in time."); +} + +void tst_QV8ProfilerService::nonBlockingConnect() +{ + connect(false, "test.qml"); + QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled); + + m_client->startProfiling(""); + QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(started())), + "No start signal received in time."); + m_client->stopProfiling(""); + QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(complete())), + "No trace received in time."); +} + +void tst_QV8ProfilerService::snapshot() +{ + connect(false, "test.qml"); + QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled); + + m_client->takeSnapshot(); + QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(snapshot())), + "No trace received in time."); +} + +void tst_QV8ProfilerService::profileOnExit() +{ + connect(true, "exit.qml"); + QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled); + + m_client->startProfiling(""); + QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(started())), + "No start signal received in time."); + + QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(complete())), + "No trace received in time."); + //QVERIFY(!m_client->traceMessages.isEmpty()); +} + +void tst_QV8ProfilerService::console() +{ + connect(true, "console.qml"); + QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled); + + m_client->stopProfiling(""); + + QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(started())), + "No start signal received in time."); + QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(complete())), + "No trace received in time."); + QVERIFY(!m_client->traceMessages.isEmpty()); +} + +QTEST_MAIN(tst_QV8ProfilerService) + +#include "tst_qv8profilerservice.moc" diff --git a/tests/auto/qml/debugger/shared/debugutil.cpp b/tests/auto/qml/debugger/shared/debugutil.cpp new file mode 100644 index 0000000000..8ed64d3a5d --- /dev/null +++ b/tests/auto/qml/debugger/shared/debugutil.cpp @@ -0,0 +1,190 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "debugutil_p.h" + +#include <QEventLoop> +#include <QTimer> + +#include <private/qqmldebugclient_p.h> +#include <private/qqmldebugservice_p.h> + +bool QQmlDebugTest::waitForSignal(QObject *receiver, const char *member, int timeout) { + QEventLoop loop; + QTimer timer; + timer.setSingleShot(true); + QObject::connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); + QObject::connect(receiver, member, &loop, SLOT(quit())); + timer.start(timeout); + loop.exec(); + return timer.isActive(); +} + +QQmlDebugTestService::QQmlDebugTestService(const QString &s, float version, QObject *parent) + : QQmlDebugService(s, version, parent) +{ + registerService(); +} + +void QQmlDebugTestService::messageReceived(const QByteArray &ba) +{ + sendMessage(ba); +} + +void QQmlDebugTestService::stateChanged(State) +{ + emit stateHasChanged(); +} + + +QQmlDebugTestClient::QQmlDebugTestClient(const QString &s, QQmlDebugConnection *c) + : QQmlDebugClient(s, c) +{ +} + +QByteArray QQmlDebugTestClient::waitForResponse() +{ + lastMsg.clear(); + QQmlDebugTest::waitForSignal(this, SIGNAL(serverMessage(QByteArray))); + if (lastMsg.isEmpty()) { + qWarning() << "tst_QQmlDebugTestClient: no response from server!"; + return QByteArray(); + } + return lastMsg; +} + +void QQmlDebugTestClient::stateChanged(State stat) +{ + QCOMPARE(stat, state()); + emit stateHasChanged(); +} + +void QQmlDebugTestClient::messageReceived(const QByteArray &ba) +{ + lastMsg = ba; + emit serverMessage(ba); +} + +QQmlDebugProcess::QQmlDebugProcess(const QString &executable) + : m_executable(executable) + , m_started(false) +{ + m_process.setProcessChannelMode(QProcess::MergedChannels); + m_timer.setSingleShot(true); + m_timer.setInterval(5000); + connect(&m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(processAppOutput())); + connect(&m_timer, SIGNAL(timeout()), &m_eventLoop, SLOT(quit())); +} + +QQmlDebugProcess::~QQmlDebugProcess() +{ + stop(); +} + +void QQmlDebugProcess::start(const QStringList &arguments) +{ + m_mutex.lock(); + m_process.setEnvironment(m_environment); + m_process.start(m_executable, arguments); + m_process.waitForStarted(); + m_timer.start(); + m_mutex.unlock(); +} + +void QQmlDebugProcess::stop() +{ + if (m_process.state() != QProcess::NotRunning) { + m_process.kill(); + m_process.waitForFinished(5000); + } +} + +bool QQmlDebugProcess::waitForSessionStart() +{ + if (m_process.state() != QProcess::Running) { + qWarning() << "Could not start up " << m_executable; + return false; + } + m_eventLoop.exec(QEventLoop::ExcludeUserInputEvents); + + return m_started; +} + +void QQmlDebugProcess::setEnvironment(const QStringList &environment) +{ + m_environment = environment; +} + +QString QQmlDebugProcess::output() const +{ + return m_output; +} + +void QQmlDebugProcess::processAppOutput() +{ + m_mutex.lock(); + + QString newOutput = m_process.readAll(); + m_output.append(newOutput); + m_outputBuffer.append(newOutput); + + while (true) { + const int nlIndex = m_outputBuffer.indexOf(QLatin1Char('\n')); + if (nlIndex < 0) // no further complete lines + break; + const QString line = m_outputBuffer.left(nlIndex); + m_outputBuffer = m_outputBuffer.right(m_outputBuffer.size() - nlIndex - 1); + + if (line.startsWith("Qml debugging is enabled")) // ignore + continue; + if (line.startsWith("QQmlDebugServer:")) { + if (line.contains("Waiting for connection ")) { + m_started = true; + m_eventLoop.quit(); + continue; + } + if (line.contains("Connection established")) { + continue; + } + } + } + m_mutex.unlock(); +} diff --git a/tests/auto/qml/debugger/shared/debugutil_p.h b/tests/auto/qml/debugger/shared/debugutil_p.h new file mode 100644 index 0000000000..194f8fa14c --- /dev/null +++ b/tests/auto/qml/debugger/shared/debugutil_p.h @@ -0,0 +1,127 @@ + +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DEBUGUTIL_H +#define DEBUGUTIL_H + +#include <QEventLoop> +#include <QTimer> +#include <QThread> +#include <QTest> +#include <QProcess> + +#include <QtQml/qqmlengine.h> + +#include <private/qqmldebugclient_p.h> +#include <private/qqmldebugservice_p.h> + +class QQmlDebugTest +{ +public: + static bool waitForSignal(QObject *receiver, const char *member, int timeout = 5000); +}; + +class QQmlDebugTestService : public QQmlDebugService +{ + Q_OBJECT +public: + QQmlDebugTestService(const QString &s, float version = 1, QObject *parent = 0); + +signals: + void stateHasChanged(); + +protected: + virtual void messageReceived(const QByteArray &ba); + virtual void stateChanged(State state); +}; + +class QQmlDebugTestClient : public QQmlDebugClient +{ + Q_OBJECT +public: + QQmlDebugTestClient(const QString &s, QQmlDebugConnection *c); + + QByteArray waitForResponse(); + +signals: + void stateHasChanged(); + void serverMessage(const QByteArray &); + +protected: + virtual void stateChanged(State state); + virtual void messageReceived(const QByteArray &ba); + +private: + QByteArray lastMsg; +}; + +class QQmlDebugProcess : public QObject +{ + Q_OBJECT +public: + QQmlDebugProcess(const QString &executable); + ~QQmlDebugProcess(); + + void setEnvironment(const QStringList &environment); + + void start(const QStringList &arguments); + bool waitForSessionStart(); + + QString output() const; + void stop(); + +private slots: + void processAppOutput(); + +private: + QString m_executable; + QProcess m_process; + QString m_outputBuffer; + QString m_output; + QTimer m_timer; + QEventLoop m_eventLoop; + QMutex m_mutex; + bool m_started; + QStringList m_environment; +}; + +#endif // DEBUGUTIL_H |