1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include "debugutil_p.h"
30#include "qqmldebugprocess_p.h"
31#include "../../../shared/util.h"
32
33#include <private/qqmlenginedebugclient_p.h>
34#include <private/qv4debugclient_p.h>
35#include <private/qqmldebugconnection_p.h>
36#include <private/qpacket_p.h>
37
38#include <QtTest/qtest.h>
39#include <QtTest/qtestsystem.h>
40#include <QtCore/qprocess.h>
41#include <QtCore/qfileinfo.h>
42#include <QtCore/qdir.h>
43#include <QtCore/qmutex.h>
44#include <QtCore/qlibraryinfo.h>
45#include <QtCore/qjsonobject.h>
46#include <QtCore/qjsonarray.h>
47
48const char *BLOCKMODE = "-qmljsdebugger=port:3771,3800,block";
49const char *NORMALMODE = "-qmljsdebugger=port:3771,3800";
50const char *BLOCKRESTRICTEDMODE = "-qmljsdebugger=port:3771,3800,block,services:V8Debugger";
51const char *NORMALRESTRICTEDMODE = "-qmljsdebugger=port:3771,3800,services:V8Debugger";
52const char *TEST_QMLFILE = "test.qml";
53const char *TEST_JSFILE = "test.js";
54const char *TIMER_QMLFILE = "timer.qml";
55const char *LOADJSFILE_QMLFILE = "loadjsfile.qml";
56const char *EXCEPTION_QMLFILE = "exception.qml";
57const char *ONCOMPLETED_QMLFILE = "oncompleted.qml";
58const char *CREATECOMPONENT_QMLFILE = "createComponent.qml";
59const char *CONDITION_QMLFILE = "condition.qml";
60const char *QUIT_QMLFILE = "quit.qml";
61const char *QUITINJS_QMLFILE = "quitInJS.qml";
62const char *QUIT_JSFILE = "quit.js";
63const char *CHANGEBREAKPOINT_QMLFILE = "changeBreakpoint.qml";
64const char *STEPACTION_QMLFILE = "stepAction.qml";
65const char *BREAKPOINTRELOCATION_QMLFILE = "breakpointRelocation.qml";
66const char *ENCODEQMLSCOPE_QMLFILE = "encodeQmlScope.qml";
67const char *BREAKONANCHOR_QMLFILE = "breakOnAnchor.qml";
68const char *BREAKPOINTIDS_QMLFILE = "breakPointIds.qml";
69const char *LETCONSTLOCALS_QMLFILE = "letConstLocals.qml";
70
71#undef QVERIFY
72#define QVERIFY(statement) \
73do {\
74 if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__)) {\
75 if (QTest::currentTestFailed()) \
76 qDebug().nospace() << "\nDEBUGGEE OUTPUT:\n" << m_process->output();\
77 return;\
78 }\
79} while (0)
80
81class tst_QQmlDebugJS : public QQmlDebugTest
82{
83 Q_OBJECT
84
85private slots:
86 void initTestCase() override;
87
88 void connect_data();
89 void connect();
90 void interrupt_data() { targetData(); }
91 void interrupt();
92 void getVersion_data() { targetData(); }
93 void getVersion();
94 void getVersionWhenAttaching_data() { targetData(); }
95 void getVersionWhenAttaching();
96
97 void disconnect_data() { targetData(); }
98 void disconnect();
99
100 void setBreakpointInScriptOnCompleted_data() { targetData(); }
101 void setBreakpointInScriptOnCompleted();
102 void setBreakpointInScriptOnComponentCreated_data() { targetData(); }
103 void setBreakpointInScriptOnComponentCreated();
104 void setBreakpointInScriptOnTimerCallback_data() { targetData(); }
105 void setBreakpointInScriptOnTimerCallback();
106 void setBreakpointInScriptInDifferentFile_data() { targetData(); }
107 void setBreakpointInScriptInDifferentFile();
108 void setBreakpointInScriptOnComment_data() { targetData(); }
109 void setBreakpointInScriptOnComment();
110 void setBreakpointInScriptOnEmptyLine_data() { targetData(); }
111 void setBreakpointInScriptOnEmptyLine();
112 void setBreakpointInScriptOnOptimizedBinding_data() { targetData(); }
113 void setBreakpointInScriptOnOptimizedBinding();
114 void setBreakpointInScriptWithCondition_data() { targetData(); }
115 void setBreakpointInScriptWithCondition();
116 void setBreakpointInScriptThatQuits_data() { targetData(); };
117 void setBreakpointInScriptThatQuits();
118 void setBreakpointInJavaScript_data();
119 void setBreakpointInJavaScript();
120 void setBreakpointWhenAttaching();
121
122 void clearBreakpoint_data() { targetData(); }
123 void clearBreakpoint();
124
125 void changeBreakpoint_data() { targetData(); }
126 void changeBreakpoint();
127
128 void setExceptionBreak_data() { targetData(); }
129 void setExceptionBreak();
130
131 void stepNext_data() { targetData(); }
132 void stepNext();
133 void stepIn_data() { targetData(); }
134 void stepIn();
135 void stepOut_data() { targetData(); }
136 void stepOut();
137 void continueDebugging_data() { targetData(); }
138 void continueDebugging();
139
140 void backtrace_data() { targetData(); }
141 void backtrace();
142
143 void getFrameDetails_data() { targetData(); }
144 void getFrameDetails();
145
146 void getScopeDetails_data() { targetData(); }
147 void getScopeDetails();
148
149 void evaluateInGlobalScope();
150 void evaluateInLocalScope_data() { targetData(); }
151 void evaluateInLocalScope();
152
153 void evaluateInContext();
154
155 void getScripts_data() { targetData(); }
156 void getScripts();
157
158 void encodeQmlScope();
159 void breakOnAnchor();
160
161 void breakPointIds();
162 void letConstLocals();
163
164private:
165 ConnectResult init(bool qmlscene, const QString &qmlFile = QString(TEST_QMLFILE),
166 bool blockMode = true, bool restrictServices = false);
167 QList<QQmlDebugClient *> createClients() override;
168 QPointer<QV4DebugClient> m_client;
169
170 void targetData();
171 bool waitForClientSignal(const char *signal, int timeout = 30000);
172 void checkVersionParameters();
173 int setBreakPoint(const QString &file, int sourceLine, bool enabled);
174 void clearBreakPoint(int id);
175};
176
177
178void tst_QQmlDebugJS::initTestCase()
179{
180 QQmlDebugTest::initTestCase();
181}
182
183QQmlDebugTest::ConnectResult tst_QQmlDebugJS::init(bool qmlscene, const QString &qmlFile,
184 bool blockMode, bool restrictServices)
185{
186 const QString executable = qmlscene
187 ? QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene"
188 : debugJsServerPath(selfPath: "qqmldebugjs");
189 return QQmlDebugTest::connectTo(
190 executable, services: restrictServices ? QStringLiteral("V8Debugger") : QString(),
191 extraArgs: testFile(fileName: qmlFile), block: blockMode);
192}
193
194void tst_QQmlDebugJS::connect_data()
195{
196 QTest::addColumn<bool>(name: "blockMode");
197 QTest::addColumn<bool>(name: "restrictMode");
198 QTest::addColumn<bool>(name: "qmlscene");
199 QTest::newRow(dataTag: "normal / unrestricted / custom") << false << false << false;
200 QTest::newRow(dataTag: "block / unrestricted / custom") << true << false << false;
201 QTest::newRow(dataTag: "normal / restricted / custom") << false << true << false;
202 QTest::newRow(dataTag: "block / restricted / custom") << true << true << false;
203 QTest::newRow(dataTag: "normal / unrestricted / qmlscene") << false << false << true;
204 QTest::newRow(dataTag: "block / unrestricted / qmlscene") << true << false << true;
205 QTest::newRow(dataTag: "normal / restricted / qmlscene") << false << true << true;
206 QTest::newRow(dataTag: "block / restricted / qmlscene") << true << true << true;
207}
208
209void tst_QQmlDebugJS::connect()
210{
211 QFETCH(bool, blockMode);
212 QFETCH(bool, restrictMode);
213 QFETCH(bool, qmlscene);
214
215 QCOMPARE(init(qmlscene, QString(TEST_QMLFILE), blockMode, restrictMode), ConnectSuccess);
216 m_client->connect();
217 QVERIFY(waitForClientSignal(SIGNAL(connected())));
218}
219
220void tst_QQmlDebugJS::interrupt()
221{
222 //void connect()
223 QFETCH(bool, qmlscene);
224
225 QCOMPARE(init(qmlscene), ConnectSuccess);
226 m_client->connect();
227
228 m_client->interrupt();
229 QVERIFY(waitForClientSignal(SIGNAL(interrupted())));
230}
231
232void tst_QQmlDebugJS::getVersion()
233{
234 //void version()
235 QFETCH(bool, qmlscene);
236
237 QCOMPARE(init(qmlscene), ConnectSuccess);
238 m_client->connect();
239 QVERIFY(waitForClientSignal(SIGNAL(connected())));
240
241 m_client->version();
242 QVERIFY(waitForClientSignal(SIGNAL(result())));
243 checkVersionParameters();
244}
245
246void tst_QQmlDebugJS::getVersionWhenAttaching()
247{
248 //void version()
249 QFETCH(bool, qmlscene);
250
251 QCOMPARE(init(qmlscene, QLatin1String(TIMER_QMLFILE), false), ConnectSuccess);
252 m_client->connect();
253
254 m_client->version();
255 QVERIFY(waitForClientSignal(SIGNAL(result())));
256 checkVersionParameters();
257}
258
259void tst_QQmlDebugJS::disconnect()
260{
261 //void disconnect()
262 QFETCH(bool, qmlscene);
263
264 QCOMPARE(init(qmlscene), ConnectSuccess);
265 m_client->connect();
266
267 m_client->disconnect();
268 QVERIFY(waitForClientSignal(SIGNAL(result())));
269}
270
271void tst_QQmlDebugJS::setBreakpointInScriptOnCompleted()
272{
273 //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1)
274 QFETCH(bool, qmlscene);
275
276 int sourceLine = 34;
277 QCOMPARE(init(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess);
278
279 m_client->setBreakpoint(target: QLatin1String(ONCOMPLETED_QMLFILE), line: sourceLine, column: -1, enabled: true);
280 m_client->connect();
281 QVERIFY(waitForClientSignal(SIGNAL(stopped())));
282
283 const QJsonObject body = m_client->response().body.toObject();
284
285 QCOMPARE(body.value("sourceLine").toInt(), sourceLine);
286 QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(),
287 QLatin1String(ONCOMPLETED_QMLFILE));
288}
289
290void tst_QQmlDebugJS::setBreakpointInScriptOnComponentCreated()
291{
292 //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1)
293 QFETCH(bool, qmlscene);
294
295 int sourceLine = 34;
296 QCOMPARE(init(qmlscene, CREATECOMPONENT_QMLFILE), ConnectSuccess);
297
298 m_client->setBreakpoint(target: QLatin1String(ONCOMPLETED_QMLFILE), line: sourceLine, column: -1, enabled: true);
299 m_client->connect();
300 QVERIFY(waitForClientSignal(SIGNAL(stopped())));
301
302 const QJsonObject body = m_client->response().body.toObject();
303
304 QCOMPARE(body.value("sourceLine").toInt(), sourceLine);
305 QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(),
306 QLatin1String(ONCOMPLETED_QMLFILE));
307}
308
309void tst_QQmlDebugJS::setBreakpointInScriptOnTimerCallback()
310{
311 QFETCH(bool, qmlscene);
312
313 int sourceLine = 35;
314 QCOMPARE(init(qmlscene, TIMER_QMLFILE), ConnectSuccess);
315
316 m_client->connect();
317 //We can set the breakpoint after connect() here because the timer is repeating and if we miss
318 //its first iteration we can still catch the second one.
319 m_client->setBreakpoint(target: QLatin1String(TIMER_QMLFILE), line: sourceLine, column: -1, enabled: true);
320 QVERIFY(waitForClientSignal(SIGNAL(stopped())));
321
322 const QJsonObject body = m_client->response().body.toObject();
323
324 QCOMPARE(body.value("sourceLine").toInt(), sourceLine);
325 QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(),
326 QLatin1String(TIMER_QMLFILE));
327}
328
329void tst_QQmlDebugJS::setBreakpointInScriptInDifferentFile()
330{
331 //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1)
332 QFETCH(bool, qmlscene);
333
334 int sourceLine = 31;
335 QCOMPARE(init(qmlscene, LOADJSFILE_QMLFILE), ConnectSuccess);
336
337 m_client->setBreakpoint(target: QLatin1String(TEST_JSFILE), line: sourceLine, column: -1, enabled: true);
338 m_client->connect();
339 QVERIFY(waitForClientSignal(SIGNAL(stopped())));
340
341 const QJsonObject body = m_client->response().body.toObject();
342
343 QCOMPARE(body.value("sourceLine").toInt(), sourceLine);
344 QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(),
345 QLatin1String(TEST_JSFILE));
346}
347
348void tst_QQmlDebugJS::setBreakpointInScriptOnComment()
349{
350 //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1)
351 QFETCH(bool, qmlscene);
352
353 int sourceLine = 34;
354 int actualLine = 36;
355 QCOMPARE(init(qmlscene, BREAKPOINTRELOCATION_QMLFILE), ConnectSuccess);
356
357 m_client->setBreakpoint(target: QLatin1String(BREAKPOINTRELOCATION_QMLFILE), line: sourceLine, column: -1, enabled: true);
358 m_client->connect();
359 QEXPECT_FAIL("", "Relocation of breakpoints is disabled right now", Abort);
360 QVERIFY(waitForClientSignal(SIGNAL(stopped()), 1));
361
362 const QJsonObject body = m_client->response().body.toObject();
363
364 QCOMPARE(body.value("sourceLine").toInt(), actualLine);
365 QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(),
366 QLatin1String(BREAKPOINTRELOCATION_QMLFILE));
367}
368
369void tst_QQmlDebugJS::setBreakpointInScriptOnEmptyLine()
370{
371 //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1)
372 QFETCH(bool, qmlscene);
373
374 int sourceLine = 35;
375 int actualLine = 36;
376 QCOMPARE(init(qmlscene, BREAKPOINTRELOCATION_QMLFILE), ConnectSuccess);
377
378 m_client->setBreakpoint(target: QLatin1String(BREAKPOINTRELOCATION_QMLFILE), line: sourceLine, column: -1, enabled: true);
379 m_client->connect();
380 QEXPECT_FAIL("", "Relocation of breakpoints is disabled right now", Abort);
381 QVERIFY(waitForClientSignal(SIGNAL(stopped()), 1));
382
383 const QJsonObject body = m_client->response().body.toObject();
384
385 QCOMPARE(body.value("sourceLine").toInt(), actualLine);
386 QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(),
387 QLatin1String(BREAKPOINTRELOCATION_QMLFILE));
388}
389
390void tst_QQmlDebugJS::setBreakpointInScriptOnOptimizedBinding()
391{
392 //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1)
393 QFETCH(bool, qmlscene);
394
395 int sourceLine = 39;
396 QCOMPARE(init(qmlscene, BREAKPOINTRELOCATION_QMLFILE), ConnectSuccess);
397
398 m_client->setBreakpoint(target: QLatin1String(BREAKPOINTRELOCATION_QMLFILE), line: sourceLine, column: -1, enabled: true);
399 m_client->connect();
400 QVERIFY(waitForClientSignal(SIGNAL(stopped())));
401
402 const QJsonObject body = m_client->response().body.toObject();
403
404 QCOMPARE(body.value("sourceLine").toInt(), sourceLine);
405 QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(),
406 QLatin1String(BREAKPOINTRELOCATION_QMLFILE));
407}
408
409void tst_QQmlDebugJS::setBreakpointInScriptWithCondition()
410{
411 QFETCH(bool, qmlscene);
412
413 int out = 10;
414 int sourceLine = 37;
415 QCOMPARE(init(qmlscene, CONDITION_QMLFILE), ConnectSuccess);
416
417 m_client->connect();
418 //The breakpoint is in a timer loop so we can set it after connect().
419 m_client->setBreakpoint(target: QLatin1String(CONDITION_QMLFILE), line: sourceLine, column: 1, enabled: true, condition: QLatin1String("a > 10"));
420 QVERIFY(waitForClientSignal(SIGNAL(stopped())));
421
422 //Get the frame index
423 {
424 const QJsonObject body = m_client->response().body.toObject();
425 int frameIndex = body.value(key: "index").toInt();
426
427 //Verify the value of 'result'
428 m_client->evaluate(expr: QLatin1String("a"),frame: frameIndex);
429 QVERIFY(waitForClientSignal(SIGNAL(result())));
430 }
431
432 const QJsonObject body = m_client->response().body.toObject();
433 QVERIFY(!body.isEmpty());
434 QJsonValue val = body.value(key: "value");
435 QVERIFY(val.isDouble());
436
437 const int a = val.toInt();
438 QVERIFY(a > out);
439}
440
441void tst_QQmlDebugJS::setBreakpointInScriptThatQuits()
442{
443 QFETCH(bool, qmlscene);
444
445 QCOMPARE(init(qmlscene, QUIT_QMLFILE), ConnectSuccess);
446
447 int sourceLine = 36;
448
449 m_client->setBreakpoint(target: QLatin1String(QUIT_QMLFILE), line: sourceLine, column: -1, enabled: true);
450 m_client->connect();
451 QVERIFY(waitForClientSignal(SIGNAL(stopped())));
452
453 const QJsonObject body = m_client->response().body.toObject();
454
455 QCOMPARE(body.value("sourceLine").toInt(), sourceLine);
456 QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), QLatin1String(QUIT_QMLFILE));
457
458 m_client->continueDebugging(stepAction: QV4DebugClient::Continue);
459 QVERIFY(m_process->waitForFinished());
460 QCOMPARE(m_process->exitStatus(), QProcess::NormalExit);
461}
462
463void tst_QQmlDebugJS::setBreakpointInJavaScript_data()
464{
465 QTest::addColumn<bool>(name: "qmlscene");
466 QTest::addColumn<bool>(name: "seedCache");
467 QTest::newRow(dataTag: "custom / immediate") << false << false;
468 QTest::newRow(dataTag: "qmlscene / immediate") << true << false;
469 QTest::newRow(dataTag: "custom / seeded") << false << true;
470 QTest::newRow(dataTag: "qmlscene / seeded") << true << true;
471}
472
473void tst_QQmlDebugJS::setBreakpointInJavaScript()
474{
475 QFETCH(bool, qmlscene);
476 QFETCH(bool, seedCache);
477
478 if (seedCache) { // Make sure there is a qmlc file that the engine should _not_ laod.
479 QProcess process;
480 process.start(program: QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene",
481 arguments: { testFile(fileName: QUITINJS_QMLFILE) });
482 QTRY_COMPARE(process.state(), QProcess::NotRunning);
483 }
484
485 QCOMPARE(init(qmlscene, QUITINJS_QMLFILE), ConnectSuccess);
486
487 const int sourceLine = 2;
488
489 m_client->setBreakpoint(target: QLatin1String(QUIT_JSFILE), line: sourceLine, column: -1, enabled: true);
490 m_client->connect();
491 QVERIFY(waitForClientSignal(SIGNAL(stopped())));
492
493 const QJsonObject body = m_client->response().body.toObject();
494
495 QCOMPARE(body.value("sourceLine").toInt(), sourceLine);
496 QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(),
497 QLatin1String(QUIT_JSFILE));
498
499 m_client->continueDebugging(stepAction: QV4DebugClient::Continue);
500
501 QVERIFY(m_process->waitForFinished());
502 QCOMPARE(m_process->exitStatus(), QProcess::NormalExit);
503}
504
505void tst_QQmlDebugJS::setBreakpointWhenAttaching()
506{
507 int sourceLine = 35;
508 QCOMPARE(init(true, QLatin1String(TIMER_QMLFILE), false), ConnectSuccess);
509
510 m_client->connect();
511
512 QSKIP("\nThe breakpoint may not hit because the engine may run in JIT mode or not have debug\n"
513 "instructions, as we've connected in non-blocking mode above. That means we may have\n"
514 "connected after the engine was already running, with all the QML already compiled.");
515
516 //The breakpoint is in a timer loop so we can set it after connect().
517 m_client->setBreakpoint(target: QLatin1String(TIMER_QMLFILE), line: sourceLine);
518
519 QVERIFY(waitForClientSignal(SIGNAL(stopped())));
520}
521
522void tst_QQmlDebugJS::clearBreakpoint()
523{
524 //void clearBreakpoint(int breakpoint);
525 QFETCH(bool, qmlscene);
526
527 int sourceLine1 = 37;
528 int sourceLine2 = 38;
529 QCOMPARE(init(qmlscene, CHANGEBREAKPOINT_QMLFILE), ConnectSuccess);
530
531 m_client->connect();
532 //The breakpoints are in a timer loop so we can set them after connect().
533 //Furthermore the breakpoints should be hit in the right order because setting of breakpoints
534 //can only occur in the QML event loop. (see QCOMPARE for sourceLine2 below)
535 m_client->setBreakpoint(target: QLatin1String(CHANGEBREAKPOINT_QMLFILE), line: sourceLine1, column: -1, enabled: true);
536 m_client->setBreakpoint(target: QLatin1String(CHANGEBREAKPOINT_QMLFILE), line: sourceLine2, column: -1, enabled: true);
537
538 QVERIFY(waitForClientSignal(SIGNAL(stopped())));
539
540 {
541 //Will hit 1st brakpoint, change this breakpoint enable = false
542 const QJsonObject body = m_client->response().body.toObject();
543 const QJsonArray breakpointsHit = body.value(key: "breakpoints").toArray();
544
545 int breakpoint = breakpointsHit.at(i: 0).toInt();
546 m_client->clearBreakpoint(breakpoint);
547
548 QVERIFY(waitForClientSignal(SIGNAL(result())));
549
550 //Continue with debugging
551 m_client->continueDebugging(stepAction: QV4DebugClient::Continue);
552 //Hit 2nd breakpoint
553 QVERIFY(waitForClientSignal(SIGNAL(stopped())));
554
555 //Continue with debugging
556 m_client->continueDebugging(stepAction: QV4DebugClient::Continue);
557 }
558
559 //Should stop at 2nd breakpoint
560 QVERIFY(waitForClientSignal(SIGNAL(stopped())));
561
562 {
563 const QJsonObject body = m_client->response().body.toObject();
564 QCOMPARE(body.value("sourceLine").toInt(), sourceLine2);
565 }
566}
567
568void tst_QQmlDebugJS::changeBreakpoint()
569{
570 //void clearBreakpoint(int breakpoint);
571 QFETCH(bool, qmlscene);
572
573 int sourceLine2 = 37;
574 int sourceLine1 = 38;
575 const QString file = QLatin1String(CHANGEBREAKPOINT_QMLFILE);
576 QCOMPARE(init(qmlscene, file), ConnectSuccess);
577
578 bool isStopped = false;
579 QObject::connect(sender: m_client.data(), signal: &QV4DebugClient::stopped, context: this, slot: [&]() { isStopped = true; });
580
581 auto continueDebugging = [&]() {
582 m_client->continueDebugging(stepAction: QV4DebugClient::Continue);
583 isStopped = false;
584 };
585
586 m_client->connect();
587
588 auto extractBody = [&]() {
589 return m_client->response().body.toObject();
590 };
591
592 auto extractBreakPointId = [&](const QJsonObject &body) {
593 const QJsonArray breakpointsHit = body.value(key: "breakpoints").toArray();
594 if (breakpointsHit.size() != 1)
595 return -1;
596 return breakpointsHit[0].toInt();
597 };
598
599 //The breakpoints are in a timer loop so we can set them after connect().
600 //Furthermore the breakpoints should be hit in the right order because setting of breakpoints
601 //can only occur in the QML event loop. (see QCOMPARE for sourceLine2 below)
602 const int breakpoint1 = setBreakPoint(file, sourceLine: sourceLine1, enabled: false);
603 QVERIFY(breakpoint1 >= 0);
604
605 const int breakpoint2 = setBreakPoint(file, sourceLine: sourceLine2, enabled: true);
606 QVERIFY(breakpoint2 >= 0);
607
608 auto verifyBreakpoint = [&](int sourceLine, int breakpointId) {
609 QTRY_VERIFY_WITH_TIMEOUT(isStopped, 30000);
610 const QJsonObject body = extractBody();
611 QCOMPARE(body.value("sourceLine").toInt(), sourceLine);
612 QCOMPARE(extractBreakPointId(body), breakpointId);
613 };
614
615 verifyBreakpoint(sourceLine2, breakpoint2);
616
617 continueDebugging();
618 verifyBreakpoint(sourceLine2, breakpoint2);
619
620 m_client->changeBreakpoint(breakpoint: breakpoint2, enabled: false);
621 QVERIFY(waitForClientSignal(SIGNAL(result())));
622
623 m_client->changeBreakpoint(breakpoint: breakpoint1, enabled: true);
624 QVERIFY(waitForClientSignal(SIGNAL(result())));
625
626 continueDebugging();
627 verifyBreakpoint(sourceLine1, breakpoint1);
628
629 continueDebugging();
630 verifyBreakpoint(sourceLine1, breakpoint1);
631
632 m_client->changeBreakpoint(breakpoint: breakpoint2, enabled: true);
633 QVERIFY(waitForClientSignal(SIGNAL(result())));
634
635 m_client->changeBreakpoint(breakpoint: breakpoint1, enabled: false);
636 QVERIFY(waitForClientSignal(SIGNAL(result())));
637
638 for (int i = 0; i < 3; ++i) {
639 continueDebugging();
640 verifyBreakpoint(sourceLine2, breakpoint2);
641 }
642}
643
644void tst_QQmlDebugJS::setExceptionBreak()
645{
646 //void setExceptionBreak(QString type, bool enabled = false);
647 QFETCH(bool, qmlscene);
648
649 QCOMPARE(init(qmlscene, EXCEPTION_QMLFILE), ConnectSuccess);
650 m_client->setExceptionBreak(type: QV4DebugClient::All,enabled: true);
651 m_client->connect();
652 QVERIFY(waitForClientSignal(SIGNAL(stopped())));
653}
654
655void tst_QQmlDebugJS::stepNext()
656{
657 //void continueDebugging(StepAction stepAction, int stepCount = 1);
658 QFETCH(bool, qmlscene);
659
660 int sourceLine = 37;
661 QCOMPARE(init(qmlscene, STEPACTION_QMLFILE), ConnectSuccess);
662
663 m_client->setBreakpoint(target: QLatin1String(STEPACTION_QMLFILE), line: sourceLine, column: -1, enabled: true);
664 m_client->connect();
665 QVERIFY(waitForClientSignal(SIGNAL(stopped())));
666
667 m_client->continueDebugging(stepAction: QV4DebugClient::Next);
668 QVERIFY(waitForClientSignal(SIGNAL(stopped())));
669
670 const QJsonObject body = m_client->response().body.toObject();
671
672 QCOMPARE(body.value("sourceLine").toInt(), sourceLine + 1);
673 QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(),
674 QLatin1String(STEPACTION_QMLFILE));
675}
676
677static QJsonObject responseBody(QV4DebugClient *client)
678{
679 return client->response().body.toObject();
680}
681
682void tst_QQmlDebugJS::stepIn()
683{
684 //void continueDebugging(StepAction stepAction, int stepCount = 1);
685 QFETCH(bool, qmlscene);
686
687 int sourceLine = 41;
688 int actualLine = 36;
689 QCOMPARE(init(qmlscene, STEPACTION_QMLFILE), ConnectSuccess);
690
691 m_client->setBreakpoint(target: QLatin1String(STEPACTION_QMLFILE), line: sourceLine, column: 1, enabled: true);
692 m_client->connect();
693 QVERIFY(waitForClientSignal(SIGNAL(stopped())));
694 QCOMPARE(responseBody(m_client).value("sourceLine").toInt(), sourceLine);
695
696 m_client->continueDebugging(stepAction: QV4DebugClient::In);
697 QVERIFY(waitForClientSignal(SIGNAL(stopped())));
698
699 const QJsonObject body = responseBody(client: m_client);
700 QCOMPARE(body.value("sourceLine").toInt(), actualLine);
701 QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), QLatin1String(STEPACTION_QMLFILE));
702}
703
704void tst_QQmlDebugJS::stepOut()
705{
706 //void continueDebugging(StepAction stepAction, int stepCount = 1);
707 QFETCH(bool, qmlscene);
708
709 int sourceLine = 37;
710 int actualLine = 41;
711 QCOMPARE(init(qmlscene, STEPACTION_QMLFILE), ConnectSuccess);
712
713 m_client->setBreakpoint(target: QLatin1String(STEPACTION_QMLFILE), line: sourceLine, column: -1, enabled: true);
714 m_client->connect();
715 QVERIFY(waitForClientSignal(SIGNAL(stopped())));
716 QCOMPARE(responseBody(m_client).value("sourceLine").toInt(), sourceLine);
717
718 m_client->continueDebugging(stepAction: QV4DebugClient::Out);
719 QVERIFY(waitForClientSignal(SIGNAL(stopped())));
720
721 const QJsonObject body = responseBody(client: m_client);
722 QCOMPARE(body.value("sourceLine").toInt(), actualLine);
723 QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(), QLatin1String(STEPACTION_QMLFILE));
724}
725
726void tst_QQmlDebugJS::continueDebugging()
727{
728 //void continueDebugging(StepAction stepAction, int stepCount = 1);
729 QFETCH(bool, qmlscene);
730
731 int sourceLine1 = 41;
732 int sourceLine2 = 38;
733 QCOMPARE(init(qmlscene, STEPACTION_QMLFILE), ConnectSuccess);
734
735 m_client->setBreakpoint(target: QLatin1String(STEPACTION_QMLFILE), line: sourceLine1, column: -1, enabled: true);
736 m_client->setBreakpoint(target: QLatin1String(STEPACTION_QMLFILE), line: sourceLine2, column: -1, enabled: true);
737 m_client->connect();
738 QVERIFY(waitForClientSignal(SIGNAL(stopped())));
739
740 m_client->continueDebugging(stepAction: QV4DebugClient::Continue);
741 QVERIFY(waitForClientSignal(SIGNAL(stopped())));
742
743 const QJsonObject body = responseBody(client: m_client);
744
745 QCOMPARE(body.value("sourceLine").toInt(), sourceLine2);
746 QCOMPARE(QFileInfo(body.value("script").toObject().value("name").toString()).fileName(),
747 QLatin1String(STEPACTION_QMLFILE));
748}
749
750void tst_QQmlDebugJS::backtrace()
751{
752 //void backtrace(int fromFrame = -1, int toFrame = -1, bool bottom = false);
753 QFETCH(bool, qmlscene);
754
755 int sourceLine = 34;
756 QCOMPARE(init(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess);
757
758 m_client->setBreakpoint(target: QLatin1String(ONCOMPLETED_QMLFILE), line: sourceLine, column: -1, enabled: true);
759 m_client->connect();
760 QVERIFY(waitForClientSignal(SIGNAL(stopped())));
761
762 m_client->backtrace();
763 QVERIFY(waitForClientSignal(SIGNAL(result())));
764}
765
766void tst_QQmlDebugJS::getFrameDetails()
767{
768 //void frame(int number = -1);
769 QFETCH(bool, qmlscene);
770
771 int sourceLine = 34;
772 QCOMPARE(init(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess);
773
774 m_client->setBreakpoint(target: QLatin1String(ONCOMPLETED_QMLFILE), line: sourceLine, column: -1, enabled: true);
775 m_client->connect();
776 QVERIFY(waitForClientSignal(SIGNAL(stopped())));
777
778 m_client->frame();
779 QVERIFY(waitForClientSignal(SIGNAL(result())));
780}
781
782void tst_QQmlDebugJS::getScopeDetails()
783{
784 //void scope(int number = -1, int frameNumber = -1);
785 QFETCH(bool, qmlscene);
786
787 int sourceLine = 34;
788 QCOMPARE(init(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess);
789
790 m_client->setBreakpoint(target: QLatin1String(ONCOMPLETED_QMLFILE), line: sourceLine, column: -1, enabled: true);
791 m_client->connect();
792 QVERIFY(waitForClientSignal(SIGNAL(stopped())));
793
794 m_client->scope();
795 QVERIFY(waitForClientSignal(SIGNAL(result())));
796}
797
798void tst_QQmlDebugJS::evaluateInGlobalScope()
799{
800 //void evaluate(QString expr, int frame = -1);
801 QCOMPARE(init(true), ConnectSuccess);
802
803 m_client->connect();
804
805 for (int i = 0; i < 10; ++i) {
806 // The engine might not be initialized, yet. We just try until it shows up.
807 m_client->evaluate(expr: QLatin1String("console.log('Hello World')"));
808 if (waitForClientSignal(SIGNAL(result()), timeout: 500))
809 break;
810 }
811
812 //Verify the return value of 'console.log()', which is "undefined"
813 QCOMPARE(responseBody(m_client).value("type").toString(), QLatin1String("undefined"));
814}
815
816void tst_QQmlDebugJS::evaluateInLocalScope()
817{
818 //void evaluate(QString expr, bool global = false, bool disableBreak = false, int frame = -1, const QVariantMap &addContext = QVariantMap());
819 QFETCH(bool, qmlscene);
820
821 int sourceLine = 34;
822 QCOMPARE(init(qmlscene, ONCOMPLETED_QMLFILE), ConnectSuccess);
823
824 m_client->setBreakpoint(target: QLatin1String(ONCOMPLETED_QMLFILE), line: sourceLine, column: -1, enabled: true);
825 m_client->connect();
826 QVERIFY(waitForClientSignal(SIGNAL(stopped())));
827
828 m_client->frame();
829 QVERIFY(waitForClientSignal(SIGNAL(result())));
830
831 {
832 //Get the frame index
833 const QJsonObject body = responseBody(client: m_client);
834 int frameIndex = body.value(key: "index").toInt();
835 m_client->evaluate(expr: QLatin1String("root.a"), frame: frameIndex);
836 QVERIFY(waitForClientSignal(SIGNAL(result())));
837 }
838
839 {
840 //Verify the value of 'timer.interval'
841 const QJsonObject body = responseBody(client: m_client);
842 QCOMPARE(body.value("value").toInt(),10);
843 }
844}
845
846void tst_QQmlDebugJS::evaluateInContext()
847{
848 m_connection = new QQmlDebugConnection();
849 m_process = new QQmlDebugProcess(
850 QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene", this);
851 m_client = new QV4DebugClient(m_connection);
852 QScopedPointer<QQmlEngineDebugClient> engineClient(new QQmlEngineDebugClient(m_connection));
853 m_process->start(arguments: QStringList() << QLatin1String(BLOCKMODE) << testFile(fileName: ONCOMPLETED_QMLFILE));
854
855 QVERIFY(m_process->waitForSessionStart());
856
857 m_connection->connectToHost(hostName: "127.0.0.1", port: m_process->debugPort());
858 QVERIFY(m_connection->waitForConnected());
859
860 QTRY_COMPARE(m_client->state(), QQmlEngineDebugClient::Enabled);
861 QTRY_COMPARE(engineClient->state(), QQmlEngineDebugClient::Enabled);
862 m_client->connect();
863
864 // "a" not accessible without extra context
865 m_client->evaluate(expr: QLatin1String("a + 10"), frame: -1, context: -1);
866 QVERIFY(waitForClientSignal(SIGNAL(failure())));
867
868 bool success = false;
869 engineClient->queryAvailableEngines(success: &success);
870 QVERIFY(success);
871 QVERIFY(QQmlDebugTest::waitForSignal(engineClient.data(), SIGNAL(result())));
872
873 QVERIFY(engineClient->engines().count());
874 engineClient->queryRootContexts(engineClient->engines()[0], success: &success);
875 QVERIFY(success);
876 QVERIFY(QQmlDebugTest::waitForSignal(engineClient.data(), SIGNAL(result())));
877
878 auto contexts = engineClient->rootContext().contexts;
879 QCOMPARE(contexts.count(), 1);
880 auto objects = contexts[0].objects;
881 QCOMPARE(objects.count(), 1);
882 engineClient->queryObjectRecursive(objects[0], success: &success);
883 QVERIFY(success);
884 QVERIFY(QQmlDebugTest::waitForSignal(engineClient.data(), SIGNAL(result())));
885 auto object = engineClient->object();
886
887 // "a" accessible in context of surrounding object
888 m_client->evaluate(expr: QLatin1String("a + 10"), frame: -1, context: object.debugId);
889 QVERIFY(waitForClientSignal(SIGNAL(result())));
890
891 QTRY_COMPARE(responseBody(m_client).value("value").toInt(), 20);
892
893 auto childObjects = object.children;
894 QVERIFY(childObjects.count() > 0); // QQmlComponentAttached is also in there
895 QCOMPARE(childObjects[0].className, QString::fromLatin1("Item"));
896
897 // "b" accessible in context of surrounding (child) object
898 m_client->evaluate(expr: QLatin1String("b"), frame: -1, context: childObjects[0].debugId);
899 QVERIFY(waitForClientSignal(SIGNAL(result())));
900
901 QTRY_COMPARE(responseBody(m_client).value("value").toInt(), 11);
902}
903
904void tst_QQmlDebugJS::getScripts()
905{
906 //void scripts(int types = -1, QList<int> ids = QList<int>(), bool includeSource = false, QVariant filter = QVariant());
907 QFETCH(bool, qmlscene);
908
909 QCOMPARE(init(qmlscene), ConnectSuccess);
910
911 m_client->setBreakpoint(target: QString(TEST_QMLFILE), line: 35, column: -1, enabled: true);
912 m_client->connect();
913 QVERIFY(waitForClientSignal(SIGNAL(stopped())));
914
915 m_client->scripts();
916 QVERIFY(waitForClientSignal(SIGNAL(result())));
917
918 const QJsonArray scripts = m_client->response().body.toArray();
919
920 QCOMPARE(scripts.count(), 1);
921 QVERIFY(scripts.first().toObject()[QStringLiteral("name")].toString()
922 .endsWith(QStringLiteral("data/test.qml")));
923}
924
925void tst_QQmlDebugJS::encodeQmlScope()
926{
927 QString file(ENCODEQMLSCOPE_QMLFILE);
928 QCOMPARE(init(true, file), ConnectSuccess);
929
930 int numFrames = 0;
931 int numExpectedScopes = 0;
932 int numReceivedScopes = 0;
933 bool isStopped = false;
934 bool scopesFailed = false;
935
936 QObject::connect(sender: m_client.data(), signal: &QV4DebugClient::failure, context: this, slot: [&]() {
937 qWarning() << "received failure" << m_client->response().body;
938 scopesFailed = true;
939 m_process->stop();
940 numFrames = 2;
941 isStopped = false;
942 });
943
944 QObject::connect(sender: m_client.data(), signal: &QV4DebugClient::stopped, context: this, slot: [&]() {
945 m_client->frame();
946 isStopped = true;
947 });
948
949 QObject::connect(sender: m_client.data(), signal: &QV4DebugClient::result, context: this, slot: [&]() {
950 const QV4DebugClient::Response value = m_client->response();
951
952 if (value.command == QString("scope")) {
953 // If the scope commands fail we get a failure() signal above.
954 if (++numReceivedScopes == numExpectedScopes) {
955 m_client->continueDebugging(stepAction: QV4DebugClient::Continue);
956 isStopped = false;
957 }
958 } else if (value.command == QString("frame")) {
959
960 // We want at least a global scope and some kind of local scope here.
961 const QJsonArray scopes = value.body.toObject().value(key: "scopes").toArray();
962 if (scopes.count() < 2)
963 scopesFailed = true;
964
965 for (const QJsonValue &scope : scopes) {
966 ++numExpectedScopes;
967 m_client->scope(number: scope.toObject().value(key: "index").toInt());
968 }
969
970 ++numFrames;
971 }
972 });
973
974 m_client->setBreakpoint(target: file, line: 6);
975 m_client->setBreakpoint(target: file, line: 8);
976 m_client->connect();
977
978 QTRY_COMPARE(numFrames, 2);
979 QVERIFY(numExpectedScopes > 3);
980 QVERIFY(!scopesFailed);
981 QTRY_VERIFY(!isStopped);
982 QCOMPARE(numReceivedScopes, numExpectedScopes);
983}
984
985void tst_QQmlDebugJS::breakOnAnchor()
986{
987 QString file(BREAKONANCHOR_QMLFILE);
988 QCOMPARE(init(true, file), ConnectSuccess);
989
990 int breaks = 0;
991 bool stopped = false;
992 QObject::connect(sender: m_client.data(), signal: &QV4DebugClient::stopped, context: this, slot: [&]() {
993 stopped = true;
994 ++breaks;
995 m_client->evaluate(expr: "this", frame: 0, context: -1);
996 });
997
998 QObject::connect(sender: m_client.data(), signal: &QV4DebugClient::result, context: this, slot: [&]() {
999 if (stopped) {
1000 m_client->continueDebugging(stepAction: QV4DebugClient::Continue);
1001 stopped = false;
1002 }
1003 });
1004
1005 QObject::connect(sender: m_client.data(), signal: &QV4DebugClient::failure, context: this, slot: [&]() {
1006 qWarning() << "received failure" << m_client->response().body;
1007 });
1008
1009 m_client->setBreakpoint(target: file, line: 34);
1010 m_client->setBreakpoint(target: file, line: 37);
1011
1012 QTRY_COMPARE(m_process->state(), QProcess::Running);
1013
1014 m_client->connect();
1015
1016 QTRY_COMPARE(m_process->state(), QProcess::NotRunning);
1017 QCOMPARE(m_process->exitStatus(), QProcess::NormalExit);
1018
1019 QCOMPARE(breaks, 2);
1020}
1021
1022void tst_QQmlDebugJS::breakPointIds()
1023{
1024 QString file(BREAKPOINTIDS_QMLFILE);
1025 QCOMPARE(init(true, file), ConnectSuccess);
1026
1027 int breaks = 0;
1028 int breakPointIds[] = { -1, -1, -1, -1, -1, -1};
1029
1030 QObject::connect(sender: m_client.data(), signal: &QV4DebugClient::stopped, context: this, slot: [&]() {
1031 const QJsonObject body = m_client->response().body.toObject();
1032 QCOMPARE(body.value("sourceLine").toInt(), breaks + 4);
1033 const QJsonArray breakpointsHit = body.value(key: "breakpoints").toArray();
1034 QVERIFY(breakpointsHit.size() > 0);
1035 QCOMPARE(breakpointsHit[0].toInt(), breakPointIds[breaks]);
1036 ++breaks;
1037 m_client->continueDebugging(stepAction: QV4DebugClient::Continue);
1038 });
1039
1040 for (int i = 0; i < 6; ++i)
1041 breakPointIds[i] = setBreakPoint(file, sourceLine: i + 4, enabled: true);
1042
1043 clearBreakPoint(id: breakPointIds[2]);
1044 breakPointIds[2] = setBreakPoint(file, sourceLine: 6, enabled: true);
1045
1046 QTRY_COMPARE(m_process->state(), QProcess::Running);
1047 m_client->connect();
1048
1049 QTRY_COMPARE(m_process->state(), QProcess::NotRunning);
1050 QCOMPARE(m_process->exitStatus(), QProcess::NormalExit);
1051
1052 QCOMPARE(breaks, 6);
1053}
1054
1055void tst_QQmlDebugJS::letConstLocals()
1056{
1057 QString file(LETCONSTLOCALS_QMLFILE);
1058 QCOMPARE(init(true, file), ConnectSuccess);
1059
1060 QObject::connect(sender: m_client.data(), signal: &QV4DebugClient::stopped, context: this, slot: [&]() {
1061 m_client->frame();
1062 });
1063
1064 int numScopes = 0;
1065 QString expectedMembers = QStringLiteral("abcde");
1066 QObject::connect(sender: m_client.data(), signal: &QV4DebugClient::result, context: this, slot: [&]() {
1067 const auto value = m_client->response();
1068 if (value.command == QStringLiteral("frame")) {
1069 const auto scopes = value.body.toObject().value(QStringLiteral("scopes")).toArray();
1070 for (const auto &scope : scopes) {
1071 const auto scopeObject = scope.toObject();
1072 const int type = scopeObject.value(key: "type").toInt();
1073 if (type == 1 || type == 4) {
1074 m_client->scope(number: scopeObject.value(key: "index").toInt());
1075 ++numScopes;
1076 }
1077 }
1078 QVERIFY(numScopes > 0);
1079 } else if (value.command == QStringLiteral("scope")) {
1080 const auto props = value.body.toObject().value(QStringLiteral("object")).toObject()
1081 .value(QStringLiteral("properties")).toArray();
1082 for (const auto &prop : props) {
1083 const auto propObj = prop.toObject();
1084 const QString name = propObj.value(QStringLiteral("name")).toString();
1085 if (name == QStringLiteral("onCompleted"))
1086 continue;
1087 QVERIFY(name.length() == 1);
1088 auto i = expectedMembers.indexOf(c: name.at(i: 0));
1089 QVERIFY(i != -1);
1090 expectedMembers.remove(i, len: 1);
1091 QCOMPARE(propObj.value(QStringLiteral("type")).toString(),
1092 QStringLiteral("number"));
1093 QCOMPARE(propObj.value(QStringLiteral("value")).toInt(),
1094 int(name.at(0).toLatin1()));
1095 }
1096 if (--numScopes == 0) {
1097 QVERIFY(expectedMembers.isEmpty());
1098 m_client->continueDebugging(stepAction: QV4DebugClient::Continue);
1099 }
1100 }
1101 });
1102
1103 setBreakPoint(file, sourceLine: 10, enabled: true);
1104
1105 QTRY_COMPARE(m_process->state(), QProcess::Running);
1106 m_client->connect();
1107
1108 QTRY_COMPARE(m_process->state(), QProcess::NotRunning);
1109 QCOMPARE(m_process->exitStatus(), QProcess::NormalExit);
1110}
1111
1112QList<QQmlDebugClient *> tst_QQmlDebugJS::createClients()
1113{
1114 m_client = new QV4DebugClient(m_connection);
1115 return QList<QQmlDebugClient *>({m_client});
1116}
1117
1118void tst_QQmlDebugJS::targetData()
1119{
1120 QTest::addColumn<bool>(name: "qmlscene");
1121 QTest::newRow(dataTag: "custom") << false;
1122 QTest::newRow(dataTag: "qmlscene") << true;
1123}
1124
1125bool tst_QQmlDebugJS::waitForClientSignal(const char *signal, int timeout)
1126{
1127 return QQmlDebugTest::waitForSignal(receiver: m_client.data(), member: signal, timeout);
1128}
1129
1130void tst_QQmlDebugJS::checkVersionParameters()
1131{
1132 const QV4DebugClient::Response value = m_client->response();
1133 QCOMPARE(value.command, QString("version"));
1134 const QJsonObject body = value.body.toObject();
1135 QCOMPARE(body.value("UnpausedEvaluate").toBool(), true);
1136 QCOMPARE(body.value("ContextEvaluate").toBool(), true);
1137 QCOMPARE(body.value("ChangeBreakpoint").toBool(), true);
1138}
1139
1140int tst_QQmlDebugJS::setBreakPoint(const QString &file, int sourceLine, bool enabled)
1141{
1142 int id = -1;
1143 auto connection = QObject::connect(sender: m_client.data(), signal: &QV4DebugClient::result, slot: [&]() {
1144 id = m_client->response().body.toObject().value(key: "breakpoint").toInt();
1145 });
1146
1147 m_client->setBreakpoint(target: file, line: sourceLine, column: -1, enabled);
1148 bool success = QTest::qWaitFor(predicate: [&]() { return id >= 0; });
1149 Q_UNUSED(success);
1150
1151 QObject::disconnect(connection);
1152 return id;
1153}
1154
1155void tst_QQmlDebugJS::clearBreakPoint(int id)
1156{
1157 bool ok = false;
1158 auto connection = QObject::connect(sender: m_client.data(), signal: &QV4DebugClient::result, slot: [&]() {
1159 ok = true;
1160 });
1161
1162 m_client->clearBreakpoint(breakpoint: id);
1163 bool success = QTest::qWaitFor(predicate: [&]() { return ok; });
1164 Q_UNUSED(success);
1165
1166 QObject::disconnect(connection);
1167}
1168
1169QTEST_MAIN(tst_QQmlDebugJS)
1170
1171#include "tst_qqmldebugjs.moc"
1172
1173

source code of qtdeclarative/tests/auto/qml/debugger/qqmldebugjs/tst_qqmldebugjs.cpp