From 73a620559749cb636766ae7f94a32e0cc7cdb6e7 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 3 May 2021 17:20:54 +0200 Subject: V4 debugger: Properly count break points We cannot just take the number of active breakpoints as ID for the next one. It's possible to remove breakpoints after all. Fixes: QTBUG-93404 Change-Id: Icde7a8e47c740e930f2313ffd9034b00033a54aa Reviewed-by: hjk Reviewed-by: Fabian Kosmale (cherry picked from commit 7f12cf3346d65d0bff78fff8000ed519fbb921ba) Reviewed-by: Qt Cherry-pick Bot --- .../debugger/qqmldebugjs/data/breakPointIds.qml | 15 ++++ .../qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp | 89 +++++++++++++++++----- 2 files changed, 86 insertions(+), 18 deletions(-) create mode 100644 tests/auto/qml/debugger/qqmldebugjs/data/breakPointIds.qml (limited to 'tests/auto/qml/debugger/qqmldebugjs') diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/breakPointIds.qml b/tests/auto/qml/debugger/qqmldebugjs/data/breakPointIds.qml new file mode 100644 index 0000000000..c3e7687831 --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugjs/data/breakPointIds.qml @@ -0,0 +1,15 @@ +import QtQml 2.15 +Timer { + Component.onCompleted: { + console.log('0') + console.log('1') + console.log('2') + console.log('3') + console.log('4') + console.log('5') + running = true + } + + interval: 0 + onTriggered: Qt.quit() +} diff --git a/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp b/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp index 91470e0651..59cab6eb75 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp +++ b/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp @@ -65,6 +65,7 @@ const char *STEPACTION_QMLFILE = "stepAction.qml"; const char *BREAKPOINTRELOCATION_QMLFILE = "breakpointRelocation.qml"; const char *ENCODEQMLSCOPE_QMLFILE = "encodeQmlScope.qml"; const char *BREAKONANCHOR_QMLFILE = "breakOnAnchor.qml"; +const char *BREAKPOINTIDS_QMLFILE = "breakPointIds.qml"; #undef QVERIFY #define QVERIFY(statement) \ @@ -156,6 +157,8 @@ private slots: void encodeQmlScope(); void breakOnAnchor(); + void breakPointIds(); + private: ConnectResult init(bool qmlscene, const QString &qmlFile = QString(TEST_QMLFILE), bool blockMode = true, bool restrictServices = false); @@ -165,10 +168,11 @@ private: void targetData(); bool waitForClientSignal(const char *signal, int timeout = 30000); void checkVersionParameters(); + int setBreakPoint(const QString &file, int sourceLine, bool enabled); + void clearBreakPoint(int id); }; - void tst_QQmlDebugJS::initTestCase() { QQmlDebugTest::initTestCase(); @@ -566,7 +570,8 @@ void tst_QQmlDebugJS::changeBreakpoint() int sourceLine2 = 37; int sourceLine1 = 38; - QCOMPARE(init(qmlscene, CHANGEBREAKPOINT_QMLFILE), ConnectSuccess); + const QString file = QLatin1String(CHANGEBREAKPOINT_QMLFILE); + QCOMPARE(init(qmlscene, file), ConnectSuccess); bool isStopped = false; QObject::connect(m_client.data(), &QV4DebugClient::stopped, this, [&]() { isStopped = true; }); @@ -589,27 +594,13 @@ void tst_QQmlDebugJS::changeBreakpoint() 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); + const int breakpoint1 = setBreakPoint(file, sourceLine1, false); QVERIFY(breakpoint1 >= 0); - const int breakpoint2 = setBreakPoint(sourceLine2, true); + const int breakpoint2 = setBreakPoint(file, sourceLine2, true); QVERIFY(breakpoint2 >= 0); auto verifyBreakpoint = [&](int sourceLine, int breakpointId) { @@ -1026,6 +1017,39 @@ void tst_QQmlDebugJS::breakOnAnchor() QCOMPARE(breaks, 2); } +void tst_QQmlDebugJS::breakPointIds() +{ + QString file(BREAKPOINTIDS_QMLFILE); + QCOMPARE(init(true, file), ConnectSuccess); + + int breaks = 0; + int breakPointIds[] = { -1, -1, -1, -1, -1, -1}; + + QObject::connect(m_client.data(), &QV4DebugClient::stopped, this, [&]() { + const QJsonObject body = m_client->response().body.toObject(); + QCOMPARE(body.value("sourceLine").toInt(), breaks + 4); + const QJsonArray breakpointsHit = body.value("breakpoints").toArray(); + QVERIFY(breakpointsHit.size() > 0); + QCOMPARE(breakpointsHit[0].toInt(), breakPointIds[breaks]); + ++breaks; + m_client->continueDebugging(QV4DebugClient::Continue); + }); + + for (int i = 0; i < 6; ++i) + breakPointIds[i] = setBreakPoint(file, i + 4, true); + + clearBreakPoint(breakPointIds[2]); + breakPointIds[2] = setBreakPoint(file, 6, true); + + 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, 6); +} + QList tst_QQmlDebugJS::createClients() { m_client = new QV4DebugClient(m_connection); @@ -1054,6 +1078,35 @@ void tst_QQmlDebugJS::checkVersionParameters() QCOMPARE(body.value("ChangeBreakpoint").toBool(), true); } +int tst_QQmlDebugJS::setBreakPoint(const QString &file, int sourceLine, bool enabled) +{ + int id = -1; + auto connection = QObject::connect(m_client.data(), &QV4DebugClient::result, [&]() { + id = m_client->response().body.toObject().value("breakpoint").toInt(); + }); + + m_client->setBreakpoint(file, sourceLine, -1, enabled); + bool success = QTest::qWaitFor([&]() { return id >= 0; }); + Q_UNUSED(success); + + QObject::disconnect(connection); + return id; +} + +void tst_QQmlDebugJS::clearBreakPoint(int id) +{ + bool ok = false; + auto connection = QObject::connect(m_client.data(), &QV4DebugClient::result, [&]() { + ok = true; + }); + + m_client->clearBreakpoint(id); + bool success = QTest::qWaitFor([&]() { return ok; }); + Q_UNUSED(success); + + QObject::disconnect(connection); +} + QTEST_MAIN(tst_QQmlDebugJS) #include "tst_qqmldebugjs.moc" -- cgit v1.2.3 From 6ce50ad548aad6caca815b3058ee1a2be923344f Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 3 May 2021 19:23:49 +0200 Subject: V4 Debugger: Collect locals also from block scopes Block scopes can contain "const" and "let" members. Fixes: QTBUG-92224 Change-Id: Ie13d7d573e2759c510e1ea48c6edc68a095f40a0 Reviewed-by: Fabian Kosmale (cherry picked from commit 051dd3178bc4c9214af60c69cecfc2c28f13174d) --- .../debugger/qqmldebugjs/data/letConstLocals.qml | 16 ++++++ .../qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp | 59 ++++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 tests/auto/qml/debugger/qqmldebugjs/data/letConstLocals.qml (limited to 'tests/auto/qml/debugger/qqmldebugjs') diff --git a/tests/auto/qml/debugger/qqmldebugjs/data/letConstLocals.qml b/tests/auto/qml/debugger/qqmldebugjs/data/letConstLocals.qml new file mode 100644 index 0000000000..1715992490 --- /dev/null +++ b/tests/auto/qml/debugger/qqmldebugjs/data/letConstLocals.qml @@ -0,0 +1,16 @@ +import QtQml 2.15 + +Timer { + Component.onCompleted: { + var a = 97 + var b = 98 + var c = 99 + let d = 100 + const e = 101 + console.log("onClicked") // Set breakpoint + running = true + } + + interval: 0 + onTriggered: Qt.quit() +} diff --git a/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp b/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp index 59cab6eb75..43c81ee515 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp +++ b/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp @@ -66,6 +66,7 @@ const char *BREAKPOINTRELOCATION_QMLFILE = "breakpointRelocation.qml"; const char *ENCODEQMLSCOPE_QMLFILE = "encodeQmlScope.qml"; const char *BREAKONANCHOR_QMLFILE = "breakOnAnchor.qml"; const char *BREAKPOINTIDS_QMLFILE = "breakPointIds.qml"; +const char *LETCONSTLOCALS_QMLFILE = "letConstLocals.qml"; #undef QVERIFY #define QVERIFY(statement) \ @@ -158,6 +159,7 @@ private slots: void breakOnAnchor(); void breakPointIds(); + void letConstLocals(); private: ConnectResult init(bool qmlscene, const QString &qmlFile = QString(TEST_QMLFILE), @@ -1050,6 +1052,63 @@ void tst_QQmlDebugJS::breakPointIds() QCOMPARE(breaks, 6); } +void tst_QQmlDebugJS::letConstLocals() +{ + QString file(LETCONSTLOCALS_QMLFILE); + QCOMPARE(init(true, file), ConnectSuccess); + + QObject::connect(m_client.data(), &QV4DebugClient::stopped, this, [&]() { + m_client->frame(); + }); + + int numScopes = 0; + QString expectedMembers = QStringLiteral("abcde"); + QObject::connect(m_client.data(), &QV4DebugClient::result, this, [&]() { + const auto value = m_client->response(); + if (value.command == QStringLiteral("frame")) { + const auto scopes = value.body.toObject().value(QStringLiteral("scopes")).toArray(); + for (const auto &scope : scopes) { + const auto scopeObject = scope.toObject(); + const int type = scopeObject.value("type").toInt(); + if (type == 1 || type == 4) { + m_client->scope(scopeObject.value("index").toInt()); + ++numScopes; + } + } + QVERIFY(numScopes > 0); + } else if (value.command == QStringLiteral("scope")) { + const auto props = value.body.toObject().value(QStringLiteral("object")).toObject() + .value(QStringLiteral("properties")).toArray(); + for (const auto &prop : props) { + const auto propObj = prop.toObject(); + const QString name = propObj.value(QStringLiteral("name")).toString(); + if (name == QStringLiteral("onCompleted")) + continue; + QVERIFY(name.length() == 1); + auto i = expectedMembers.indexOf(name.at(0)); + QVERIFY(i != -1); + expectedMembers.remove(i, 1); + QCOMPARE(propObj.value(QStringLiteral("type")).toString(), + QStringLiteral("number")); + QCOMPARE(propObj.value(QStringLiteral("value")).toInt(), + int(name.at(0).toLatin1())); + } + if (--numScopes == 0) { + QVERIFY(expectedMembers.isEmpty()); + m_client->continueDebugging(QV4DebugClient::Continue); + } + } + }); + + setBreakPoint(file, 10, true); + + QTRY_COMPARE(m_process->state(), QProcess::Running); + m_client->connect(); + + QTRY_COMPARE(m_process->state(), QProcess::NotRunning); + QCOMPARE(m_process->exitStatus(), QProcess::NormalExit); +} + QList tst_QQmlDebugJS::createClients() { m_client = new QV4DebugClient(m_connection); -- cgit v1.2.3