diff options
Diffstat (limited to 'tests/auto/qml/debugger')
79 files changed, 3607 insertions, 3767 deletions
diff --git a/tests/auto/qml/debugger/debugger.pro b/tests/auto/qml/debugger/debugger.pro index a50411e18b..5c328fbfcc 100644 --- a/tests/auto/qml/debugger/debugger.pro +++ b/tests/auto/qml/debugger/debugger.pro @@ -1,5 +1,7 @@ TEMPLATE = subdirs +SUBDIRS += qqmldebugjsserver + PUBLICTESTS += \ qdebugmessageservice \ qqmlenginedebugservice \ @@ -10,7 +12,9 @@ PUBLICTESTS += \ qqmlenginedebuginspectorintegrationtest \ qqmlenginecontrol \ qqmldebuggingenabler \ - qqmlnativeconnector + qqmlnativeconnector \ + qqmldebugprocess \ + qqmlpreview PRIVATETESTS += \ qqmldebugclient \ @@ -20,6 +24,9 @@ PRIVATETESTS += \ SUBDIRS += $$PUBLICTESTS +qqmldebugjs.depends = qqmldebugjsserver +qqmlprofilerservice.depends = qqmldebugjsserver + qtConfig(private_tests): \ SUBDIRS += $$PRIVATETESTS diff --git a/tests/auto/qml/debugger/qdebugmessageservice/qdebugmessageservice.pro b/tests/auto/qml/debugger/qdebugmessageservice/qdebugmessageservice.pro index 6c729ab235..3f2c0ca390 100644 --- a/tests/auto/qml/debugger/qdebugmessageservice/qdebugmessageservice.pro +++ b/tests/auto/qml/debugger/qdebugmessageservice/qdebugmessageservice.pro @@ -1,12 +1,10 @@ CONFIG += testcase TARGET = tst_qdebugmessageservice -QT += qml network testlib gui-private core-private +QT += network testlib gui-private core-private osx:CONFIG -= app_bundle SOURCES += tst_qdebugmessageservice.cpp -INCLUDEPATH += ../shared -include(../../../shared/util.pri) include(../shared/debugutil.pri) TESTDATA = data/* diff --git a/tests/auto/qml/debugger/qdebugmessageservice/tst_qdebugmessageservice.cpp b/tests/auto/qml/debugger/qdebugmessageservice/tst_qdebugmessageservice.cpp index f193d3928a..d2cfd3897a 100644 --- a/tests/auto/qml/debugger/qdebugmessageservice/tst_qdebugmessageservice.cpp +++ b/tests/auto/qml/debugger/qdebugmessageservice/tst_qdebugmessageservice.cpp @@ -28,7 +28,6 @@ //QQmlDebugTest #include "debugutil_p.h" -#include "../../../shared/util.h" #include <private/qqmldebugclient_p.h> #include <private/qqmldebugconnection_p.h> @@ -38,31 +37,19 @@ #include <QtCore/qlibraryinfo.h> #include <QtTest/qtest.h> -const char *NORMALMODE = "-qmljsdebugger=port:3777,3787,block"; const char *QMLFILE = "test.qml"; class QQmlDebugMsgClient; -class tst_QDebugMessageService : public QQmlDataTest +class tst_QDebugMessageService : public QQmlDebugTest { 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; + QList<QQmlDebugClient *> createClients() override; + QPointer<QQmlDebugMsgClient> m_client; }; struct LogEntry { @@ -102,21 +89,12 @@ public: 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) { QPacket ds(connection()->currentDataStreamVersion(), data); @@ -150,73 +128,16 @@ void QQmlDebugMsgClient::messageReceived(const QByteArray &data) } } -tst_QDebugMessageService::tst_QDebugMessageService() -{ -} - -void tst_QDebugMessageService::initTestCase() +QList<QQmlDebugClient *> tst_QDebugMessageService::createClients() { - 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) + "/qml", this); m_client = new QQmlDebugMsgClient(m_connection); - - m_process->start(QStringList() << QLatin1String(NORMALMODE) << QQmlDataTest::instance()->testFile(QMLFILE)); - QVERIFY2(m_process->waitForSessionStart(), - "Could not launch application, or did not get 'Waiting for connection'."); - - const int port = m_process->debugPort(); - m_connection->connectToHost("127.0.0.1", port); - QVERIFY(m_connection->waitForConnected()); - - if (m_client->state() != QQmlDebugClient::Enabled) - QQmlDebugTest::waitForSignal(m_client, SIGNAL(enabled())); - - QCOMPARE(m_client->state(), QQmlDebugClient::Enabled); -} - -void tst_QDebugMessageService::cleanup() -{ - if (QTest::currentTestFailed()) { - qDebug() << "Process State:" << m_process->state(); - qDebug() << "Application Output:" << 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; + return QList<QQmlDebugClient *>({m_client}); } void tst_QDebugMessageService::retrieveDebugOutput() { - init(); + QCOMPARE(QQmlDebugTest::connect(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qml", + QString(), testFile(QMLFILE), true), ConnectSuccess); QTRY_VERIFY(m_client->logBuffer.size() >= 2); diff --git a/tests/auto/qml/debugger/qpacketprotocol/qpacketprotocol.pro b/tests/auto/qml/debugger/qpacketprotocol/qpacketprotocol.pro index a7c0fa7f8e..dc0120cd87 100644 --- a/tests/auto/qml/debugger/qpacketprotocol/qpacketprotocol.pro +++ b/tests/auto/qml/debugger/qpacketprotocol/qpacketprotocol.pro @@ -4,7 +4,6 @@ osx:CONFIG -= app_bundle SOURCES += tst_qpacketprotocol.cpp -INCLUDEPATH += ../shared include(../shared/debugutil.pri) -QT += qml network testlib gui-private core-private +QT += network testlib gui-private core-private diff --git a/tests/auto/qml/debugger/qpacketprotocol/tst_qpacketprotocol.cpp b/tests/auto/qml/debugger/qpacketprotocol/tst_qpacketprotocol.cpp index cadc2a7cc4..21ca921304 100644 --- a/tests/auto/qml/debugger/qpacketprotocol/tst_qpacketprotocol.cpp +++ b/tests/auto/qml/debugger/qpacketprotocol/tst_qpacketprotocol.cpp @@ -60,7 +60,7 @@ private slots: void tst_QPacketProtocol::init() { m_server = new QTcpServer(this); - m_serverConn = 0; + m_serverConn = nullptr; QVERIFY(m_server->listen(QHostAddress("127.0.0.1"))); m_client = new QTcpSocket(this); diff --git a/tests/auto/qml/debugger/qqmldebugclient/qqmldebugclient.pro b/tests/auto/qml/debugger/qqmldebugclient/qqmldebugclient.pro index 622b373692..673330a3cf 100644 --- a/tests/auto/qml/debugger/qqmldebugclient/qqmldebugclient.pro +++ b/tests/auto/qml/debugger/qqmldebugclient/qqmldebugclient.pro @@ -7,7 +7,6 @@ HEADERS += ../shared/qqmldebugtestservice.h SOURCES += tst_qqmldebugclient.cpp \ ../shared/qqmldebugtestservice.cpp -INCLUDEPATH += ../shared include(../shared/debugutil.pri) DEFINES += QT_QML_DEBUG_NO_WARNING diff --git a/tests/auto/qml/debugger/qqmldebugclient/tst_qqmldebugclient.cpp b/tests/auto/qml/debugger/qqmldebugclient/tst_qqmldebugclient.cpp index 04bc5c3c1a..ffdbf72ded 100644 --- a/tests/auto/qml/debugger/qqmldebugclient/tst_qqmldebugclient.cpp +++ b/tests/auto/qml/debugger/qqmldebugclient/tst_qqmldebugclient.cpp @@ -66,17 +66,15 @@ void tst_QQmlDebugClient::initTestCase() QQmlDebugConnector::setPluginKey(QLatin1String("QQmlDebugServer")); QQmlDebugConnector::setServices(QStringList() << QStringLiteral("tst_QQmlDebugClient::handshake()")); - QTest::ignoreMessage(QtWarningMsg, - "QML debugger: Cannot set plugin key after loading the plugin."); m_service = new QQmlDebugTestService("tst_QQmlDebugClient::handshake()"); foreach (const QString &service, QQmlDebuggingEnabler::debuggerServices()) - QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)0); + QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)nullptr); foreach (const QString &service, QQmlDebuggingEnabler::inspectorServices()) - QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)0); + QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)nullptr); foreach (const QString &service, QQmlDebuggingEnabler::profilerServices()) - QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)0); + QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)nullptr); const QString waitingMsg = QString("QML Debugger: Waiting for connection on port %1...").arg(PORT); QTest::ignoreMessage(QtDebugMsg, waitingMsg.toLatin1().constData()); @@ -126,7 +124,7 @@ void tst_QQmlDebugClient::state() QQmlDebugClient client2("tst_QQmlDebugClient::state()", m_conn); QCOMPARE(client2.state(), QQmlDebugClient::NotConnected); - QQmlDebugClient client3("tst_QQmlDebugClient::state3()", 0); + QQmlDebugClient client3("tst_QQmlDebugClient::state3()", nullptr); QCOMPARE(client3.state(), QQmlDebugClient::NotConnected); } diff --git a/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/qqmldebuggingenabler.pro b/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/qqmldebuggingenabler.pro index f8014f04f4..bd382ebaab 100644 --- a/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/qqmldebuggingenabler.pro +++ b/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/qqmldebuggingenabler.pro @@ -6,8 +6,6 @@ osx:CONFIG -= app_bundle SOURCES += tst_qqmldebuggingenabler.cpp -INCLUDEPATH += ../../shared -include(../../../../shared/util.pri) include(../../shared/debugutil.pri) OTHER_FILES += data/test.qml diff --git a/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/tst_qqmldebuggingenabler.cpp b/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/tst_qqmldebuggingenabler.cpp index 3aa3a5c87e..37118f4bd0 100644 --- a/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/tst_qqmldebuggingenabler.cpp +++ b/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/tst_qqmldebuggingenabler.cpp @@ -27,11 +27,14 @@ ****************************************************************************/ #include "debugutil_p.h" +#include "qqmldebugprocess_p.h" #include "../../../shared/util.h" #include <private/qqmldebugclient_p.h> #include <private/qqmldebugconnection_p.h> +#include <QtQml/qqmldebug.h> + #include <QtTest/qtest.h> #include <QtCore/qprocess.h> #include <QtCore/qtimer.h> @@ -40,17 +43,11 @@ #include <QtCore/qmutex.h> #include <QtCore/qlibraryinfo.h> -class tst_QQmlDebuggingEnabler : public QQmlDataTest +class tst_QQmlDebuggingEnabler : public QQmlDebugTest { Q_OBJECT - bool init(bool blockMode, bool qmlscene, int portFrom, int portTo); - private slots: - void initTestCase(); - void cleanupTestCase(); - void cleanup(); - void qmlscene_data(); void qmlscene(); void custom_data(); @@ -58,88 +55,8 @@ private slots: private: void data(); - QQmlDebugProcess *process; - QQmlDebugConnection *connection; - QTime t; }; -void tst_QQmlDebuggingEnabler::initTestCase() -{ - QQmlDataTest::initTestCase(); - t.start(); - process = 0; - connection = 0; -} - -void tst_QQmlDebuggingEnabler::cleanupTestCase() -{ - if (process) { - process->stop(); - delete process; - } - - if (connection) - delete connection; -} - -bool tst_QQmlDebuggingEnabler::init(bool blockMode, bool qmlscene, int portFrom, int portTo) -{ - connection = new QQmlDebugConnection(); - - if (qmlscene) { - process = new QQmlDebugProcess(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene", this); - process->setMaximumBindErrors(1); - } else { - process = new QQmlDebugProcess(QCoreApplication::applicationDirPath() + QLatin1String("/qqmldebuggingenablerserver"), this); - process->setMaximumBindErrors(portTo - portFrom); - } - - if (qmlscene) { - process->start(QStringList() << QLatin1String("-qmljsdebugger=port:") + - QString::number(portFrom) + QLatin1Char(',') + QString::number(portTo) + - QLatin1String(blockMode ? ",block": "") << - testFile(QLatin1String("test.qml"))); - } else { - QStringList args; - if (blockMode) - args << QLatin1String("-block"); - args << QString::number(portFrom) << QString::number(portTo); - process->start(args); - } - - if (!process->waitForSessionStart()) { - return false; - } - - const int port = process->debugPort(); - connection->connectToHost("127.0.0.1", port); - if (!connection->waitForConnected()) { - qDebug() << "could not connect to host!"; - return false; - } - return true; -} - -void tst_QQmlDebuggingEnabler::cleanup() -{ - if (QTest::currentTestFailed()) { - qDebug() << "Process State:" << process->state(); - qDebug() << "Application Output:" << process->output(); - } - - if (process) { - process->stop(); - delete process; - } - - - if (connection) - delete connection; - - process = 0; - connection = 0; -} - void tst_QQmlDebuggingEnabler::data() { QTest::addColumn<QString>("connector"); @@ -185,32 +102,34 @@ void tst_QQmlDebuggingEnabler::qmlscene() QFETCH(bool, blockMode); QFETCH(QStringList, services); - process = new QQmlDebugProcess(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene", - this); - process->setMaximumBindErrors(1); - process->start(QStringList() - << QString::fromLatin1("-qmljsdebugger=connector:%1%2%3%4") - .arg(connector + (connector == QLatin1String("QQmlDebugServer") ? - QLatin1String(",port:5555,5565") : QString())) - .arg(blockMode ? QLatin1String(",block") : QString()) - .arg(services.isEmpty() ? QString() : QString::fromLatin1(",services:")) - .arg(services.isEmpty() ? QString() : services.join(",")) - << testFile(QLatin1String("test.qml"))); + m_process = new QQmlDebugProcess( + QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene", this); + m_process->setMaximumBindErrors(1); + m_process->start(QStringList() + << QString::fromLatin1("-qmljsdebugger=connector:%1%2%3%4") + .arg(connector + (connector == QLatin1String("QQmlDebugServer") + ? QLatin1String(",port:5555,5565") : QString())) + .arg(blockMode ? QLatin1String(",block") : QString()) + .arg(services.isEmpty() ? QString() : QString::fromLatin1(",services:")) + .arg(services.isEmpty() ? QString() : services.join(",")) + << testFile(QLatin1String("test.qml"))); if (connector == QLatin1String("QQmlDebugServer")) { - QVERIFY(process->waitForSessionStart()); - connection = new QQmlDebugConnection(); - QList<QQmlDebugClient *> clients = QQmlDebugTest::createOtherClients(connection); - connection->connectToHost("127.0.0.1", process->debugPort()); - QVERIFY(connection->waitForConnected()); - foreach (QQmlDebugClient *client, clients) + QVERIFY(m_process->waitForSessionStart()); + m_connection = new QQmlDebugConnection(); + m_clients = QQmlDebugTest::createOtherClients(m_connection); + m_connection->connectToHost("127.0.0.1", m_process->debugPort()); + QVERIFY(m_connection->waitForConnected()); + foreach (QQmlDebugClient *client, m_clients) QCOMPARE(client->state(), (services.isEmpty() || services.contains(client->name())) ? QQmlDebugClient::Enabled : QQmlDebugClient::Unavailable); } - QCOMPARE(process->state(), QLatin1String("running")); - if (!blockMode) - QTRY_VERIFY(process->output().contains(QLatin1String("qml: Component.onCompleted"))); + QCOMPARE(m_process->state(), QProcess::Running); + if (!blockMode) { + QTRY_VERIFY_WITH_TIMEOUT(m_process->output().contains( + QLatin1String("Component.onCompleted")), 15000); + } } void tst_QQmlDebuggingEnabler::custom_data() @@ -226,9 +145,9 @@ void tst_QQmlDebuggingEnabler::custom() const int portFrom = 5555; const int portTo = 5565; - process = new QQmlDebugProcess(QCoreApplication::applicationDirPath() + - QLatin1String("/qqmldebuggingenablerserver"), this); - process->setMaximumBindErrors(portTo - portFrom); + m_process = new QQmlDebugProcess(QCoreApplication::applicationDirPath() + + QLatin1String("/qqmldebuggingenablerserver"), this); + m_process->setMaximumBindErrors(portTo - portFrom); QStringList args; if (blockMode) @@ -240,22 +159,24 @@ void tst_QQmlDebuggingEnabler::custom() if (!services.isEmpty()) args << QLatin1String("-services") << services; - process->start(args); + m_process->start(args); if (connector == QLatin1String("QQmlDebugServer")) { - QVERIFY(process->waitForSessionStart()); - connection = new QQmlDebugConnection(); - QList<QQmlDebugClient *> clients = QQmlDebugTest::createOtherClients(connection); - connection->connectToHost("127.0.0.1", process->debugPort()); - QVERIFY(connection->waitForConnected()); - foreach (QQmlDebugClient *client, clients) + QVERIFY(m_process->waitForSessionStart()); + m_connection = new QQmlDebugConnection(); + m_clients = QQmlDebugTest::createOtherClients(m_connection); + m_connection->connectToHost("127.0.0.1", m_process->debugPort()); + QVERIFY(m_connection->waitForConnected()); + for (QQmlDebugClient *client : qAsConst(m_clients)) QCOMPARE(client->state(), (services.isEmpty() || services.contains(client->name())) ? QQmlDebugClient::Enabled : QQmlDebugClient::Unavailable); } - QCOMPARE(process->state(), QLatin1String("running")); - if (!blockMode) - QTRY_VERIFY(process->output().contains(QLatin1String("QQmlEngine created"))); + QCOMPARE(m_process->state(), QProcess::Running); + if (!blockMode) { + QTRY_VERIFY_WITH_TIMEOUT(m_process->output().contains(QLatin1String("QQmlEngine created")), + 15000); + } } QTEST_MAIN(tst_QQmlDebuggingEnabler) diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/breakOnAnchor.qml b/tests/auto/qml/debugger/qqmldebugjs/data/breakOnAnchor.qml new file mode 100644 index 0000000000..72a8c9559c --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugjs/data/breakOnAnchor.qml @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.11 + + +Item { + visible: true + Text { + anchors.centerIn: parent + text: "bla" + MouseArea { + anchors.fill: parent + } + } + + Timer { + interval: 100; + running: true; + onTriggered: { + Qt.quit(); + } + } +} diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/breakpointRelocation.qml b/tests/auto/qml/debugger/qqmldebugjs/data/breakpointRelocation.qml index 06aabc94f9..06aabc94f9 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/breakpointRelocation.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/breakpointRelocation.qml diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/changeBreakpoint.qml b/tests/auto/qml/debugger/qqmldebugjs/data/changeBreakpoint.qml index 00a85e56ac..00a85e56ac 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/changeBreakpoint.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/changeBreakpoint.qml diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/condition.qml b/tests/auto/qml/debugger/qqmldebugjs/data/condition.qml index 3a50ba2eb7..3a50ba2eb7 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/condition.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/condition.qml diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/createComponent.qml b/tests/auto/qml/debugger/qqmldebugjs/data/createComponent.qml index 089cc03733..089cc03733 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/createComponent.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/createComponent.qml diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/encodeQmlScope.qml b/tests/auto/qml/debugger/qqmldebugjs/data/encodeQmlScope.qml new file mode 100644 index 0000000000..7ea048044f --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugjs/data/encodeQmlScope.qml @@ -0,0 +1,19 @@ +import QtQuick 2.0 + +Item { + property int a: 0 + property int b: 0 + onAChanged: console.log("inline") + onBChanged: { + console.log("extra braces"); + } + + Timer { + interval: 10 + running: true + onTriggered: { + parent.a += 10; + parent.b -= 10; + } + } +} diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/exception.qml b/tests/auto/qml/debugger/qqmldebugjs/data/exception.qml index 06f11fa016..06f11fa016 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/exception.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/exception.qml diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/loadjsfile.qml b/tests/auto/qml/debugger/qqmldebugjs/data/loadjsfile.qml index 088c1b19fd..088c1b19fd 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/loadjsfile.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/loadjsfile.qml diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/oncompleted.qml b/tests/auto/qml/debugger/qqmldebugjs/data/oncompleted.qml index deba24cf91..deba24cf91 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/oncompleted.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/oncompleted.qml diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/quit.qml b/tests/auto/qml/debugger/qqmldebugjs/data/quit.qml index bc8c2b90ae..bc8c2b90ae 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/quit.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/quit.qml diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/stepAction.qml b/tests/auto/qml/debugger/qqmldebugjs/data/stepAction.qml index fb0b6c401c..fb0b6c401c 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/stepAction.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/stepAction.qml diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/test.js b/tests/auto/qml/debugger/qqmldebugjs/data/test.js index 92e61d103c..92e61d103c 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/test.js +++ b/tests/auto/qml/debugger/qqmldebugjs/data/test.js diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/test.qml b/tests/auto/qml/debugger/qqmldebugjs/data/test.qml index a36d0cae91..a36d0cae91 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/test.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/test.qml diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/timer.qml b/tests/auto/qml/debugger/qqmldebugjs/data/timer.qml index 66e6b96e18..66e6b96e18 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/data/timer.qml +++ b/tests/auto/qml/debugger/qqmldebugjs/data/timer.qml diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs.pro b/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs.pro index bd6debcea1..acd5546a02 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs.pro +++ b/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs.pro @@ -1,4 +1,24 @@ -TEMPLATE = subdirs -SUBDIRS = qqmldebugjs qqmldebugjsserver +CONFIG += testcase +TARGET = tst_qqmldebugjs +QT += testlib gui-private core-private +macos:CONFIG -= app_bundle -qqmldebugjs.depends = qqmldebugjsserver +SOURCES += tst_qqmldebugjs.cpp + +INCLUDEPATH += ../shared +include(../shared/debugutil.pri) + +TESTDATA = data/* + +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 \ + data/encodeQmlScope.qml \ + data/breakOnAnchor.qml diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/qqmldebugjs.pro b/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/qqmldebugjs.pro deleted file mode 100644 index cbaf3b5309..0000000000 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/qqmldebugjs.pro +++ /dev/null @@ -1,25 +0,0 @@ -CONFIG += testcase -TARGET = tst_qqmldebugjs -QT += qml testlib gui-private core-private -CONFIG -= debug_and_release_target -osx:CONFIG -= app_bundle - -SOURCES += tst_qqmldebugjs.cpp - -INCLUDEPATH += ../../shared -include(../../../../shared/util.pri) -include(../../shared/debugutil.pri) -include(../../shared/qqmlenginedebugclient.pri) - -TESTDATA = data/* - -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/qqmldebugjs/tst_qqmldebugjs.cpp b/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/tst_qqmldebugjs.cpp deleted file mode 100644 index d248cf9708..0000000000 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/tst_qqmldebugjs.cpp +++ /dev/null @@ -1,1552 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "debugutil_p.h" -#include "../../shared/qqmlenginedebugclient.h" -#include "../../../../shared/util.h" - -#include <private/qqmldebugclient_p.h> -#include <private/qqmldebugconnection_p.h> -#include <private/qpacket_p.h> - -#include <QtTest/qtest.h> -#include <QtCore/qprocess.h> -#include <QtCore/qtimer.h> -#include <QtCore/qfileinfo.h> -#include <QtCore/qdir.h> -#include <QtCore/qmutex.h> -#include <QtCore/qlibraryinfo.h> -#include <QtQml/qjsengine.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 *CONTEXT = "context"; -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 *CLEARBREAKPOINT = "clearbreakpoint"; -const char *SETEXCEPTIONBREAK = "setexceptionbreak"; -const char *VERSION = "version"; -const char *DISCONNECT = "disconnect"; -const char *GARBAGECOLLECTOR = "gc"; - -const char *CONNECT = "connect"; -const char *INTERRUPT = "interrupt"; - -const char *REQUEST = "request"; -const char *IN = "in"; -const char *NEXT = "next"; -const char *OUT = "out"; - -const char *SCRIPT = "script"; -const char *SCRIPTREGEXP = "scriptRegExp"; -const char *EVENT = "event"; - -const char *ALL = "all"; -const char *UNCAUGHT = "uncaught"; - -const char *BLOCKMODE = "-qmljsdebugger=port:3771,3800,block"; -const char *NORMALMODE = "-qmljsdebugger=port:3771,3800"; -const char *BLOCKRESTRICTEDMODE = "-qmljsdebugger=port:3771,3800,block,services:V8Debugger"; -const char *NORMALRESTRICTEDMODE = "-qmljsdebugger=port:3771,3800,services:V8Debugger"; -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 *QUIT_QMLFILE = "quit.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 - - void init(bool qmlscene, const QString &qmlFile = QString(TEST_QMLFILE), bool blockMode = true, - bool restrictServices = false); - -private slots: - void initTestCase(); - void cleanupTestCase(); - - void cleanup(); - - void connect_data(); - void connect(); - void interrupt_data() { targetData(); } - void interrupt(); - void getVersion_data() { targetData(); } - void getVersion(); - void getVersionWhenAttaching_data() { targetData(); } - void getVersionWhenAttaching(); - - void disconnect_data() { targetData(); } - void disconnect(); - - void setBreakpointInScriptOnCompleted_data() { targetData(); } - void setBreakpointInScriptOnCompleted(); - void setBreakpointInScriptOnComponentCreated_data() { targetData(); } - void setBreakpointInScriptOnComponentCreated(); - void setBreakpointInScriptOnTimerCallback_data() { targetData(); } - void setBreakpointInScriptOnTimerCallback(); - void setBreakpointInScriptInDifferentFile_data() { targetData(); } - void setBreakpointInScriptInDifferentFile(); - void setBreakpointInScriptOnComment_data() { targetData(); } - void setBreakpointInScriptOnComment(); - void setBreakpointInScriptOnEmptyLine_data() { targetData(); } - void setBreakpointInScriptOnEmptyLine(); - void setBreakpointInScriptOnOptimizedBinding_data() { targetData(); } - void setBreakpointInScriptOnOptimizedBinding(); - void setBreakpointInScriptWithCondition_data() { targetData(); } - void setBreakpointInScriptWithCondition(); - void setBreakpointInScriptThatQuits_data() { targetData(); } - void setBreakpointInScriptThatQuits(); - void setBreakpointWhenAttaching(); - - void clearBreakpoint_data() { targetData(); } - void clearBreakpoint(); - - void setExceptionBreak_data() { targetData(); } - void setExceptionBreak(); - - void stepNext_data() { targetData(); } - void stepNext(); - void stepIn_data() { targetData(); } - void stepIn(); - void stepOut_data() { targetData(); } - void stepOut(); - void continueDebugging_data() { targetData(); } - void continueDebugging(); - - void backtrace_data() { targetData(); } - void backtrace(); - - void getFrameDetails_data() { targetData(); } - void getFrameDetails(); - - void getScopeDetails_data() { targetData(); } - void getScopeDetails(); - - void evaluateInGlobalScope(); - void evaluateInLocalScope_data() { targetData(); } - void evaluateInLocalScope(); - - void evaluateInContext(); - - void getScripts_data() { targetData(); } - void getScripts(); - -private: - void targetData(); - - 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 - }; - - QJSDebugClient(QQmlDebugConnection *connection) - : QQmlDebugClient(QLatin1String("V8Debugger"), connection), - seq(0) - { - parser = jsEngine.evaluate(QLatin1String("JSON.parse")); - stringify = jsEngine.evaluate(QLatin1String("JSON.stringify")); - } - - void connect(bool redundantRefs = false, bool namesAsObjects = false); - void interrupt(); - - void continueDebugging(StepAction stepAction); - void evaluate(QString expr, int frame = -1, int context = -1); - 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 scripts(int types = 4, QList<int> ids = QList<int>(), bool includeSource = false, QVariant filter = QVariant()); - void setBreakpoint(QString target, int line = -1, int column = -1, bool enabled = true, - QString condition = QString(), int ignoreCount = -1); - void clearBreakpoint(int breakpoint); - void setExceptionBreak(Exception type, bool enabled = false); - void version(); - void disconnect(); - -protected: - //inherited from QQmlDebugClient - void stateChanged(State state); - void messageReceived(const QByteArray &data); - -signals: - void enabled(); - void connected(); - void interruptRequested(); - void result(); - void failure(); - 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(bool redundantRefs, bool namesAsObjects) -{ - QJSValue jsonVal = parser.call(QJSValueList() << QLatin1String("{}")); - jsonVal.setProperty("redundantRefs", QJSValue(redundantRefs)); - jsonVal.setProperty("namesAsObjects", QJSValue(namesAsObjects)); - sendMessage(packMessage(CONNECT, - stringify.call(QJSValueList() << jsonVal).toString().toUtf8())); -} - -void QJSDebugClient::interrupt() -{ - sendMessage(packMessage(INTERRUPT)); -} - -void QJSDebugClient::continueDebugging(StepAction action) -{ - // { "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()) { - jsonVal.setProperty(QLatin1String(ARGUMENTS),args); - } - } - QJSValue json = stringify.call(QJSValueList() << jsonVal); - sendMessage(packMessage(V8REQUEST, json.toString().toUtf8())); -} - -void QJSDebugClient::evaluate(QString expr, int frame, int context) -{ - // { "seq" : <number>, - // "type" : "request", - // "command" : "evaluate", - // "arguments" : { "expression" : <expression to evaluate>, - // "frame" : <number>, - // "context" : <object ID> - // } - // } - 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 (context != -1) - args.setProperty(QLatin1String(CONTEXT), QJSValue(context)); - - 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::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::setBreakpoint(QString target, int line, int column, bool enabled, - QString condition, int ignoreCount) -{ - // { "seq" : <number>, - // "type" : "request", - // "command" : "setbreakpoint", - // "arguments" : { "type" : "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> - // } - // } - - VARIANTMAPINIT; - jsonVal.setProperty(QLatin1String(COMMAND),QJSValue(QLatin1String(SETBREAKPOINT))); - - QJSValue args = parser.call(QJSValueList() << obj); - - args.setProperty(QLatin1String(TYPE),QJSValue(QLatin1String(SCRIPTREGEXP))); - 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::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::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::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::stateChanged(State state) -{ - if (state == Enabled) { - flushSendBuffer(); - emit enabled(); - } -} - -void QJSDebugClient::messageReceived(const QByteArray &data) -{ - QPacket ds(connection()->currentDataStreamVersion(), 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()) { - emit failure(); - qDebug() << "Received success == false response from application"; - return; - } - - QString debugCommand(value.value("command").toString()); - if (debugCommand == "backtrace" || - debugCommand == "lookup" || - debugCommand == "setbreakpoint" || - debugCommand == "evaluate" || - debugCommand == "version" || - 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(); - } - - } - } -} - -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) -{ - QPacket rs(connection()->currentDataStreamVersion()); - QByteArray cmd = "V8DEBUG"; - rs << cmd << type << message; - return rs.data(); -} - -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; -} - -void tst_QQmlDebugJS::init(bool qmlscene, const QString &qmlFile, bool blockMode, - bool restrictServices) -{ - connection = new QQmlDebugConnection(); - if (qmlscene) - process = new QQmlDebugProcess(QLibraryInfo::location(QLibraryInfo::BinariesPath) + - "/qmlscene", this); - else - process = new QQmlDebugProcess(QCoreApplication::applicationDirPath() + - QLatin1String("/qqmldebugjsserver"), this); - client = new QJSDebugClient(connection); - QList<QQmlDebugClient *> others = QQmlDebugTest::createOtherClients(connection); - - const char *args = 0; - if (blockMode) - args = restrictServices ? BLOCKRESTRICTEDMODE : BLOCKMODE; - else - args = restrictServices ? NORMALRESTRICTEDMODE : NORMALMODE; - - process->start(QStringList() << QLatin1String(args) << testFile(qmlFile)); - - QVERIFY(process->waitForSessionStart()); - - const int port = process->debugPort(); - connection->connectToHost("127.0.0.1", port); - QVERIFY(connection->waitForConnected()); - - - if (client->state() != QQmlDebugClient::Enabled) - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(enabled()))); - - foreach (QQmlDebugClient *otherClient, others) - QCOMPARE(otherClient->state(), restrictServices ? QQmlDebugClient::Unavailable : - QQmlDebugClient::Enabled); - qDeleteAll(others); -} - -void tst_QQmlDebugJS::cleanup() -{ - if (QTest::currentTestFailed()) { - qDebug() << "Process State:" << process->state(); - qDebug() << "Application Output:" << process->output(); - } - - if (process) { - process->stop(); - delete process; - } - - if (client) - delete client; - - if (connection) - delete connection; - - process = 0; - client = 0; - connection = 0; -} - -void tst_QQmlDebugJS::connect_data() -{ - QTest::addColumn<bool>("blockMode"); - QTest::addColumn<bool>("restrictMode"); - QTest::addColumn<bool>("qmlscene"); - QTest::newRow("normal / unrestricted / custom") << false << false << false; - QTest::newRow("block / unrestricted / custom") << true << false << false; - QTest::newRow("normal / restricted / custom") << false << true << false; - QTest::newRow("block / restricted / custom") << true << true << false; - QTest::newRow("normal / unrestricted / qmlscene") << false << false << true; - QTest::newRow("block / unrestricted / qmlscene") << true << false << true; - QTest::newRow("normal / restricted / qmlscene") << false << true << true; - QTest::newRow("block / restricted / qmlscene") << true << true << true; -} - -void tst_QQmlDebugJS::connect() -{ - QFETCH(bool, blockMode); - QFETCH(bool, restrictMode); - QFETCH(bool, qmlscene); - init(qmlscene, QString(TEST_QMLFILE), blockMode, restrictMode); - client->connect(); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(connected()))); -} - -void tst_QQmlDebugJS::interrupt() -{ - //void connect() - - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - init(qmlscene); - client->connect(redundantRefs, namesAsObjects); - - client->interrupt(); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(interruptRequested()))); -} - -void tst_QQmlDebugJS::getVersion() -{ - //void version() - - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - init(qmlscene); - client->connect(redundantRefs, namesAsObjects); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(connected()))); - - client->version(); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); -} - -void tst_QQmlDebugJS::getVersionWhenAttaching() -{ - //void version() - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - - init(qmlscene, QLatin1String(TIMER_QMLFILE), false); - client->connect(redundantRefs, namesAsObjects); - - client->version(); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); -} - -void tst_QQmlDebugJS::disconnect() -{ - //void disconnect() - - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - init(qmlscene); - client->connect(redundantRefs, namesAsObjects); - - client->disconnect(); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); -} - -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) - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - - int sourceLine = 34; - init(qmlscene, ONCOMPLETED_QMLFILE); - - client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); - client->connect(redundantRefs, namesAsObjects); - 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) - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - - int sourceLine = 34; - init(qmlscene, CREATECOMPONENT_QMLFILE); - - client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); - client->connect(redundantRefs, namesAsObjects); - 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() -{ - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - int sourceLine = 35; - init(qmlscene, TIMER_QMLFILE); - - client->connect(redundantRefs, namesAsObjects); - //We can set the breakpoint after connect() here because the timer is repeating and if we miss - //its first iteration we can still catch the second one. - client->setBreakpoint(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) - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - - int sourceLine = 31; - init(qmlscene, LOADJSFILE_QMLFILE); - - client->setBreakpoint(QLatin1String(TEST_JSFILE), sourceLine, -1, true); - client->connect(redundantRefs, namesAsObjects); - 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) - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - - int sourceLine = 34; - int actualLine = 36; - init(qmlscene, BREAKPOINTRELOCATION_QMLFILE); - - client->setBreakpoint(QLatin1String(BREAKPOINTRELOCATION_QMLFILE), sourceLine, -1, true); - client->connect(redundantRefs, namesAsObjects); - 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) - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - - int sourceLine = 35; - int actualLine = 36; - init(qmlscene, BREAKPOINTRELOCATION_QMLFILE); - - client->setBreakpoint(QLatin1String(BREAKPOINTRELOCATION_QMLFILE), sourceLine, -1, true); - client->connect(redundantRefs, namesAsObjects); - 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::setBreakpointInScriptOnOptimizedBinding() -{ - //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - - int sourceLine = 39; - init(qmlscene, BREAKPOINTRELOCATION_QMLFILE); - - client->setBreakpoint(QLatin1String(BREAKPOINTRELOCATION_QMLFILE), sourceLine, -1, true); - client->connect(redundantRefs, namesAsObjects); - 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(BREAKPOINTRELOCATION_QMLFILE)); -} - -void tst_QQmlDebugJS::setBreakpointInScriptWithCondition() -{ - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - int out = 10; - int sourceLine = 37; - init(qmlscene, CONDITION_QMLFILE); - - client->connect(redundantRefs, namesAsObjects); - //The breakpoint is in a timer loop so we can set it after connect(). - client->setBreakpoint(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; - QJSValue val = client->parser.call(QJSValueList() << QJSValue(jsonString)); - QVERIFY(val.isObject()); - QJSValue body = val.property(QStringLiteral("body")); - QVERIFY(body.isObject()); - val = body.property("value"); - QVERIFY(val.isNumber()); - - const int a = val.toInt(); - QVERIFY(a > out); -} - -void tst_QQmlDebugJS::setBreakpointInScriptThatQuits() -{ - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - init(qmlscene, QUIT_QMLFILE); - - int sourceLine = 36; - - client->setBreakpoint(QLatin1String(QUIT_QMLFILE), sourceLine, -1, true); - client->connect(redundantRefs, namesAsObjects); - 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(QUIT_QMLFILE)); - - client->continueDebugging(QJSDebugClient::Continue); - QVERIFY(process->waitForFinished()); - QCOMPARE(process->exitStatus(), QProcess::NormalExit); -} - -void tst_QQmlDebugJS::setBreakpointWhenAttaching() -{ - int sourceLine = 35; - init(true, QLatin1String(TIMER_QMLFILE), false); - - client->connect(); - - QSKIP("\nThe breakpoint may not hit because the engine may run in JIT mode or not have debug\n" - "instructions, as we've connected in non-blocking mode above. That means we may have\n" - "connected after the engine was already running, with all the QML already compiled."); - - //The breakpoint is in a timer loop so we can set it after connect(). - client->setBreakpoint(QLatin1String(TIMER_QMLFILE), sourceLine); - - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); -} - -void tst_QQmlDebugJS::clearBreakpoint() -{ - //void clearBreakpoint(int breakpoint); - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - - int sourceLine1 = 37; - int sourceLine2 = 38; - init(qmlscene, CHANGEBREAKPOINT_QMLFILE); - - client->connect(redundantRefs, namesAsObjects); - //The breakpoints are in a timer loop so we can set them after connect(). - //Furthermore the breakpoints should be hit in the right order because setting of breakpoints - //can only occur in the QML event loop. (see QCOMPARE for sourceLine2 below) - client->setBreakpoint(QLatin1String(CHANGEBREAKPOINT_QMLFILE), sourceLine1, -1, true); - client->setBreakpoint(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); - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - - init(qmlscene, EXCEPTION_QMLFILE); - client->setExceptionBreak(QJSDebugClient::All,true); - client->connect(redundantRefs, namesAsObjects); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); -} - -void tst_QQmlDebugJS::stepNext() -{ - //void continueDebugging(StepAction stepAction, int stepCount = 1); - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - - int sourceLine = 37; - init(qmlscene, STEPACTION_QMLFILE); - - client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine, -1, true); - client->connect(redundantRefs, namesAsObjects); - 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::stepIn() -{ - //void continueDebugging(StepAction stepAction, int stepCount = 1); - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - - int sourceLine = 41; - int actualLine = 37; - init(qmlscene, STEPACTION_QMLFILE); - - client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine, 1, true); - client->connect(redundantRefs, namesAsObjects); - 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); - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - - int sourceLine = 37; - int actualLine = 41; - init(qmlscene, STEPACTION_QMLFILE); - - client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine, -1, true); - client->connect(redundantRefs, namesAsObjects); - 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); - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - - int sourceLine1 = 41; - int sourceLine2 = 38; - init(qmlscene, STEPACTION_QMLFILE); - - client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine1, -1, true); - client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine2, -1, true); - client->connect(redundantRefs, namesAsObjects); - 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); - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - - int sourceLine = 34; - init(qmlscene, ONCOMPLETED_QMLFILE); - - client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); - client->connect(redundantRefs, namesAsObjects); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); - - client->backtrace(); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); -} - -void tst_QQmlDebugJS::getFrameDetails() -{ - //void frame(int number = -1); - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - - int sourceLine = 34; - init(qmlscene, ONCOMPLETED_QMLFILE); - - client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); - client->connect(redundantRefs, namesAsObjects); - 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); - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - - int sourceLine = 34; - init(qmlscene, ONCOMPLETED_QMLFILE); - - client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); - client->connect(redundantRefs, namesAsObjects); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); - - client->scope(); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); -} - -void tst_QQmlDebugJS::evaluateInGlobalScope() -{ - //void evaluate(QString expr, int frame = -1); - init(true); - - client->connect(); - - do { - // The engine might not be initialized, yet. We just try until it shows up. - client->evaluate(QLatin1String("console.log('Hello World')")); - } while (!QQmlDebugTest::waitForSignal(client, SIGNAL(result()), 500)); - - //Verify the return value of 'console.log()', which is "undefined" - QString jsonString(client->response); - QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - QVariantMap body = value.value("body").toMap(); - QCOMPARE(body.value("type").toString(),QLatin1String("undefined")); -} - -void tst_QQmlDebugJS::evaluateInLocalScope() -{ - //void evaluate(QString expr, bool global = false, bool disableBreak = false, int frame = -1, const QVariantMap &addContext = QVariantMap()); - - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - int sourceLine = 34; - init(qmlscene, ONCOMPLETED_QMLFILE); - - client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); - client->connect(redundantRefs, namesAsObjects); - 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::evaluateInContext() -{ - connection = new QQmlDebugConnection(); - process = new QQmlDebugProcess(QLibraryInfo::location(QLibraryInfo::BinariesPath) - + "/qmlscene", this); - client = new QJSDebugClient(connection); - QScopedPointer<QQmlEngineDebugClient> engineClient(new QQmlEngineDebugClient(connection)); - process->start(QStringList() << QLatin1String(BLOCKMODE) << testFile(ONCOMPLETED_QMLFILE)); - - QVERIFY(process->waitForSessionStart()); - - connection->connectToHost("127.0.0.1", process->debugPort()); - QVERIFY(connection->waitForConnected()); - - QTRY_COMPARE(client->state(), QQmlEngineDebugClient::Enabled); - QTRY_COMPARE(engineClient->state(), QQmlEngineDebugClient::Enabled); - client->connect(); - - // "a" not accessible without extra context - client->evaluate(QLatin1String("a + 10"), -1, -1); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(failure()))); - - bool success = false; - engineClient->queryAvailableEngines(&success); - QVERIFY(success); - QVERIFY(QQmlDebugTest::waitForSignal(engineClient.data(), SIGNAL(result()))); - - QVERIFY(engineClient->engines().count()); - engineClient->queryRootContexts(engineClient->engines()[0].debugId, &success); - QVERIFY(success); - QVERIFY(QQmlDebugTest::waitForSignal(engineClient.data(), SIGNAL(result()))); - - auto contexts = engineClient->rootContext().contexts; - QCOMPARE(contexts.count(), 1); - auto objects = contexts[0].objects; - QCOMPARE(objects.count(), 1); - engineClient->queryObjectRecursive(objects[0], &success); - QVERIFY(success); - QVERIFY(QQmlDebugTest::waitForSignal(engineClient.data(), SIGNAL(result()))); - auto object = engineClient->object(); - - // "a" accessible in context of surrounding object - client->evaluate(QLatin1String("a + 10"), -1, object.debugId); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); - - QString jsonString = client->response; - QVariantMap value = client->parser.call(QJSValueList() << QJSValue(jsonString)).toVariant().toMap(); - - QVariantMap body = value.value("body").toMap(); - QTRY_COMPARE(body.value("value").toInt(), 20); -} - -void tst_QQmlDebugJS::getScripts() -{ - //void scripts(int types = -1, QList<int> ids = QList<int>(), bool includeSource = false, QVariant filter = QVariant()); - - QFETCH(bool, qmlscene); - QFETCH(bool, redundantRefs); - QFETCH(bool, namesAsObjects); - init(qmlscene); - - client->setBreakpoint(QString(TEST_QMLFILE), 35, -1, true); - client->connect(redundantRefs, namesAsObjects); - QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); - - 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(), 1); - QVERIFY(scripts.first().toMap()[QStringLiteral("name")].toString().endsWith(QStringLiteral("data/test.qml"))); -} - -void tst_QQmlDebugJS::targetData() -{ - QTest::addColumn<bool>("qmlscene"); - QTest::addColumn<bool>("redundantRefs"); - QTest::addColumn<bool>("namesAsObjects"); - QTest::newRow("custom / redundant / objects") << false << true << true; - QTest::newRow("qmlscene / redundant / objects") << true << true << true; - QTest::newRow("custom / redundant / strings") << false << true << false; - QTest::newRow("qmlscene / redundant / strings") << true << true << false; - QTest::newRow("custom / sparse / objects") << false << false << true; - QTest::newRow("qmlscene / sparse / objects") << true << false << true; - QTest::newRow("custom / sparse / strings") << false << false << false; - QTest::newRow("qmlscene / sparse / strings") << true << false << false; -} - -QTEST_MAIN(tst_QQmlDebugJS) - -#include "tst_qqmldebugjs.moc" - diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjsserver/qqmldebugjsserver.pro b/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjsserver/qqmldebugjsserver.pro deleted file mode 100644 index 837eaed9f1..0000000000 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjsserver/qqmldebugjsserver.pro +++ /dev/null @@ -1,12 +0,0 @@ -QT += qml testlib -osx:CONFIG -= app_bundle -CONFIG -= debug_and_release_target -INCLUDEPATH += ../../shared -SOURCES += qqmldebugjsserver.cpp -DEFINES += QT_QML_DEBUG_NO_WARNING - -DESTDIR = ../qqmldebugjs - -target.path = $$[QT_INSTALL_TESTS]/tst_qqmldebugjs -INSTALLS += target - 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..1ac28c473b --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp @@ -0,0 +1,1008 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "debugutil_p.h" +#include "qqmldebugprocess_p.h" +#include "../../../shared/util.h" + +#include <private/qqmlenginedebugclient_p.h> +#include <private/qv4debugclient_p.h> +#include <private/qqmldebugconnection_p.h> +#include <private/qpacket_p.h> + +#include <QtTest/qtest.h> +#include <QtTest/qtestsystem.h> +#include <QtCore/qprocess.h> +#include <QtCore/qtimer.h> +#include <QtCore/qfileinfo.h> +#include <QtCore/qdir.h> +#include <QtCore/qmutex.h> +#include <QtCore/qlibraryinfo.h> +#include <QtCore/qjsonobject.h> +#include <QtCore/qjsonarray.h> + +const char *BLOCKMODE = "-qmljsdebugger=port:3771,3800,block"; +const char *NORMALMODE = "-qmljsdebugger=port:3771,3800"; +const char *BLOCKRESTRICTEDMODE = "-qmljsdebugger=port:3771,3800,block,services:V8Debugger"; +const char *NORMALRESTRICTEDMODE = "-qmljsdebugger=port:3771,3800,services:V8Debugger"; +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 *QUIT_QMLFILE = "quit.qml"; +const char *CHANGEBREAKPOINT_QMLFILE = "changeBreakpoint.qml"; +const char *STEPACTION_QMLFILE = "stepAction.qml"; +const char *BREAKPOINTRELOCATION_QMLFILE = "breakpointRelocation.qml"; +const char *ENCODEQMLSCOPE_QMLFILE = "encodeQmlScope.qml"; +const char *BREAKONANCHOR_QMLFILE = "breakOnAnchor.qml"; + +#undef QVERIFY +#define QVERIFY(statement) \ +do {\ + if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__)) {\ + if (QTest::currentTestFailed()) \ + qDebug().nospace() << "\nDEBUGGEE OUTPUT:\n" << m_process->output();\ + return;\ + }\ +} while (0) + +class tst_QQmlDebugJS : public QQmlDebugTest +{ + Q_OBJECT + +private slots: + void initTestCase() override; + + void connect_data(); + void connect(); + void interrupt_data() { targetData(); } + void interrupt(); + void getVersion_data() { targetData(); } + void getVersion(); + void getVersionWhenAttaching_data() { targetData(); } + void getVersionWhenAttaching(); + + void disconnect_data() { targetData(); } + void disconnect(); + + void setBreakpointInScriptOnCompleted_data() { targetData(); } + void setBreakpointInScriptOnCompleted(); + void setBreakpointInScriptOnComponentCreated_data() { targetData(); } + void setBreakpointInScriptOnComponentCreated(); + void setBreakpointInScriptOnTimerCallback_data() { targetData(); } + void setBreakpointInScriptOnTimerCallback(); + void setBreakpointInScriptInDifferentFile_data() { targetData(); } + void setBreakpointInScriptInDifferentFile(); + void setBreakpointInScriptOnComment_data() { targetData(); } + void setBreakpointInScriptOnComment(); + void setBreakpointInScriptOnEmptyLine_data() { targetData(); } + void setBreakpointInScriptOnEmptyLine(); + void setBreakpointInScriptOnOptimizedBinding_data() { targetData(); } + void setBreakpointInScriptOnOptimizedBinding(); + void setBreakpointInScriptWithCondition_data() { targetData(); } + void setBreakpointInScriptWithCondition(); + void setBreakpointInScriptThatQuits_data() { targetData(); } + void setBreakpointInScriptThatQuits(); + void setBreakpointWhenAttaching(); + + void clearBreakpoint_data() { targetData(); } + void clearBreakpoint(); + + void changeBreakpoint_data() { targetData(); } + void changeBreakpoint(); + + void setExceptionBreak_data() { targetData(); } + void setExceptionBreak(); + + void stepNext_data() { targetData(); } + void stepNext(); + void stepIn_data() { targetData(); } + void stepIn(); + void stepOut_data() { targetData(); } + void stepOut(); + void continueDebugging_data() { targetData(); } + void continueDebugging(); + + void backtrace_data() { targetData(); } + void backtrace(); + + void getFrameDetails_data() { targetData(); } + void getFrameDetails(); + + void getScopeDetails_data() { targetData(); } + void getScopeDetails(); + + void evaluateInGlobalScope(); + void evaluateInLocalScope_data() { targetData(); } + void evaluateInLocalScope(); + + void evaluateInContext(); + + void getScripts_data() { targetData(); } + void getScripts(); + + void encodeQmlScope(); + void breakOnAnchor(); + +private: + ConnectResult init(bool qmlscene, const QString &qmlFile = QString(TEST_QMLFILE), + bool blockMode = true, bool restrictServices = false); + QList<QQmlDebugClient *> createClients() override; + QPointer<QV4DebugClient> m_client; + + void targetData(); + bool waitForClientSignal(const char *signal, int timeout = 30000); + void checkVersionParameters(); + + QTime t; +}; + + + +void tst_QQmlDebugJS::initTestCase() +{ + QQmlDebugTest::initTestCase(); + t.start(); +} + +QQmlDebugTest::ConnectResult tst_QQmlDebugJS::init(bool qmlscene, const QString &qmlFile, + bool blockMode, bool restrictServices) +{ + const QString executable = qmlscene + ? QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene" + : debugJsServerPath("qqmldebugjs"); + return QQmlDebugTest::connect( + executable, restrictServices ? QStringLiteral("V8Debugger") : QString(), + testFile(qmlFile), blockMode); +} + +void tst_QQmlDebugJS::connect_data() +{ + QTest::addColumn<bool>("blockMode"); + QTest::addColumn<bool>("restrictMode"); + QTest::addColumn<bool>("qmlscene"); + QTest::newRow("normal / unrestricted / custom") << false << false << false; + QTest::newRow("block / unrestricted / custom") << true << false << false; + QTest::newRow("normal / restricted / custom") << false << true << false; + QTest::newRow("block / restricted / custom") << true << true << false; + QTest::newRow("normal / unrestricted / qmlscene") << false << false << true; + QTest::newRow("block / unrestricted / qmlscene") << true << false << true; + QTest::newRow("normal / restricted / qmlscene") << false << true << true; + QTest::newRow("block / restricted / qmlscene") << true << true << true; +} + +void tst_QQmlDebugJS::connect() +{ + QFETCH(bool, blockMode); + QFETCH(bool, restrictMode); + QFETCH(bool, qmlscene); + + QCOMPARE(init(qmlscene, QString(TEST_QMLFILE), blockMode, restrictMode), ConnectSuccess); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(connected()))); +} + +void tst_QQmlDebugJS::interrupt() +{ + //void connect() + QFETCH(bool, qmlscene); + + QCOMPARE(init(qmlscene), ConnectSuccess); + m_client->connect(); + + m_client->interrupt(); + QVERIFY(waitForClientSignal(SIGNAL(interrupted()))); +} + +void tst_QQmlDebugJS::getVersion() +{ + //void version() + QFETCH(bool, qmlscene); + + QCOMPARE(init(qmlscene), ConnectSuccess); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(connected()))); + + m_client->version(); + QVERIFY(waitForClientSignal(SIGNAL(result()))); + checkVersionParameters(); +} + +void tst_QQmlDebugJS::getVersionWhenAttaching() +{ + //void version() + QFETCH(bool, qmlscene); + + QCOMPARE(init(qmlscene, QLatin1String(TIMER_QMLFILE), false), ConnectSuccess); + m_client->connect(); + + m_client->version(); + QVERIFY(waitForClientSignal(SIGNAL(result()))); + checkVersionParameters(); +} + +void tst_QQmlDebugJS::disconnect() +{ + //void disconnect() + QFETCH(bool, qmlscene); + + QCOMPARE(init(qmlscene), ConnectSuccess); + m_client->connect(); + + m_client->disconnect(); + QVERIFY(waitForClientSignal(SIGNAL(result()))); +} + +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) + QFETCH(bool, qmlscene); + + int sourceLine = 34; + QCOMPARE(init(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess); + + m_client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + const QJsonObject body = m_client->response().body.toObject(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine); + QCOMPARE(QFileInfo(body.value("script").toObject().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) + QFETCH(bool, qmlscene); + + int sourceLine = 34; + QCOMPARE(init(qmlscene, CREATECOMPONENT_QMLFILE), ConnectSuccess); + + m_client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + const QJsonObject body = m_client->response().body.toObject(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), + QLatin1String(ONCOMPLETED_QMLFILE)); +} + +void tst_QQmlDebugJS::setBreakpointInScriptOnTimerCallback() +{ + QFETCH(bool, qmlscene); + + int sourceLine = 35; + QCOMPARE(init(qmlscene, TIMER_QMLFILE), ConnectSuccess); + + m_client->connect(); + //We can set the breakpoint after connect() here because the timer is repeating and if we miss + //its first iteration we can still catch the second one. + m_client->setBreakpoint(QLatin1String(TIMER_QMLFILE), sourceLine, -1, true); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + const QJsonObject body = m_client->response().body.toObject(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine); + QCOMPARE(QFileInfo(body.value("script").toObject().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) + QFETCH(bool, qmlscene); + + int sourceLine = 31; + QCOMPARE(init(qmlscene, LOADJSFILE_QMLFILE), ConnectSuccess); + + m_client->setBreakpoint(QLatin1String(TEST_JSFILE), sourceLine, -1, true); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + const QJsonObject body = m_client->response().body.toObject(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine); + QCOMPARE(QFileInfo(body.value("script").toObject().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) + QFETCH(bool, qmlscene); + + int sourceLine = 34; + int actualLine = 36; + QCOMPARE(init(qmlscene, BREAKPOINTRELOCATION_QMLFILE), ConnectSuccess); + + m_client->setBreakpoint(QLatin1String(BREAKPOINTRELOCATION_QMLFILE), sourceLine, -1, true); + m_client->connect(); + QEXPECT_FAIL("", "Relocation of breakpoints is disabled right now", Abort); + QVERIFY(waitForClientSignal(SIGNAL(stopped()), 1)); + + const QJsonObject body = m_client->response().body.toObject(); + + QCOMPARE(body.value("sourceLine").toInt(), actualLine); + QCOMPARE(QFileInfo(body.value("script").toObject().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) + QFETCH(bool, qmlscene); + + int sourceLine = 35; + int actualLine = 36; + QCOMPARE(init(qmlscene, BREAKPOINTRELOCATION_QMLFILE), ConnectSuccess); + + m_client->setBreakpoint(QLatin1String(BREAKPOINTRELOCATION_QMLFILE), sourceLine, -1, true); + m_client->connect(); + QEXPECT_FAIL("", "Relocation of breakpoints is disabled right now", Abort); + QVERIFY(waitForClientSignal(SIGNAL(stopped()), 1)); + + const QJsonObject body = m_client->response().body.toObject(); + + QCOMPARE(body.value("sourceLine").toInt(), actualLine); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), + QLatin1String(BREAKPOINTRELOCATION_QMLFILE)); +} + +void tst_QQmlDebugJS::setBreakpointInScriptOnOptimizedBinding() +{ + //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) + QFETCH(bool, qmlscene); + + int sourceLine = 39; + QCOMPARE(init(qmlscene, BREAKPOINTRELOCATION_QMLFILE), ConnectSuccess); + + m_client->setBreakpoint(QLatin1String(BREAKPOINTRELOCATION_QMLFILE), sourceLine, -1, true); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + const QJsonObject body = m_client->response().body.toObject(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), + QLatin1String(BREAKPOINTRELOCATION_QMLFILE)); +} + +void tst_QQmlDebugJS::setBreakpointInScriptWithCondition() +{ + QFETCH(bool, qmlscene); + + int out = 10; + int sourceLine = 37; + QCOMPARE(init(qmlscene, CONDITION_QMLFILE), ConnectSuccess); + + m_client->connect(); + //The breakpoint is in a timer loop so we can set it after connect(). + m_client->setBreakpoint(QLatin1String(CONDITION_QMLFILE), sourceLine, 1, true, QLatin1String("a > 10")); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + //Get the frame index + { + const QJsonObject body = m_client->response().body.toObject(); + int frameIndex = body.value("index").toInt(); + + //Verify the value of 'result' + m_client->evaluate(QLatin1String("a"),frameIndex); + QVERIFY(waitForClientSignal(SIGNAL(result()))); + } + + const QJsonObject body = m_client->response().body.toObject(); + QVERIFY(!body.isEmpty()); + QJsonValue val = body.value("value"); + QVERIFY(val.isDouble()); + + const int a = val.toInt(); + QVERIFY(a > out); +} + +void tst_QQmlDebugJS::setBreakpointInScriptThatQuits() +{ + QFETCH(bool, qmlscene); + + QCOMPARE(init(qmlscene, QUIT_QMLFILE), ConnectSuccess); + + int sourceLine = 36; + + m_client->setBreakpoint(QLatin1String(QUIT_QMLFILE), sourceLine, -1, true); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + const QJsonObject body = m_client->response().body.toObject(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), QLatin1String(QUIT_QMLFILE)); + + m_client->continueDebugging(QV4DebugClient::Continue); + QVERIFY(m_process->waitForFinished()); + QCOMPARE(m_process->exitStatus(), QProcess::NormalExit); +} + +void tst_QQmlDebugJS::setBreakpointWhenAttaching() +{ + int sourceLine = 35; + QCOMPARE(init(true, QLatin1String(TIMER_QMLFILE), false), ConnectSuccess); + + m_client->connect(); + + QSKIP("\nThe breakpoint may not hit because the engine may run in JIT mode or not have debug\n" + "instructions, as we've connected in non-blocking mode above. That means we may have\n" + "connected after the engine was already running, with all the QML already compiled."); + + //The breakpoint is in a timer loop so we can set it after connect(). + m_client->setBreakpoint(QLatin1String(TIMER_QMLFILE), sourceLine); + + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); +} + +void tst_QQmlDebugJS::clearBreakpoint() +{ + //void clearBreakpoint(int breakpoint); + QFETCH(bool, qmlscene); + + int sourceLine1 = 37; + int sourceLine2 = 38; + QCOMPARE(init(qmlscene, CHANGEBREAKPOINT_QMLFILE), ConnectSuccess); + + m_client->connect(); + //The breakpoints are in a timer loop so we can set them after connect(). + //Furthermore the breakpoints should be hit in the right order because setting of breakpoints + //can only occur in the QML event loop. (see QCOMPARE for sourceLine2 below) + m_client->setBreakpoint(QLatin1String(CHANGEBREAKPOINT_QMLFILE), sourceLine1, -1, true); + m_client->setBreakpoint(QLatin1String(CHANGEBREAKPOINT_QMLFILE), sourceLine2, -1, true); + + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + { + //Will hit 1st brakpoint, change this breakpoint enable = false + const QJsonObject body = m_client->response().body.toObject(); + const QJsonArray breakpointsHit = body.value("breakpoints").toArray(); + + int breakpoint = breakpointsHit.at(0).toInt(); + m_client->clearBreakpoint(breakpoint); + + QVERIFY(waitForClientSignal(SIGNAL(result()))); + + //Continue with debugging + m_client->continueDebugging(QV4DebugClient::Continue); + //Hit 2nd breakpoint + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + //Continue with debugging + m_client->continueDebugging(QV4DebugClient::Continue); + } + + //Should stop at 2nd breakpoint + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + { + const QJsonObject body = m_client->response().body.toObject(); + QCOMPARE(body.value("sourceLine").toInt(), sourceLine2); + } +} + +void tst_QQmlDebugJS::changeBreakpoint() +{ + //void clearBreakpoint(int breakpoint); + QFETCH(bool, qmlscene); + + int sourceLine2 = 37; + int sourceLine1 = 38; + QCOMPARE(init(qmlscene, CHANGEBREAKPOINT_QMLFILE), ConnectSuccess); + + bool isStopped = false; + QObject::connect(m_client.data(), &QV4DebugClient::stopped, this, [&]() { isStopped = true; }); + + auto continueDebugging = [&]() { + m_client->continueDebugging(QV4DebugClient::Continue); + isStopped = false; + }; + + m_client->connect(); + + auto extractBody = [&]() { + return m_client->response().body.toObject(); + }; + + auto extractBreakPointId = [&](const QJsonObject &body) { + const QJsonArray breakpointsHit = body.value("breakpoints").toArray(); + if (breakpointsHit.size() != 1) + return -1; + return breakpointsHit[0].toInt(); + }; + + auto setBreakPoint = [&](int sourceLine, bool enabled) { + int id = -1; + auto connection = QObject::connect(m_client.data(), &QV4DebugClient::result, [&]() { + id = extractBody().value("breakpoint").toInt(); + }); + + m_client->setBreakpoint(QLatin1String(CHANGEBREAKPOINT_QMLFILE), sourceLine, -1, enabled); + bool success = QTest::qWaitFor([&]() { return id >= 0; }); + Q_UNUSED(success); + + QObject::disconnect(connection); + return id; + }; + + //The breakpoints are in a timer loop so we can set them after connect(). + //Furthermore the breakpoints should be hit in the right order because setting of breakpoints + //can only occur in the QML event loop. (see QCOMPARE for sourceLine2 below) + const int breakpoint1 = setBreakPoint(sourceLine1, false); + QVERIFY(breakpoint1 >= 0); + + const int breakpoint2 = setBreakPoint(sourceLine2, true); + QVERIFY(breakpoint2 >= 0); + + auto verifyBreakpoint = [&](int sourceLine, int breakpointId) { + QTRY_VERIFY_WITH_TIMEOUT(isStopped, 30000); + const QJsonObject body = extractBody(); + QCOMPARE(body.value("sourceLine").toInt(), sourceLine); + QCOMPARE(extractBreakPointId(body), breakpointId); + }; + + verifyBreakpoint(sourceLine2, breakpoint2); + + continueDebugging(); + verifyBreakpoint(sourceLine2, breakpoint2); + + m_client->changeBreakpoint(breakpoint2, false); + QVERIFY(waitForClientSignal(SIGNAL(result()))); + + m_client->changeBreakpoint(breakpoint1, true); + QVERIFY(waitForClientSignal(SIGNAL(result()))); + + continueDebugging(); + verifyBreakpoint(sourceLine1, breakpoint1); + + continueDebugging(); + verifyBreakpoint(sourceLine1, breakpoint1); + + m_client->changeBreakpoint(breakpoint2, true); + QVERIFY(waitForClientSignal(SIGNAL(result()))); + + m_client->changeBreakpoint(breakpoint1, false); + QVERIFY(waitForClientSignal(SIGNAL(result()))); + + for (int i = 0; i < 3; ++i) { + continueDebugging(); + verifyBreakpoint(sourceLine2, breakpoint2); + } +} + +void tst_QQmlDebugJS::setExceptionBreak() +{ + //void setExceptionBreak(QString type, bool enabled = false); + QFETCH(bool, qmlscene); + + QCOMPARE(init(qmlscene, EXCEPTION_QMLFILE), ConnectSuccess); + m_client->setExceptionBreak(QV4DebugClient::All,true); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); +} + +void tst_QQmlDebugJS::stepNext() +{ + //void continueDebugging(StepAction stepAction, int stepCount = 1); + QFETCH(bool, qmlscene); + + int sourceLine = 37; + QCOMPARE(init(qmlscene, STEPACTION_QMLFILE), ConnectSuccess); + + m_client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine, -1, true); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + m_client->continueDebugging(QV4DebugClient::Next); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + const QJsonObject body = m_client->response().body.toObject(); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine + 1); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), + QLatin1String(STEPACTION_QMLFILE)); +} + +static QJsonObject responseBody(QV4DebugClient *client) +{ + return client->response().body.toObject(); +} + +void tst_QQmlDebugJS::stepIn() +{ + //void continueDebugging(StepAction stepAction, int stepCount = 1); + QFETCH(bool, qmlscene); + + int sourceLine = 41; + int actualLine = 36; + QCOMPARE(init(qmlscene, STEPACTION_QMLFILE), ConnectSuccess); + + m_client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine, 1, true); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + QCOMPARE(responseBody(m_client).value("sourceLine").toInt(), sourceLine); + + m_client->continueDebugging(QV4DebugClient::In); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + const QJsonObject body = responseBody(m_client); + QCOMPARE(body.value("sourceLine").toInt(), actualLine); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), QLatin1String(STEPACTION_QMLFILE)); +} + +void tst_QQmlDebugJS::stepOut() +{ + //void continueDebugging(StepAction stepAction, int stepCount = 1); + QFETCH(bool, qmlscene); + + int sourceLine = 37; + int actualLine = 41; + QCOMPARE(init(qmlscene, STEPACTION_QMLFILE), ConnectSuccess); + + m_client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine, -1, true); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + QCOMPARE(responseBody(m_client).value("sourceLine").toInt(), sourceLine); + + m_client->continueDebugging(QV4DebugClient::Out); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + const QJsonObject body = responseBody(m_client); + QCOMPARE(body.value("sourceLine").toInt(), actualLine); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), QLatin1String(STEPACTION_QMLFILE)); +} + +void tst_QQmlDebugJS::continueDebugging() +{ + //void continueDebugging(StepAction stepAction, int stepCount = 1); + QFETCH(bool, qmlscene); + + int sourceLine1 = 41; + int sourceLine2 = 38; + QCOMPARE(init(qmlscene, STEPACTION_QMLFILE), ConnectSuccess); + + m_client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine1, -1, true); + m_client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine2, -1, true); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + m_client->continueDebugging(QV4DebugClient::Continue); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + const QJsonObject body = responseBody(m_client); + + QCOMPARE(body.value("sourceLine").toInt(), sourceLine2); + QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), + QLatin1String(STEPACTION_QMLFILE)); +} + +void tst_QQmlDebugJS::backtrace() +{ + //void backtrace(int fromFrame = -1, int toFrame = -1, bool bottom = false); + QFETCH(bool, qmlscene); + + int sourceLine = 34; + QCOMPARE(init(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess); + + m_client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + m_client->backtrace(); + QVERIFY(waitForClientSignal(SIGNAL(result()))); +} + +void tst_QQmlDebugJS::getFrameDetails() +{ + //void frame(int number = -1); + QFETCH(bool, qmlscene); + + int sourceLine = 34; + QCOMPARE(init(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess); + + m_client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + m_client->frame(); + QVERIFY(waitForClientSignal(SIGNAL(result()))); +} + +void tst_QQmlDebugJS::getScopeDetails() +{ + //void scope(int number = -1, int frameNumber = -1); + QFETCH(bool, qmlscene); + + int sourceLine = 34; + QCOMPARE(init(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess); + + m_client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + m_client->scope(); + QVERIFY(waitForClientSignal(SIGNAL(result()))); +} + +void tst_QQmlDebugJS::evaluateInGlobalScope() +{ + //void evaluate(QString expr, int frame = -1); + QCOMPARE(init(true), ConnectSuccess); + + m_client->connect(); + + for (int i = 0; i < 10; ++i) { + // The engine might not be initialized, yet. We just try until it shows up. + m_client->evaluate(QLatin1String("console.log('Hello World')")); + if (waitForClientSignal(SIGNAL(result()), 500)) + break; + } + + //Verify the return value of 'console.log()', which is "undefined" + QCOMPARE(responseBody(m_client).value("type").toString(), QLatin1String("undefined")); +} + +void tst_QQmlDebugJS::evaluateInLocalScope() +{ + //void evaluate(QString expr, bool global = false, bool disableBreak = false, int frame = -1, const QVariantMap &addContext = QVariantMap()); + QFETCH(bool, qmlscene); + + int sourceLine = 34; + QCOMPARE(init(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess); + + m_client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + m_client->frame(); + QVERIFY(waitForClientSignal(SIGNAL(result()))); + + { + //Get the frame index + const QJsonObject body = responseBody(m_client); + int frameIndex = body.value("index").toInt(); + m_client->evaluate(QLatin1String("root.a"), frameIndex); + QVERIFY(waitForClientSignal(SIGNAL(result()))); + } + + { + //Verify the value of 'timer.interval' + const QJsonObject body = responseBody(m_client); + QCOMPARE(body.value("value").toInt(),10); + } +} + +void tst_QQmlDebugJS::evaluateInContext() +{ + m_connection = new QQmlDebugConnection(); + m_process = new QQmlDebugProcess( + QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene", this); + m_client = new QV4DebugClient(m_connection); + QScopedPointer<QQmlEngineDebugClient> engineClient(new QQmlEngineDebugClient(m_connection)); + m_process->start(QStringList() << QLatin1String(BLOCKMODE) << testFile(ONCOMPLETED_QMLFILE)); + + QVERIFY(m_process->waitForSessionStart()); + + m_connection->connectToHost("127.0.0.1", m_process->debugPort()); + QVERIFY(m_connection->waitForConnected()); + + QTRY_COMPARE(m_client->state(), QQmlEngineDebugClient::Enabled); + QTRY_COMPARE(engineClient->state(), QQmlEngineDebugClient::Enabled); + m_client->connect(); + + // "a" not accessible without extra context + m_client->evaluate(QLatin1String("a + 10"), -1, -1); + QVERIFY(waitForClientSignal(SIGNAL(failure()))); + + bool success = false; + engineClient->queryAvailableEngines(&success); + QVERIFY(success); + QVERIFY(QQmlDebugTest::waitForSignal(engineClient.data(), SIGNAL(result()))); + + QVERIFY(engineClient->engines().count()); + engineClient->queryRootContexts(engineClient->engines()[0], &success); + QVERIFY(success); + QVERIFY(QQmlDebugTest::waitForSignal(engineClient.data(), SIGNAL(result()))); + + auto contexts = engineClient->rootContext().contexts; + QCOMPARE(contexts.count(), 1); + auto objects = contexts[0].objects; + QCOMPARE(objects.count(), 1); + engineClient->queryObjectRecursive(objects[0], &success); + QVERIFY(success); + QVERIFY(QQmlDebugTest::waitForSignal(engineClient.data(), SIGNAL(result()))); + auto object = engineClient->object(); + + // "a" accessible in context of surrounding object + m_client->evaluate(QLatin1String("a + 10"), -1, object.debugId); + QVERIFY(waitForClientSignal(SIGNAL(result()))); + + QTRY_COMPARE(responseBody(m_client).value("value").toInt(), 20); +} + +void tst_QQmlDebugJS::getScripts() +{ + //void scripts(int types = -1, QList<int> ids = QList<int>(), bool includeSource = false, QVariant filter = QVariant()); + QFETCH(bool, qmlscene); + + QCOMPARE(init(qmlscene), ConnectSuccess); + + m_client->setBreakpoint(QString(TEST_QMLFILE), 35, -1, true); + m_client->connect(); + QVERIFY(waitForClientSignal(SIGNAL(stopped()))); + + m_client->scripts(); + QVERIFY(waitForClientSignal(SIGNAL(result()))); + + const QJsonArray scripts = m_client->response().body.toArray(); + + QCOMPARE(scripts.count(), 1); + QVERIFY(scripts.first().toObject()[QStringLiteral("name")].toString() + .endsWith(QStringLiteral("data/test.qml"))); +} + +void tst_QQmlDebugJS::encodeQmlScope() +{ + QString file(ENCODEQMLSCOPE_QMLFILE); + QCOMPARE(init(true, file), ConnectSuccess); + + int numFrames = 0; + int numExpectedScopes = 0; + int numReceivedScopes = 0; + bool isStopped = false; + bool scopesFailed = false; + + QObject::connect(m_client.data(), &QV4DebugClient::failure, this, [&]() { + qWarning() << "received failure" << m_client->response().body; + scopesFailed = true; + m_process->stop(); + numFrames = 2; + isStopped = false; + }); + + QObject::connect(m_client.data(), &QV4DebugClient::stopped, this, [&]() { + m_client->frame(); + isStopped = true; + }); + + QObject::connect(m_client.data(), &QV4DebugClient::result, this, [&]() { + const QV4DebugClient::Response value = m_client->response(); + + if (value.command == QString("scope")) { + // If the scope commands fail we get a failure() signal above. + if (++numReceivedScopes == numExpectedScopes) { + m_client->continueDebugging(QV4DebugClient::Continue); + isStopped = false; + } + } else if (value.command == QString("frame")) { + + // We want at least a global scope and some kind of local scope here. + const QJsonArray scopes = value.body.toObject().value("scopes").toArray(); + if (scopes.count() < 2) + scopesFailed = true; + + for (const QJsonValue &scope : scopes) { + ++numExpectedScopes; + m_client->scope(scope.toObject().value("index").toInt()); + } + + ++numFrames; + } + }); + + m_client->setBreakpoint(file, 6); + m_client->setBreakpoint(file, 8); + m_client->connect(); + + QTRY_COMPARE(numFrames, 2); + QVERIFY(numExpectedScopes > 3); + QVERIFY(!scopesFailed); + QTRY_VERIFY(!isStopped); + QCOMPARE(numReceivedScopes, numExpectedScopes); +} + +void tst_QQmlDebugJS::breakOnAnchor() +{ + QString file(BREAKONANCHOR_QMLFILE); + QCOMPARE(init(true, file), ConnectSuccess); + + int breaks = 0; + bool stopped = false; + QObject::connect(m_client.data(), &QV4DebugClient::stopped, this, [&]() { + stopped = true; + ++breaks; + m_client->evaluate("this", 0, -1); + }); + + QObject::connect(m_client.data(), &QV4DebugClient::result, this, [&]() { + if (stopped) { + m_client->continueDebugging(QV4DebugClient::Continue); + stopped = false; + } + }); + + QObject::connect(m_client.data(), &QV4DebugClient::failure, this, [&]() { + qWarning() << "received failure" << m_client->response().body; + }); + + m_client->setBreakpoint(file, 34); + m_client->setBreakpoint(file, 37); + + QTRY_COMPARE(m_process->state(), QProcess::Running); + + m_client->connect(); + + QTRY_COMPARE(m_process->state(), QProcess::NotRunning); + QCOMPARE(m_process->exitStatus(), QProcess::NormalExit); + + QCOMPARE(breaks, 2); +} + +QList<QQmlDebugClient *> tst_QQmlDebugJS::createClients() +{ + m_client = new QV4DebugClient(m_connection); + return QList<QQmlDebugClient *>({m_client}); +} + +void tst_QQmlDebugJS::targetData() +{ + QTest::addColumn<bool>("qmlscene"); + QTest::newRow("custom") << false; + QTest::newRow("qmlscene") << true; +} + +bool tst_QQmlDebugJS::waitForClientSignal(const char *signal, int timeout) +{ + return QQmlDebugTest::waitForSignal(m_client.data(), signal, timeout); +} + +void tst_QQmlDebugJS::checkVersionParameters() +{ + const QV4DebugClient::Response value = m_client->response(); + QCOMPARE(value.command, QString("version")); + const QJsonObject body = value.body.toObject(); + QCOMPARE(body.value("UnpausedEvaluate").toBool(), true); + QCOMPARE(body.value("ContextEvaluate").toBool(), true); + QCOMPARE(body.value("ChangeBreakpoint").toBool(), true); +} + +QTEST_MAIN(tst_QQmlDebugJS) + +#include "tst_qqmldebugjs.moc" + diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjsserver/qqmldebugjsserver.cpp b/tests/auto/qml/debugger/qqmldebugjsserver/qqmldebugjsserver.cpp index 6a4ec5cc75..6a4ec5cc75 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjsserver/qqmldebugjsserver.cpp +++ b/tests/auto/qml/debugger/qqmldebugjsserver/qqmldebugjsserver.cpp diff --git a/tests/auto/qml/debugger/qqmldebugjsserver/qqmldebugjsserver.pro b/tests/auto/qml/debugger/qqmldebugjsserver/qqmldebugjsserver.pro new file mode 100644 index 0000000000..a31da57054 --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugjsserver/qqmldebugjsserver.pro @@ -0,0 +1,9 @@ +QT += qml testlib +macos:CONFIG -= app_bundle +INCLUDEPATH += ../shared +SOURCES += qqmldebugjsserver.cpp +DEFINES += QT_QML_DEBUG_NO_WARNING + +target.path = $$[QT_INSTALL_TESTS]/qqmldebugjsserver +INSTALLS += target + diff --git a/tests/auto/qml/debugger/qqmldebuglocal/qqmldebuglocal.pro b/tests/auto/qml/debugger/qqmldebuglocal/qqmldebuglocal.pro index 860d39cca4..1dc9de8f34 100644 --- a/tests/auto/qml/debugger/qqmldebuglocal/qqmldebuglocal.pro +++ b/tests/auto/qml/debugger/qqmldebuglocal/qqmldebuglocal.pro @@ -7,7 +7,6 @@ HEADERS += ../shared/qqmldebugtestservice.h SOURCES += tst_qqmldebuglocal.cpp \ ../shared/qqmldebugtestservice.cpp -INCLUDEPATH += ../shared include(../shared/debugutil.pri) QT += qml-private testlib gui-private core-private diff --git a/tests/auto/qml/debugger/qqmldebuglocal/tst_qqmldebuglocal.cpp b/tests/auto/qml/debugger/qqmldebuglocal/tst_qqmldebuglocal.cpp index 8d21a8a45a..4e47c92c2a 100644 --- a/tests/auto/qml/debugger/qqmldebuglocal/tst_qqmldebuglocal.cpp +++ b/tests/auto/qml/debugger/qqmldebuglocal/tst_qqmldebuglocal.cpp @@ -32,6 +32,8 @@ #include <private/qqmldebugconnector_p.h> #include <private/qqmldebugconnection_p.h> +#include <QtQml/qqmlengine.h> + #include <QtTest/qtest.h> #include <QtTest/qsignalspy.h> #include <QtNetwork/qhostaddress.h> @@ -61,10 +63,8 @@ private slots: void tst_QQmlDebugLocal::initTestCase() { - fileName = QString::fromLatin1("tst_QQmlDebugLocal%1").arg(std::time(0)); + fileName = QString::fromLatin1("tst_QQmlDebugLocal%1").arg(std::time(nullptr)); QQmlDebugConnector::setPluginKey("QQmlDebugServer"); - QTest::ignoreMessage(QtWarningMsg, - "QML debugger: Cannot set plugin key after loading the plugin."); m_service = new QQmlDebugTestService("tst_QQmlDebugLocal::handshake()"); const QString waitingMsg = QString("QML Debugger: Connecting to socket %1...").arg(fileName); @@ -116,7 +116,7 @@ void tst_QQmlDebugLocal::state() QQmlDebugClient client2("tst_QQmlDebugLocal::state()", m_conn); QCOMPARE(client2.state(), QQmlDebugClient::NotConnected); - QQmlDebugClient client3("tst_QQmlDebugLocal::state3()", 0); + QQmlDebugClient client3("tst_QQmlDebugLocal::state3()", nullptr); QCOMPARE(client3.state(), QQmlDebugClient::NotConnected); } diff --git a/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess.pro b/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess.pro new file mode 100644 index 0000000000..331d87b9f1 --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs +SUBDIRS = qqmldebugprocess qqmldebugprocessprocess + +qqmldebugprocess.depends = qqmldebugprocessprocess diff --git a/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess/qqmldebugprocess.pro b/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess/qqmldebugprocess.pro new file mode 100644 index 0000000000..9bea2d222c --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess/qqmldebugprocess.pro @@ -0,0 +1,14 @@ +CONFIG += testcase +TARGET = tst_qqmldebugprocess +QT = core testlib +CONFIG -= debug_and_release_target +macos:CONFIG -= app_bundle + +SOURCES += \ + ../../shared/qqmldebugprocess.cpp \ + tst_qqmldebugprocess.cpp + +HEADERS += \ + ../../shared/qqmldebugprocess_p.h + +INCLUDEPATH += ../../shared diff --git a/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess/tst_qqmldebugprocess.cpp b/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess/tst_qqmldebugprocess.cpp new file mode 100644 index 0000000000..35bd912d9b --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocess/tst_qqmldebugprocess.cpp @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qqmldebugprocess_p.h> +#include <QtTest> + +#include <QtCore/qcoreapplication.h> +#include <QtCore/qscopedpointer.h> + +class tst_QQmlDebugProcess : public QObject +{ + Q_OBJECT + +private slots: + void sessionStart_data(); + void sessionStart(); +}; + +void tst_QQmlDebugProcess::sessionStart_data() +{ + QTest::addColumn<int>("delay"); + QTest::addColumn<QString>("arg"); + QTest::addColumn<bool>("sessionExpected"); + QTest::addColumn<bool>("outputExpected"); + + QTest::addRow("synchronous / waiting") << -1 + << "QML Debugger: Waiting for connection on port 2423..." + << true << false; + QTest::addRow("synchronous / failed") << -1 << "QML Debugger: Unable to listen to port 242." + << false << false; + QTest::addRow("synchronous / unknown") << -1 << "QML Debugger: You don't know this string." + << false << true; + QTest::addRow("synchronous / appout") << -1 << "Output from app itself." + << false << true; + + QTest::addRow("no delay / waiting") << 0 + << "QML Debugger: Waiting for connection on port 2423..." + << true << false; + QTest::addRow("no delay / failed") << 0 << "QML Debugger: Unable to listen to port 242." + << false << false; + QTest::addRow("no delay / unknown") << 0 << "QML Debugger: You don't know this string." + << false << true; + QTest::addRow("no delay / appout") << 0 << "Output from app itself." + << false << true; + + QTest::addRow("delay / waiting") << 1000 + << "QML Debugger: Waiting for connection on port 2423..." + << true << false; + QTest::addRow("delay / failed") << 1000 << "QML Debugger: Unable to listen to port 242." + << false << false; + QTest::addRow("delay / unknown") << 1000 << "QML Debugger: You don't know this string." + << false << true; + QTest::addRow("delay / appout") << 1000 << "Output from app itself." + << false << true; +} + +void tst_QQmlDebugProcess::sessionStart() +{ + QFETCH(int, delay); + QFETCH(QString, arg); + QFETCH(bool, sessionExpected); + QFETCH(bool, outputExpected); + + QScopedPointer<QQmlDebugProcess> process( + new QQmlDebugProcess(QCoreApplication::applicationDirPath() + + QLatin1String("/qqmldebugprocessprocess"), this)); + QVERIFY(process); + + bool outputReceived = false; + connect(process.data(), &QQmlDebugProcess::readyReadStandardOutput, this, [&]() { + QVERIFY(outputExpected); + QVERIFY(!outputReceived); + QCOMPARE(process->output().trimmed(), arg); + outputReceived = true; + QTimer::singleShot(qMax(delay, 0), process.data(), &QQmlDebugProcess::stop); + }); + + if (!outputExpected && !sessionExpected) + QTest::ignoreMessage(QtWarningMsg, "App was unable to bind to port!"); + process->start(QStringList({arg})); + + bool done = false; + auto wait = [&](){ + QCOMPARE(process->waitForSessionStart(), sessionExpected); + QCOMPARE(outputReceived, outputExpected); + process->stop(); + done = true; + }; + + if (delay < 0) + wait(); + else + QTimer::singleShot(delay, process.data(), wait); + + QTRY_VERIFY(done); + QCOMPARE(process->state(), QProcess::NotRunning); +} + +QTEST_MAIN(tst_QQmlDebugProcess) + +#include "tst_qqmldebugprocess.moc" diff --git a/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocessprocess/qqmldebugprocessprocess.cpp b/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocessprocess/qqmldebugprocessprocess.cpp new file mode 100644 index 0000000000..21cce53fc3 --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocessprocess/qqmldebugprocessprocess.cpp @@ -0,0 +1,39 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qdebug.h> +#include <QtCore/qcoreapplication.h> + +// Process that just outputs a fake "Waiting" message. +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + if (argc > 1) + qDebug() << argv[1]; + return app.exec(); +} diff --git a/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocessprocess/qqmldebugprocessprocess.pro b/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocessprocess/qqmldebugprocessprocess.pro new file mode 100644 index 0000000000..a8eb4885d4 --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugprocess/qqmldebugprocessprocess/qqmldebugprocessprocess.pro @@ -0,0 +1,11 @@ +QT = core +macos:CONFIG -= app_bundle +CONFIG -= debug_and_release_target +CONFIG += console +SOURCES += qqmldebugprocessprocess.cpp + +DESTDIR = ../qqmldebugprocess + +target.path = $$[QT_INSTALL_TESTS]/tst_qqmldebugprocess +INSTALLS += target + diff --git a/tests/auto/qml/debugger/qqmldebugservice/qqmldebugservice.pro b/tests/auto/qml/debugger/qqmldebugservice/qqmldebugservice.pro index 79cbe52331..3101d09ea5 100644 --- a/tests/auto/qml/debugger/qqmldebugservice/qqmldebugservice.pro +++ b/tests/auto/qml/debugger/qqmldebugservice/qqmldebugservice.pro @@ -7,8 +7,6 @@ HEADERS += ../shared/qqmldebugtestservice.h SOURCES += tst_qqmldebugservice.cpp \ ../shared/qqmldebugtestservice.cpp -INCLUDEPATH += ../shared -include(../../../shared/util.pri) include(../shared/debugutil.pri) TESTDATA = data/* diff --git a/tests/auto/qml/debugger/qqmldebugservice/tst_qqmldebugservice.cpp b/tests/auto/qml/debugger/qqmldebugservice/tst_qqmldebugservice.cpp index 8092faba04..3557940386 100644 --- a/tests/auto/qml/debugger/qqmldebugservice/tst_qqmldebugservice.cpp +++ b/tests/auto/qml/debugger/qqmldebugservice/tst_qqmldebugservice.cpp @@ -29,6 +29,7 @@ #include "qqmldebugtestservice.h" #include "debugutil_p.h" +#include "qqmldebugprocess_p.h" #include "../../../shared/util.h" #include <private/qqmldebugclient_p.h> @@ -74,16 +75,14 @@ void tst_QQmlDebugService::initTestCase() QQmlDebugConnector::setPluginKey(QLatin1String("QQmlDebugServer")); QQmlDebugConnector::setServices(QStringList() << QStringLiteral("tst_QQmlDebugService")); - QTest::ignoreMessage(QtWarningMsg, - "QML debugger: Cannot set plugin key after loading the plugin."); m_service = new QQmlDebugTestService("tst_QQmlDebugService", 2); foreach (const QString &service, QQmlDebuggingEnabler::debuggerServices()) - QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)0); + QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)nullptr); foreach (const QString &service, QQmlDebuggingEnabler::inspectorServices()) - QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)0); + QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)nullptr); foreach (const QString &service, QQmlDebuggingEnabler::profilerServices()) - QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)0); + QCOMPARE(QQmlDebugConnector::instance()->service(service), (QQmlDebugService *)nullptr); const QString waitingMsg = QString("QML Debugger: Waiting for connection on port %1...").arg(PORT); QTest::ignoreMessage(QtDebugMsg, waitingMsg.toLatin1().constData()); @@ -105,10 +104,13 @@ void tst_QQmlDebugService::initTestCase() void tst_QQmlDebugService::checkPortRange() { - QQmlDebugConnection *connection1 = new QQmlDebugConnection(); - QQmlDebugProcess *process1 = new QQmlDebugProcess(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene", this); + QScopedPointer<QQmlDebugConnection> connection1(new QQmlDebugConnection()); + QScopedPointer<QQmlDebugProcess> process1( + new QQmlDebugProcess(QLibraryInfo::location(QLibraryInfo::BinariesPath) + + "/qmlscene", this)); - process1->start(QStringList() << QLatin1String("-qmljsdebugger=port:3782,3792") << testFile("test.qml")); + process1->start(QStringList() << QLatin1String("-qmljsdebugger=port:3782,3792") + << testFile("test.qml")); if (!process1->waitForSessionStart()) QFAIL("could not launch application, or did not get 'Waiting for connection'."); @@ -119,10 +121,13 @@ void tst_QQmlDebugService::checkPortRange() QFAIL("could not connect to host!"); // Second instance - QQmlDebugConnection *connection2 = new QQmlDebugConnection(); - QQmlDebugProcess *process2 = new QQmlDebugProcess(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene", this); + QScopedPointer<QQmlDebugConnection> connection2(new QQmlDebugConnection()); + QScopedPointer<QQmlDebugProcess> process2( + new QQmlDebugProcess(QLibraryInfo::location(QLibraryInfo::BinariesPath) + + "/qmlscene", this)); - process2->start(QStringList() << QLatin1String("-qmljsdebugger=port:3782,3792") << testFile("test.qml")); + process2->start(QStringList() << QLatin1String("-qmljsdebugger=port:3782,3792") + << testFile("test.qml")); if (!process2->waitForSessionStart()) QFAIL("could not launch application, or did not get 'Waiting for connection'."); @@ -131,11 +136,6 @@ void tst_QQmlDebugService::checkPortRange() connection2->connectToHost("127.0.0.1", port2); if (!connection2->waitForConnected()) QFAIL("could not connect to host!"); - - delete connection1; - delete process1; - delete connection2; - delete process2; } void tst_QQmlDebugService::name() @@ -207,7 +207,7 @@ void tst_QQmlDebugService::checkSupportForDataStreamVersion() void tst_QQmlDebugService::idForObject() { - QCOMPARE(QQmlDebugService::idForObject(0), -1); + QCOMPARE(QQmlDebugService::idForObject(nullptr), -1); QObject *objA = new QObject; @@ -229,15 +229,15 @@ void tst_QQmlDebugService::idForObject() void tst_QQmlDebugService::objectForId() { - QCOMPARE(QQmlDebugService::objectForId(-1), static_cast<QObject*>(0)); - QCOMPARE(QQmlDebugService::objectForId(1), static_cast<QObject*>(0)); + QCOMPARE(QQmlDebugService::objectForId(-1), static_cast<QObject*>(nullptr)); + QCOMPARE(QQmlDebugService::objectForId(1), static_cast<QObject*>(nullptr)); QObject *obj = new QObject; int id = QQmlDebugService::idForObject(obj); QCOMPARE(QQmlDebugService::objectForId(id), obj); delete obj; - QCOMPARE(QQmlDebugService::objectForId(id), static_cast<QObject*>(0)); + QCOMPARE(QQmlDebugService::objectForId(id), static_cast<QObject*>(nullptr)); } void tst_QQmlDebugService::checkSupportForOldDataStreamVersion() diff --git a/tests/auto/qml/debugger/qqmlenginecontrol/qqmlenginecontrol.pro b/tests/auto/qml/debugger/qqmlenginecontrol/qqmlenginecontrol.pro index 2518650493..36957628b2 100644 --- a/tests/auto/qml/debugger/qqmlenginecontrol/qqmlenginecontrol.pro +++ b/tests/auto/qml/debugger/qqmlenginecontrol/qqmlenginecontrol.pro @@ -4,13 +4,11 @@ osx:CONFIG -= app_bundle SOURCES += tst_qqmlenginecontrol.cpp -INCLUDEPATH += ../shared -include(../../../shared/util.pri) include(../shared/debugutil.pri) TESTDATA = data/* -QT += core qml testlib gui-private core-private +QT += core testlib gui-private core-private OTHER_FILES += \ data/test.qml \ diff --git a/tests/auto/qml/debugger/qqmlenginecontrol/tst_qqmlenginecontrol.cpp b/tests/auto/qml/debugger/qqmlenginecontrol/tst_qqmlenginecontrol.cpp index 3f8731ce6b..a8c43b1c75 100644 --- a/tests/auto/qml/debugger/qqmlenginecontrol/tst_qqmlenginecontrol.cpp +++ b/tests/auto/qml/debugger/qqmlenginecontrol/tst_qqmlenginecontrol.cpp @@ -37,9 +37,6 @@ #include <QtTest/qtest.h> #include <QtCore/qlibraryinfo.h> -#define STR_PORT_FROM "13773" -#define STR_PORT_TO "13783" - class QQmlEngineBlocker : public QObject { Q_OBJECT @@ -64,77 +61,37 @@ void QQmlEngineBlocker::blockEngine(int engineId, const QString &name) static_cast<QQmlEngineControlClient *>(parent())->blockEngine(engineId); } -class tst_QQmlEngineControl : public QQmlDataTest +class tst_QQmlEngineControl : public QQmlDebugTest { Q_OBJECT -public: - tst_QQmlEngineControl() - : m_process(0) - , m_connection(0) - , m_client(0) - {} - private: - QQmlDebugProcess *m_process; - QQmlDebugConnection *m_connection; - QQmlEngineControlClient *m_client; + ConnectResult connect(const QString &testFile, bool restrictServices); + QList<QQmlDebugClient *> createClients() override; - void connect(const QString &testFile, bool restrictServices); void engine_data(); + QPointer<QQmlEngineControlClient> m_client; private slots: - void cleanup(); - void startEngine_data(); void startEngine(); void stopEngine_data(); void stopEngine(); }; -void tst_QQmlEngineControl::connect(const QString &testFile, bool restrictServices) +QQmlDebugTest::ConnectResult tst_QQmlEngineControl::connect(const QString &file, + bool restrictServices) { - const QString executable = QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene"; - QStringList arguments; - arguments << QString::fromLatin1("-qmljsdebugger=port:%1,%2,block%3") - .arg(STR_PORT_FROM).arg(STR_PORT_TO) - .arg(restrictServices ? QStringLiteral(",services:EngineControl") : QString()); - - arguments << QQmlDataTest::instance()->testFile(testFile); - - m_process = new QQmlDebugProcess(executable, this); - m_process->start(QStringList() << arguments); - QVERIFY2(m_process->waitForSessionStart(), "Could not launch application, or did not get 'Waiting for connection'."); - - m_connection = new QQmlDebugConnection(); - m_client = new QQmlEngineControlClient(m_connection); - new QQmlEngineBlocker(m_client); - QList<QQmlDebugClient *> others = QQmlDebugTest::createOtherClients(m_connection); - - const int port = m_process->debugPort(); - m_connection->connectToHost(QLatin1String("127.0.0.1"), port); - - QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled); - foreach (QQmlDebugClient *other, others) - QCOMPARE(other->state(), restrictServices ? QQmlDebugClient::Unavailable : - QQmlDebugClient::Enabled); - qDeleteAll(others); + return QQmlDebugTest::connect(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene", + restrictServices ? QStringLiteral("EngineControl") : QString(), + testFile(file), true); } -void tst_QQmlEngineControl::cleanup() +QList<QQmlDebugClient *> tst_QQmlEngineControl::createClients() { - if (QTest::currentTestFailed()) { - qDebug() << "Process State:" << (m_process ? m_process->state() : QLatin1String("null")); - qDebug() << "Application Output:" << (m_process ? m_process->output() : QLatin1String("null")); - qDebug() << "Connection State:" << QQmlDebugTest::connectionStateString(m_connection); - qDebug() << "Client State:" << QQmlDebugTest::clientStateString(m_client); - } - delete m_process; - m_process = 0; - delete m_client; - m_client = 0; - delete m_connection; - m_connection = 0; + m_client = new QQmlEngineControlClient(m_connection); + new QQmlEngineBlocker(m_client); + return QList<QQmlDebugClient *>({m_client}); } void tst_QQmlEngineControl::engine_data() @@ -152,14 +109,16 @@ void tst_QQmlEngineControl::startEngine_data() void tst_QQmlEngineControl::startEngine() { QFETCH(bool, restrictMode); - - connect("test.qml", restrictMode); + QCOMPARE(connect("test.qml", restrictMode), ConnectSuccess); QTRY_VERIFY(!m_client->blockedEngines().empty()); m_client->releaseEngine(m_client->blockedEngines().last()); + QVERIFY(m_client->blockedEngines().isEmpty()); QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(engineAdded(int,QString))), "No engine start message received in time."); + + QVERIFY(m_client->blockedEngines().isEmpty()); } void tst_QQmlEngineControl::stopEngine_data() @@ -171,19 +130,23 @@ void tst_QQmlEngineControl::stopEngine() { QFETCH(bool, restrictMode); - connect("exit.qml", restrictMode); + QCOMPARE(connect("exit.qml", restrictMode), ConnectSuccess); QTRY_VERIFY(!m_client->blockedEngines().empty()); m_client->releaseEngine(m_client->blockedEngines().last()); + QVERIFY(m_client->blockedEngines().isEmpty()); QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(engineAdded(int,QString))), "No engine start message received in time."); + QVERIFY(m_client->blockedEngines().isEmpty()); QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(engineAboutToBeRemoved(int,QString))), "No engine about to stop message received in time."); m_client->releaseEngine(m_client->blockedEngines().last()); + QVERIFY(m_client->blockedEngines().isEmpty()); QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(engineRemoved(int,QString))), "No engine stop message received in time."); + QVERIFY(m_client->blockedEngines().isEmpty()); } QTEST_MAIN(tst_QQmlEngineControl) diff --git a/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/qqmlenginedebuginspectorintegrationtest.pro b/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/qqmlenginedebuginspectorintegrationtest.pro index e11ccdc6ca..454a1e3ab0 100644 --- a/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/qqmlenginedebuginspectorintegrationtest.pro +++ b/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/qqmlenginedebuginspectorintegrationtest.pro @@ -1,15 +1,11 @@ CONFIG += testcase TARGET = tst_qqmlenginedebuginspectorintegration -QT += qml testlib gui-private core-private +QT += testlib gui-private core-private osx:CONFIG -= app_bundle SOURCES += tst_qqmlenginedebuginspectorintegration.cpp -INCLUDEPATH += ../shared -include(../../../shared/util.pri) -include(../shared/qqmlinspectorclient.pri) -include(../shared/qqmlenginedebugclient.pri) include(../shared/debugutil.pri) TESTDATA = data/* diff --git a/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/tst_qqmlenginedebuginspectorintegration.cpp b/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/tst_qqmlenginedebuginspectorintegration.cpp index 940f89e936..980e2be1f1 100644 --- a/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/tst_qqmlenginedebuginspectorintegration.cpp +++ b/tests/auto/qml/debugger/qqmlenginedebuginspectorintegrationtest/tst_qqmlenginedebuginspectorintegration.cpp @@ -26,12 +26,12 @@ ** ****************************************************************************/ -#include "qqmlinspectorclient.h" -#include "qqmlenginedebugclient.h" #include "../shared/debugutil_p.h" #include "../../../shared/util.h" #include <private/qqmldebugconnection_p.h> +#include <private/qqmlenginedebugclient_p.h> +#include <private/qqmlinspectorclient_p.h> #include <QtTest/qtest.h> #include <QtTest/qsignalspy.h> @@ -41,35 +41,21 @@ #include <QtCore/qthread.h> #include <QtCore/qlibraryinfo.h> -#define STR_PORT_FROM "3776" -#define STR_PORT_TO "3786" - -class tst_QQmlEngineDebugInspectorIntegration : public QQmlDataTest +class tst_QQmlEngineDebugInspectorIntegration : public QQmlDebugTest { Q_OBJECT -public: - tst_QQmlEngineDebugInspectorIntegration() - : m_process(0) - , m_inspectorClient(0) - , m_engineDebugClient(0) - , m_recipient(0) - { - } - - private: - void init(bool restrictServices); - QmlDebugObjectReference findRootObject(); + ConnectResult init(bool restrictServices); + QList<QQmlDebugClient *> createClients() override; - QQmlDebugProcess *m_process; - QQmlInspectorClient *m_inspectorClient; - QQmlEngineDebugClient *m_engineDebugClient; - QQmlInspectorResultRecipient *m_recipient; + QQmlEngineDebugObjectReference findRootObject(); -private slots: - void cleanup(); + QPointer<QQmlInspectorClient> m_inspectorClient; + QPointer<QQmlEngineDebugClient> m_engineDebugClient; + QPointer<QQmlInspectorResultRecipient> m_recipient; +private slots: void connect_data(); void connect(); void objectLocationLookup(); @@ -79,73 +65,42 @@ private slots: void destroyObject(); }; -QmlDebugObjectReference tst_QQmlEngineDebugInspectorIntegration::findRootObject() +QQmlEngineDebugObjectReference tst_QQmlEngineDebugInspectorIntegration::findRootObject() { bool success = false; m_engineDebugClient->queryAvailableEngines(&success); if (!QQmlDebugTest::waitForSignal(m_engineDebugClient, SIGNAL(result()))) - return QmlDebugObjectReference(); + return QQmlEngineDebugObjectReference(); - m_engineDebugClient->queryRootContexts(m_engineDebugClient->engines()[0].debugId, &success); + m_engineDebugClient->queryRootContexts(m_engineDebugClient->engines()[0], &success); if (!QQmlDebugTest::waitForSignal(m_engineDebugClient, SIGNAL(result()))) - return QmlDebugObjectReference(); + return QQmlEngineDebugObjectReference(); int count = m_engineDebugClient->rootContext().contexts.count(); m_engineDebugClient->queryObject( m_engineDebugClient->rootContext().contexts[count - 1].objects[0], &success); if (!QQmlDebugTest::waitForSignal(m_engineDebugClient, SIGNAL(result()))) - return QmlDebugObjectReference(); + return QQmlEngineDebugObjectReference(); return m_engineDebugClient->object(); } -void tst_QQmlEngineDebugInspectorIntegration::init(bool restrictServices) +QQmlDebugTest::ConnectResult tst_QQmlEngineDebugInspectorIntegration::init(bool restrictServices) { - const QString argument = QString::fromLatin1("-qmljsdebugger=port:%1,%2,block%3") - .arg(STR_PORT_FROM).arg(STR_PORT_TO) - .arg(restrictServices ? QStringLiteral(",services:QmlDebugger,QmlInspector") : - QString()); - - m_process = new QQmlDebugProcess(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qml", - this); - m_process->start(QStringList() << argument << testFile("qtquick2.qml")); - QVERIFY2(m_process->waitForSessionStart(), - "Could not launch application, or did not get 'Waiting for connection'."); - - QQmlDebugConnection *m_connection = new QQmlDebugConnection(this); - m_inspectorClient = new QQmlInspectorClient(m_connection); - m_engineDebugClient = new QQmlEngineDebugClient(m_connection); - m_recipient = new QQmlInspectorResultRecipient(this); - QObject::connect(m_inspectorClient, &QQmlInspectorClient::responseReceived, - m_recipient, &QQmlInspectorResultRecipient::recordResponse); - - QList<QQmlDebugClient *> others = QQmlDebugTest::createOtherClients(m_connection); - - m_connection->connectToHost(QLatin1String("127.0.0.1"), m_process->debugPort()); - QVERIFY(m_connection->waitForConnected()); - foreach (QQmlDebugClient *other, others) - QCOMPARE(other->state(), restrictServices ? QQmlDebugClient::Unavailable : - QQmlDebugClient::Enabled); - qDeleteAll(others); - - QTRY_COMPARE(m_inspectorClient->state(), QQmlDebugClient::Enabled); - QTRY_COMPARE(m_engineDebugClient->state(), QQmlDebugClient::Enabled); + return QQmlDebugTest::connect( + QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qml", + restrictServices ? QStringLiteral("QmlDebugger,QmlInspector") : QString(), + testFile("qtquick2.qml"), true); } -void tst_QQmlEngineDebugInspectorIntegration::cleanup() +QList<QQmlDebugClient *> tst_QQmlEngineDebugInspectorIntegration::createClients() { - if (QTest::currentTestFailed()) { - qDebug() << "Process State:" << m_process->state(); - qDebug() << "Application Output:" << m_process->output(); - } - delete m_process; - m_process = 0; - delete m_engineDebugClient; - m_engineDebugClient = 0; - delete m_inspectorClient; - m_inspectorClient = 0; - delete m_recipient; - m_recipient = 0; + m_inspectorClient = new QQmlInspectorClient(m_connection); + m_engineDebugClient = new QQmlEngineDebugClient(m_connection); + m_recipient = new QQmlInspectorResultRecipient(m_inspectorClient); + QObject::connect(m_inspectorClient.data(), &QQmlInspectorClient::responseReceived, + m_recipient.data(), &QQmlInspectorResultRecipient::recordResponse); + return QList<QQmlDebugClient *>({m_inspectorClient, m_engineDebugClient}); } void tst_QQmlEngineDebugInspectorIntegration::connect_data() @@ -158,15 +113,15 @@ void tst_QQmlEngineDebugInspectorIntegration::connect_data() void tst_QQmlEngineDebugInspectorIntegration::connect() { QFETCH(bool, restrictMode); - init(restrictMode); + QCOMPARE(init(restrictMode), ConnectSuccess); } void tst_QQmlEngineDebugInspectorIntegration::objectLocationLookup() { - init(true); + QCOMPARE(init(true), ConnectSuccess); bool success = false; - QmlDebugObjectReference rootObject = findRootObject(); + QQmlEngineDebugObjectReference rootObject = findRootObject(); QVERIFY(rootObject.debugId != -1); const QString fileName = QFileInfo(rootObject.source.url.toString()).fileName(); int lineNumber = rootObject.source.lineNumber; @@ -176,7 +131,7 @@ void tst_QQmlEngineDebugInspectorIntegration::objectLocationLookup() QVERIFY(success); QVERIFY(QQmlDebugTest::waitForSignal(m_engineDebugClient, SIGNAL(result()))); - foreach (QmlDebugObjectReference child, rootObject.children) { + foreach (QQmlEngineDebugObjectReference child, rootObject.children) { success = false; lineNumber = child.source.lineNumber; columnNumber = child.source.columnNumber; @@ -189,11 +144,12 @@ void tst_QQmlEngineDebugInspectorIntegration::objectLocationLookup() void tst_QQmlEngineDebugInspectorIntegration::select() { - init(true); - QmlDebugObjectReference rootObject = findRootObject(); + QCOMPARE(init(true), ConnectSuccess); + + QQmlEngineDebugObjectReference rootObject = findRootObject(); QList<int> childIds; int requestId = 0; - foreach (const QmlDebugObjectReference &child, rootObject.children) { + foreach (const QQmlEngineDebugObjectReference &child, rootObject.children) { requestId = m_inspectorClient->select(QList<int>() << child.debugId); QTRY_COMPARE(m_recipient->lastResponseId, requestId); QVERIFY(m_recipient->lastResult); @@ -206,7 +162,7 @@ void tst_QQmlEngineDebugInspectorIntegration::select() void tst_QQmlEngineDebugInspectorIntegration::createObject() { - init(true); + QCOMPARE(init(true), ConnectSuccess); QString qml = QLatin1String("Rectangle {\n" " id: xxxyxxx\n" @@ -215,7 +171,7 @@ void tst_QQmlEngineDebugInspectorIntegration::createObject() " color: \"blue\"\n" "}"); - QmlDebugObjectReference rootObject = findRootObject(); + QQmlEngineDebugObjectReference rootObject = findRootObject(); QVERIFY(rootObject.debugId != -1); QCOMPARE(rootObject.children.length(), 2); @@ -233,8 +189,10 @@ void tst_QQmlEngineDebugInspectorIntegration::createObject() void tst_QQmlEngineDebugInspectorIntegration::moveObject() { - init(true); - QmlDebugObjectReference rootObject = findRootObject(); + QCOMPARE(init(true), ConnectSuccess); + + QCOMPARE(m_inspectorClient->state(), QQmlDebugClient::Enabled); + QQmlEngineDebugObjectReference rootObject = findRootObject(); QVERIFY(rootObject.debugId != -1); QCOMPARE(rootObject.children.length(), 2); @@ -256,8 +214,10 @@ void tst_QQmlEngineDebugInspectorIntegration::moveObject() void tst_QQmlEngineDebugInspectorIntegration::destroyObject() { - init(true); - QmlDebugObjectReference rootObject = findRootObject(); + QCOMPARE(init(true), ConnectSuccess); + + QCOMPARE(m_inspectorClient->state(), QQmlDebugClient::Enabled); + QQmlEngineDebugObjectReference rootObject = findRootObject(); QVERIFY(rootObject.debugId != -1); QCOMPARE(rootObject.children.length(), 2); diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/qqmlenginedebugservice.pro b/tests/auto/qml/debugger/qqmlenginedebugservice/qqmlenginedebugservice.pro index 06250d9940..5ff65ba276 100644 --- a/tests/auto/qml/debugger/qqmlenginedebugservice/qqmlenginedebugservice.pro +++ b/tests/auto/qml/debugger/qqmlenginedebugservice/qqmlenginedebugservice.pro @@ -5,11 +5,8 @@ osx:CONFIG -= app_bundle SOURCES += \ tst_qqmlenginedebugservice.cpp -INCLUDEPATH += ../shared -include(../../../shared/util.pri) -include(../shared/qqmlenginedebugclient.pri) include(../shared/debugutil.pri) DEFINES += QT_QML_DEBUG_NO_WARNING -QT += core-private qml-private quick-private testlib gui-private +QT += quick qml-private testlib diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp index d9a4777115..99c90c142f 100644 --- a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp +++ b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp @@ -26,7 +26,6 @@ ** ****************************************************************************/ -#include "qqmlenginedebugclient.h" #include "debugutil_p.h" #include "../../../shared/util.h" @@ -36,6 +35,7 @@ #include <private/qqmlmetatype_p.h> #include <private/qqmlproperty_p.h> #include <private/qqmldebugconnection_p.h> +#include <private/qqmlenginedebugclient_p.h> #include <QtTest/qtest.h> #include <QtTest/qsignalspy.h> @@ -53,11 +53,14 @@ #include <QtCore/qdebug.h> #include <QtCore/qthread.h> #include <QtCore/qabstractitemmodel.h> +#include <QtCore/qjsonobject.h> +#include <QtCore/qjsonarray.h> +#include <QtCore/qjsondocument.h> #define QVERIFYOBJECT(statement) \ do {\ if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__)) {\ - return QmlDebugObjectReference();\ + return QQmlEngineDebugObjectReference();\ }\ } while (0) @@ -77,28 +80,64 @@ class CustomTypes : public QObject Q_OBJECT Q_PROPERTY(QModelIndex modelIndex READ modelIndex) public: - CustomTypes(QObject *parent = 0) : QObject(parent) {} + CustomTypes(QObject *parent = nullptr) : QObject(parent) {} QModelIndex modelIndex() { return QModelIndex(); } }; +class JsonTest : public QObject +{ + Q_OBJECT + Q_PROPERTY(QJsonObject data READ data WRITE setData NOTIFY dataChanged) + +public: + JsonTest(QObject *parent = 0) : QObject(parent) + { + m_data["foo"] = QJsonValue(12); + m_data["ttt"] = QJsonArray({4, 5, 4, 3, 2}); + m_data["a"] = QJsonValue(QJsonValue::Null); + m_data["b"] = QJsonValue(QJsonValue::Undefined); + m_data["c"] = QJsonValue("fffff"); + } + + QJsonObject data() const { return m_data; } + +signals: + void dataChanged(const QJsonObject &data); + +public slots: + void setData(const QJsonObject &data) + { + if (data != m_data) { + m_data = data; + emit dataChanged(data); + } + } + +private: + QJsonObject m_data; +}; + + class tst_QQmlEngineDebugService : public QObject { Q_OBJECT public: - tst_QQmlEngineDebugService() : m_conn(0), m_dbg(0), m_engine(0), m_rootItem(0) {} + tst_QQmlEngineDebugService() : m_conn(nullptr), m_dbg(nullptr), m_engine(nullptr), m_rootItem(nullptr) {} private: - QmlDebugObjectReference findRootObject(int context = 0, + QQmlEngineDebugObjectReference findRootObject(int context = 0, bool recursive = false); - QmlDebugPropertyReference findProperty( - const QList<QmlDebugPropertyReference> &props, + QQmlEngineDebugPropertyReference findProperty( + const QList<QQmlEngineDebugPropertyReference> &props, const QString &name) const; void recursiveObjectTest(QObject *o, - const QmlDebugObjectReference &oref, + const QQmlEngineDebugObjectReference &oref, bool recursive) const; + void getContexts(); + QQmlDebugConnection *m_conn; QQmlEngineDebugClient *m_dbg; QQmlEngine *m_engine; @@ -137,10 +176,13 @@ private slots: void regression_QTCREATORBUG_7451(); void queryObjectWithNonStreamableTypes(); + void jsonData(); void asynchronousCreate(); + void invalidContexts(); + void createObjectOnDestruction(); }; -QmlDebugObjectReference tst_QQmlEngineDebugService::findRootObject( +QQmlEngineDebugObjectReference tst_QQmlEngineDebugService::findRootObject( int context, bool recursive) { bool success = false; @@ -149,7 +191,7 @@ QmlDebugObjectReference tst_QQmlEngineDebugService::findRootObject( QVERIFYOBJECT(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); QVERIFYOBJECT(m_dbg->engines().count()); - m_dbg->queryRootContexts(m_dbg->engines()[0].debugId, &success); + m_dbg->queryRootContexts(m_dbg->engines()[0], &success); QVERIFYOBJECT(success); QVERIFYOBJECT(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); @@ -165,18 +207,18 @@ QmlDebugObjectReference tst_QQmlEngineDebugService::findRootObject( return m_dbg->object(); } -QmlDebugPropertyReference tst_QQmlEngineDebugService::findProperty( - const QList<QmlDebugPropertyReference> &props, const QString &name) const +QQmlEngineDebugPropertyReference tst_QQmlEngineDebugService::findProperty( + const QList<QQmlEngineDebugPropertyReference> &props, const QString &name) const { - foreach (const QmlDebugPropertyReference &p, props) { + foreach (const QQmlEngineDebugPropertyReference &p, props) { if (p.name == name) return p; } - return QmlDebugPropertyReference(); + return QQmlEngineDebugPropertyReference(); } void tst_QQmlEngineDebugService::recursiveObjectTest( - QObject *o, const QmlDebugObjectReference &oref, bool recursive) const + QObject *o, const QQmlEngineDebugObjectReference &oref, bool recursive) const { const QMetaObject *meta = o->metaObject(); @@ -194,8 +236,8 @@ void tst_QQmlEngineDebugService::recursiveObjectTest( int debugId = QQmlDebugService::idForObject(child); QVERIFY(debugId >= 0); - QmlDebugObjectReference cref; - foreach (const QmlDebugObjectReference &ref, oref.children) { + QQmlEngineDebugObjectReference cref; + foreach (const QQmlEngineDebugObjectReference &ref, oref.children) { QVERIFY(!ref.className.isEmpty()); if (ref.debugId == debugId) { cref = ref; @@ -208,7 +250,7 @@ void tst_QQmlEngineDebugService::recursiveObjectTest( recursiveObjectTest(child, cref, true); } - foreach (const QmlDebugPropertyReference &p, oref.properties) { + foreach (const QQmlEngineDebugPropertyReference &p, oref.properties) { QCOMPARE(p.objectDebugId, QQmlDebugService::idForObject(o)); // signal properties are fake - they are generated from QQmlAbstractBoundSignal children @@ -226,9 +268,24 @@ void tst_QQmlEngineDebugService::recursiveObjectTest( 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 (pmeta.userType() == QMetaType::QObjectStar) { + const QQmlEngineDebugObjectReference ref + = qvariant_cast<QQmlEngineDebugObjectReference>(p.value); + QObject *pobj = qvariant_cast<QObject *>(pmeta.read(o)); + if (pobj) { + if (pobj->objectName().isEmpty()) + QCOMPARE(ref.name, QString("<unnamed object>")); + else + QCOMPARE(ref.name, pobj->objectName()); + } else { + QCOMPARE(ref.name, QString("<unknown value>")); + } + } else if (pmeta.type() < QVariant::UserType && pmeta.userType() != QMetaType::QVariant) { + const QVariant expected = pmeta.read(o); + QVERIFY2(p.value == expected, QString::fromLatin1("%1 != %2. Details: %3/%4/%5/%6") + .arg(QTest::toString(p.value)).arg(QTest::toString(expected)).arg(p.name) + .arg(p.valueTypeName).arg(pmeta.type()).arg(pmeta.userType()).toUtf8()); + } if (p.name == "parent") QVERIFY(p.valueTypeName == "QGraphicsObject*" || @@ -248,6 +305,22 @@ void tst_QQmlEngineDebugService::recursiveObjectTest( } } +void tst_QQmlEngineDebugService::getContexts() +{ + bool success = false; + + m_dbg->queryAvailableEngines(&success); + QVERIFY(success); + QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); + + QList<QQmlEngineDebugEngineReference> engines = m_dbg->engines(); + QCOMPARE(engines.count(), 1); + m_dbg->queryRootContexts(engines.first(), &success); + + QVERIFY(success); + QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); +} + void tst_QQmlEngineDebugService::initTestCase() { qmlRegisterType<NonScriptProperty>("Test", 1, 0, "NonScriptPropertyElement"); @@ -329,6 +402,11 @@ void tst_QQmlEngineDebugService::initTestCase() "CustomTypes {}" ; + qmlRegisterType<JsonTest>("JsonTest", 1, 0, "JsonTest"); + qml << "import JsonTest 1.0\n" + "JsonTest {}" + ; + for (int i=0; i<qml.count(); i++) { QQmlComponent component(m_engine); component.setData(qml[i], QUrl::fromLocalFile("")); @@ -364,7 +442,7 @@ void tst_QQmlEngineDebugService::cleanupTestCase() void tst_QQmlEngineDebugService::setMethodBody() { bool success; - QmlDebugObjectReference obj = findRootObject(2); + QQmlEngineDebugObjectReference obj = findRootObject(2); QVERIFY(!obj.className.isEmpty()); QObject *root = m_components.at(2); @@ -406,18 +484,18 @@ void tst_QQmlEngineDebugService::setMethodBody() void tst_QQmlEngineDebugService::watch_property() { - QmlDebugObjectReference obj = findRootObject(); + QQmlEngineDebugObjectReference obj = findRootObject(); QVERIFY(!obj.className.isEmpty()); - QmlDebugPropertyReference prop = findProperty(obj.properties, "width"); + QQmlEngineDebugPropertyReference prop = findProperty(obj.properties, "width"); bool success; - QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(0); + QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr); unconnected->addWatch(prop, &success); QVERIFY(!success); delete unconnected; - m_dbg->addWatch(QmlDebugPropertyReference(), &success); + m_dbg->addWatch(QQmlEngineDebugPropertyReference(), &success); QVERIFY(success); QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); QCOMPARE(m_dbg->valid(), false); @@ -451,17 +529,17 @@ void tst_QQmlEngineDebugService::watch_property() void tst_QQmlEngineDebugService::watch_object() { - QmlDebugObjectReference obj = findRootObject(); + QQmlEngineDebugObjectReference obj = findRootObject(); QVERIFY(!obj.className.isEmpty()); bool success; - QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(0); + QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr); unconnected->addWatch(obj, &success); QVERIFY(!success); delete unconnected; - m_dbg->addWatch(QmlDebugObjectReference(), &success); + m_dbg->addWatch(QQmlEngineDebugObjectReference(), &success); QVERIFY(success); QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); QCOMPARE(m_dbg->valid(), false); @@ -517,17 +595,17 @@ void tst_QQmlEngineDebugService::watch_expression() int origWidth = m_rootItem->property("width").toInt(); - QmlDebugObjectReference obj = findRootObject(); + QQmlEngineDebugObjectReference obj = findRootObject(); QVERIFY(!obj.className.isEmpty()); bool success; - QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(0); + QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr); unconnected->addWatch(obj, expr, &success); QVERIFY(!success); delete unconnected; - m_dbg->addWatch(QmlDebugObjectReference(), expr, &success); + m_dbg->addWatch(QQmlEngineDebugObjectReference(), expr, &success); QVERIFY(success); QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); QCOMPARE(m_dbg->valid(), false); @@ -577,7 +655,7 @@ void tst_QQmlEngineDebugService::watch_expression_data() void tst_QQmlEngineDebugService::watch_context() { - QmlDebugContextReference c; + QQmlEngineDebugContextReference c; QTest::ignoreMessage(QtWarningMsg, "QQmlEngineDebugClient::addWatch(): Not implemented"); bool success; m_dbg->addWatch(c, QString(), &success); @@ -586,7 +664,7 @@ void tst_QQmlEngineDebugService::watch_context() void tst_QQmlEngineDebugService::watch_file() { - QmlDebugFileReference f; + QQmlEngineDebugFileReference f; QTest::ignoreMessage(QtWarningMsg, "QQmlEngineDebugClient::addWatch(): Not implemented"); bool success; m_dbg->addWatch(f, &success); @@ -597,7 +675,7 @@ void tst_QQmlEngineDebugService::queryAvailableEngines() { bool success; - QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(0); + QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr); unconnected->queryAvailableEngines(&success); QVERIFY(!success); delete unconnected; @@ -607,10 +685,10 @@ void tst_QQmlEngineDebugService::queryAvailableEngines() QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); // TODO test multiple engines - QList<QmlDebugEngineReference> engines = m_dbg->engines(); + QList<QQmlEngineDebugEngineReference> engines = m_dbg->engines(); QCOMPARE(engines.count(), 1); - foreach (const QmlDebugEngineReference &e, engines) { + foreach (const QQmlEngineDebugEngineReference &e, engines) { QCOMPARE(e.debugId, QQmlDebugService::idForObject(m_engine)); QCOMPARE(e.name, m_engine->objectName()); } @@ -623,26 +701,26 @@ void tst_QQmlEngineDebugService::queryRootContexts() QVERIFY(success); QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); QVERIFY(m_dbg->engines().count()); - int engineId = m_dbg->engines()[0].debugId; + const QQmlEngineDebugEngineReference engine = m_dbg->engines()[0]; - QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(0); - unconnected->queryRootContexts(engineId, &success); + QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr); + unconnected->queryRootContexts(engine, &success); QVERIFY(!success); delete unconnected; - m_dbg->queryRootContexts(engineId, &success); + m_dbg->queryRootContexts(engine, &success); QVERIFY(success); QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); QQmlContext *actualContext = m_engine->rootContext(); - QmlDebugContextReference context = m_dbg->rootContext(); + QQmlEngineDebugContextReference context = m_dbg->rootContext(); QCOMPARE(context.debugId, QQmlDebugService::idForObject(actualContext)); QCOMPARE(context.name, actualContext->objectName()); // root context query sends only root object data - it doesn't fill in // the children or property info QCOMPARE(context.objects.count(), 0); - QCOMPARE(context.contexts.count(), 6); + QCOMPARE(context.contexts.count(), 7); QVERIFY(context.contexts[0].debugId >= 0); QCOMPARE(context.contexts[0].name, QString("tst_QQmlDebug_childContext")); } @@ -653,10 +731,10 @@ void tst_QQmlEngineDebugService::queryObject() bool success; - QmlDebugObjectReference rootObject = findRootObject(); + QQmlEngineDebugObjectReference rootObject = findRootObject(); QVERIFY(!rootObject.className.isEmpty()); - QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(0); + QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr); recursive ? unconnected->queryObjectRecursive(rootObject, &success) : unconnected->queryObject(rootObject, &success); QVERIFY(!success); delete unconnected; @@ -665,11 +743,11 @@ void tst_QQmlEngineDebugService::queryObject() QVERIFY(success); QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); - QmlDebugObjectReference obj = m_dbg->object(); + QQmlEngineDebugObjectReference obj = m_dbg->object(); QVERIFY(!obj.className.isEmpty()); // check source as defined in main() - QmlDebugFileReference source = obj.source; + QQmlEngineDebugFileReference source = obj.source; QCOMPARE(source.url, QUrl::fromLocalFile("")); QCOMPARE(source.lineNumber, 3); QCOMPARE(source.columnNumber, 1); @@ -678,14 +756,14 @@ void tst_QQmlEngineDebugService::queryObject() recursiveObjectTest(m_rootItem, obj, recursive); if (recursive) { - foreach (const QmlDebugObjectReference &child, obj.children) { + foreach (const QQmlEngineDebugObjectReference &child, obj.children) { QVERIFY(!child.className.isEmpty()); QVERIFY(child.properties.count() > 0); } - QmlDebugObjectReference rect; - QmlDebugObjectReference text; - foreach (const QmlDebugObjectReference &child, obj.children) { + QQmlEngineDebugObjectReference rect; + QQmlEngineDebugObjectReference text; + foreach (const QQmlEngineDebugObjectReference &child, obj.children) { QVERIFY(!child.className.isEmpty()); if (child.className == "Rectangle") rect = child; @@ -700,7 +778,7 @@ void tst_QQmlEngineDebugService::queryObject() QCOMPARE(findProperty(text.properties, "color").value, qVariantFromValue(QColor("blue"))); } else { - foreach (const QmlDebugObjectReference &child, obj.children) { + foreach (const QQmlEngineDebugObjectReference &child, obj.children) { QVERIFY(!child.className.isEmpty()); QCOMPARE(child.properties.count(), 0); } @@ -721,14 +799,14 @@ void tst_QQmlEngineDebugService::queryObjectsForLocation() bool success; - QmlDebugObjectReference rootObject = findRootObject(); + QQmlEngineDebugObjectReference rootObject = findRootObject(); QVERIFY(!rootObject.className.isEmpty()); const QString fileName = QFileInfo(rootObject.source.url.toString()).fileName(); int lineNumber = rootObject.source.lineNumber; int columnNumber = rootObject.source.columnNumber; - QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(0); + QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr); recursive ? unconnected->queryObjectsForLocationRecursive(fileName, lineNumber, columnNumber, &success) : unconnected->queryObjectsForLocation(fileName, lineNumber, @@ -744,11 +822,11 @@ void tst_QQmlEngineDebugService::queryObjectsForLocation() QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); QCOMPARE(m_dbg->objects().count(), 1); - QmlDebugObjectReference obj = m_dbg->objects().first(); + QQmlEngineDebugObjectReference obj = m_dbg->objects().first(); QVERIFY(!obj.className.isEmpty()); // check source as defined in main() - QmlDebugFileReference source = obj.source; + QQmlEngineDebugFileReference source = obj.source; QCOMPARE(source.url, QUrl(fileName)); QCOMPARE(source.lineNumber, lineNumber); QCOMPARE(source.columnNumber, columnNumber); @@ -757,14 +835,14 @@ void tst_QQmlEngineDebugService::queryObjectsForLocation() recursiveObjectTest(m_rootItem, obj, recursive); if (recursive) { - foreach (const QmlDebugObjectReference &child, obj.children) { + foreach (const QQmlEngineDebugObjectReference &child, obj.children) { QVERIFY(!child.className.isEmpty()); QVERIFY(child.properties.count() > 0); } - QmlDebugObjectReference rect; - QmlDebugObjectReference text; - foreach (const QmlDebugObjectReference &child, obj.children) { + QQmlEngineDebugObjectReference rect; + QQmlEngineDebugObjectReference text; + foreach (const QQmlEngineDebugObjectReference &child, obj.children) { QVERIFY(!child.className.isEmpty()); if (child.className == "Rectangle") rect = child; @@ -779,7 +857,7 @@ void tst_QQmlEngineDebugService::queryObjectsForLocation() QCOMPARE(findProperty(text.properties, "color").value, qVariantFromValue(QColor("blue"))); } else { - foreach (const QmlDebugObjectReference &child, obj.children) { + foreach (const QQmlEngineDebugObjectReference &child, obj.children) { QVERIFY(!child.className.isEmpty()); QCOMPARE(child.properties.count(), 0); } @@ -796,7 +874,7 @@ void tst_QQmlEngineDebugService::queryObjectsForLocation_data() void tst_QQmlEngineDebugService::regression_QTCREATORBUG_7451() { - QmlDebugObjectReference rootObject = findRootObject(); + QQmlEngineDebugObjectReference rootObject = findRootObject(); QVERIFY(!rootObject.className.isEmpty()); int contextId = rootObject.contextDebugId; QQmlContext *context = qobject_cast<QQmlContext *>(QQmlDebugService::objectForId(contextId)); @@ -823,7 +901,7 @@ void tst_QQmlEngineDebugService::regression_QTCREATORBUG_7451() QVERIFY(success); QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); - foreach (QmlDebugObjectReference child, rootObject.children) { + foreach (QQmlEngineDebugObjectReference child, rootObject.children) { QVERIFY(!child.className.isEmpty()); success = false; lineNumber = child.source.lineNumber; @@ -846,7 +924,7 @@ void tst_QQmlEngineDebugService::regression_QTCREATORBUG_7451() QVERIFY(success); QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); - foreach (QmlDebugObjectReference child, rootObject.children) { + foreach (QQmlEngineDebugObjectReference child, rootObject.children) { QVERIFY(!child.className.isEmpty()); success = false; lineNumber = child.source.lineNumber; @@ -862,10 +940,10 @@ void tst_QQmlEngineDebugService::queryObjectWithNonStreamableTypes() { bool success; - QmlDebugObjectReference rootObject = findRootObject(4, true); + QQmlEngineDebugObjectReference rootObject = findRootObject(4, true); QVERIFY(!rootObject.className.isEmpty()); - QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(0); + QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr); unconnected->queryObject(rootObject, &success); QVERIFY(!success); delete unconnected; @@ -874,12 +952,31 @@ void tst_QQmlEngineDebugService::queryObjectWithNonStreamableTypes() QVERIFY(success); QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); - QmlDebugObjectReference obj = m_dbg->object(); + QQmlEngineDebugObjectReference obj = m_dbg->object(); QVERIFY(!obj.className.isEmpty()); - QCOMPARE(findProperty(obj.properties, "modelIndex").value, QVariant()); + QCOMPARE(findProperty(obj.properties, "modelIndex").value, + QVariant(QLatin1String("QModelIndex()"))); } +void tst_QQmlEngineDebugService::jsonData() +{ + bool success; + + QQmlEngineDebugObjectReference rootObject = findRootObject(5, true); + QVERIFY(!rootObject.className.isEmpty()); + + m_dbg->queryObject(rootObject, &success); + QVERIFY(success); + QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); + + QQmlEngineDebugObjectReference obj = m_dbg->object(); + QVERIFY(!obj.className.isEmpty()); + + QCOMPARE(findProperty(obj.properties, "data").value, + QJsonDocument::fromJson("{\"a\":null,\"c\":\"fffff\",\"foo\":12,\"ttt\":[4,5,4,3,2]}") + .toVariant()); +} void tst_QQmlEngineDebugService::queryExpressionResult() { @@ -890,7 +987,7 @@ void tst_QQmlEngineDebugService::queryExpressionResult() bool success; - QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(0); + QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr); unconnected->queryExpressionResult(objectId, expr, &success); QVERIFY(!success); delete unconnected; @@ -938,7 +1035,7 @@ void tst_QQmlEngineDebugService::queryExpressionResultBC() bool success; - QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(0); + QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(nullptr); unconnected->queryExpressionResultBC(objectId, expr, &success); QVERIFY(!success); delete unconnected; @@ -968,10 +1065,10 @@ void tst_QQmlEngineDebugService::queryExpressionResultBC_data() void tst_QQmlEngineDebugService::setBindingForObject() { - QmlDebugObjectReference rootObject = findRootObject(); + QQmlEngineDebugObjectReference rootObject = findRootObject(); QVERIFY(!rootObject.className.isEmpty()); QVERIFY(rootObject.debugId != -1); - QmlDebugPropertyReference widthPropertyRef = findProperty(rootObject.properties, "width"); + QQmlEngineDebugPropertyReference widthPropertyRef = findProperty(rootObject.properties, "width"); QCOMPARE(widthPropertyRef.value, QVariant(10)); QCOMPARE(widthPropertyRef.binding, QString()); @@ -1016,7 +1113,7 @@ void tst_QQmlEngineDebugService::setBindingForObject() rootObject = findRootObject(); QVERIFY(!rootObject.className.isEmpty()); QCOMPARE(rootObject.children.size(), 5); // Rectangle, Text, MouseArea, Component.onCompleted, NonScriptPropertyElement - QmlDebugObjectReference mouseAreaObject = rootObject.children.at(2); + QQmlEngineDebugObjectReference mouseAreaObject = rootObject.children.at(2); QVERIFY(!mouseAreaObject.className.isEmpty()); m_dbg->queryObjectRecursive(mouseAreaObject, &success); QVERIFY(success); @@ -1024,11 +1121,11 @@ void tst_QQmlEngineDebugService::setBindingForObject() mouseAreaObject = m_dbg->object(); QCOMPARE(mouseAreaObject.className, QString("MouseArea")); - QmlDebugPropertyReference onEnteredRef = findProperty(mouseAreaObject.properties, "onEntered"); + QQmlEngineDebugPropertyReference onEnteredRef = findProperty(mouseAreaObject.properties, "onEntered"); QCOMPARE(onEnteredRef.name, QString("onEntered")); // Sorry, can't do that anymore: QCOMPARE(onEnteredRef.value, QVariant("{ console.log('hello') }")); - QCOMPARE(onEnteredRef.value, QVariant("function() { [code] }")); + QCOMPARE(onEnteredRef.value, QVariant("function() { [native code] }")); m_dbg->setBindingForObject(mouseAreaObject.debugId, "onEntered", "{console.log('hello, world') }", false, @@ -1048,15 +1145,15 @@ void tst_QQmlEngineDebugService::setBindingForObject() QVERIFY(!mouseAreaObject.className.isEmpty()); onEnteredRef = findProperty(mouseAreaObject.properties, "onEntered"); QCOMPARE(onEnteredRef.name, QString("onEntered")); - QCOMPARE(onEnteredRef.value, QVariant("function() { [code] }")); + QCOMPARE(onEnteredRef.value, QVariant("function() { [native code] }")); } void tst_QQmlEngineDebugService::resetBindingForObject() { - QmlDebugObjectReference rootObject = findRootObject(); + QQmlEngineDebugObjectReference rootObject = findRootObject(); QVERIFY(!rootObject.className.isEmpty()); QVERIFY(rootObject.debugId != -1); - QmlDebugPropertyReference widthPropertyRef = findProperty(rootObject.properties, "width"); + QQmlEngineDebugPropertyReference widthPropertyRef = findProperty(rootObject.properties, "width"); bool success = false; @@ -1092,7 +1189,7 @@ void tst_QQmlEngineDebugService::resetBindingForObject() rootObject = findRootObject(); QVERIFY(!rootObject.className.isEmpty()); - QmlDebugPropertyReference boldPropertyRef = findProperty(rootObject.properties, "font.bold"); + QQmlEngineDebugPropertyReference boldPropertyRef = findProperty(rootObject.properties, "font.bold"); QCOMPARE(boldPropertyRef.value.toBool(), false); QCOMPARE(boldPropertyRef.binding, QString()); @@ -1104,7 +1201,7 @@ void tst_QQmlEngineDebugService::setBindingInStates() const int sourceIndex = 3; - QmlDebugObjectReference obj = findRootObject(sourceIndex); + QQmlEngineDebugObjectReference obj = findRootObject(sourceIndex); QVERIFY(!obj.className.isEmpty()); QVERIFY(obj.debugId != -1); QVERIFY(obj.children.count() >= 2); @@ -1137,11 +1234,11 @@ void tst_QQmlEngineDebugService::setBindingInStates() // change the binding - QmlDebugObjectReference state = obj.children[1]; + QQmlEngineDebugObjectReference state = obj.children[1]; QCOMPARE(state.className, QString("State")); QVERIFY(state.children.count() > 0); - QmlDebugObjectReference propertyChange = state.children[0]; + QQmlEngineDebugObjectReference propertyChange = state.children[0]; QVERIFY(!propertyChange.className.isEmpty()); QVERIFY(propertyChange.debugId != -1); @@ -1220,51 +1317,53 @@ void tst_QQmlEngineDebugService::queryObjectTree() { const int sourceIndex = 3; - QmlDebugObjectReference obj = findRootObject(sourceIndex, true); + QQmlEngineDebugObjectReference obj = findRootObject(sourceIndex, true); QVERIFY(!obj.className.isEmpty()); QVERIFY(obj.debugId != -1); QVERIFY(obj.children.count() >= 2); // check state - QmlDebugObjectReference state = obj.children[1]; + QQmlEngineDebugObjectReference state = obj.children[1]; QCOMPARE(state.className, QString("State")); QVERIFY(state.children.count() > 0); - QmlDebugObjectReference propertyChange = state.children[0]; + QQmlEngineDebugObjectReference propertyChange = state.children[0]; QVERIFY(!propertyChange.className.isEmpty()); QVERIFY(propertyChange.debugId != -1); - QmlDebugPropertyReference propertyChangeTarget = findProperty(propertyChange.properties,"target"); + QQmlEngineDebugPropertyReference propertyChangeTarget = findProperty(propertyChange.properties,"target"); QCOMPARE(propertyChangeTarget.objectDebugId, propertyChange.debugId); - QmlDebugObjectReference targetReference = qvariant_cast<QmlDebugObjectReference>(propertyChangeTarget.value); + QQmlEngineDebugObjectReference targetReference = qvariant_cast<QQmlEngineDebugObjectReference>(propertyChangeTarget.value); QVERIFY(!targetReference.className.isEmpty()); - QVERIFY(targetReference.debugId != -1); + QCOMPARE(targetReference.debugId, -1); + QCOMPARE(targetReference.name, QString("<unnamed object>")); // check transition - QmlDebugObjectReference transition = obj.children[0]; + QQmlEngineDebugObjectReference 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); - QmlDebugObjectReference animation = transition.children[0]; + QQmlEngineDebugObjectReference animation = transition.children[0]; QVERIFY(!animation.className.isEmpty()); QVERIFY(animation.debugId != -1); - QmlDebugPropertyReference animationTarget = findProperty(animation.properties,"target"); + QQmlEngineDebugPropertyReference animationTarget = findProperty(animation.properties,"target"); QCOMPARE(animationTarget.objectDebugId, animation.debugId); - targetReference = qvariant_cast<QmlDebugObjectReference>(animationTarget.value); + targetReference = qvariant_cast<QQmlEngineDebugObjectReference>(animationTarget.value); QVERIFY(!targetReference.className.isEmpty()); - QVERIFY(targetReference.debugId != -1); + QCOMPARE(targetReference.debugId, -1); + QCOMPARE(targetReference.name, QString("<unnamed object>")); QCOMPARE(findProperty(animation.properties,"property").value.toString(), QString("width")); QCOMPARE(findProperty(animation.properties,"duration").value.toInt(), 100); } void tst_QQmlEngineDebugService::asynchronousCreate() { - QmlDebugObjectReference object; + QQmlEngineDebugObjectReference object; auto connection = connect(m_dbg, &QQmlEngineDebugClient::newObject, this, [&](int objectId) { object.debugId = objectId; }); @@ -1289,6 +1388,49 @@ void tst_QQmlEngineDebugService::asynchronousCreate() { QTRY_COMPARE(m_dbg->object().idString, QLatin1String("asyncRect")); } +void tst_QQmlEngineDebugService::invalidContexts() +{ + getContexts(); + const int base = m_dbg->rootContext().contexts.count(); + QQmlContext context(m_engine); + getContexts(); + QCOMPARE(m_dbg->rootContext().contexts.count(), base + 1); + QQmlContextData *contextData = QQmlContextData::get(&context); + contextData->invalidate(); + getContexts(); + QCOMPARE(m_dbg->rootContext().contexts.count(), base); + QQmlContextData *rootData = QQmlContextData::get(m_engine->rootContext()); + rootData->invalidate(); + getContexts(); + QCOMPARE(m_dbg->rootContext().contexts.count(), 0); + contextData->setParent(rootData); // makes context valid again, but not root. + getContexts(); + QCOMPARE(m_dbg->rootContext().contexts.count(), 0); +} + +void tst_QQmlEngineDebugService::createObjectOnDestruction() +{ + QSignalSpy spy(m_dbg, SIGNAL(newObject(int))); + { + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData( + "import QtQml 2.0;" + "QtObject {" + "property Component x:" + "Qt.createQmlObject('import QtQml 2.0; Component { QtObject { } }'," + "this, 'x.qml');" + "Component.onDestruction: x.createObject(this, {});" + "}", QUrl::fromLocalFile("x.qml")); + QVERIFY(component.isReady()); + QVERIFY(component.create()); + QTRY_COMPARE(spy.count(), 2); + } + // Doesn't crash and doesn't give us another signal for the object created on destruction. + QTest::qWait(500); + QCOMPARE(spy.count(), 2); +} + int main(int argc, char *argv[]) { int _argc = argc + 1; diff --git a/tests/auto/qml/debugger/qqmlinspector/qqmlinspector.pro b/tests/auto/qml/debugger/qqmlinspector/qqmlinspector.pro index ee5f3c708a..0d42030b52 100644 --- a/tests/auto/qml/debugger/qqmlinspector/qqmlinspector.pro +++ b/tests/auto/qml/debugger/qqmlinspector/qqmlinspector.pro @@ -1,14 +1,11 @@ CONFIG += testcase TARGET = tst_qqmlinspector -QT += qml testlib gui-private core-private +QT += testlib gui-private core-private osx:CONFIG -= app_bundle SOURCES += tst_qqmlinspector.cpp -INCLUDEPATH += ../shared -include(../../../shared/util.pri) -include(../shared/qqmlinspectorclient.pri) include(../shared/debugutil.pri) TESTDATA = data/* diff --git a/tests/auto/qml/debugger/qqmlinspector/tst_qqmlinspector.cpp b/tests/auto/qml/debugger/qqmlinspector/tst_qqmlinspector.cpp index 9461922eff..6685558bb5 100644 --- a/tests/auto/qml/debugger/qqmlinspector/tst_qqmlinspector.cpp +++ b/tests/auto/qml/debugger/qqmlinspector/tst_qqmlinspector.cpp @@ -26,11 +26,12 @@ ** ****************************************************************************/ -#include "qqmlinspectorclient.h" #include "../shared/debugutil_p.h" +#include "../shared/qqmldebugprocess_p.h" #include "../../../shared/util.h" #include <private/qqmldebugconnection_p.h> +#include <private/qqmlinspectorclient_p.h> #include <QtTest/qtest.h> #include <QtTest/qsignalspy.h> @@ -40,64 +41,32 @@ #include <QtCore/qlibraryinfo.h> #include <QtNetwork/qhostaddress.h> -#define STR_PORT_FROM "3772" -#define STR_PORT_TO "3782" - - -class tst_QQmlInspector : public QQmlDataTest +class tst_QQmlInspector : public QQmlDebugTest { Q_OBJECT private: - void startQmlProcess(const QString &qmlFile, bool restrictMode = true); + ConnectResult startQmlProcess(const QString &qmlFile, bool restrictMode = true); void checkAnimationSpeed(int targetMillisPerDegree); + QList<QQmlDebugClient *> createClients() override; + QQmlDebugProcess *createProcess(const QString &executable) override; -private: - QScopedPointer<QQmlDebugProcess> m_process; - QScopedPointer<QQmlDebugConnection> m_connection; - QScopedPointer<QQmlInspectorClient> m_client; - QScopedPointer<QQmlInspectorResultRecipient> m_recipient; + QPointer<QQmlInspectorClient> m_client; + QPointer<QQmlInspectorResultRecipient> m_recipient; private slots: - void cleanup(); - void connect_data(); void connect(); void setAnimationSpeed(); void showAppOnTop(); }; -void tst_QQmlInspector::startQmlProcess(const QString &qmlFile, bool restrictServices) +QQmlDebugTest::ConnectResult tst_QQmlInspector::startQmlProcess(const QString &qmlFile, + bool restrictServices) { - const QString argument = QString::fromLatin1("-qmljsdebugger=port:%1,%2,block%3") - .arg(STR_PORT_FROM).arg(STR_PORT_TO) - .arg(restrictServices ? QStringLiteral(",services:QmlInspector") : QString()); - - m_process.reset(new QQmlDebugProcess(QLibraryInfo::location(QLibraryInfo::BinariesPath) + - "/qml")); - // Make sure the animation timing is exact - m_process->addEnvironment(QLatin1String("QSG_RENDER_LOOP=basic")); - m_process->start(QStringList() << argument << testFile(qmlFile)); - QVERIFY2(m_process->waitForSessionStart(), - "Could not launch application, or did not get 'Waiting for connection'."); - - m_client.reset(); - m_connection.reset(new QQmlDebugConnection); - m_client.reset(new QQmlInspectorClient(m_connection.data())); - - m_recipient.reset(new QQmlInspectorResultRecipient); - QObject::connect(m_client.data(), &QQmlInspectorClient::responseReceived, - m_recipient.data(), &QQmlInspectorResultRecipient::recordResponse); - - QList<QQmlDebugClient *> others = QQmlDebugTest::createOtherClients(m_connection.data()); - - m_connection->connectToHost(QLatin1String("127.0.0.1"), m_process->debugPort()); - QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled); - - foreach (QQmlDebugClient *other, others) - QCOMPARE(other->state(), restrictServices ? QQmlDebugClient::Unavailable : - QQmlDebugClient::Enabled); - qDeleteAll(others); + return QQmlDebugTest::connect(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qml", + restrictServices ? QStringLiteral("QmlInspector") : QString(), + testFile(qmlFile), true); } void tst_QQmlInspector::checkAnimationSpeed(int targetMillisPerDegree) @@ -114,8 +83,7 @@ void tst_QQmlInspector::checkAnimationSpeed(int targetMillisPerDegree) QString output = m_process->output(); int position = output.length(); do { - QVERIFY(QQmlDebugTest::waitForSignal(m_process.data(), - SIGNAL(readyReadStandardOutput()))); + QVERIFY(QQmlDebugTest::waitForSignal(m_process, SIGNAL(readyReadStandardOutput()))); output = m_process->output(); } while (!output.mid(position).contains(markerString)); @@ -144,12 +112,21 @@ void tst_QQmlInspector::checkAnimationSpeed(int targetMillisPerDegree) .arg(targetMillisPerDegree).toLocal8Bit().constData()); } -void tst_QQmlInspector::cleanup() +QList<QQmlDebugClient *> tst_QQmlInspector::createClients() { - if (QTest::currentTestFailed()) { - qDebug() << "Process State:" << m_process->state(); - qDebug() << "Application Output:" << m_process->output(); - } + m_client = new QQmlInspectorClient(m_connection); + m_recipient = new QQmlInspectorResultRecipient(m_client); + QObject::connect(m_client.data(), &QQmlInspectorClient::responseReceived, + m_recipient.data(), &QQmlInspectorResultRecipient::recordResponse); + return QList<QQmlDebugClient *>({m_client}); +} + +QQmlDebugProcess *tst_QQmlInspector::createProcess(const QString &executable) +{ + QQmlDebugProcess *process = QQmlDebugTest::createProcess(executable); + // Make sure the animation timing is exact + process->addEnvironment(QLatin1String("QSG_RENDER_LOOP=basic")); + return process; } void tst_QQmlInspector::connect_data() @@ -166,7 +143,7 @@ void tst_QQmlInspector::connect() { QFETCH(QString, file); QFETCH(bool, restrictMode); - startQmlProcess(file, restrictMode); + QCOMPARE(startQmlProcess(file, restrictMode), ConnectSuccess); QVERIFY(m_client); QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled); @@ -181,7 +158,7 @@ void tst_QQmlInspector::connect() void tst_QQmlInspector::showAppOnTop() { - startQmlProcess("qtquick2.qml"); + QCOMPARE(startQmlProcess("qtquick2.qml"), ConnectSuccess); QVERIFY(m_client); QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled); @@ -196,7 +173,7 @@ void tst_QQmlInspector::showAppOnTop() void tst_QQmlInspector::setAnimationSpeed() { - startQmlProcess("qtquick2.qml"); + QCOMPARE(startQmlProcess("qtquick2.qml"), ConnectSuccess); QVERIFY(m_client); QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled); checkAnimationSpeed(10); diff --git a/tests/auto/qml/debugger/qqmlnativeconnector/tst_qqmlnativeconnector.cpp b/tests/auto/qml/debugger/qqmlnativeconnector/tst_qqmlnativeconnector.cpp index 33d3c6369f..6283a93881 100644 --- a/tests/auto/qml/debugger/qqmlnativeconnector/tst_qqmlnativeconnector.cpp +++ b/tests/auto/qml/debugger/qqmlnativeconnector/tst_qqmlnativeconnector.cpp @@ -113,7 +113,7 @@ int main(int argc, char *argv[]) argv2[i] = argv[i]; argv2[argc] = strdup("-qmljsdebugger=native,services:NativeQmlDebugger"); ++argc; - argv2[argc] = 0; + argv2[argc] = nullptr; Application app(argc, argv2); return QTest::qExec(&app, argc, argv); } diff --git a/tests/auto/qml/debugger/qqmlpreview/data/broken.qml b/tests/auto/qml/debugger/qqmlpreview/data/broken.qml new file mode 100644 index 0000000000..4ebbf7576a --- /dev/null +++ b/tests/auto/qml/debugger/qqmlpreview/data/broken.qml @@ -0,0 +1,31 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Item { diff --git a/tests/auto/qml/debugger/qqmlpreview/data/i18n/qml_fr_FR.qm b/tests/auto/qml/debugger/qqmlpreview/data/i18n/qml_fr_FR.qm new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/auto/qml/debugger/qqmlpreview/data/i18n/qml_fr_FR.qm diff --git a/tests/auto/qml/debugger/qqmlpreview/data/qtquick2.qml b/tests/auto/qml/debugger/qqmlpreview/data/qtquick2.qml new file mode 100644 index 0000000000..b525ab4fa0 --- /dev/null +++ b/tests/auto/qml/debugger/qqmlpreview/data/qtquick2.qml @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Rectangle { + width: 100 + height: 100 + color: "blue" + + RotationAnimation on rotation { + duration: 3600 + loops: Animation.Infinite + from: 0 + to: 360 + } + + Timer { + interval: 300 + repeat: true + running: true + property int prevHit: -1 + property int prevRotation: -1 + onTriggered: { + var date = new Date; + var millis = date.getMilliseconds() + + if (prevHit < 0) { + prevHit = millis; + prevRotation = parent.rotation + return; + } + + var milliDelta = millis - prevHit; + if (milliDelta <= 0) + milliDelta += 1000; + prevHit = millis; + + var delta = parent.rotation - prevRotation; + if (delta < 0) + delta += 360 + prevRotation = parent.rotation + console.log(milliDelta, delta, "ms/degrees "); + } + } +} diff --git a/tests/auto/qml/debugger/qqmlpreview/data/window.qml b/tests/auto/qml/debugger/qqmlpreview/data/window.qml new file mode 100644 index 0000000000..f9f8d5aeb1 --- /dev/null +++ b/tests/auto/qml/debugger/qqmlpreview/data/window.qml @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtQuick.Window 2.0 + +Window { + visible: true + + height: 100 + width: 100 + + Timer { + repeat: false + interval: 1000 + running: true + onTriggered: console.log("window.qml"); + } +} diff --git a/tests/auto/qml/debugger/qqmlpreview/data/window1.qml b/tests/auto/qml/debugger/qqmlpreview/data/window1.qml new file mode 100644 index 0000000000..e8e9ad706d --- /dev/null +++ b/tests/auto/qml/debugger/qqmlpreview/data/window1.qml @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtQuick.Window 2.3 + +Window { + visible: true + + height: 200 + width: 100 + + Timer { + interval: 1000 + running: true + onTriggered: console.log("window1.qml"); + } +} diff --git a/tests/auto/qml/debugger/qqmlpreview/data/window2.qml b/tests/auto/qml/debugger/qqmlpreview/data/window2.qml new file mode 100644 index 0000000000..9ad42d2ee2 --- /dev/null +++ b/tests/auto/qml/debugger/qqmlpreview/data/window2.qml @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtQuick.Window 2.3 + +Window { + visible: true + + height: 100 + width: 200 + + Timer { + interval: 1000 + running: true + onTriggered: console.log("window2.qml"); + } +} diff --git a/tests/auto/qml/debugger/qqmlpreview/data/zoom.qml b/tests/auto/qml/debugger/qqmlpreview/data/zoom.qml new file mode 100644 index 0000000000..0aca235de1 --- /dev/null +++ b/tests/auto/qml/debugger/qqmlpreview/data/zoom.qml @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtQuick.Window 2.2 + +Window { + id: w + height: 100 + width: 100 + visible: true + + Rectangle { + width: 50 + height: 50 + color: "blue" + anchors.centerIn: parent + } + + Timer { + interval: 100 + running: true + repeat: true + onTriggered: console.log("zoom", w.screen.devicePixelRatio) + } +} diff --git a/tests/auto/qml/debugger/qqmlpreview/qqmlpreview.pro b/tests/auto/qml/debugger/qqmlpreview/qqmlpreview.pro new file mode 100644 index 0000000000..2c27306517 --- /dev/null +++ b/tests/auto/qml/debugger/qqmlpreview/qqmlpreview.pro @@ -0,0 +1,25 @@ +CONFIG += testcase +TARGET = tst_qqmlpreview + +QT += qml testlib core qmldebug-private +macos:CONFIG -= app_bundle + +INCLUDEPATH += ../../../../../src/plugins/qmltooling/qmldbg_preview/ + +SOURCES += \ + tst_qqmlpreview.cpp \ + ../../../../../src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.cpp + +HEADERS += \ + ../../../../../src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.h + +include(../shared/debugutil.pri) + +TESTDATA = \ + data/window.qml \ + data/qtquick2.qml \ + data/window2.qml \ + data/window1.qml \ + data/broken.qml \ + data/zoom.qml \ + data/i18n/qml_fr_FR.qm diff --git a/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp b/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp new file mode 100644 index 0000000000..4c4c514832 --- /dev/null +++ b/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp @@ -0,0 +1,365 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmldebugprocess_p.h" +#include "debugutil_p.h" +#include "qqmlpreviewblacklist.h" + +#include <QtTest/qtest.h> +#include <QtTest/qsignalspy.h> +#include <QtCore/qtimer.h> +#include <QtCore/qdebug.h> +#include <QtCore/qthread.h> +#include <QtCore/qlibraryinfo.h> +#include <QtNetwork/qhostaddress.h> + +#include <private/qqmldebugconnection_p.h> +#include <private/qqmlpreviewclient_p.h> + +class tst_QQmlPreview : public QQmlDebugTest +{ + Q_OBJECT + +private: + ConnectResult startQmlProcess(const QString &qmlFile); + void serveRequest(const QString &path); + QList<QQmlDebugClient *> createClients() override; + void verifyProcessOutputContains(const QString &string) const; + + QPointer<QQmlPreviewClient> m_client; + + QStringList m_files; + QStringList m_filesNotFound; + QStringList m_directories; + QStringList m_serviceErrors; + QQmlPreviewClient::FpsInfo m_frameStats; + +private slots: + void cleanup() final; + + void connect(); + void load(); + void rerun(); + void blacklist(); + void error(); + void zoom(); + void fps(); + void language(); +}; + +QQmlDebugTest::ConnectResult tst_QQmlPreview::startQmlProcess(const QString &qmlFile) +{ + return QQmlDebugTest::connect(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qml", + QStringLiteral("QmlPreview"), testFile(qmlFile), true); +} + +void tst_QQmlPreview::serveRequest(const QString &path) +{ + QFileInfo info(path); + + if (info.isDir()) { + m_directories.append(path); + m_client->sendDirectory(path, QDir(path).entryList()); + } else { + QFile file(path); + if (file.open(QIODevice::ReadOnly)) { + m_files.append(path); + m_client->sendFile(path, file.readAll()); + } else { + m_filesNotFound.append(path); + m_client->sendError(path); + } + } +} + +QList<QQmlDebugClient *> tst_QQmlPreview::createClients() +{ + m_client = new QQmlPreviewClient(m_connection); + + QObject::connect(m_client.data(), &QQmlPreviewClient::request, this, &tst_QQmlPreview::serveRequest); + QObject::connect(m_client.data(), &QQmlPreviewClient::error, this, [this](const QString &error) { + m_serviceErrors.append(error); + }); + QObject::connect(m_client.data(), &QQmlPreviewClient::fps, + this, [this](const QQmlPreviewClient::FpsInfo &info) { + m_frameStats = info; + }); + + return QList<QQmlDebugClient *>({m_client}); +} + +void tst_QQmlPreview::verifyProcessOutputContains(const QString &string) const +{ + QTRY_VERIFY_WITH_TIMEOUT(m_process->output().contains(string), 30000); +} + +void checkFiles(const QStringList &files) +{ + QVERIFY(!files.contains("/etc/localtime")); + QVERIFY(!files.contains("/etc/timezome")); + QVERIFY(!files.contains(":/qgradient/webgradients.binaryjson")); +} + +void tst_QQmlPreview::cleanup() +{ + // Use a separate function so that we don't return early from cleanup() on failure. + checkFiles(m_files); + + QQmlDebugTest::cleanup(); + if (QTest::currentTestFailed()) { + qDebug() << "Files loaded:" << m_files; + qDebug() << "Files not loaded:" << m_filesNotFound; + qDebug() << "Directories loaded:" << m_directories; + qDebug() << "Errors reported:" << m_serviceErrors; + } + + m_directories.clear(); + m_files.clear(); + m_filesNotFound.clear(); + m_serviceErrors.clear(); + m_frameStats = QQmlPreviewClient::FpsInfo(); +} + +void tst_QQmlPreview::connect() +{ + const QString file("window.qml"); + QCOMPARE(startQmlProcess(file), ConnectSuccess); + QVERIFY(m_client); + QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled); + m_client->triggerLoad(testFileUrl(file)); + QTRY_VERIFY(m_files.contains(testFile(file))); + verifyProcessOutputContains(file); + m_process->stop(); + QTRY_COMPARE(m_client->state(), QQmlDebugClient::NotConnected); + QVERIFY(m_serviceErrors.isEmpty()); +} + +void tst_QQmlPreview::load() +{ + const QString file("qtquick2.qml"); + QCOMPARE(startQmlProcess(file), ConnectSuccess); + QVERIFY(m_client); + QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled); + m_client->triggerLoad(testFileUrl(file)); + QTRY_VERIFY(m_files.contains(testFile(file))); + verifyProcessOutputContains("ms/degrees"); + + const QStringList files({"window2.qml", "window1.qml", "window.qml"}); + for (const QString &newFile : files) { + m_client->triggerLoad(testFileUrl(newFile)); + QTRY_VERIFY(m_files.contains(testFile(newFile))); + verifyProcessOutputContains(newFile); + } + + m_process->stop(); + QTRY_COMPARE(m_client->state(), QQmlDebugClient::NotConnected); + QVERIFY(m_serviceErrors.isEmpty()); +} + +void tst_QQmlPreview::rerun() +{ + const QString file("window.qml"); + QCOMPARE(startQmlProcess(file), ConnectSuccess); + QVERIFY(m_client); + m_client->triggerLoad(testFileUrl(file)); + const QLatin1String message("window.qml"); + verifyProcessOutputContains(message); + const int pos = m_process->output().lastIndexOf(message) + message.size(); + QVERIFY(pos >= 0); + + m_client->triggerRerun(); + QTRY_VERIFY_WITH_TIMEOUT(m_process->output().indexOf(message, pos) >= pos, 30000); + + m_process->stop(); + QVERIFY(m_serviceErrors.isEmpty()); +} + +void tst_QQmlPreview::blacklist() +{ + QQmlPreviewBlacklist blacklist; + + QStringList strings({ + "lalala", "lulul", "trakdkd", "suppe", "zack" + }); + + for (const QString &string : strings) + QVERIFY(!blacklist.isBlacklisted(string)); + + for (const QString &string : strings) + blacklist.blacklist(string); + + for (const QString &string : strings) { + QVERIFY(blacklist.isBlacklisted(string)); + QVERIFY(!blacklist.isBlacklisted(string.left(string.size() / 2))); + QVERIFY(!blacklist.isBlacklisted(string + "45")); + QVERIFY(!blacklist.isBlacklisted(" " + string)); + QVERIFY(blacklist.isBlacklisted(string + "/45")); + } + + for (auto begin = strings.begin(), it = begin, end = strings.end(); it != end; ++it) { + std::rotate(begin, it, end); + QString path = "/" + strings.join('/'); + blacklist.blacklist(path); + QVERIFY(blacklist.isBlacklisted(path)); + QVERIFY(blacklist.isBlacklisted(path + "/file")); + QVERIFY(!blacklist.isBlacklisted(path + "more")); + path.chop(1); + QVERIFY(!blacklist.isBlacklisted(path)); + std::reverse(begin, end); + } + + blacklist.clear(); + for (const QString &string : strings) + QVERIFY(!blacklist.isBlacklisted(string)); + + blacklist.blacklist(":/qt-project.org"); + QVERIFY(blacklist.isBlacklisted(":/qt-project.org/QmlRuntime/conf/configuration.qml")); + QVERIFY(!blacklist.isBlacklisted(":/qt-project.orgQmlRuntime/conf/configuration.qml")); + + QQmlPreviewBlacklist blacklist2; + + blacklist2.blacklist(":/qt-project.org"); + blacklist2.blacklist(":/QtQuick/Controls/Styles"); + blacklist2.blacklist(":/ExtrasImports/QtQuick/Controls/Styles"); + blacklist2.blacklist(QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath)); + blacklist2.blacklist("/home/ulf/.local/share/QtProject/Qml Runtime/configuration.qml"); + blacklist2.blacklist("/usr/share"); + blacklist2.blacklist("/usr/share/QtProject/Qml Runtime/configuration.qml"); + QVERIFY(blacklist2.isBlacklisted(QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath))); + blacklist2.blacklist("/usr/local/share/QtProject/Qml Runtime/configuration.qml"); + blacklist2.blacklist("qml"); + blacklist2.blacklist(""); // This should not remove all other paths. + + QVERIFY(blacklist2.isBlacklisted(QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath) + + "/QtQuick/Window.2.0")); + QVERIFY(blacklist2.isBlacklisted(QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath))); + QVERIFY(blacklist2.isBlacklisted("/usr/share/QtProject/Qml Runtime/configuration.qml")); + QVERIFY(blacklist2.isBlacklisted("/usr/share/stuff")); + QVERIFY(blacklist2.isBlacklisted("")); + + QQmlPreviewBlacklist blacklist3; + blacklist3.blacklist("/usr/share"); + blacklist3.blacklist("/usr"); + blacklist3.blacklist("/usrdings"); + QVERIFY(blacklist3.isBlacklisted("/usrdings")); + QVERIFY(blacklist3.isBlacklisted("/usr/src")); + QVERIFY(!blacklist3.isBlacklisted("/opt/share")); + QVERIFY(!blacklist3.isBlacklisted("/opt")); + + blacklist3.whitelist("/usr/share"); + QVERIFY(blacklist3.isBlacklisted("/usrdings")); + QVERIFY(!blacklist3.isBlacklisted("/usr")); + QVERIFY(!blacklist3.isBlacklisted("/usr/share")); + QVERIFY(!blacklist3.isBlacklisted("/usr/src")); + QVERIFY(!blacklist3.isBlacklisted("/opt/share")); + QVERIFY(!blacklist3.isBlacklisted("/opt")); +} + +void tst_QQmlPreview::error() +{ + QCOMPARE(startQmlProcess("window.qml"), ConnectSuccess); + QVERIFY(m_client); + m_client->triggerLoad(testFileUrl("broken.qml")); + QTRY_COMPARE_WITH_TIMEOUT(m_serviceErrors.count(), 1, 10000); + QVERIFY(m_serviceErrors.first().contains("broken.qml:32 Expected token `}'")); +} + +static float parseZoomFactor(const QString &output) +{ + const QString prefix("zoom "); + const int start = output.lastIndexOf(prefix) + prefix.length(); + if (start < 0) + return -1; + const int end = output.indexOf('\n', start); + if (end < 0) + return -1; + bool ok = false; + const float zoomFactor = output.mid(start, end - start).toFloat(&ok); + if (!ok) + return -1; + return zoomFactor; +} + +static void verifyZoomFactor(const QQmlDebugProcess *process, float factor) +{ + QTRY_VERIFY_WITH_TIMEOUT(qFuzzyCompare(parseZoomFactor(process->output()), factor), 30000); +} + +void tst_QQmlPreview::zoom() +{ + const QString file("zoom.qml"); + QCOMPARE(startQmlProcess(file), ConnectSuccess); + QVERIFY(m_client); + m_client->triggerLoad(testFileUrl(file)); + QTRY_VERIFY(m_files.contains(testFile(file))); + float baseZoomFactor = -1; + QTRY_VERIFY_WITH_TIMEOUT((baseZoomFactor = parseZoomFactor(m_process->output())) > 0, 30000); + m_client->triggerZoom(2.0f); + verifyZoomFactor(m_process, baseZoomFactor * 2.0f); + m_client->triggerZoom(1.5f); + verifyZoomFactor(m_process, baseZoomFactor * 1.5f); + m_client->triggerZoom(0.5f); + verifyZoomFactor(m_process, baseZoomFactor * 0.5f); + m_client->triggerZoom(-1.0f); + verifyZoomFactor(m_process, baseZoomFactor); + m_process->stop(); + QVERIFY(m_serviceErrors.isEmpty()); +} + +void tst_QQmlPreview::fps() +{ + const QString file("qtquick2.qml"); + QCOMPARE(startQmlProcess(file), ConnectSuccess); + QVERIFY(m_client); + m_client->triggerLoad(testFileUrl(file)); + if (QGuiApplication::platformName() != "offscreen") { + QTRY_VERIFY(m_frameStats.numSyncs > 10); + QVERIFY(m_frameStats.minSync <= m_frameStats.maxSync); + QVERIFY(m_frameStats.totalSync / m_frameStats.numSyncs >= m_frameStats.minSync - 1); + QVERIFY(m_frameStats.totalSync / m_frameStats.numSyncs <= m_frameStats.maxSync); + + QVERIFY(m_frameStats.numRenders > 0); + QVERIFY(m_frameStats.minRender <= m_frameStats.maxRender); + QVERIFY(m_frameStats.totalRender / m_frameStats.numRenders >= m_frameStats.minRender - 1); + QVERIFY(m_frameStats.totalRender / m_frameStats.numRenders <= m_frameStats.maxRender); + } else { + QSKIP("offscreen rendering doesn't produce any frames"); + } +} + +void tst_QQmlPreview::language() +{ + QCOMPARE(startQmlProcess("window.qml"), ConnectSuccess); + QVERIFY(m_client); + m_client->triggerLanguage(dataDirectoryUrl(), "fr_FR"); + QTRY_VERIFY_WITH_TIMEOUT(m_files.contains(testFile("i18n/qml_fr_FR.qm")), 30000); +} + +QTEST_MAIN(tst_QQmlPreview) + +#include "tst_qqmlpreview.moc" diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/data/batchOverflow.qml b/tests/auto/qml/debugger/qqmlprofilerservice/data/batchOverflow.qml new file mode 100644 index 0000000000..dde1def947 --- /dev/null +++ b/tests/auto/qml/debugger/qqmlprofilerservice/data/batchOverflow.qml @@ -0,0 +1,20 @@ +import QtQml 2.2 + +QtObject { + function iterate(dictionaryTable, j) { + var word = "a" + j.toString() + dictionaryTable[word] = null; + } + + Component.onCompleted: { + var dictionaryTable = {}; + for (var j = 0; j < 256; ++j) + iterate(dictionaryTable, j); + } + + property Timer timer: Timer { + interval: 1 + running: true; + onTriggered: Qt.quit(); + } +} diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/data/controlFromJS.qml b/tests/auto/qml/debugger/qqmlprofilerservice/data/controlFromJS.qml index dd7cb2055d..4235a2d55f 100644 --- a/tests/auto/qml/debugger/qqmlprofilerservice/data/controlFromJS.qml +++ b/tests/auto/qml/debugger/qqmlprofilerservice/data/controlFromJS.qml @@ -43,13 +43,6 @@ QtObject { interval: 1000 onTriggered: { console.profileEnd(); - endTimer.start(); } } - - property var endTimer: Timer { - id: endTimer - interval: 1000 - onTriggered: Qt.quit(); - } } diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/data/exit.qml b/tests/auto/qml/debugger/qqmlprofilerservice/data/exit.qml index 4236d70ea3..3b28e65174 100644 --- a/tests/auto/qml/debugger/qqmlprofilerservice/data/exit.qml +++ b/tests/auto/qml/debugger/qqmlprofilerservice/data/exit.qml @@ -1,7 +1,7 @@ import QtQml 2.0 QtObject { - Timer { + property Timer timer: Timer { running: true interval: 1 onTriggered: Qt.quit(); diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/data/memory.qml b/tests/auto/qml/debugger/qqmlprofilerservice/data/memory.qml new file mode 100644 index 0000000000..f39dcdf16a --- /dev/null +++ b/tests/auto/qml/debugger/qqmlprofilerservice/data/memory.qml @@ -0,0 +1,17 @@ +import QtQml 2.0 + +Timer { + interval: 1 + running: true + + function recurse(i) { + var x = { t: [1, 2, 3, 4] } + console.log(x.t[i]); + if (i < 3) + recurse(i + 1); + else + Qt.quit(); + } + + onTriggered: recurse(0) +} diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/data/qstr.qml b/tests/auto/qml/debugger/qqmlprofilerservice/data/qstr.qml new file mode 100644 index 0000000000..09dcd34b5c --- /dev/null +++ b/tests/auto/qml/debugger/qqmlprofilerservice/data/qstr.qml @@ -0,0 +1,9 @@ +import QtQml 2.0 + +Timer { + property string stuff: qsTr("foo") + + running: true + interval: 1 + onTriggered: Qt.quit(); +} diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/data/quit.qml b/tests/auto/qml/debugger/qqmlprofilerservice/data/quit.qml new file mode 100644 index 0000000000..bc8c2b90ae --- /dev/null +++ b/tests/auto/qml/debugger/qqmlprofilerservice/data/quit.qml @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +//DO NOT CHANGE + +Item { + Timer { + running: true + triggeredOnStart: true + onTriggered: Qt.quit(); + } +} + diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/qqmlprofilerservice.pro b/tests/auto/qml/debugger/qqmlprofilerservice/qqmlprofilerservice.pro index 71a58d6f34..0cd4b331f2 100644 --- a/tests/auto/qml/debugger/qqmlprofilerservice/qqmlprofilerservice.pro +++ b/tests/auto/qml/debugger/qqmlprofilerservice/qqmlprofilerservice.pro @@ -4,13 +4,11 @@ osx:CONFIG -= app_bundle SOURCES += tst_qqmlprofilerservice.cpp -INCLUDEPATH += ../shared -include(../../../shared/util.pri) include(../shared/debugutil.pri) TESTDATA = data/* -QT += core qml testlib gui-private core-private +QT += testlib gui-private OTHER_FILES += \ data/pixmapCacheTest.qml \ @@ -21,4 +19,7 @@ OTHER_FILES += \ data/TestImage_2x2.png \ data/signalSourceLocation.qml \ data/javascript.qml \ - data/timer.qml + data/timer.qml \ + data/qstr.qml \ + data/memory.qml \ + data/batchOverflow.qml diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp index 692e70d7da..085eb7b87a 100644 --- a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp +++ b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp @@ -27,254 +27,160 @@ ****************************************************************************/ #include "debugutil_p.h" +#include "qqmldebugprocess_p.h" #include "../../../shared/util.h" #include <private/qqmlprofilerclient_p.h> #include <private/qqmldebugconnection_p.h> #include <QtTest/qtest.h> +#include <QtTest/qsignalspy.h> #include <QtCore/qlibraryinfo.h> -#define STR_PORT_FROM "13773" -#define STR_PORT_TO "13783" +#include <QtGui/private/qguiapplication_p.h> +#include <QtGui/qpa/qplatformintegration.h> -struct QQmlProfilerData -{ - QQmlProfilerData(qint64 time = -2, int messageType = -1, int detailType = -1, - const QString &detailData = QString()) : - time(time), messageType(messageType), detailType(detailType), detailData(detailData), - line(-1), column(-1), framerate(-1), animationcount(-1), amount(-1) - {} - - qint64 time; - int messageType; - int detailType; - - //### - QString detailData; //used by RangeData and RangeLocation - int line; //used by RangeLocation - int column; //used by RangeLocation - int framerate; //used by animation events - int animationcount; //used by animation events - qint64 amount; //used by heap events -}; - -class QQmlProfilerTestClient : public QQmlProfilerClient +class QQmlProfilerTestClient : public QQmlProfilerEventReceiver { Q_OBJECT public: - QQmlProfilerTestClient(QQmlDebugConnection *connection) : QQmlProfilerClient(connection), - lastTimestamp(-1) {} + QQmlProfilerTestClient(QQmlDebugConnection *connection) : + client(new QQmlProfilerClient(connection, this)) + { + connect(client.data(), &QQmlProfilerClient::traceStarted, + this, &QQmlProfilerTestClient::startTrace); + connect(client.data(), &QQmlProfilerClient::traceFinished, + this, &QQmlProfilerTestClient::endTrace); + } + + void startTrace(qint64 timestamp, const QList<int> &engineIds); + void endTrace(qint64 timestamp, const QList<int> &engineIds); - QVector<QQmlProfilerData> qmlMessages; - QVector<QQmlProfilerData> javascriptMessages; - QVector<QQmlProfilerData> jsHeapMessages; - QVector<QQmlProfilerData> asynchronousMessages; - QVector<QQmlProfilerData> pixmapMessages; + QPointer<QQmlProfilerClient> client; // Owned by QQmlDebugTest + QVector<QQmlProfilerEventType> types; - qint64 lastTimestamp; + QVector<QQmlProfilerEvent> qmlMessages; + QVector<QQmlProfilerEvent> javascriptMessages; + QVector<QQmlProfilerEvent> jsHeapMessages; + QVector<QQmlProfilerEvent> asynchronousMessages; + QVector<QQmlProfilerEvent> pixmapMessages; -signals: - void recordingFinished(); + int numLoadedEventTypes() const override; + void addEventType(const QQmlProfilerEventType &type) override; + void addEvent(const QQmlProfilerEvent &event) override; private: - void traceStarted(qint64 time, int engineId); - void traceFinished(qint64 time, int engineId); - void rangeStart(QQmlProfilerDefinitions::RangeType type, qint64 startTime); - void rangeData(QQmlProfilerDefinitions::RangeType type, qint64 time, const QString &data); - void rangeLocation(QQmlProfilerDefinitions::RangeType type, qint64 time, - const QQmlEventLocation &location); - void rangeEnd(QQmlProfilerDefinitions::RangeType type, qint64 endTime); - void animationFrame(qint64 time, int frameRate, int animationCount, int threadId); - void sceneGraphEvent(QQmlProfilerDefinitions::SceneGraphFrameType type, qint64 time, - qint64 numericData1, qint64 numericData2, qint64 numericData3, - qint64 numericData4, qint64 numericData5); - void pixmapCacheEvent(QQmlProfilerDefinitions::PixmapEventType type, qint64 time, - const QString &url, int numericData1, int numericData2); - void memoryAllocation(QQmlProfilerDefinitions::MemoryType type, qint64 time, qint64 amount); - void inputEvent(QQmlProfilerDefinitions::InputEventType type, qint64 time, int a, int b); - void complete(); - - void unknownEvent(QQmlProfilerDefinitions::Message messageType, qint64 time, int detailType); - void unknownData(QPacket &stream); + qint64 lastTimestamp = -1; }; -void QQmlProfilerTestClient::traceStarted(qint64 time, int engineId) +void QQmlProfilerTestClient::startTrace(qint64 timestamp, const QList<int> &engineIds) { - asynchronousMessages.append(QQmlProfilerData(time, QQmlProfilerDefinitions::Event, - QQmlProfilerDefinitions::StartTrace, - QString::number(engineId))); + types.append(QQmlProfilerEventType(Event, MaximumRangeType, StartTrace)); + asynchronousMessages.append(QQmlProfilerEvent(timestamp, types.length() - 1, + engineIds.toVector())); } -void QQmlProfilerTestClient::traceFinished(qint64 time, int engineId) +void QQmlProfilerTestClient::endTrace(qint64 timestamp, const QList<int> &engineIds) { - asynchronousMessages.append(QQmlProfilerData(time, QQmlProfilerDefinitions::Event, - QQmlProfilerDefinitions::EndTrace, - QString::number(engineId))); + types.append(QQmlProfilerEventType(Event, MaximumRangeType, EndTrace)); + asynchronousMessages.append(QQmlProfilerEvent(timestamp, types.length() - 1, + engineIds.toVector())); } -void QQmlProfilerTestClient::rangeStart(QQmlProfilerDefinitions::RangeType type, qint64 startTime) +int QQmlProfilerTestClient::numLoadedEventTypes() const { - QVERIFY(type >= 0 && type < QQmlProfilerDefinitions::MaximumRangeType); - QVERIFY(lastTimestamp <= startTime); - lastTimestamp = startTime; - QQmlProfilerData data(startTime, QQmlProfilerDefinitions::RangeStart, type); - if (type == QQmlProfilerDefinitions::Javascript) - javascriptMessages.append(data); - else - qmlMessages.append(data); + return types.length(); } -void QQmlProfilerTestClient::rangeData(QQmlProfilerDefinitions::RangeType type, qint64 time, - const QString &string) +void QQmlProfilerTestClient::addEventType(const QQmlProfilerEventType &type) { - QVERIFY(type >= 0 && type < QQmlProfilerDefinitions::MaximumRangeType); - QVERIFY(lastTimestamp <= time); - lastTimestamp = time; - QQmlProfilerData data(time, QQmlProfilerDefinitions::RangeData, type, string); - if (type == QQmlProfilerDefinitions::Javascript) - javascriptMessages.append(data); - else - qmlMessages.append(data); + types.append(type); } -void QQmlProfilerTestClient::rangeLocation(QQmlProfilerDefinitions::RangeType type, qint64 time, - const QQmlEventLocation &location) +void QQmlProfilerTestClient::addEvent(const QQmlProfilerEvent &event) { - QVERIFY(type >= 0 && type < QQmlProfilerDefinitions::MaximumRangeType); - QVERIFY(location.line >= -2); - QVERIFY(lastTimestamp <= time); - lastTimestamp = time; - QQmlProfilerData data(time, QQmlProfilerDefinitions::RangeLocation, type, location.filename); - data.line = location.line; - data.column = location.column; - if (type == QQmlProfilerDefinitions::Javascript) - javascriptMessages.append(data); - else - qmlMessages.append(data); -} + const int typeIndex = event.typeIndex(); + QVERIFY(typeIndex < types.length()); -void QQmlProfilerTestClient::rangeEnd(QQmlProfilerDefinitions::RangeType type, qint64 endTime) -{ - QVERIFY(type >= 0 && type < QQmlProfilerDefinitions::MaximumRangeType); - QVERIFY(lastTimestamp <= endTime); - lastTimestamp = endTime; - QQmlProfilerData data(endTime, QQmlProfilerDefinitions::RangeEnd, type); - if (type == QQmlProfilerDefinitions::Javascript) - javascriptMessages.append(data); - else - qmlMessages.append(data); -} + const QQmlProfilerEventType &type = types[typeIndex]; -void QQmlProfilerTestClient::animationFrame(qint64 time, int frameRate, int animationCount, int threadId) -{ - QVERIFY(threadId >= 0); - QVERIFY(frameRate != -1); - QVERIFY(animationCount != -1); - QVERIFY(lastTimestamp <= time); - lastTimestamp = time; - QQmlProfilerData data(time, QQmlProfilerDefinitions::Event, - QQmlProfilerDefinitions::AnimationFrame); - data.framerate = frameRate; - data.animationcount = animationCount; - asynchronousMessages.append(data); -} + QVERIFY(event.timestamp() >= lastTimestamp); + lastTimestamp = event.timestamp(); -void QQmlProfilerTestClient::sceneGraphEvent(QQmlProfilerDefinitions::SceneGraphFrameType type, - qint64 time, qint64 numericData1, qint64 numericData2, - qint64 numericData3, qint64 numericData4, - qint64 numericData5) -{ - Q_UNUSED(numericData1); - Q_UNUSED(numericData2); - Q_UNUSED(numericData3); - Q_UNUSED(numericData4); - Q_UNUSED(numericData5); - QVERIFY(lastTimestamp <= time); - lastTimestamp = time; - asynchronousMessages.append(QQmlProfilerData(time, QQmlProfilerDefinitions::SceneGraphFrame, - type)); -} - -void QQmlProfilerTestClient::pixmapCacheEvent(QQmlProfilerDefinitions::PixmapEventType type, - qint64 time, const QString &url, int numericData1, - int numericData2) -{ - QVERIFY(lastTimestamp <= time); - lastTimestamp = time; - QQmlProfilerData data(time, QQmlProfilerDefinitions::PixmapCacheEvent, type, url); - switch (type) { - case QQmlProfilerDefinitions::PixmapSizeKnown: - data.line = numericData1; - data.column = numericData2; + switch (type.message()) { + case Event: { + switch (type.detailType()) { + case StartTrace: + QFAIL("StartTrace should not be passed on as event"); + break; + case EndTrace: + QFAIL("EndTrace should not be passed on as event"); + break; + case AnimationFrame: + asynchronousMessages.append(event); + break; + case Mouse: + case Key: + qmlMessages.append(event); + break; + default: + QFAIL(qPrintable(QString::fromLatin1("Event with unknown detailType %1 received at %2.") + .arg(type.detailType()).arg(event.timestamp()))); + break; + } + break; + } + case RangeStart: + case RangeData: + case RangeLocation: + case RangeEnd: + QFAIL("Range stages are transmitted as part of events"); + break; + case Complete: + QFAIL("Complete should not be passed on as event"); + break; + case PixmapCacheEvent: + pixmapMessages.append(event); break; - case QQmlProfilerDefinitions::PixmapReferenceCountChanged: - case QQmlProfilerDefinitions::PixmapCacheCountChanged: - data.animationcount = numericData1; + case SceneGraphFrame: + asynchronousMessages.append(event); break; - default: + case MemoryAllocation: + jsHeapMessages.append(event); + break; + case DebugMessage: + // Unhandled + break; + case MaximumMessage: + switch (type.rangeType()) { + case Painting: + QFAIL("QtQuick1 paint message received."); + break; + case Compiling: + case Creating: + case Binding: + case HandlingSignal: + qmlMessages.append(event); + break; + case Javascript: + javascriptMessages.append(event); + break; + default: + QFAIL(qPrintable( + QString::fromLatin1("Unknown range event %1 received at %2.") + .arg(type.rangeType()).arg(event.timestamp()))); + break; + } break; } - pixmapMessages.append(data); -} - -void QQmlProfilerTestClient::memoryAllocation(QQmlProfilerDefinitions::MemoryType type, qint64 time, - qint64 amount) -{ - QVERIFY(lastTimestamp <= time); - lastTimestamp = time; - QQmlProfilerData data(time, QQmlProfilerDefinitions::MemoryAllocation, type); - data.amount = amount; - jsHeapMessages.append(data); } -void QQmlProfilerTestClient::inputEvent(QQmlProfilerDefinitions::InputEventType type, qint64 time, - int a, int b) -{ - QVERIFY(lastTimestamp <= time); - lastTimestamp = time; - qmlMessages.append(QQmlProfilerData(time, QQmlProfilerDefinitions::Event, type, - QString::number(a) + QLatin1Char('x') + - QString::number(b))); -} - -void QQmlProfilerTestClient::unknownEvent(QQmlProfilerDefinitions::Message messageType, qint64 time, - int detailType) -{ - QFAIL(qPrintable(QString::fromLatin1("Unknown event %1 with detail type %2 received at %3.") - .arg(messageType).arg(detailType).arg(time))); -} - -void QQmlProfilerTestClient::unknownData(QPacket &stream) -{ - QFAIL(qPrintable(QString::fromLatin1("%1 bytes of extra data after receiving message.") - .arg(stream.device()->bytesAvailable()))); -} - -void QQmlProfilerTestClient::complete() -{ - emit recordingFinished(); -} - -class tst_QQmlProfilerService : public QQmlDataTest +class tst_QQmlProfilerService : public QQmlDebugTest { Q_OBJECT -public: - tst_QQmlProfilerService() - : m_process(0) - , m_connection(0) - , m_client(0) - { - } - - private: - QQmlDebugProcess *m_process; - QQmlDebugConnection *m_connection; - QQmlProfilerTestClient *m_client; - enum MessageListType { MessageListQML, MessageListJavaScript, @@ -289,18 +195,28 @@ private: CheckLine = 1 << 2, CheckColumn = 1 << 3, CheckDataEndsWith = 1 << 4, + CheckFileEndsWith = 1 << 5, + CheckNumbers = 1 << 6, - CheckAll = CheckMessageType | CheckDetailType | CheckLine | CheckColumn | CheckDataEndsWith + CheckType = CheckMessageType | CheckDetailType | CheckLine | CheckColumn | CheckFileEndsWith }; - void connect(bool block, const QString &testFile, bool restrictServices = true); + ConnectResult connect(bool block, const QString &file, bool recordFromStart = true, + uint flushInterval = 0, bool restrictServices = true, + const QString &executable + = QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene"); + void checkProcessTerminated(); void checkTraceReceived(); void checkJsHeap(); - bool verify(MessageListType type, int expectedPosition, const QQmlProfilerData &expected, - quint32 checks); + bool verify(MessageListType type, int expectedPosition, + const QQmlProfilerEventType &expected, quint32 checks, + const QVector<qint64> &expectedNumbers); + + QList<QQmlDebugClient *> createClients() override; + QScopedPointer<QQmlProfilerTestClient> m_client; private slots: - void cleanup(); + void cleanup() override; void connect_data(); void connect(); @@ -311,58 +227,75 @@ private slots: void signalSourceLocation(); void javascript(); void flushInterval(); + void translationBinding(); + void memory(); + void compile(); + void multiEngine(); + void batchOverflow(); + +private: + bool m_recordFromStart = true; + bool m_flushInterval = false; + bool m_isComplete = false; + + // Don't use ({...}) here as MSVC will interpret that as the "QVector(int size)" ctor. + const QVector<qint64> m_rangeStart = (QVector<qint64>() << RangeStart); + const QVector<qint64> m_rangeEnd = (QVector<qint64>() << RangeEnd); }; -#define VERIFY(type, position, expected, checks) QVERIFY(verify(type, position, expected, checks)) +#define VERIFY(type, position, expected, checks, numbers) \ + QVERIFY(verify(type, position, expected, checks, numbers)) -void tst_QQmlProfilerService::connect(bool block, const QString &testFile, bool restrictServices) +QQmlDebugTest::ConnectResult tst_QQmlProfilerService::connect( + bool block, const QString &file, bool recordFromStart, uint flushInterval, + bool restrictServices, const QString &executable) { + m_recordFromStart = recordFromStart; + m_flushInterval = flushInterval; + m_isComplete = false; + // ### Still using qmlscene due to QTBUG-33377 - const QString executable = QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene"; - QStringList arguments; - arguments << QString::fromLatin1("-qmljsdebugger=port:%1,%2%3%4") - .arg(STR_PORT_FROM).arg(STR_PORT_TO) - .arg(block ? QStringLiteral(",block") : QString()) - .arg(restrictServices ? QStringLiteral(",services:CanvasFrameRate") : QString()) - << QQmlDataTest::instance()->testFile(testFile); - - m_process = new QQmlDebugProcess(executable, this); - m_process->start(QStringList() << arguments); - QVERIFY2(m_process->waitForSessionStart(), "Could not launch application, or did not get 'Waiting for connection'."); - - m_connection = new QQmlDebugConnection(); - m_client = new QQmlProfilerTestClient(m_connection); - QList<QQmlDebugClient *> others = QQmlDebugTest::createOtherClients(m_connection); - - const int port = m_process->debugPort(); - m_connection->connectToHost(QLatin1String("127.0.0.1"), port); - QVERIFY(m_client); - QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled); + return QQmlDebugTest::connect( + executable, + restrictServices ? "CanvasFrameRate,EngineControl,DebugMessages" : QString(), + testFile(file), block); +} - foreach (QQmlDebugClient *other, others) - QCOMPARE(other->state(), restrictServices ? QQmlDebugClient::Unavailable : - QQmlDebugClient::Enabled); - qDeleteAll(others); +void tst_QQmlProfilerService::checkProcessTerminated() +{ + // If the process ends before connect(), we get a non-success value from connect() + // That's not a problem as we will still receive the trace. We check that process has terminated + // cleanly here. + + // Wait for the process to finish by itself, if that hasn't happened already + QVERIFY(m_client); + QVERIFY(m_client->client); + QTRY_COMPARE(m_client->client->state(), QQmlDebugClient::NotConnected); + QVERIFY(m_process); + QVERIFY(m_process->exitStatus() != QProcess::CrashExit); + QTRY_COMPARE(m_process->exitStatus(), QProcess::NormalExit); } void tst_QQmlProfilerService::checkTraceReceived() { - QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(recordingFinished())), - "No trace received in time."); + QVERIFY(m_process->exitStatus() != QProcess::CrashExit); + QTRY_VERIFY2(m_isComplete, "No trace received in time."); + + QVector<qint64> numbers; // must start with "StartTrace" - QQmlProfilerData expected(0, QQmlProfilerDefinitions::Event, - QQmlProfilerDefinitions::StartTrace); - VERIFY(MessageListAsynchronous, 0, expected, CheckMessageType | CheckDetailType); + QQmlProfilerEventType expected(Event, MaximumRangeType, StartTrace); + VERIFY(MessageListAsynchronous, 0, expected, CheckMessageType | CheckDetailType, numbers); // must end with "EndTrace" - expected.detailType = QQmlProfilerDefinitions::EndTrace; + expected = QQmlProfilerEventType(Event, MaximumRangeType, EndTrace); VERIFY(MessageListAsynchronous, m_client->asynchronousMessages.length() - 1, expected, - CheckMessageType | CheckDetailType); + CheckMessageType | CheckDetailType, numbers); } void tst_QQmlProfilerService::checkJsHeap() { + QVERIFY(m_client); QVERIFY2(m_client->jsHeapMessages.count() > 0, "no JavaScript heap messages received"); bool seen_alloc = false; @@ -371,33 +304,36 @@ void tst_QQmlProfilerService::checkJsHeap() qint64 allocated = 0; qint64 used = 0; qint64 lastTimestamp = -1; - foreach (const QQmlProfilerData &message, m_client->jsHeapMessages) { - switch (message.detailType) { - case QV4::Profiling::HeapPage: - allocated += message.amount; + foreach (const QQmlProfilerEvent &message, m_client->jsHeapMessages) { + const auto amount = message.number<qint64>(0); + const QQmlProfilerEventType &type = m_client->types.at(message.typeIndex()); + switch (type.detailType()) { + case HeapPage: + allocated += amount; seen_alloc = true; break; - case QV4::Profiling::SmallItem: - used += message.amount; + case SmallItem: + used += amount; seen_small = true; break; - case QV4::Profiling::LargeItem: - allocated += message.amount; - used += message.amount; + case LargeItem: + allocated += amount; + used += amount; seen_large = true; break; } - QVERIFY(message.time >= lastTimestamp); + QVERIFY(message.timestamp() >= lastTimestamp); // The heap will only be consistent after all events of the same timestamp are processed. if (lastTimestamp == -1) { - lastTimestamp = message.time; - continue; - } else if (message.time == lastTimestamp) { + lastTimestamp = message.timestamp(); continue; } - lastTimestamp = message.time; + if (message.timestamp() == lastTimestamp) + continue; + + lastTimestamp = message.timestamp(); QVERIFY2(used >= 0, QString::fromLatin1("Negative memory usage seen: %1") .arg(used).toUtf8().constData()); @@ -416,10 +352,15 @@ void tst_QQmlProfilerService::checkJsHeap() } bool tst_QQmlProfilerService::verify(tst_QQmlProfilerService::MessageListType type, - int expectedPosition, const QQmlProfilerData &expected, - quint32 checks) + int expectedPosition, const QQmlProfilerEventType &expected, + quint32 checks, const QVector<qint64> &expectedNumbers) { - QVector<QQmlProfilerData> *target = 0; + if (!m_client) { + qWarning() << "No debug client available"; + return false; + } + + const QVector<QQmlProfilerEvent> *target = nullptr; switch (type) { case MessageListQML: target = &(m_client->qmlMessages); break; case MessageListJavaScript: target = &(m_client->javascriptMessages); break; @@ -428,48 +369,93 @@ bool tst_QQmlProfilerService::verify(tst_QQmlProfilerService::MessageListType ty case MessageListPixmap: target = &(m_client->pixmapMessages); break; } + if (!target) { + qWarning() << "Invalid MessageListType" << type; + return false; + } + if (target->length() <= expectedPosition) { qWarning() << "Not enough events. expected position:" << expectedPosition << "length:" << target->length(); return false; } - uint position = expectedPosition; - qint64 timestamp = target->at(expectedPosition).time; - while (position > 0 && target->at(position - 1).time == timestamp) + int position = expectedPosition; + qint64 timestamp = target->at(expectedPosition).timestamp(); + while (position > 0 && target->at(position - 1).timestamp() == timestamp) --position; QStringList warnings; do { - const QQmlProfilerData &actual = target->at(position); - if ((checks & CheckMessageType) && actual.messageType != expected.messageType) { - warnings << QString::fromLatin1("%1: unexpected messageType. actual: %2 - expected: %3") - .arg(position).arg(actual.messageType).arg(expected.messageType); + const QQmlProfilerEvent &event = target->at(position); + const QQmlProfilerEventType &actual = m_client->types.at(event.typeIndex()); + if ((checks & CheckMessageType) && + (actual.message() != expected.message() + || actual.rangeType() != expected.rangeType())) { + warnings << QString::fromLatin1("%1: unexpected messageType or rangeType. " + "actual: %2, %3 - expected: %4, %5") + .arg(position).arg(actual.message()).arg(actual.rangeType()) + .arg(expected.message()).arg(expected.rangeType()); continue; } - if ((checks & CheckDetailType) && actual.detailType != expected.detailType) { + if ((checks & CheckDetailType) && actual.detailType() != expected.detailType()) { warnings << QString::fromLatin1("%1: unexpected detailType. actual: %2 - expected: %3") - .arg(position).arg(actual.detailType).arg(expected.detailType); + .arg(position).arg(actual.detailType()).arg(expected.detailType()); continue; } - if ((checks & CheckLine) && actual.line != expected.line) { + + const QQmlProfilerEventLocation expectedLocation = expected.location(); + const QQmlProfilerEventLocation actualLocation = actual.location(); + + if ((checks & CheckLine) && actualLocation.line() != expectedLocation.line()) { warnings << QString::fromLatin1("%1: unexpected line. actual: %2 - expected: %3") - .arg(position).arg(actual.line).arg(expected.line); + .arg(position).arg(actualLocation.line()) + .arg(expectedLocation.line()); continue; } - if ((checks & CheckColumn) && actual.column != expected.column) { + if ((checks & CheckColumn) && actualLocation.column() != expectedLocation.column()) { warnings << QString::fromLatin1("%1: unexpected column. actual: %2 - expected: %3") - .arg(position).arg(actual.column).arg(expected.column); + .arg(position).arg(actualLocation.column()) + .arg(expectedLocation.column()); continue; } - if ((checks & CheckDataEndsWith) && !actual.detailData.endsWith(expected.detailData)) { + if ((checks & CheckFileEndsWith) && + !actualLocation.filename().endsWith(expectedLocation.filename())) { + warnings << QString::fromLatin1("%1: unexpected fileName. actual: %2 - expected: %3") + .arg(position).arg(actualLocation.filename()) + .arg(expectedLocation.filename()); + continue; + } + + if ((checks & CheckDataEndsWith) && !actual.data().endsWith(expected.data())) { warnings << QString::fromLatin1("%1: unexpected detailData. actual: %2 - expected: %3") - .arg(position).arg(actual.detailData).arg(expected.detailData); + .arg(position).arg(actual.data()).arg(expected.data()); continue; } + + if (checks & CheckNumbers) { + const QVector<qint64> actualNumbers = event.numbers<QVector<qint64>>(); + if (actualNumbers != expectedNumbers) { + + QStringList expectedList; + for (qint64 number : expectedNumbers) + expectedList.append(QString::number(number)); + QStringList actualList; + for (qint64 number : actualNumbers) + actualList.append(QString::number(number)); + + warnings << QString::fromLatin1( + "%1: unexpected numbers. actual [%2] - expected: [%3]") + .arg(position) + .arg(actualList.join(QLatin1String(", "))) + .arg(expectedList.join(QLatin1String(", "))); + continue; + } + } + return true; - } while (target->at(++position).time == timestamp); + } while (++position < target->length() && target->at(position).timestamp() == timestamp); foreach (const QString &message, warnings) qWarning() << message.toLocal8Bit().constData(); @@ -477,54 +463,62 @@ bool tst_QQmlProfilerService::verify(tst_QQmlProfilerService::MessageListType ty return false; } +QList<QQmlDebugClient *> tst_QQmlProfilerService::createClients() +{ + m_client.reset(new QQmlProfilerTestClient(m_connection)); + m_client->client->setRecording(m_recordFromStart); + m_client->client->setFlushInterval(m_flushInterval); + QObject::connect(m_client->client.data(), &QQmlProfilerClient::complete, + this, [this](){ m_isComplete = true; }); + return QList<QQmlDebugClient *>({m_client->client}); +} + void tst_QQmlProfilerService::cleanup() { + auto log = [this](const QQmlProfilerEvent &data, int i) { + const QQmlProfilerEventType &type = m_client->types.at(data.typeIndex()); + const QQmlProfilerEventLocation location = type.location(); + qDebug() << i << data.timestamp() << type.message() << type.rangeType() << type.detailType() + << location.filename() << location.line() << location.column() + << data.numbers<QVector<qint64>>(); + }; + if (m_client && QTest::currentTestFailed()) { qDebug() << "QML Messages:" << m_client->qmlMessages.count(); int i = 0; - foreach (const QQmlProfilerData &data, m_client->qmlMessages) { - qDebug() << i++ << data.time << data.messageType << data.detailType << data.detailData - << data.line << data.column; - } + for (const QQmlProfilerEvent &data : qAsConst(m_client->qmlMessages)) + log(data, i++); + qDebug() << " "; qDebug() << "JavaScript Messages:" << m_client->javascriptMessages.count(); i = 0; - foreach (const QQmlProfilerData &data, m_client->javascriptMessages) { - qDebug() << i++ << data.time << data.messageType << data.detailType << data.detailData - << data.line << data.column; - } + + for (const QQmlProfilerEvent &data : qAsConst(m_client->javascriptMessages)) + log(data, i++); + qDebug() << " "; qDebug() << "Asynchronous Messages:" << m_client->asynchronousMessages.count(); i = 0; - foreach (const QQmlProfilerData &data, m_client->asynchronousMessages) { - qDebug() << i++ << data.time << data.messageType << data.detailType << data.detailData - << data.framerate << data.animationcount << data.line << data.column; - } + for (const QQmlProfilerEvent &data : qAsConst(m_client->asynchronousMessages)) + log(data, i++); + qDebug() << " "; qDebug() << "Pixmap Cache Messages:" << m_client->pixmapMessages.count(); i = 0; - foreach (const QQmlProfilerData &data, m_client->pixmapMessages) { - qDebug() << i++ << data.time << data.messageType << data.detailType << data.detailData - << data.line << data.column; - } + for (const QQmlProfilerEvent &data : qAsConst(m_client->pixmapMessages)) + log(data, i++); + qDebug() << " "; qDebug() << "Javascript Heap Messages:" << m_client->jsHeapMessages.count(); i = 0; - foreach (const QQmlProfilerData &data, m_client->jsHeapMessages) { - qDebug() << i++ << data.time << data.messageType << data.detailType; - } + for (const QQmlProfilerEvent &data : qAsConst(m_client->jsHeapMessages)) + log(data, i++); + qDebug() << " "; - qDebug() << "Process State:" << (m_process ? m_process->state() : QLatin1String("null")); - qDebug() << "Application Output:" << (m_process ? m_process->output() : QLatin1String("null")); - qDebug() << "Connection State:" << QQmlDebugTest::connectionStateString(m_connection); - qDebug() << "Client State:" << QQmlDebugTest::clientStateString(m_client); } - delete m_process; - m_process = 0; - delete m_client; - m_client = 0; - delete m_connection; - m_connection = 0; + + m_client.reset(); + QQmlDebugTest::cleanup(); } void tst_QQmlProfilerService::connect_data() @@ -548,63 +542,63 @@ void tst_QQmlProfilerService::connect() QFETCH(bool, restrictMode); QFETCH(bool, traceEnabled); - connect(blockMode, "test.qml", restrictMode); + QCOMPARE(connect(blockMode, "test.qml", traceEnabled, 0, restrictMode), ConnectSuccess); - // if the engine is waiting, then the first message determines if it starts with trace enabled if (!traceEnabled) - m_client->sendRecordingStatus(false); - m_client->sendRecordingStatus(true); - m_client->sendRecordingStatus(false); + m_client->client->setRecording(true); + + QTRY_VERIFY(m_client->numLoadedEventTypes() > 0); + m_client->client->setRecording(false); checkTraceReceived(); checkJsHeap(); } void tst_QQmlProfilerService::pixmapCacheData() { - connect(true, "pixmapCacheTest.qml"); - m_client->sendRecordingStatus(true); - QVERIFY(QQmlDebugTest::waitForSignal(m_process, SIGNAL(readyReadStandardOutput()))); + QCOMPARE(connect(true, "pixmapCacheTest.qml"), ConnectSuccess); + // Don't wait for readyReadStandardOutput before the loop. It may have already arrived. while (m_process->output().indexOf(QLatin1String("image loaded")) == -1 && m_process->output().indexOf(QLatin1String("image error")) == -1) QVERIFY(QQmlDebugTest::waitForSignal(m_process, SIGNAL(readyReadStandardOutput()))); - m_client->sendRecordingStatus(false); + m_client->client->setRecording(false); checkTraceReceived(); checkJsHeap(); - QQmlProfilerData expected(0, QQmlProfilerDefinitions::PixmapCacheEvent); + auto createType = [](PixmapEventType type) { + return QQmlProfilerEventType(PixmapCacheEvent, MaximumRangeType, type); + }; + + QVector<qint64> numbers; // image starting to load - expected.detailType = QQmlProfilerDefinitions::PixmapLoadingStarted; - VERIFY(MessageListPixmap, 0, expected, CheckMessageType | CheckDetailType); + VERIFY(MessageListPixmap, 0, createType(PixmapLoadingStarted), + CheckMessageType | CheckDetailType, numbers); // image size - expected.detailType = QQmlProfilerDefinitions::PixmapSizeKnown; - expected.line = expected.column = 2; // width and height, in fact - VERIFY(MessageListPixmap, 1, expected, - CheckMessageType | CheckDetailType | CheckLine | CheckColumn); + numbers = QVector<qint64>({2, 2, 1}); + VERIFY(MessageListPixmap, 1, createType(PixmapSizeKnown), + CheckMessageType | CheckDetailType | CheckNumbers, numbers); // image loaded - expected.detailType = QQmlProfilerDefinitions::PixmapLoadingFinished; - VERIFY(MessageListPixmap, 2, expected, CheckMessageType | CheckDetailType); + VERIFY(MessageListPixmap, 2, createType(PixmapLoadingFinished), + CheckMessageType | CheckDetailType, numbers); // cache size - expected.detailType = QQmlProfilerDefinitions::PixmapCacheCountChanged; - VERIFY(MessageListPixmap, 3, expected, CheckMessageType | CheckDetailType); + VERIFY(MessageListPixmap, 3, createType(PixmapCacheCountChanged), + CheckMessageType | CheckDetailType, numbers); } void tst_QQmlProfilerService::scenegraphData() { - connect(true, "scenegraphTest.qml"); - - m_client->sendRecordingStatus(true); + QCOMPARE(connect(true, "scenegraphTest.qml"), ConnectSuccess); while (!m_process->output().contains(QLatin1String("tick"))) QVERIFY(QQmlDebugTest::waitForSignal(m_process, SIGNAL(readyReadStandardOutput()))); - m_client->sendRecordingStatus(false); + m_client->client->setRecording(false); checkTraceReceived(); checkJsHeap(); @@ -619,34 +613,39 @@ void tst_QQmlProfilerService::scenegraphData() qint64 contextFrameTime = -1; qint64 renderFrameTime = -1; #if QT_CONFIG(opengl) //Software renderer doesn't have context frames - foreach (const QQmlProfilerData &msg, m_client->asynchronousMessages) { - if (msg.messageType == QQmlProfilerDefinitions::SceneGraphFrame) { - if (msg.detailType == QQmlProfilerDefinitions::SceneGraphContextFrame) { - contextFrameTime = msg.time; - break; + if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL)) { + foreach (const QQmlProfilerEvent &msg, m_client->asynchronousMessages) { + const QQmlProfilerEventType &type = m_client->types.at(msg.typeIndex()); + if (type.message() == SceneGraphFrame) { + if (type.detailType() == SceneGraphContextFrame) { + contextFrameTime = msg.timestamp(); + break; + } } } - } - QVERIFY(contextFrameTime != -1); + QVERIFY(contextFrameTime != -1); + } #endif - foreach (const QQmlProfilerData &msg, m_client->asynchronousMessages) { - if (msg.detailType == QQmlProfilerDefinitions::SceneGraphRendererFrame) { - QVERIFY(msg.time >= contextFrameTime); - renderFrameTime = msg.time; + foreach (const QQmlProfilerEvent &msg, m_client->asynchronousMessages) { + const QQmlProfilerEventType &type = m_client->types.at(msg.typeIndex()); + if (type.detailType() == SceneGraphRendererFrame) { + QVERIFY(msg.timestamp() >= contextFrameTime); + renderFrameTime = msg.timestamp(); break; } } QVERIFY(renderFrameTime != -1); - foreach (const QQmlProfilerData &msg, m_client->asynchronousMessages) { - if (msg.detailType == QQmlProfilerDefinitions::SceneGraphRenderLoopFrame) { - if (msg.time >= contextFrameTime) { + foreach (const QQmlProfilerEvent &msg, m_client->asynchronousMessages) { + const QQmlProfilerEventType &type = m_client->types.at(msg.typeIndex()); + if (type.detailType() == SceneGraphRenderLoopFrame) { + if (msg.timestamp() >= contextFrameTime) { // Make sure SceneGraphRenderLoopFrame is not between SceneGraphContextFrame and // SceneGraphRendererFrame. A SceneGraphRenderLoopFrame before everything else is // OK as the scene graph might decide to do an initial rendering. - QVERIFY(msg.time >= renderFrameTime); + QVERIFY(msg.timestamp() >= renderFrameTime); break; } } @@ -655,9 +654,8 @@ void tst_QQmlProfilerService::scenegraphData() void tst_QQmlProfilerService::profileOnExit() { - connect(true, "exit.qml"); - - m_client->sendRecordingStatus(true); + QCOMPARE(connect(true, "exit.qml"), ConnectSuccess); + checkProcessTerminated(); checkTraceReceived(); checkJsHeap(); @@ -665,78 +663,182 @@ void tst_QQmlProfilerService::profileOnExit() void tst_QQmlProfilerService::controlFromJS() { - connect(true, "controlFromJS.qml"); + QCOMPARE(connect(true, "controlFromJS.qml", false), ConnectSuccess); - m_client->sendRecordingStatus(false); + QTRY_VERIFY(m_client->numLoadedEventTypes() > 0); + m_client->client->setRecording(false); checkTraceReceived(); checkJsHeap(); } void tst_QQmlProfilerService::signalSourceLocation() { - connect(true, "signalSourceLocation.qml"); + QCOMPARE(connect(true, "signalSourceLocation.qml"), ConnectSuccess); - m_client->sendRecordingStatus(true); while (!(m_process->output().contains(QLatin1String("500")))) QVERIFY(QQmlDebugTest::waitForSignal(m_process, SIGNAL(readyReadStandardOutput()))); - m_client->sendRecordingStatus(false); + m_client->client->setRecording(false); checkTraceReceived(); checkJsHeap(); - QQmlProfilerData expected(0, QQmlProfilerDefinitions::RangeLocation, - QQmlProfilerDefinitions::HandlingSignal, - QLatin1String("signalSourceLocation.qml")); - expected.line = 8; - expected.column = 28; - VERIFY(MessageListQML, 9, expected, CheckAll); + auto createType = [](int line, int column) { + return QQmlProfilerEventType( + MaximumMessage, HandlingSignal, -1, + QQmlProfilerEventLocation(QLatin1String("signalSourceLocation.qml"), line, + column)); + }; - expected.line = 7; - expected.column = 21; - VERIFY(MessageListQML, 11, expected, CheckAll); + VERIFY(MessageListQML, 4, createType(8, 28), CheckType | CheckNumbers, m_rangeStart); + VERIFY(MessageListQML, 6, createType(7, 21), CheckType | CheckNumbers, m_rangeEnd); } void tst_QQmlProfilerService::javascript() { - connect(true, "javascript.qml"); + QCOMPARE(connect(true, "javascript.qml"), ConnectSuccess); - m_client->sendRecordingStatus(true); while (!(m_process->output().contains(QLatin1String("done")))) QVERIFY(QQmlDebugTest::waitForSignal(m_process, SIGNAL(readyReadStandardOutput()))); - m_client->sendRecordingStatus(false); + m_client->client->setRecording(false); checkTraceReceived(); checkJsHeap(); - QQmlProfilerData expected(0, QQmlProfilerDefinitions::RangeStart, - QQmlProfilerDefinitions::Javascript); - VERIFY(MessageListJavaScript, 6, expected, CheckMessageType | CheckDetailType); + VERIFY(MessageListJavaScript, 2, QQmlProfilerEventType(MaximumMessage, Javascript), + CheckMessageType | CheckDetailType | CheckNumbers, m_rangeStart); - expected.messageType = QQmlProfilerDefinitions::RangeLocation; - expected.detailData = QLatin1String("javascript.qml"); - expected.line = 4; - expected.column = 5; - VERIFY(MessageListJavaScript, 7, expected, CheckAll); + VERIFY(MessageListJavaScript, 3, + QQmlProfilerEventType( + MaximumMessage, Javascript, -1, + QQmlProfilerEventLocation(QLatin1String("javascript.qml"), 4, 5)), + CheckType | CheckNumbers, m_rangeStart); - expected.messageType = QQmlProfilerDefinitions::RangeData; - expected.detailData = QLatin1String("something"); - VERIFY(MessageListJavaScript, 8, expected, - CheckMessageType | CheckDetailType | CheckDataEndsWith); + VERIFY(MessageListJavaScript, 4, QQmlProfilerEventType( + MaximumMessage, Javascript, -1, + QQmlProfilerEventLocation(), QLatin1String("something")), + CheckMessageType | CheckDetailType | CheckDataEndsWith | CheckNumbers, m_rangeStart); - expected.messageType = QQmlProfilerDefinitions::RangeEnd; - VERIFY(MessageListJavaScript, 21, expected, CheckMessageType | CheckDetailType); + VERIFY(MessageListJavaScript, 10, QQmlProfilerEventType(MaximumMessage, Javascript), + CheckMessageType | CheckDetailType | CheckNumbers, m_rangeEnd); } void tst_QQmlProfilerService::flushInterval() { - connect(true, "timer.qml"); - - m_client->sendRecordingStatus(true, -1, 1); + QCOMPARE(connect(true, "timer.qml", true, 1), ConnectSuccess); // Make sure we get multiple messages QTRY_VERIFY(m_client->qmlMessages.length() > 0); QVERIFY(m_client->qmlMessages.length() < 100); QTRY_VERIFY(m_client->qmlMessages.length() > 100); - m_client->sendRecordingStatus(false); + m_client->client->setRecording(false); + checkTraceReceived(); + checkJsHeap(); +} + +void tst_QQmlProfilerService::translationBinding() +{ + QCOMPARE(connect(true, "qstr.qml"), ConnectSuccess); + checkProcessTerminated(); + + checkTraceReceived(); + checkJsHeap(); + + const QQmlProfilerEventType type(MaximumMessage, Binding); + + VERIFY(MessageListQML, 4, type, CheckDetailType | CheckMessageType | CheckNumbers, + m_rangeStart); + VERIFY(MessageListQML, 5, type, CheckDetailType | CheckMessageType | CheckNumbers, + m_rangeEnd); +} + +void tst_QQmlProfilerService::memory() +{ + QCOMPARE(connect(true, "memory.qml"), ConnectSuccess); + checkProcessTerminated(); + + checkTraceReceived(); + checkJsHeap(); + + QVERIFY(m_client); + int smallItems = 0; + for (const auto& message : m_client->jsHeapMessages) { + const QQmlProfilerEventType &type = m_client->types[message.typeIndex()]; + if (type.detailType() == SmallItem) + ++smallItems; + } + + QVERIFY(smallItems > 5); +} + +static bool hasCompileEvents(const QVector<QQmlProfilerEventType> &types) +{ + for (const QQmlProfilerEventType &type : types) { + if (type.message() == MaximumMessage && type.rangeType() == Compiling) + return true; + } + return false; +} + +void tst_QQmlProfilerService::compile() +{ + // Flush interval so that we actually get the events before we stop recording. + connect(true, "test.qml", true, 100); + + QVERIFY(m_client); + + // We need to check specifically for compile events as we can otherwise stop recording after the + // StartTrace has arrived, but before it compiles anything. + QTRY_VERIFY(hasCompileEvents(m_client->types)); + m_client->client->setRecording(false); + + checkTraceReceived(); + checkJsHeap(); + + Message rangeStage = MaximumMessage; + for (const auto& message : m_client->qmlMessages) { + const QQmlProfilerEventType &type = m_client->types[message.typeIndex()]; + if (type.rangeType() == Compiling) { + switch (rangeStage) { + case MaximumMessage: + QCOMPARE(message.rangeStage(), RangeStart); + break; + case RangeStart: + QCOMPARE(message.rangeStage(), RangeEnd); + break; + default: + QFAIL("Wrong range stage"); + } + rangeStage = message.rangeStage(); + QCOMPARE(type.message(), MaximumMessage); + QCOMPARE(type.location().filename(), testFileUrl("test.qml").toString()); + QCOMPARE(type.location().line(), 0); + QCOMPARE(type.location().column(), 0); + } + } + + QCOMPARE(rangeStage, RangeEnd); +} + +void tst_QQmlProfilerService::multiEngine() +{ + QCOMPARE(connect(true, "quit.qml", true, 0, false, debugJsServerPath("qqmlprofilerservice")), + ConnectSuccess); + + QSignalSpy spy(m_client->client, SIGNAL(complete(qint64))); + + checkTraceReceived(); + checkJsHeap(); + + QTRY_COMPARE(m_process->state(), QProcess::NotRunning); + QCOMPARE(m_process->exitStatus(), QProcess::NormalExit); + + QCOMPARE(spy.count(), 1); +} + +void tst_QQmlProfilerService::batchOverflow() +{ + // The trace client checks that the events are received in order. + QCOMPARE(connect(true, "batchOverflow.qml"), ConnectSuccess); + checkProcessTerminated(); checkTraceReceived(); checkJsHeap(); } diff --git a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp index 441f8c113f..b75fb6b895 100644 --- a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp +++ b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp @@ -38,7 +38,6 @@ #include <private/qv4debugging_p.h> #include <private/qv8engine_p.h> #include <private/qv4objectiterator_p.h> -#include <private/qv4isel_moth_p.h> #include <private/qv4string_p.h> #include <private/qqmlbuiltinfunctions_p.h> #include <private/qqmldebugservice_p.h> @@ -46,7 +45,7 @@ using namespace QV4; using namespace QV4::Debugging; -typedef void (*InjectedFunction)(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); +typedef QV4::ReturnedValue (*InjectedFunction)(const FunctionObject *b, const QV4::Value *, const QV4::Value *, int); Q_DECLARE_METATYPE(InjectedFunction) static bool waitForSignal(QObject* obj, const char* signal, int timeout = 10000) @@ -79,7 +78,7 @@ public: emit evaluateFinished(); } - QV4::ExecutionEngine *v4Engine() { return QV8Engine::getV4(this); } + QV4::ExecutionEngine *v4Engine() { return handle(); } Q_INVOKABLE void injectFunction(const QString &functionName, InjectedFunction injectedFunction) { @@ -87,8 +86,7 @@ public: QV4::Scope scope(v4); QV4::ScopedString name(scope, v4->newString(functionName)); - QV4::ScopedContext ctx(scope, v4->rootContext()); - QV4::ScopedValue function(scope, BuiltinFunction::create(ctx, name, injectedFunction)); + QV4::ScopedValue function(scope, FunctionObject::createBuiltinFunction(v4, name, injectedFunction, 0)); v4->globalObject->put(name, function); } @@ -107,7 +105,7 @@ public: void run() { QV4::Scope scope(collector->engine()); QV4::ScopedValue v(scope, *collector->engine()->exceptionValue); - exception = collector->collect(v); + exception = collector->addValueRef(v); } QV4DataCollector::Ref exceptionValue() const { return exception; } @@ -167,7 +165,7 @@ public: , m_thrownValue(-1) , collector(engine) , m_resumeSpeed(QV4Debugger::FullThrottle) - , m_debugger(0) + , m_debugger(nullptr) { } @@ -221,16 +219,26 @@ public: { for (int i = 0, ei = m_stackTrace.size(); i != ei; ++i) { m_capturedScope.append(NamedRefs()); - ScopeJob job(&collector, i, 0); + FrameJob frameJob(&collector, i); + debugger->runInEngine(&frameJob); + QJsonObject frameObj = frameJob.returnValue(); + QJsonArray scopes = frameObj.value(QLatin1String("scopes")).toArray(); + int nscopes = scopes.size(); + int s = 0; + for (s = 0; s < nscopes; ++s) { + QJsonObject o = scopes.at(s).toObject(); + if (o.value(QLatin1String("type")).toInt(-2) == 1) // CallContext + break; + } + if (s == nscopes) + return; + + ScopeJob job(&collector, i, s); debugger->runInEngine(&job); NamedRefs &refs = m_capturedScope.last(); QJsonObject object = job.returnValue(); object = object.value(QLatin1String("object")).toObject(); - if (object.contains("ref") && !object.contains("properties")) { - QVERIFY(collector.redundantRefs()); - object = collector.lookupRef(object.value("ref").toInt(), true); - QVERIFY(object.contains("properties")); - } + QVERIFY(!object.contains("ref") || object.contains("properties")); foreach (const QJsonValue &value, object.value(QLatin1String("properties")).toArray()) { QJsonObject property = value.toObject(); QString name = property.value(QLatin1String("name")).toString(); @@ -296,13 +304,10 @@ private slots: void conditionalBreakPointInQml(); // context access: - void readArguments_data() { redundancy_data(); } void readArguments(); - void readLocals_data() { redundancy_data(); } + void readComplicatedArguments(); void readLocals(); - void readObject_data() { redundancy_data(); } void readObject(); - void readContextInAllFrames_data() { redundancy_data(); } void readContextInAllFrames(); // exceptions: @@ -310,13 +315,13 @@ private slots: void breakInCatch(); void breakInWith(); - void evaluateExpression_data() { redundancy_data(); } void evaluateExpression(); - void stepToEndOfScript_data() { redundancy_data(); } void stepToEndOfScript(); void lastLineOfConditional_data(); void lastLineOfConditional(); + + void readThis(); private: QV4Debugger *debugger() const { @@ -330,8 +335,6 @@ private: waitForSignal(m_engine, SIGNAL(evaluateFinished()), /*timeout*/0); } - void redundancy_data(); - TestEngine *m_engine; QV4::ExecutionEngine *m_v4; TestAgent *m_debuggerAgent; @@ -343,7 +346,6 @@ void tst_qv4debugger::init() m_javaScriptThread = new QThread; m_engine = new TestEngine; m_v4 = m_engine->v4Engine(); - m_v4->iselFactory.reset(new QV4::Moth::ISelFactory); m_v4->setDebugger(new QV4Debugger(m_v4)); m_engine->moveToThread(m_javaScriptThread); m_javaScriptThread->start(); @@ -357,10 +359,10 @@ void tst_qv4debugger::cleanup() m_javaScriptThread->wait(); delete m_engine; delete m_javaScriptThread; - m_engine = 0; - m_v4 = 0; + m_engine = nullptr; + m_v4 = nullptr; delete m_debuggerAgent; - m_debuggerAgent = 0; + m_debuggerAgent = nullptr; } void tst_qv4debugger::breakAnywhere() @@ -438,9 +440,9 @@ void tst_qv4debugger::addBreakPointWhilePaused() QCOMPARE(state.lineNumber, 2); } -static void someCall(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *) +static QV4::ReturnedValue someCall(const FunctionObject *function, const QV4::Value *, const QV4::Value *, int) { - static_cast<QV4Debugger *>(scope.engine->debugger()) + static_cast<QV4Debugger *>(function->engine()->debugger()) ->removeBreakPoint("removeBreakPointForNextInstruction", 2); RETURN_UNDEFINED(); } @@ -464,7 +466,7 @@ void tst_qv4debugger::conditionalBreakPoint() { m_debuggerAgent->m_captureContextInfo = true; QString script = - "function test() {\n" + "var test = function() {\n" " for (var i = 0; i < 15; ++i) {\n" " var x = i;\n" " }\n" @@ -481,7 +483,7 @@ void tst_qv4debugger::conditionalBreakPoint() QVERIFY(m_debuggerAgent->m_capturedScope.size() > 1); const TestAgent::NamedRefs &frame0 = m_debuggerAgent->m_capturedScope.at(0); - QCOMPARE(frame0.size(), 2); + QCOMPARE(frame0.size(), 3); QVERIFY(frame0.contains("i")); QCOMPARE(frame0.value("i").toInt(), 11); } @@ -489,9 +491,8 @@ void tst_qv4debugger::conditionalBreakPoint() void tst_qv4debugger::conditionalBreakPointInQml() { QQmlEngine engine; - QV4::ExecutionEngine *v4 = QV8Engine::getV4(&engine); + QV4::ExecutionEngine *v4 = engine.handle(); QV4Debugger *v4Debugger = new QV4Debugger(v4); - v4->iselFactory.reset(new QV4::Moth::ISelFactory); v4->setDebugger(v4Debugger); QScopedPointer<QThread> debugThread(new QThread); @@ -526,12 +527,9 @@ void tst_qv4debugger::conditionalBreakPointInQml() void tst_qv4debugger::readArguments() { - QFETCH(bool, redundantRefs); - m_debuggerAgent->collector.setRedundantRefs(redundantRefs); - m_debuggerAgent->m_captureContextInfo = true; QString script = - "function f(a, b, c, d) {\n" + "var f = function(a, b, c, d) {\n" " return a === b\n" "}\n" "var four;\n" @@ -541,7 +539,7 @@ void tst_qv4debugger::readArguments() QVERIFY(m_debuggerAgent->m_wasPaused); QVERIFY(m_debuggerAgent->m_capturedScope.size() > 1); const TestAgent::NamedRefs &frame0 = m_debuggerAgent->m_capturedScope.at(0); - QCOMPARE(frame0.size(), 4); + QCOMPARE(frame0.size(), 5); QVERIFY(frame0.contains(QStringLiteral("a"))); QCOMPARE(frame0.type(QStringLiteral("a")), QStringLiteral("number")); QCOMPARE(frame0.value(QStringLiteral("a")).toDouble(), 1.0); @@ -550,14 +548,31 @@ void tst_qv4debugger::readArguments() QCOMPARE(frame0.value(QStringLiteral("b")).toString(), QStringLiteral("two")); } -void tst_qv4debugger::readLocals() +void tst_qv4debugger::readComplicatedArguments() { - QFETCH(bool, redundantRefs); - m_debuggerAgent->collector.setRedundantRefs(redundantRefs); + m_debuggerAgent->m_captureContextInfo = true; + QString script = + "var f = function(a) {\n" + " a = 12;\n" + " return a;\n" + "}\n" + "f(1, 2);\n"; + debugger()->addBreakPoint("readArguments", 3); + evaluateJavaScript(script, "readArguments"); + QVERIFY(m_debuggerAgent->m_wasPaused); + QVERIFY(m_debuggerAgent->m_capturedScope.size() > 1); + const TestAgent::NamedRefs &frame0 = m_debuggerAgent->m_capturedScope.at(0); + QCOMPARE(frame0.size(), 2); + QVERIFY(frame0.contains(QStringLiteral("a"))); + QCOMPARE(frame0.type(QStringLiteral("a")), QStringLiteral("number")); + QCOMPARE(frame0.value(QStringLiteral("a")).toInt(), 12); +} +void tst_qv4debugger::readLocals() +{ m_debuggerAgent->m_captureContextInfo = true; QString script = - "function f(a, b) {\n" + "var f = function(a, b) {\n" " var c = a + b\n" " var d = a - b\n" // breakpoint, c should be set, d should be undefined " return c === d\n" @@ -568,7 +583,7 @@ void tst_qv4debugger::readLocals() QVERIFY(m_debuggerAgent->m_wasPaused); QVERIFY(m_debuggerAgent->m_capturedScope.size() > 1); const TestAgent::NamedRefs &frame0 = m_debuggerAgent->m_capturedScope.at(0); - QCOMPARE(frame0.size(), 4); // locals and parameters + QCOMPARE(frame0.size(), 5); // locals and parameters QVERIFY(frame0.contains("c")); QCOMPARE(frame0.type("c"), QStringLiteral("number")); QCOMPARE(frame0.value("c").toDouble(), 3.0); @@ -578,12 +593,9 @@ void tst_qv4debugger::readLocals() void tst_qv4debugger::readObject() { - QFETCH(bool, redundantRefs); - m_debuggerAgent->collector.setRedundantRefs(redundantRefs); - m_debuggerAgent->m_captureContextInfo = true; QString script = - "function f(a) {\n" + "var f = function(a) {\n" " var b = a\n" " return b\n" "}\n" @@ -593,7 +605,7 @@ void tst_qv4debugger::readObject() QVERIFY(m_debuggerAgent->m_wasPaused); QVERIFY(m_debuggerAgent->m_capturedScope.size() > 1); const TestAgent::NamedRefs &frame0 = m_debuggerAgent->m_capturedScope.at(0); - QCOMPARE(frame0.size(), 2); + QCOMPARE(frame0.size(), 3); QVERIFY(frame0.contains("b")); QCOMPARE(frame0.type("b"), QStringLiteral("object")); QJsonObject b = frame0.rawValue("b"); @@ -602,7 +614,7 @@ void tst_qv4debugger::readObject() QVERIFY(!b.contains(QStringLiteral("properties"))); QVERIFY(b.value("value").isDouble()); QCOMPARE(b.value("value").toInt(), 2); - b = m_debuggerAgent->collector.lookupRef(b.value("ref").toInt(), true); + b = m_debuggerAgent->collector.lookupRef(b.value("ref").toInt()); QVERIFY(b.contains(QStringLiteral("properties"))); QVERIFY(b.value("properties").isArray()); QJsonArray b_props = b.value("properties").toArray(); @@ -618,8 +630,7 @@ void tst_qv4debugger::readObject() QCOMPARE(b_tail.value("name").toString(), QStringLiteral("tail")); QVERIFY(b_tail.contains("ref")); - QJsonObject b_tail_value = m_debuggerAgent->collector.lookupRef(b_tail.value("ref").toInt(), - true); + QJsonObject b_tail_value = m_debuggerAgent->collector.lookupRef(b_tail.value("ref").toInt()); QCOMPARE(b_tail_value.value("type").toString(), QStringLiteral("object")); QVERIFY(b_tail_value.contains("properties")); QJsonArray b_tail_props = b_tail_value.value("properties").toArray(); @@ -630,18 +641,15 @@ void tst_qv4debugger::readObject() QCOMPARE(b_tail_head.value("value").toString(), QStringLiteral("asdf")); QJsonObject b_tail_tail = b_tail_props.at(1).toObject(); QCOMPARE(b_tail_tail.value("name").toString(), QStringLiteral("tail")); - QCOMPARE(b_tail_tail.value("type").toString(), QStringLiteral("null")); + QCOMPARE(b_tail_tail.value("type").toString(), QStringLiteral("object")); QVERIFY(b_tail_tail.value("value").isNull()); } void tst_qv4debugger::readContextInAllFrames() { - QFETCH(bool, redundantRefs); - m_debuggerAgent->collector.setRedundantRefs(redundantRefs); - m_debuggerAgent->m_captureContextInfo = true; QString script = - "function fact(n) {\n" + "var fact = function(n) {\n" " if (n > 1) {\n" " var n_1 = n - 1;\n" " n_1 = fact(n_1);\n" @@ -658,7 +666,7 @@ void tst_qv4debugger::readContextInAllFrames() for (int i = 0; i < 12; ++i) { const TestAgent::NamedRefs &scope = m_debuggerAgent->m_capturedScope.at(i); - QCOMPARE(scope.size(), 2); + QCOMPARE(scope.size(), 3); QVERIFY(scope.contains("n")); QCOMPARE(scope.type("n"), QStringLiteral("number")); QCOMPARE(scope.value("n").toDouble(), i + 1.0); @@ -686,8 +694,7 @@ void tst_qv4debugger::pauseOnThrow() QCOMPARE(m_debuggerAgent->m_pauseReason, QV4Debugger::Throwing); QCOMPARE(m_debuggerAgent->m_stackTrace.size(), 2); QVERIFY(m_debuggerAgent->m_thrownValue >= qint64(0)); - QJsonObject exception = m_debuggerAgent->collector.lookupRef(m_debuggerAgent->m_thrownValue, - true); + QJsonObject exception = m_debuggerAgent->collector.lookupRef(m_debuggerAgent->m_thrownValue); // DUMP_JSON(exception); QCOMPARE(exception.value("type").toString(), QStringLiteral("string")); QCOMPARE(exception.value("value").toString(), QStringLiteral("hard")); @@ -731,9 +738,6 @@ void tst_qv4debugger::breakInWith() void tst_qv4debugger::evaluateExpression() { - QFETCH(bool, redundantRefs); - m_debuggerAgent->collector.setRedundantRefs(redundantRefs); - QString script = "function testFunction() {\n" " var x = 10\n" @@ -775,9 +779,6 @@ void tst_qv4debugger::evaluateExpression() void tst_qv4debugger::stepToEndOfScript() { - QFETCH(bool, redundantRefs); - m_debuggerAgent->collector.setRedundantRefs(redundantRefs); - QString script = "var ret = 0;\n" "ret += 4;\n" @@ -814,13 +815,13 @@ void tst_qv4debugger::lastLineOfConditional_data() QTest::newRow("do..while {block}") << "do {\n" << "} while (ret < 10);" << 4 << 7; QTest::newRow("if true {block}") << "if (true) {\n" << "}" - << 4 << 7; + << 4 << 8; QTest::newRow("if false {block}") << "if (false) {\n" << "}" << 2 << 8; QTest::newRow("if true else {block}") << "if (true) {\n" << "} else {\n ret += 8;\n}" - << 4 << 7; + << 4 << 10; QTest::newRow("if false else {block}") << "if (false) {\n" << "} else {\n ret += 8;\n}" - << 8 << 9; + << 8 << 10; QTest::newRow("for statement") << "for (var i = 0; i < 10; ++i)\n" << "" << 4 << 2; QTest::newRow("for..in statement") << "for (var i in [0, 1, 2, 3, 4])\n" << "" << 4 << 2; @@ -829,11 +830,11 @@ void tst_qv4debugger::lastLineOfConditional_data() // For two nested if statements without blocks, we need to map the jump from the inner to the // outer one on the outer "if". There is just no better place. - QTest::newRow("if true statement") << "if (true)\n" << "" << 4 << 2; + QTest::newRow("if true statement") << "if (true)\n" << "" << 4 << 8; QTest::newRow("if false statement") << "if (false)\n" << "" << 2 << 8; // Also two nested ifs without blocks. - QTest::newRow("if true else statement") << "if (true)\n" << "else\n ret += 8;" << 4 << 2; + QTest::newRow("if true else statement") << "if (true)\n" << "else\n ret += 8;" << 4 << 9; QTest::newRow("if false else statement") << "if (false)\n" << "else\n ret += 8;" << 8 << 9; } @@ -868,11 +869,34 @@ void tst_qv4debugger::lastLineOfConditional() QCOMPARE(secondState.lineNumber, lastLine); } -void tst_qv4debugger::redundancy_data() +void tst_qv4debugger::readThis() { - QTest::addColumn<bool>("redundantRefs"); - QTest::addRow("redundant") << true; - QTest::addRow("sparse") << false; + m_debuggerAgent->m_captureContextInfo = true; + QString script = + "var x = function() {\n" + " return this.a;\n" + "}.apply({a : 5}, []);\n"; + + TestAgent::ExpressionRequest request; + request.expression = "this"; + request.frameNr = 0; + request.context = -1; // no extra context + m_debuggerAgent->m_expressionRequests << request; + + debugger()->addBreakPoint("applyThis", 2); + evaluateJavaScript(script, "applyThis"); + QVERIFY(m_debuggerAgent->m_wasPaused); + + QCOMPARE(m_debuggerAgent->m_expressionResults.count(), 1); + QJsonObject result0 = m_debuggerAgent->m_expressionResults[0]; + QCOMPARE(result0.value("type").toString(), QStringLiteral("object")); + QCOMPARE(result0.value("value").toInt(), 1); + QJsonArray properties = result0.value("properties").toArray(); + QCOMPARE(properties.size(), 1); + QJsonObject a = properties.first().toObject(); + QCOMPARE(a.value("name").toString(), QStringLiteral("a")); + QCOMPARE(a.value("type").toString(), QStringLiteral("number")); + QCOMPARE(a.value("value").toInt(), 5); } QTEST_MAIN(tst_qv4debugger) diff --git a/tests/auto/qml/debugger/shared/debugutil.cpp b/tests/auto/qml/debugger/shared/debugutil.cpp index 7b9e935678..68446b53a4 100644 --- a/tests/auto/qml/debugger/shared/debugutil.cpp +++ b/tests/auto/qml/debugger/shared/debugutil.cpp @@ -27,13 +27,12 @@ ****************************************************************************/ #include "debugutil_p.h" +#include "qqmldebugprocess_p.h" #include <private/qqmldebugconnection_p.h> #include <QtCore/qeventloop.h> #include <QtCore/qtimer.h> -#include <QtCore/qfileinfo.h> -#include <QtCore/qdir.h> bool QQmlDebugTest::waitForSignal(QObject *receiver, const char *member, int timeout) { QEventLoop loop; @@ -51,15 +50,23 @@ bool QQmlDebugTest::waitForSignal(QObject *receiver, const char *member, int tim QList<QQmlDebugClient *> QQmlDebugTest::createOtherClients(QQmlDebugConnection *connection) { QList<QQmlDebugClient *> ret; - foreach (const QString &service, QQmlDebuggingEnabler::debuggerServices()) { + + static const auto debuggerServices + = QStringList({"V8Debugger", "QmlDebugger", "DebugMessages"}); + static const auto inspectorServices + = QStringList({"QmlInspector"}); + static const auto profilerServices + = QStringList({"CanvasFrameRate", "EngineControl", "DebugMessages"}); + + for (const QString &service : debuggerServices) { if (!connection->client(service)) ret << new QQmlDebugClient(service, connection); } - foreach (const QString &service, QQmlDebuggingEnabler::inspectorServices()) { + for (const QString &service : inspectorServices) { if (!connection->client(service)) ret << new QQmlDebugClient(service, connection); } - foreach (const QString &service, QQmlDebuggingEnabler::profilerServices()) { + for (const QString &service : profilerServices) { if (!connection->client(service)) ret << new QQmlDebugClient(service, connection); } @@ -91,6 +98,9 @@ QString QQmlDebugTest::connectionStateString(const QQmlDebugConnection *connecti QQmlDebugTestClient::QQmlDebugTestClient(const QString &s, QQmlDebugConnection *c) : QQmlDebugClient(s, c) { + connect(this, &QQmlDebugClient::stateChanged, this, [this](QQmlDebugClient::State newState) { + QCOMPARE(newState, state()); + }); } QByteArray QQmlDebugTestClient::waitForResponse() @@ -104,201 +114,150 @@ QByteArray QQmlDebugTestClient::waitForResponse() 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, QObject *parent) - : QObject(parent) - , m_executable(executable) - , m_started(false) - , m_port(0) - , m_maximumBindErrors(0) - , m_receivedBindErrors(0) +QQmlDebugTest::ConnectResult QQmlDebugTest::connect( + const QString &executable, const QString &services, const QString &extraArgs, + bool block) { - m_process.setProcessChannelMode(QProcess::MergedChannels); - m_timer.setSingleShot(true); - m_timer.setInterval(5000); - connect(&m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(processAppOutput())); - connect(&m_process, SIGNAL(errorOccurred(QProcess::ProcessError)), - this, SLOT(processError(QProcess::ProcessError))); - connect(&m_timer, SIGNAL(timeout()), SLOT(timeout())); -} + QStringList arguments; + arguments << QString::fromLatin1("-qmljsdebugger=port:13773,13783%3%4") + .arg(block ? QStringLiteral(",block") : QString()) + .arg(services.isEmpty() ? services : (QStringLiteral(",services:") + services)) + << extraArgs; -QQmlDebugProcess::~QQmlDebugProcess() -{ - stop(); -} + m_process = createProcess(executable); + if (!m_process) + return ProcessFailed; -QString QQmlDebugProcess::state() -{ - QString stateStr; - switch (m_process.state()) { - case QProcess::NotRunning: { - stateStr = "not running"; - if (m_process.exitStatus() == QProcess::CrashExit) - stateStr += " (crashed!)"; - else - stateStr += ", return value " + QString::number(m_process.exitCode()); - break; - } - case QProcess::Starting: stateStr = "starting"; break; - case QProcess::Running: stateStr = "running"; break; - } - return stateStr; -} + m_process->start(QStringList() << arguments); + if (!m_process->waitForSessionStart()) + return SessionFailed; -void QQmlDebugProcess::start(const QStringList &arguments) -{ -#ifdef Q_OS_MAC - // make sure m_executable points to the actual binary even if it's inside an app bundle - QFileInfo binFile(m_executable); - if (!binFile.isExecutable()) { - QDir bundleDir(m_executable + ".app"); - if (bundleDir.exists()) { - m_executable = bundleDir.absoluteFilePath("Contents/MacOS/" + binFile.baseName()); - //qDebug() << Q_FUNC_INFO << "found bundled binary" << m_executable; - } - } -#endif - m_mutex.lock(); - m_port = 0; - m_process.setEnvironment(QProcess::systemEnvironment() + m_environment); - m_process.start(m_executable, arguments); - if (!m_process.waitForStarted()) { - qWarning() << "QML Debug Client: Could not launch app " << m_executable - << ": " << m_process.errorString(); - m_eventLoop.quit(); - } else { - m_timer.start(); - } - m_mutex.unlock(); + m_connection = createConnection(); + if (!m_connection) + return ConnectionFailed; + + m_clients = createClients(); + if (m_clients.contains(nullptr)) + return ClientsFailed; + + ClientStateHandler stateHandler(m_clients, createOtherClients(m_connection), services.isEmpty() + ? QQmlDebugClient::Enabled : QQmlDebugClient::Unavailable); + + + const int port = m_process->debugPort(); + m_connection->connectToHost(QLatin1String("127.0.0.1"), port); + + QEventLoop loop; + QTimer timer; + QObject::connect(&stateHandler, &ClientStateHandler::allOk, &loop, &QEventLoop::quit); + QObject::connect(m_connection, &QQmlDebugConnection::disconnected, &loop, &QEventLoop::quit); + QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); + + timer.start(5000); + loop.exec(); + + if (!stateHandler.allEnabled()) + return EnableFailed; + + if (!stateHandler.othersAsExpected()) + return RestrictFailed; + + return ConnectSuccess; } -void QQmlDebugProcess::stop() +QList<QQmlDebugClient *> QQmlDebugTest::createClients() { - if (m_process.state() != QProcess::NotRunning) { - m_process.kill(); - m_process.waitForFinished(5000); - } + return QList<QQmlDebugClient *>(); } -void QQmlDebugProcess::setMaximumBindErrors(int ignore) +QQmlDebugProcess *QQmlDebugTest::createProcess(const QString &executable) { - m_maximumBindErrors = ignore; + return new QQmlDebugProcess(executable, this); } -void QQmlDebugProcess::timeout() +QQmlDebugConnection *QQmlDebugTest::createConnection() { - qWarning() << "Timeout while waiting for QML debugging messages " - "in application output. Process is in state" << m_process.state() << ", Output:" << m_output << "."; - m_eventLoop.quit(); + return new QQmlDebugConnection(this); } -bool QQmlDebugProcess::waitForSessionStart() +void QQmlDebugTest::cleanup() { - if (m_process.state() != QProcess::Running) { - qWarning() << "Could not start up " << m_executable; - return false; + if (QTest::currentTestFailed()) { + const QString null = QStringLiteral("null"); + + qDebug() << "Process State:" << (m_process ? m_process->stateString() : null); + qDebug() << "Application Output:" << (m_process ? m_process->output() : null); + qDebug() << "Connection State:" << QQmlDebugTest::connectionStateString(m_connection); + for (QQmlDebugClient *client : m_clients) { + if (client) + qDebug() << client->name() << "State:" << QQmlDebugTest::clientStateString(client); + else + qDebug() << "Failed Client:" << null; + } } - m_eventLoop.exec(); - return m_started; -} + qDeleteAll(m_clients); + m_clients.clear(); -int QQmlDebugProcess::debugPort() const -{ - return m_port; -} + delete m_connection; + m_connection = nullptr; -bool QQmlDebugProcess::waitForFinished() -{ - return m_process.waitForFinished(); + if (m_process) { + m_process->stop(); + delete m_process; + m_process = nullptr; + } } -QProcess::ExitStatus QQmlDebugProcess::exitStatus() const +ClientStateHandler::ClientStateHandler(const QList<QQmlDebugClient *> &clients, + const QList<QQmlDebugClient *> &others, + QQmlDebugClient::State expectedOthers) : + m_clients(clients), m_others(others), m_expectedOthers(expectedOthers) { - return m_process.exitStatus(); + for (QQmlDebugClient *client : m_clients) { + QObject::connect(client, &QQmlDebugClient::stateChanged, + this, &ClientStateHandler::checkStates); + } + for (QQmlDebugClient *client : m_others) { + QObject::connect(client, &QQmlDebugClient::stateChanged, + this, &ClientStateHandler::checkStates); + } } -void QQmlDebugProcess::addEnvironment(const QString &environment) +ClientStateHandler::~ClientStateHandler() { - m_environment.append(environment); + qDeleteAll(m_others); } -QString QQmlDebugProcess::output() const +void ClientStateHandler::checkStates() { - return m_output; -} + for (QQmlDebugClient *client : m_clients) { + if (client->state() != QQmlDebugClient::Enabled) + return; + } -void QQmlDebugProcess::processAppOutput() -{ - m_mutex.lock(); - - bool outputFromAppItself = false; - - 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.contains("QML Debugger:")) { - const QRegExp portRx("Waiting for connection on port (\\d+)"); - if (portRx.indexIn(line) != -1) { - m_port = portRx.cap(1).toInt(); - m_timer.stop(); - m_started = true; - m_eventLoop.quit(); - continue; - } - if (line.contains("Unable to listen")) { - if (++m_receivedBindErrors >= m_maximumBindErrors) { - if (m_maximumBindErrors == 0) - qWarning() << "App was unable to bind to port!"; - m_timer.stop(); - m_eventLoop.quit(); - } - continue; - } - } else { - // set to true if there is output not coming from the debugger - outputFromAppItself = true; - } + m_allEnabled = true; + + for (QQmlDebugClient *other : m_others) { + if (other->state() != m_expectedOthers) + return; } - m_mutex.unlock(); - if (outputFromAppItself) - emit readyReadStandardOutput(); + m_othersAsExpected = true; + emit allOk(); } -void QQmlDebugProcess::processError(QProcess::ProcessError error) +QString debugJsServerPath(const QString &selfPath) { - if (!m_eventLoop.isRunning()) - return; - - qDebug() << "An error occurred while waiting for debug process to become available:"; - switch (error) { - case QProcess::FailedToStart: qDebug() << "Process failed to start."; break; - case QProcess::Crashed: qDebug() << "Process crashed."; break; - case QProcess::Timedout: qDebug() << "Process timed out."; break; - case QProcess::WriteError: qDebug() << "Error while writing to process."; break; - case QProcess::ReadError: qDebug() << "Error while reading from process."; break; - case QProcess::UnknownError: qDebug() << "Unknown process error."; break; - } - - m_eventLoop.exit(); + static const char *debugserver = "qqmldebugjsserver"; + QString appPath = QCoreApplication::applicationDirPath(); + const int position = appPath.lastIndexOf(selfPath); + return (position == -1 ? appPath : appPath.replace(position, selfPath.length(), debugserver)) + + "/" + debugserver; } diff --git a/tests/auto/qml/debugger/shared/debugutil.pri b/tests/auto/qml/debugger/shared/debugutil.pri index 1983f3583e..13dcdb91d8 100644 --- a/tests/auto/qml/debugger/shared/debugutil.pri +++ b/tests/auto/qml/debugger/shared/debugutil.pri @@ -1,4 +1,11 @@ QT += qmldebug-private -HEADERS += $$PWD/debugutil_p.h -SOURCES += $$PWD/debugutil.cpp +INCLUDEPATH += $$PWD +include($$PWD/../../../shared/util.pri) + +HEADERS += \ + $$PWD/debugutil_p.h \ + $$PWD/qqmldebugprocess_p.h +SOURCES += \ + $$PWD/debugutil.cpp \ + $$PWD/qqmldebugprocess.cpp diff --git a/tests/auto/qml/debugger/shared/debugutil_p.h b/tests/auto/qml/debugger/shared/debugutil_p.h index 1ec0a6513d..1c32590305 100644 --- a/tests/auto/qml/debugger/shared/debugutil_p.h +++ b/tests/auto/qml/debugger/shared/debugutil_p.h @@ -1,4 +1,3 @@ - /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. @@ -41,23 +40,43 @@ // We mean it. // +#include <../../../shared/util.h> #include <private/qqmldebugclient_p.h> -#include <QtCore/qeventloop.h> -#include <QtCore/qtimer.h> -#include <QtCore/qthread.h> -#include <QtCore/qprocess.h> -#include <QtCore/qmutex.h> -#include <QtTest/qtest.h> -#include <QtQml/qqmlengine.h> - -class QQmlDebugTest +class QQmlDebugProcess; +class QQmlDebugTest : public QQmlDataTest { + Q_OBJECT public: static bool waitForSignal(QObject *receiver, const char *member, int timeout = 5000); static QList<QQmlDebugClient *> createOtherClients(QQmlDebugConnection *connection); static QString clientStateString(const QQmlDebugClient *client); static QString connectionStateString(const QQmlDebugConnection *connection); + +protected: + enum ConnectResult { + ConnectSuccess, + ProcessFailed, + SessionFailed, + ConnectionFailed, + ClientsFailed, + EnableFailed, + RestrictFailed + }; + + ConnectResult connect(const QString &executable, const QString &services, + const QString &extraArgs, bool block); + + virtual QQmlDebugProcess *createProcess(const QString &executable); + virtual QQmlDebugConnection *createConnection(); + virtual QList<QQmlDebugClient *> createClients(); + + QQmlDebugProcess *m_process = nullptr; + QQmlDebugConnection *m_connection = nullptr; + QList<QQmlDebugClient *> m_clients; + +protected slots: + virtual void cleanup(); }; class QQmlDebugTestClient : public QQmlDebugClient @@ -69,62 +88,15 @@ public: 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, QObject *parent = 0); - ~QQmlDebugProcess(); - - QString state(); - - void addEnvironment(const QString &environment); - - void start(const QStringList &arguments); - bool waitForSessionStart(); - int debugPort() const; - - bool waitForFinished(); - QProcess::ExitStatus exitStatus() const; - - QString output() const; - void stop(); - void setMaximumBindErrors(int numErrors); - -signals: - void readyReadStandardOutput(); - -private slots: - void timeout(); - void processAppOutput(); - void processError(QProcess::ProcessError error); - -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; - int m_port; - int m_maximumBindErrors; - int m_receivedBindErrors; -}; - class QQmlInspectorResultRecipient : public QObject { Q_OBJECT @@ -142,4 +114,33 @@ public: } }; +class ClientStateHandler : public QObject +{ + Q_OBJECT +public: + ClientStateHandler(const QList<QQmlDebugClient *> &clients, + const QList<QQmlDebugClient *> &others, + QQmlDebugClient::State expectedOthers); + + ~ClientStateHandler(); + + bool allEnabled() const { return m_allEnabled; } + bool othersAsExpected() const { return m_othersAsExpected; } + +signals: + void allOk(); + +private: + void checkStates(); + + const QList<QQmlDebugClient *> m_clients; + const QList<QQmlDebugClient *> m_others; + const QQmlDebugClient::State m_expectedOthers; + + bool m_allEnabled = false; + bool m_othersAsExpected = false; +}; + +QString debugJsServerPath(const QString &selfPath); + #endif // DEBUGUTIL_P_H diff --git a/tests/auto/qml/debugger/shared/qqmldebugprocess.cpp b/tests/auto/qml/debugger/shared/qqmldebugprocess.cpp new file mode 100644 index 0000000000..956e97e7ba --- /dev/null +++ b/tests/auto/qml/debugger/shared/qqmldebugprocess.cpp @@ -0,0 +1,249 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmldebugprocess_p.h" + +#include <QtCore/qdebug.h> +#include <QtCore/qfileinfo.h> +#include <QtCore/qdir.h> + +QQmlDebugProcess::QQmlDebugProcess(const QString &executable, QObject *parent) + : QObject(parent) + , m_executable(executable) + , m_state(SessionUnknown) + , m_port(0) + , m_maximumBindErrors(0) + , m_receivedBindErrors(0) +{ + m_process.setProcessChannelMode(QProcess::MergedChannels); + m_timer.setInterval(15000); + connect(&m_process, &QProcess::readyReadStandardOutput, + this, &QQmlDebugProcess::processAppOutput); + connect(&m_process, &QProcess::errorOccurred, + this, &QQmlDebugProcess::processError); + connect(&m_process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), + this, [this]() { + m_timer.stop(); + m_eventLoop.quit(); + emit finished(); + }); + connect(&m_timer, &QTimer::timeout, + this, &QQmlDebugProcess::timeout); +} + +QQmlDebugProcess::~QQmlDebugProcess() +{ + stop(); +} + +QString QQmlDebugProcess::stateString() const +{ + QString stateStr; + switch (m_process.state()) { + case QProcess::NotRunning: { + stateStr = "not running"; + if (m_process.exitStatus() == QProcess::CrashExit) + stateStr += " (crashed!)"; + else + stateStr += ", return value " + QString::number(m_process.exitCode()); + break; + } + case QProcess::Starting: + stateStr = "starting"; + break; + case QProcess::Running: + stateStr = "running"; + break; + } + return stateStr; +} + +void QQmlDebugProcess::start(const QStringList &arguments) +{ +#ifdef Q_OS_MAC + // make sure m_executable points to the actual binary even if it's inside an app bundle + QFileInfo binFile(m_executable); + if (!binFile.isExecutable()) { + QDir bundleDir(m_executable + ".app"); + if (bundleDir.exists()) { + m_executable = bundleDir.absoluteFilePath("Contents/MacOS/" + binFile.baseName()); + //qDebug() << Q_FUNC_INFO << "found bundled binary" << m_executable; + } + } +#endif + m_mutex.lock(); + m_port = 0; + m_process.setEnvironment(QProcess::systemEnvironment() + m_environment); + m_process.start(m_executable, arguments); + if (!m_process.waitForStarted()) { + qWarning() << "QML Debug Client: Could not launch app " << m_executable + << ": " << m_process.errorString(); + m_eventLoop.quit(); + } + m_mutex.unlock(); +} + +void QQmlDebugProcess::stop() +{ + if (m_process.state() != QProcess::NotRunning) { + disconnect(&m_process, &QProcess::errorOccurred, this, &QQmlDebugProcess::processError); + m_process.kill(); + m_process.waitForFinished(5000); + } +} + +void QQmlDebugProcess::setMaximumBindErrors(int ignore) +{ + m_maximumBindErrors = ignore; +} + +void QQmlDebugProcess::timeout() +{ + qWarning() << "Timeout while waiting for QML debugging messages " + "in application output. Process is in state" << m_process.state() + << ", Output:" << m_output << "."; +} + +bool QQmlDebugProcess::waitForSessionStart() +{ + if (m_process.state() != QProcess::Running) { + qWarning() << "Could not start up " << m_executable; + return false; + } else if (m_state == SessionStarted) { + return true; + } else if (m_state == SessionFailed) { + return false; + } + + m_timer.start(); + m_eventLoop.exec(); + + return m_state == SessionStarted; +} + +int QQmlDebugProcess::debugPort() const +{ + return m_port; +} + +bool QQmlDebugProcess::waitForFinished() +{ + return m_process.waitForFinished(); +} + +QProcess::ProcessState QQmlDebugProcess::state() const +{ + return m_process.state(); +} + +QProcess::ExitStatus QQmlDebugProcess::exitStatus() const +{ + return m_process.exitStatus(); +} + +void QQmlDebugProcess::addEnvironment(const QString &environment) +{ + m_environment.append(environment); +} + +QString QQmlDebugProcess::output() const +{ + return m_output; +} + +void QQmlDebugProcess::processAppOutput() +{ + m_mutex.lock(); + + bool outputFromAppItself = false; + + 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.contains("QML Debugger:")) { + const QRegExp portRx("Waiting for connection on port (\\d+)"); + if (portRx.indexIn(line) != -1) { + m_port = portRx.cap(1).toInt(); + m_timer.stop(); + m_state = SessionStarted; + m_eventLoop.quit(); + continue; + } + if (line.contains("Unable to listen")) { + if (++m_receivedBindErrors >= m_maximumBindErrors) { + if (m_maximumBindErrors == 0) + qWarning() << "App was unable to bind to port!"; + m_timer.stop(); + m_state = SessionFailed; + m_eventLoop.quit(); + } + continue; + } + } + + // set to true if there is output not coming from the debugger or we don't understand it + outputFromAppItself = true; + } + m_mutex.unlock(); + + if (outputFromAppItself) + emit readyReadStandardOutput(); +} + +void QQmlDebugProcess::processError(QProcess::ProcessError error) +{ + qDebug() << "An error occurred while waiting for debug process to become available:"; + switch (error) { + case QProcess::FailedToStart: + qDebug() << "Process failed to start."; + break; + case QProcess::Crashed: + qDebug() << "Process crashed."; + break; + case QProcess::Timedout: + qDebug() << "Process timed out."; + break; + case QProcess::WriteError: + qDebug() << "Error while writing to process."; + break; + case QProcess::ReadError: + qDebug() << "Error while reading from process."; + break; + case QProcess::UnknownError: + qDebug() << "Unknown process error."; + break; + } +} diff --git a/tests/auto/qml/debugger/shared/qqmldebugprocess_p.h b/tests/auto/qml/debugger/shared/qqmldebugprocess_p.h new file mode 100644 index 0000000000..945cc58c85 --- /dev/null +++ b/tests/auto/qml/debugger/shared/qqmldebugprocess_p.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLDEBUGPROCESS_P_H +#define QQMLDEBUGPROCESS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qprocess.h> +#include <QtCore/qtimer.h> +#include <QtCore/qeventloop.h> +#include <QtCore/qmutex.h> + +class QQmlDebugProcess : public QObject +{ + Q_OBJECT +public: + QQmlDebugProcess(const QString &executable, QObject *parent = 0); + ~QQmlDebugProcess(); + + QString stateString() const; + + void addEnvironment(const QString &environment); + + void start(const QStringList &arguments); + bool waitForSessionStart(); + int debugPort() const; + + bool waitForFinished(); + QProcess::ProcessState state() const; + QProcess::ExitStatus exitStatus() const; + + QString output() const; + void stop(); + void setMaximumBindErrors(int numErrors); + +signals: + void readyReadStandardOutput(); + void finished(); + +private slots: + void timeout(); + void processAppOutput(); + void processError(QProcess::ProcessError error); + +private: + enum SessionState { + SessionUnknown, + SessionStarted, + SessionFailed + }; + + QString m_executable; + QProcess m_process; + QString m_outputBuffer; + QString m_output; + QTimer m_timer; + QEventLoop m_eventLoop; + QMutex m_mutex; + SessionState m_state; + QStringList m_environment; + int m_port; + int m_maximumBindErrors; + int m_receivedBindErrors; +}; + +#endif // QQMLDEBUGPROCESS_P_H diff --git a/tests/auto/qml/debugger/shared/qqmldebugtestservice.cpp b/tests/auto/qml/debugger/shared/qqmldebugtestservice.cpp index 4dce07d824..896ed608fd 100644 --- a/tests/auto/qml/debugger/shared/qqmldebugtestservice.cpp +++ b/tests/auto/qml/debugger/shared/qqmldebugtestservice.cpp @@ -48,5 +48,4 @@ void QQmlDebugTestService::stateAboutToBeChanged(QQmlDebugService::State) void QQmlDebugTestService::stateChanged(State) { Q_ASSERT(QThread::currentThread() != thread()); - emit stateHasChanged(); } diff --git a/tests/auto/qml/debugger/shared/qqmldebugtestservice.h b/tests/auto/qml/debugger/shared/qqmldebugtestservice.h index 37b4a9f98c..9c39c0893d 100644 --- a/tests/auto/qml/debugger/shared/qqmldebugtestservice.h +++ b/tests/auto/qml/debugger/shared/qqmldebugtestservice.h @@ -38,9 +38,6 @@ class QQmlDebugTestService : public QQmlDebugService public: QQmlDebugTestService(const QString &s, float version = 1, QObject *parent = 0); -signals: - void stateHasChanged(); - protected: virtual void messageReceived(const QByteArray &ba); virtual void stateAboutToBeChanged(State state); diff --git a/tests/auto/qml/debugger/shared/qqmlenginedebugclient.cpp b/tests/auto/qml/debugger/shared/qqmlenginedebugclient.cpp deleted file mode 100644 index c0252a0290..0000000000 --- a/tests/auto/qml/debugger/shared/qqmlenginedebugclient.cpp +++ /dev/null @@ -1,508 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlenginedebugclient.h" -#include <private/qqmldebugconnection_p.h> - -struct QmlObjectData { - QUrl url; - int lineNumber; - int columnNumber; - QString idString; - QString objectName; - QString objectType; - int objectId; - int contextId; - int parentId; -}; - -QPacket &operator>>(QPacket &ds, QmlObjectData &data) -{ - ds >> data.url >> data.lineNumber >> data.columnNumber >> data.idString - >> data.objectName >> data.objectType >> data.objectId >> data.contextId - >> data.parentId; - return ds; -} - -struct QmlObjectProperty { - enum Type { Unknown, Basic, Object, List, SignalProperty }; - Type type; - QString name; - QVariant value; - QString valueTypeName; - QString binding; - bool hasNotifySignal; -}; - -QPacket &operator>>(QPacket &ds, QmlObjectProperty &data) -{ - int type; - ds >> type >> data.name >> data.value >> data.valueTypeName - >> data.binding >> data.hasNotifySignal; - data.type = (QmlObjectProperty::Type)type; - return ds; -} - -QQmlEngineDebugClient::QQmlEngineDebugClient( - QQmlDebugConnection *connection) - : QQmlDebugClient(QLatin1String("QmlDebugger"), connection), - m_nextId(0), - m_valid(false) -{ -} - -quint32 QQmlEngineDebugClient::addWatch( - const QmlDebugPropertyReference &property, bool *success) -{ - quint32 id = -1; - *success = false; - if (state() == QQmlDebugClient::Enabled) { - id = getId(); - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("WATCH_PROPERTY") << id << property.objectDebugId - << property.name.toUtf8(); - sendMessage(ds.data()); - *success = true; - } - return id; -} - -quint32 QQmlEngineDebugClient::addWatch( - const QmlDebugContextReference &, const QString &, bool *success) -{ - *success = false; - qWarning("QQmlEngineDebugClient::addWatch(): Not implemented"); - return 0; -} - -quint32 QQmlEngineDebugClient::addWatch( - const QmlDebugObjectReference &object, const QString &expr, - bool *success) -{ - quint32 id = -1; - *success = false; - if (state() == QQmlDebugClient::Enabled) { - id = getId(); - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("WATCH_EXPR_OBJECT") << id << object.debugId << expr; - sendMessage(ds.data()); - *success = true; - } - return id; -} - -quint32 QQmlEngineDebugClient::addWatch( - const QmlDebugObjectReference &object, bool *success) -{ - quint32 id = -1; - *success = false; - if (state() == QQmlDebugClient::Enabled) { - id = getId(); - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("WATCH_OBJECT") << id << object.debugId; - sendMessage(ds.data()); - *success = true; - } - return id; -} - -quint32 QQmlEngineDebugClient::addWatch( - const QmlDebugFileReference &, bool *success) -{ - *success = false; - qWarning("QQmlEngineDebugClient::addWatch(): Not implemented"); - return 0; -} - -void QQmlEngineDebugClient::removeWatch(quint32 id, bool *success) -{ - *success = false; - if (state() == QQmlDebugClient::Enabled) { - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("NO_WATCH") << id; - sendMessage(ds.data()); - *success = true; - } -} - -quint32 QQmlEngineDebugClient::queryAvailableEngines(bool *success) -{ - m_engines.clear(); - quint32 id = -1; - *success = false; - if (state() == QQmlDebugClient::Enabled) { - id = getId(); - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("LIST_ENGINES") << id; - sendMessage(ds.data()); - *success = true; - } - return id; -} - -quint32 QQmlEngineDebugClient::queryRootContexts( - const QmlDebugEngineReference &engine, bool *success) -{ - m_rootContext = QmlDebugContextReference(); - quint32 id = -1; - *success = false; - if (state() == QQmlDebugClient::Enabled && engine.debugId != -1) { - id = getId(); - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("LIST_OBJECTS") << id << engine.debugId; - sendMessage(ds.data()); - *success = true; - } - return id; -} - -quint32 QQmlEngineDebugClient::queryObject( - const QmlDebugObjectReference &object, bool *success) -{ - m_object = QmlDebugObjectReference(); - quint32 id = -1; - *success = false; - if (state() == QQmlDebugClient::Enabled && object.debugId != -1) { - id = getId(); - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("FETCH_OBJECT") << id << object.debugId << false << true; - sendMessage(ds.data()); - *success = true; - } - return id; -} - -quint32 QQmlEngineDebugClient::queryObjectsForLocation( - const QString &file, int lineNumber, int columnNumber, bool *success) -{ - m_objects.clear(); - quint32 id = -1; - *success = false; - if (state() == QQmlDebugClient::Enabled) { - id = getId(); - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("FETCH_OBJECTS_FOR_LOCATION") << id << file << lineNumber - << columnNumber << false << true; - sendMessage(ds.data()); - *success = true; - } - return id; -} - -quint32 QQmlEngineDebugClient::queryObjectRecursive( - const QmlDebugObjectReference &object, bool *success) -{ - m_object = QmlDebugObjectReference(); - quint32 id = -1; - *success = false; - if (state() == QQmlDebugClient::Enabled && object.debugId != -1) { - id = getId(); - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("FETCH_OBJECT") << id << object.debugId << true << true; - sendMessage(ds.data()); - *success = true; - } - return id; -} - -quint32 QQmlEngineDebugClient::queryObjectsForLocationRecursive(const QString &file, - int lineNumber, int columnNumber, bool *success) -{ - m_objects.clear(); - quint32 id = -1; - *success = false; - if (state() == QQmlDebugClient::Enabled) { - id = getId(); - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("FETCH_OBJECTS_FOR_LOCATION") << id << file << lineNumber - << columnNumber << true << true; - sendMessage(ds.data()); - *success = true; - } - return id; -} - -quint32 QQmlEngineDebugClient::queryExpressionResult( - int objectDebugId, const QString &expr, bool *success) -{ - m_exprResult = QVariant(); - quint32 id = -1; - *success = false; - if (state() == QQmlDebugClient::Enabled) { - id = getId(); - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("EVAL_EXPRESSION") << id << objectDebugId << expr - << engines()[0].debugId; - sendMessage(ds.data()); - *success = true; - } - return id; -} - -quint32 QQmlEngineDebugClient::queryExpressionResultBC( - int objectDebugId, const QString &expr, bool *success) -{ - m_exprResult = QVariant(); - quint32 id = -1; - *success = false; - if (state() == QQmlDebugClient::Enabled) { - id = getId(); - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("EVAL_EXPRESSION") << id << objectDebugId << expr; - sendMessage(ds.data()); - *success = true; - } - return id; -} - -quint32 QQmlEngineDebugClient::setBindingForObject( - int objectDebugId, - const QString &propertyName, - const QVariant &bindingExpression, - bool isLiteralValue, - QString source, int line, - bool *success) -{ - quint32 id = -1; - *success = false; - if (state() == QQmlDebugClient::Enabled && objectDebugId != -1) { - id = getId(); - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("SET_BINDING") << id << objectDebugId << propertyName - << bindingExpression << isLiteralValue << source << line; - sendMessage(ds.data()); - *success = true; - } - return id; -} - -quint32 QQmlEngineDebugClient::resetBindingForObject( - int objectDebugId, - const QString &propertyName, - bool *success) -{ - quint32 id = -1; - *success = false; - if (state() == QQmlDebugClient::Enabled && objectDebugId != -1) { - id = getId(); - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("RESET_BINDING") << id << objectDebugId << propertyName; - sendMessage(ds.data()); - *success = true; - } - return id; -} - -quint32 QQmlEngineDebugClient::setMethodBody( - int objectDebugId, const QString &methodName, - const QString &methodBody, bool *success) -{ - quint32 id = -1; - *success = false; - if (state() == QQmlDebugClient::Enabled && objectDebugId != -1) { - id = getId(); - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("SET_METHOD_BODY") << id << objectDebugId - << methodName << methodBody; - sendMessage(ds.data()); - *success = true; - } - return id; -} - -void QQmlEngineDebugClient::decode(QPacket &ds, - QmlDebugObjectReference &o, - bool simple) -{ - QmlObjectData data; - ds >> data; - o.debugId = data.objectId; - o.className = data.objectType; - o.idString = data.idString; - o.name = data.objectName; - o.source.url = data.url; - o.source.lineNumber = data.lineNumber; - o.source.columnNumber = data.columnNumber; - o.contextDebugId = data.contextId; - - if (simple) - return; - - int childCount; - bool recur; - ds >> childCount >> recur; - - for (int ii = 0; ii < childCount; ++ii) { - o.children.append(QmlDebugObjectReference()); - decode(ds, o.children.last(), !recur); - } - - int propCount; - ds >> propCount; - - for (int ii = 0; ii < propCount; ++ii) { - QmlObjectProperty data; - ds >> data; - QmlDebugPropertyReference prop; - prop.objectDebugId = o.debugId; - prop.name = data.name; - prop.binding = data.binding; - prop.hasNotifySignal = data.hasNotifySignal; - prop.valueTypeName = data.valueTypeName; - switch (data.type) { - case QmlObjectProperty::Basic: - case QmlObjectProperty::List: - case QmlObjectProperty::SignalProperty: - { - prop.value = data.value; - break; - } - case QmlObjectProperty::Object: - { - QmlDebugObjectReference obj; - obj.debugId = prop.value.toInt(); - obj.className = prop.valueTypeName; - prop.value = qVariantFromValue(obj); - break; - } - case QmlObjectProperty::Unknown: - break; - } - o.properties << prop; - } -} - -void QQmlEngineDebugClient::decode(QPacket &ds, - QList<QmlDebugObjectReference> &o, - bool simple) -{ - int count; - ds >> count; - for (int i = 0; i < count; i++) { - QmlDebugObjectReference obj; - decode(ds, obj, simple); - o << obj; - } -} - -void QQmlEngineDebugClient::decode(QPacket &ds, - QmlDebugContextReference &c) -{ - ds >> c.name >> c.debugId; - - int contextCount; - ds >> contextCount; - - for (int ii = 0; ii < contextCount; ++ii) { - c.contexts.append(QmlDebugContextReference()); - decode(ds, c.contexts.last()); - } - - int objectCount; - ds >> objectCount; - - for (int ii = 0; ii < objectCount; ++ii) { - QmlDebugObjectReference obj; - decode(ds, obj, true); - - obj.contextDebugId = c.debugId; - c.objects << obj; - } -} - -void QQmlEngineDebugClient::messageReceived(const QByteArray &data) -{ - m_valid = false; - QPacket ds(connection()->currentDataStreamVersion(), data); - - int queryId; - QByteArray type; - ds >> type >> queryId; - - //qDebug() << "QQmlEngineDebugPrivate::message()" << type; - - if (type == "LIST_ENGINES_R") { - int count; - ds >> count; - - m_engines.clear(); - for (int ii = 0; ii < count; ++ii) { - QmlDebugEngineReference eng; - ds >> eng.name; - ds >> eng.debugId; - m_engines << eng; - } - } else if (type == "LIST_OBJECTS_R") { - if (!ds.atEnd()) - decode(ds, m_rootContext); - - } else if (type == "FETCH_OBJECT_R") { - if (!ds.atEnd()) - decode(ds, m_object, false); - - } else if (type == "FETCH_OBJECTS_FOR_LOCATION_R") { - if (!ds.atEnd()) - decode(ds, m_objects, false); - - } else if (type == "EVAL_EXPRESSION_R") {; - ds >> m_exprResult; - - } else if (type == "WATCH_PROPERTY_R") { - ds >> m_valid; - - } else if (type == "WATCH_OBJECT_R") { - ds >> m_valid; - - } else if (type == "WATCH_EXPR_OBJECT_R") { - ds >> m_valid; - - } else if (type == "UPDATE_WATCH") { - int debugId; - QByteArray name; - QVariant value; - ds >> debugId >> name >> value; - emit valueChanged(name, value); - return; - - } else if (type == "OBJECT_CREATED") { - int engineId, objectId, parentId; - ds >> engineId >> objectId >> parentId; - emit newObject(objectId); - return; - } else if (type == "SET_BINDING_R") { - ds >> m_valid; - } else if (type == "RESET_BINDING_R") { - ds >> m_valid; - } else if (type == "SET_METHOD_BODY_R") { - ds >> m_valid; - } else if (type == "NO_WATCH_R") { - ds >> m_valid; - } - emit result(); -} - diff --git a/tests/auto/qml/debugger/shared/qqmlenginedebugclient.h b/tests/auto/qml/debugger/shared/qqmlenginedebugclient.h deleted file mode 100644 index 5d74f2d43c..0000000000 --- a/tests/auto/qml/debugger/shared/qqmlenginedebugclient.h +++ /dev/null @@ -1,233 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLENGINEDEBUGCLIENT_H -#define QQMLENGINEDEBUGCLIENT_H - -#include <private/qqmldebugclient_p.h> -#include <private/qpacket_p.h> - -#include <QtCore/qurl.h> -#include <QtCore/qvariant.h> - -struct QmlDebugPropertyReference -{ - QmlDebugPropertyReference() - : objectDebugId(-1), hasNotifySignal(false) - { - } - - QmlDebugPropertyReference &operator=( - const QmlDebugPropertyReference &o) - { - objectDebugId = o.objectDebugId; name = o.name; value = o.value; - valueTypeName = o.valueTypeName; binding = o.binding; - hasNotifySignal = o.hasNotifySignal; - return *this; - } - - int objectDebugId; - QString name; - QVariant value; - QString valueTypeName; - QString binding; - bool hasNotifySignal; -}; - -struct QmlDebugFileReference -{ - QmlDebugFileReference() - : lineNumber(-1), columnNumber(-1) - { - } - - QmlDebugFileReference &operator=( - const QmlDebugFileReference &o) - { - url = o.url; lineNumber = o.lineNumber; columnNumber = o.columnNumber; - return *this; - } - - QUrl url; - int lineNumber; - int columnNumber; -}; - -struct QmlDebugObjectReference -{ - QmlDebugObjectReference() - : debugId(-1), contextDebugId(-1) - { - } - - QmlDebugObjectReference(int id) - : debugId(id), contextDebugId(-1) - { - } - - QmlDebugObjectReference &operator=( - const QmlDebugObjectReference &o) - { - debugId = o.debugId; className = o.className; idString = o.idString; - name = o.name; source = o.source; contextDebugId = o.contextDebugId; - properties = o.properties; children = o.children; - return *this; - } - int debugId; - QString className; - QString idString; - QString name; - QmlDebugFileReference source; - int contextDebugId; - QList<QmlDebugPropertyReference> properties; - QList<QmlDebugObjectReference> children; -}; - -Q_DECLARE_METATYPE(QmlDebugObjectReference) - -struct QmlDebugContextReference -{ - QmlDebugContextReference() - : debugId(-1) - { - } - - QmlDebugContextReference &operator=( - const QmlDebugContextReference &o) - { - debugId = o.debugId; name = o.name; objects = o.objects; - contexts = o.contexts; - return *this; - } - - int debugId; - QString name; - QList<QmlDebugObjectReference> objects; - QList<QmlDebugContextReference> contexts; -}; - -struct QmlDebugEngineReference -{ - QmlDebugEngineReference() - : debugId(-1) - { - } - - QmlDebugEngineReference(int id) - : debugId(id) - { - } - - QmlDebugEngineReference &operator=( - const QmlDebugEngineReference &o) - { - debugId = o.debugId; name = o.name; - return *this; - } - - int debugId; - QString name; -}; - -class QQmlEngineDebugClient : public QQmlDebugClient -{ - Q_OBJECT -public: - explicit QQmlEngineDebugClient(QQmlDebugConnection *conn); - - quint32 addWatch(const QmlDebugPropertyReference &, - bool *success); - quint32 addWatch(const QmlDebugContextReference &, const QString &, - bool *success); - quint32 addWatch(const QmlDebugObjectReference &, const QString &, - bool *success); - quint32 addWatch(const QmlDebugObjectReference &, - bool *success); - quint32 addWatch(const QmlDebugFileReference &, - bool *success); - - void removeWatch(quint32 watch, bool *success); - - quint32 queryAvailableEngines(bool *success); - quint32 queryRootContexts(const QmlDebugEngineReference &, - bool *success); - quint32 queryObject(const QmlDebugObjectReference &, - bool *success); - quint32 queryObjectsForLocation(const QString &file, - int lineNumber, int columnNumber, bool *success); - quint32 queryObjectRecursive(const QmlDebugObjectReference &, - bool *success); - quint32 queryObjectsForLocationRecursive(const QString &file, - int lineNumber, int columnNumber, bool *success); - quint32 queryExpressionResult(int objectDebugId, - const QString &expr, - bool *success); - quint32 queryExpressionResultBC(int objectDebugId, - const QString &expr, - bool *success); - quint32 setBindingForObject(int objectDebugId, const QString &propertyName, - const QVariant &bindingExpression, - bool isLiteralValue, - QString source, int line, bool *success); - quint32 resetBindingForObject(int objectDebugId, - const QString &propertyName, bool *success); - quint32 setMethodBody(int objectDebugId, const QString &methodName, - const QString &methodBody, bool *success); - - quint32 getId() { return m_nextId++; } - - void decode(QPacket &ds, QmlDebugContextReference &); - void decode(QPacket &ds, QmlDebugObjectReference &, bool simple); - void decode(QPacket &ds, QList<QmlDebugObjectReference> &o, bool simple); - - QList<QmlDebugEngineReference> engines() { return m_engines; } - QmlDebugContextReference rootContext() { return m_rootContext; } - QmlDebugObjectReference object() { return m_object; } - QList<QmlDebugObjectReference> objects() { return m_objects; } - QVariant resultExpr() { return m_exprResult; } - bool valid() { return m_valid; } - -signals: - void newObject(int objectId); - void valueChanged(QByteArray,QVariant); - void result(); - -protected: - void messageReceived(const QByteArray &); - -private: - quint32 m_nextId; - bool m_valid; - QList<QmlDebugEngineReference> m_engines; - QmlDebugContextReference m_rootContext; - QmlDebugObjectReference m_object; - QList<QmlDebugObjectReference> m_objects; - QVariant m_exprResult; -}; - -#endif // QQMLENGINEDEBUGCLIENT_H diff --git a/tests/auto/qml/debugger/shared/qqmlenginedebugclient.pri b/tests/auto/qml/debugger/shared/qqmlenginedebugclient.pri deleted file mode 100644 index a969b4f153..0000000000 --- a/tests/auto/qml/debugger/shared/qqmlenginedebugclient.pri +++ /dev/null @@ -1,3 +0,0 @@ -HEADERS += $$PWD/qqmlenginedebugclient.h - -SOURCES += $$PWD/qqmlenginedebugclient.cpp diff --git a/tests/auto/qml/debugger/shared/qqmlinspectorclient.cpp b/tests/auto/qml/debugger/shared/qqmlinspectorclient.cpp deleted file mode 100644 index 20faef177e..0000000000 --- a/tests/auto/qml/debugger/shared/qqmlinspectorclient.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlinspectorclient.h" - -#include <private/qpacket_p.h> -#include <private/qqmldebugconnection_p.h> -#include <QtCore/qdebug.h> - -QQmlInspectorClient::QQmlInspectorClient(QQmlDebugConnection *connection) : - QQmlDebugClient(QLatin1String("QmlInspector"), connection), - m_lastRequestId(-1) -{ -} - -int QQmlInspectorClient::setInspectToolEnabled(bool enabled) -{ - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("request") << ++m_lastRequestId - << QByteArray(enabled ? "enable" : "disable"); - - sendMessage(ds.data()); - return m_lastRequestId; -} - -int QQmlInspectorClient::setShowAppOnTop(bool showOnTop) -{ - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("request") << ++m_lastRequestId - << QByteArray("showAppOnTop") << showOnTop; - - sendMessage(ds.data()); - return m_lastRequestId; -} - -int QQmlInspectorClient::setAnimationSpeed(qreal speed) -{ - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("request") << ++m_lastRequestId - << QByteArray("setAnimationSpeed") << speed; - - sendMessage(ds.data()); - return m_lastRequestId; -} - -int QQmlInspectorClient::select(const QList<int> &objectIds) -{ - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("request") << ++m_lastRequestId - << QByteArray("select") << objectIds; - - sendMessage(ds.data()); - return m_lastRequestId; -} - -int QQmlInspectorClient::createObject(const QString &qml, int parentId, const QStringList &imports, - const QString &filename) -{ - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("request") << ++m_lastRequestId - << QByteArray("createObject") << qml << parentId << imports << filename; - sendMessage(ds.data()); - return m_lastRequestId; -} - -int QQmlInspectorClient::moveObject(int childId, int newParentId) -{ - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("request") << ++m_lastRequestId - << QByteArray("moveObject") << childId << newParentId; - sendMessage(ds.data()); - return m_lastRequestId; -} - -int QQmlInspectorClient::destroyObject(int objectId) -{ - QPacket ds(connection()->currentDataStreamVersion()); - ds << QByteArray("request") << ++m_lastRequestId - << QByteArray("destroyObject") << objectId; - sendMessage(ds.data()); - return m_lastRequestId; -} - -void QQmlInspectorClient::messageReceived(const QByteArray &message) -{ - QPacket ds(connection()->currentDataStreamVersion(), message); - QByteArray type; - ds >> type; - - if (type != QByteArray("response")) { - qDebug() << "Unhandled message of type" << type; - return; - } - - int responseId; - bool result; - ds >> responseId >> result; - emit responseReceived(responseId, result); -} diff --git a/tests/auto/qml/debugger/shared/qqmlinspectorclient.h b/tests/auto/qml/debugger/shared/qqmlinspectorclient.h deleted file mode 100644 index bfb489c8f7..0000000000 --- a/tests/auto/qml/debugger/shared/qqmlinspectorclient.h +++ /dev/null @@ -1,59 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef QQMLINSPECTORCLIENT_H -#define QQMLINSPECTORCLIENT_H - -#include <private/qqmldebugclient_p.h> - -class QQmlInspectorClient : public QQmlDebugClient -{ - Q_OBJECT - -public: - QQmlInspectorClient(QQmlDebugConnection *connection); - - int setInspectToolEnabled(bool enabled); - int setShowAppOnTop(bool showOnTop); - int setAnimationSpeed(qreal speed); - int select(const QList<int> &objectIds); - int createObject(const QString &qml, int parentId, const QStringList &imports, - const QString &filename); - int moveObject(int childId, int newParentId); - int destroyObject(int objectId); - -signals: - void responseReceived(int requestId, bool result); - -protected: - void messageReceived(const QByteArray &message); - -private: - int m_lastRequestId; -}; - -#endif // QQMLINSPECTORCLIENT_H diff --git a/tests/auto/qml/debugger/shared/qqmlinspectorclient.pri b/tests/auto/qml/debugger/shared/qqmlinspectorclient.pri deleted file mode 100644 index c136e1313a..0000000000 --- a/tests/auto/qml/debugger/shared/qqmlinspectorclient.pri +++ /dev/null @@ -1,3 +0,0 @@ -HEADERS += $$PWD/qqmlinspectorclient.h - -SOURCES += $$PWD/qqmlinspectorclient.cpp |