1/****************************************************************************
2**
3** Copyright (C) 2018 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
30#include <QtTest/QtTest>
31
32#include <qscriptengine.h>
33#include <qscriptenginedebugger.h>
34#include <qaction.h>
35#include <qlineedit.h>
36#include <qmainwindow.h>
37#include <qmenu.h>
38#include <qplaintextedit.h>
39#include <qtoolbar.h>
40
41// Can't use QTest::qWait() because it causes event loop to hang on some platforms
42static void qsWait(int ms)
43{
44 QTimer timer;
45 timer.setSingleShot(true);
46 timer.setInterval(ms);
47 timer.start();
48 QEventLoop loop;
49 QObject::connect(sender: &timer, SIGNAL(timeout()), receiver: &loop, SLOT(quit()));
50 loop.exec();
51 QCoreApplication::processEvents();
52}
53
54class tst_QScriptEngineDebugger : public QObject
55{
56 Q_OBJECT
57
58public:
59 tst_QScriptEngineDebugger();
60 virtual ~tst_QScriptEngineDebugger();
61
62private slots:
63 void attachAndDetach();
64 void action();
65 void widget();
66 void standardObjects();
67 void debuggerSignals();
68 void consoleCommands();
69 void multithreadedDebugging();
70 void autoShowStandardWindow();
71 void standardWindowOwnership();
72 void engineDeleted();
73};
74
75tst_QScriptEngineDebugger::tst_QScriptEngineDebugger()
76{
77}
78
79tst_QScriptEngineDebugger::~tst_QScriptEngineDebugger()
80{
81}
82
83void tst_QScriptEngineDebugger::attachAndDetach()
84{
85#if defined(Q_OS_WINCE) && _WIN32_WCE < 0x600
86 QSKIP("skipped due to high mem usage until task 261062 is fixed");
87#endif
88 {
89 QScriptEngineDebugger debugger;
90 QCOMPARE(debugger.state(), QScriptEngineDebugger::SuspendedState);
91 debugger.attachTo(engine: 0);
92 QScriptEngine engine;
93 debugger.attachTo(engine: &engine);
94 QCOMPARE(debugger.state(), QScriptEngineDebugger::SuspendedState);
95 }
96 {
97 QScriptEngineDebugger debugger;
98 QScriptEngine engine;
99 QScriptValue oldPrint = engine.globalObject().property(name: "print");
100 QVERIFY(oldPrint.isFunction());
101 QVERIFY(!engine.globalObject().property("__FILE__").isValid());
102 QVERIFY(!engine.globalObject().property("__LINE__").isValid());
103
104 debugger.attachTo(engine: &engine);
105 QVERIFY(engine.globalObject().property("__FILE__").isUndefined());
106 QVERIFY(engine.globalObject().property("__LINE__").isNumber());
107 QVERIFY(!engine.globalObject().property("print").strictlyEquals(oldPrint));
108
109 debugger.detach();
110 QVERIFY(engine.globalObject().property("print").strictlyEquals(oldPrint));
111 QVERIFY(!engine.globalObject().property("__FILE__").isValid());
112 QVERIFY(!engine.globalObject().property("__LINE__").isValid());
113 }
114 {
115 QScriptEngineDebugger debugger;
116 QScriptEngine engine;
117 debugger.attachTo(engine: &engine);
118 debugger.detach();
119 QScriptEngine engine2;
120 debugger.attachTo(engine: &engine2);
121 }
122 {
123 QScriptEngineDebugger debugger;
124 QScriptEngine engine;
125 debugger.attachTo(engine: &engine);
126 debugger.detach();
127 QScriptEngine engine2;
128 debugger.attachTo(engine: &engine2);
129 debugger.detach();
130 }
131#ifndef Q_OS_WINCE // demands too much memory for WinCE
132 {
133 QScriptEngineDebugger debugger;
134 QScriptEngine engine;
135 debugger.attachTo(engine: &engine);
136 QScriptEngineDebugger debugger2;
137 debugger2.attachTo(engine: &engine);
138 }
139#endif
140 {
141 QScriptEngine *engine = new QScriptEngine;
142 QScriptEngineDebugger debugger;
143 debugger.attachTo(engine);
144 delete engine;
145 QScriptEngine engine2;
146 debugger.attachTo(engine: &engine2);
147 }
148}
149
150void tst_QScriptEngineDebugger::action()
151{
152#if defined(Q_OS_WINCE) && _WIN32_WCE < 0x600
153 QSKIP("skipped due to high mem usage until task 261062 is fixed");
154#endif
155
156 QScriptEngine engine;
157 QScriptEngineDebugger debugger;
158 debugger.attachTo(engine: &engine);
159 QList<QScriptEngineDebugger::DebuggerAction> actions;
160 actions
161 << QScriptEngineDebugger::InterruptAction
162 << QScriptEngineDebugger::ContinueAction
163 << QScriptEngineDebugger::StepIntoAction
164 << QScriptEngineDebugger::StepOverAction
165 << QScriptEngineDebugger::StepOutAction
166 << QScriptEngineDebugger::RunToCursorAction
167 << QScriptEngineDebugger::RunToNewScriptAction
168 << QScriptEngineDebugger::ToggleBreakpointAction
169 << QScriptEngineDebugger::ClearDebugOutputAction
170 << QScriptEngineDebugger::ClearErrorLogAction
171 << QScriptEngineDebugger::ClearConsoleAction
172 << QScriptEngineDebugger::FindInScriptAction
173 << QScriptEngineDebugger::FindNextInScriptAction
174 << QScriptEngineDebugger::FindPreviousInScriptAction
175 << QScriptEngineDebugger::GoToLineAction;
176 QList<QAction*> lst;
177 for (int i = 0; i < actions.size(); ++i) {
178 QScriptEngineDebugger::DebuggerAction da = actions.at(i);
179 QAction *act = debugger.action(action: da);
180 QVERIFY(act != 0);
181 QCOMPARE(act, debugger.action(da));
182 QCOMPARE(act->parent(), (QObject*)&debugger);
183 QVERIFY(lst.indexOf(act) == -1);
184 lst.append(t: act);
185 }
186}
187
188void tst_QScriptEngineDebugger::widget()
189{
190#if defined(Q_OS_WINCE) && _WIN32_WCE < 0x600
191 QSKIP("skipped due to high mem usage until task 261062 is fixed");
192#endif
193
194 QScriptEngine engine;
195 QScriptEngineDebugger debugger;
196 debugger.attachTo(engine: &engine);
197 QList<QScriptEngineDebugger::DebuggerWidget> widgets;
198 widgets
199 << QScriptEngineDebugger::ConsoleWidget
200 << QScriptEngineDebugger::StackWidget
201 << QScriptEngineDebugger::ScriptsWidget
202 << QScriptEngineDebugger::LocalsWidget
203 << QScriptEngineDebugger::CodeWidget
204 << QScriptEngineDebugger::CodeFinderWidget
205 << QScriptEngineDebugger::BreakpointsWidget
206 << QScriptEngineDebugger::DebugOutputWidget
207 << QScriptEngineDebugger::ErrorLogWidget;
208 QList<QWidget*> lst;
209 for (int i = 0; i < widgets.size(); ++i) {
210 QScriptEngineDebugger::DebuggerWidget dw = widgets.at(i);
211 QWidget *wid = debugger.widget(widget: dw);
212 QVERIFY(wid != 0);
213 QCOMPARE(wid, debugger.widget(dw));
214 QVERIFY(lst.indexOf(wid) == -1);
215 lst.append(t: wid);
216 QCOMPARE(static_cast<QWidget *>(wid->parent()), (QWidget*)0);
217 }
218}
219
220void tst_QScriptEngineDebugger::standardObjects()
221{
222#if defined(Q_OS_WINCE) && _WIN32_WCE < 0x600
223 QSKIP("skipped due to high mem usage until task 261062 is fixed");
224#endif
225
226 QScriptEngine engine;
227 QScriptEngineDebugger debugger;
228 debugger.attachTo(engine: &engine);
229
230 QMainWindow *win = debugger.standardWindow();
231 QCOMPARE(static_cast<QWidget *>(win->parent()), (QWidget*)0);
232
233 QMenu *menu = debugger.createStandardMenu();
234 QCOMPARE(static_cast<QWidget *>(menu->parent()), (QWidget*)0);
235#ifndef QT_NO_TOOLBAR
236 QToolBar *toolBar = debugger.createStandardToolBar();
237 QCOMPARE(static_cast<QWidget *>(toolBar->parent()), (QWidget*)0);
238#endif
239
240 QMenu *menu2 = debugger.createStandardMenu(parent: win);
241 QCOMPARE(static_cast<QWidget *>(menu2->parent()), (QWidget*)win);
242 QVERIFY(menu2 != menu);
243#ifndef QT_NO_TOOLBAR
244 QToolBar *toolBar2 = debugger.createStandardToolBar(parent: win);
245 QCOMPARE(static_cast<QWidget *>(toolBar2->parent()), (QWidget*)win);
246 QVERIFY(toolBar2 != toolBar);
247#endif
248
249 delete menu;
250#ifndef QT_NO_TOOLBAR
251 delete toolBar;
252#endif
253}
254
255void tst_QScriptEngineDebugger::debuggerSignals()
256{
257#if defined(Q_OS_WINCE) && _WIN32_WCE < 0x600
258 QSKIP("skipped due to high mem usage until task 261062 is fixed");
259#endif
260
261 QScriptEngine engine;
262 QScriptEngineDebugger debugger;
263 debugger.attachTo(engine: &engine);
264 debugger.setAutoShowStandardWindow(false);
265 QSignalSpy evaluationSuspendedSpy(&debugger, SIGNAL(evaluationSuspended()));
266 QSignalSpy evaluationResumedSpy(&debugger, SIGNAL(evaluationResumed()));
267 QObject::connect(sender: &debugger, SIGNAL(evaluationSuspended()),
268 receiver: debugger.action(action: QScriptEngineDebugger::ContinueAction),
269 SLOT(trigger()));
270 engine.evaluate(program: "123");
271 QCOMPARE(evaluationSuspendedSpy.count(), 0);
272 QCOMPARE(evaluationResumedSpy.count(), 0);
273 engine.evaluate(program: "debugger");
274 QCoreApplication::processEvents();
275 QCOMPARE(evaluationSuspendedSpy.count(), 1);
276 QCOMPARE(evaluationResumedSpy.count(), 1);
277}
278
279static void executeConsoleCommand(QLineEdit *inputEdit, QPlainTextEdit *outputEdit,
280 const QString &text)
281{
282 QString before = outputEdit->toPlainText();
283 inputEdit->setText(text);
284 QTest::keyPress(widget: inputEdit, key: Qt::Key_Enter);
285 const int delay = 100;
286 qsWait(ms: delay);
287 QString after = outputEdit->toPlainText();
288 int retryCount = 10;
289LAgain:
290 while ((before == after) && (retryCount != 0)) {
291 qsWait(ms: delay);
292 after = outputEdit->toPlainText();
293 --retryCount;
294 }
295 if (before != after) {
296 before = after;
297 qsWait(ms: delay);
298 after = outputEdit->toPlainText();
299 if (before != after) {
300 retryCount = 10;
301 goto LAgain;
302 }
303 }
304}
305
306class DebuggerCommandExecutor : public QObject
307{
308 Q_OBJECT
309public:
310 DebuggerCommandExecutor(QLineEdit *inputEdit,
311 QPlainTextEdit *outputEdit,
312 const QString &text,
313 QObject *parent = 0)
314 : QObject(parent), m_inputEdit(inputEdit),
315 m_outputEdit(outputEdit), m_commands(text) {}
316 DebuggerCommandExecutor(QLineEdit *inputEdit,
317 QPlainTextEdit *outputEdit,
318 const QStringList &commands,
319 QObject *parent = 0)
320 : QObject(parent), m_inputEdit(inputEdit),
321 m_outputEdit(outputEdit), m_commands(commands) {}
322public Q_SLOTS:
323 void execute() {
324 for (int i = 0; i < m_commands.size(); ++i)
325 executeConsoleCommand(inputEdit: m_inputEdit, outputEdit: m_outputEdit, text: m_commands.at(i));
326 }
327private:
328 QLineEdit *m_inputEdit;
329 QPlainTextEdit *m_outputEdit;
330 QStringList m_commands;
331};
332
333void tst_QScriptEngineDebugger::consoleCommands()
334{
335 QSKIP("This test can hang / misbehave because of timing/event loop issues (task 241300)");
336
337 QScriptEngine engine;
338 QScriptEngineDebugger debugger;
339 debugger.setAutoShowStandardWindow(false);
340 debugger.attachTo(engine: &engine);
341
342 QWidget *consoleWidget = debugger.widget(widget: QScriptEngineDebugger::ConsoleWidget);
343 QLineEdit *inputEdit = consoleWidget->findChild<QLineEdit*>();
344 QVERIFY(inputEdit != 0);
345 QPlainTextEdit *outputEdit = consoleWidget->findChild<QPlainTextEdit*>();
346 QVERIFY(outputEdit != 0);
347
348 QVERIFY(outputEdit->toPlainText().startsWith("Welcome to the Qt Script debugger."));
349 outputEdit->clear();
350
351 // print()
352 {
353 QWidget *debugOutputWidget = debugger.widget(widget: QScriptEngineDebugger::DebugOutputWidget);
354 QPlainTextEdit *debugOutputEdit = debugOutputWidget->findChild<QPlainTextEdit*>();
355 QVERIFY(debugOutputEdit != 0);
356
357 QVERIFY(debugOutputEdit->toPlainText().isEmpty());
358 executeConsoleCommand(inputEdit, outputEdit, text: "print('Test of debug output')");
359 QCOMPARE(debugOutputEdit->toPlainText(), QString::fromLatin1("Test of debug output"));
360
361 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> print('Test of debug output')"));
362 }
363
364 outputEdit->clear();
365 executeConsoleCommand(inputEdit, outputEdit, text: ".info scripts");
366 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .info scripts\nNo scripts loaded."));
367
368 outputEdit->clear();
369 executeConsoleCommand(inputEdit, outputEdit, text: ".break foo.qs:123");
370 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .break foo.qs:123\nBreakpoint 1: foo.qs, line 123."));
371
372 outputEdit->clear();
373 executeConsoleCommand(inputEdit, outputEdit, text: ".break 123");
374 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .break 123\nNo script."));
375
376 outputEdit->clear();
377 executeConsoleCommand(inputEdit, outputEdit, text: ".info breakpoints");
378 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .info breakpoints\nId\tEnabled\tWhere\n1\tyes\tfoo.qs:123"));
379
380 outputEdit->clear();
381 executeConsoleCommand(inputEdit, outputEdit, text: ".disable 1");
382 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .disable 1"));
383
384 outputEdit->clear();
385 executeConsoleCommand(inputEdit, outputEdit, text: ".info breakpoints");
386 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .info breakpoints\nId\tEnabled\tWhere\n1\tno\tfoo.qs:123"));
387
388 outputEdit->clear();
389 executeConsoleCommand(inputEdit, outputEdit, text: ".disable 1");
390 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .disable 1"));
391
392 outputEdit->clear();
393 executeConsoleCommand(inputEdit, outputEdit, text: ".disable 123");
394 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .disable 123\nNo breakpoint number 123."));
395
396 outputEdit->clear();
397 executeConsoleCommand(inputEdit, outputEdit, text: ".enable 1");
398 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .enable 1"));
399
400 outputEdit->clear();
401 executeConsoleCommand(inputEdit, outputEdit, text: ".info breakpoints");
402 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .info breakpoints\nId\tEnabled\tWhere\n1\tyes\tfoo.qs:123"));
403
404 outputEdit->clear();
405 executeConsoleCommand(inputEdit, outputEdit, text: ".enable 123");
406 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .enable 123\nNo breakpoint number 123."));
407
408 outputEdit->clear();
409 executeConsoleCommand(inputEdit, outputEdit, text: ".condition 1 i > 456");
410 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .condition 1 i > 456"));
411
412 outputEdit->clear();
413 executeConsoleCommand(inputEdit, outputEdit, text: ".info breakpoints");
414 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .info breakpoints\nId\tEnabled\tWhere\n1\tyes\tfoo.qs:123\n\tstop only if i > 456"));
415
416 outputEdit->clear();
417 executeConsoleCommand(inputEdit, outputEdit, text: ".condition 1");
418 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .condition 1\nBreakpoint 1 now unconditional."));
419
420 outputEdit->clear();
421 executeConsoleCommand(inputEdit, outputEdit, text: ".info breakpoints");
422 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .info breakpoints\nId\tEnabled\tWhere\n1\tyes\tfoo.qs:123"));
423
424 outputEdit->clear();
425 executeConsoleCommand(inputEdit, outputEdit, text: ".condition 123");
426 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .condition 123\nNo breakpoint number 123."));
427
428 outputEdit->clear();
429 executeConsoleCommand(inputEdit, outputEdit, text: ".ignore 1");
430 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .ignore 1\nMissing argument (ignore-count)."));
431
432 outputEdit->clear();
433 executeConsoleCommand(inputEdit, outputEdit, text: ".ignore 1 10");
434 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .ignore 1 10\nBreakpoint 1 will be ignored the next 10 time(s)."));
435
436 outputEdit->clear();
437 executeConsoleCommand(inputEdit, outputEdit, text: ".delete 1");
438 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .delete 1"));
439
440 outputEdit->clear();
441 executeConsoleCommand(inputEdit, outputEdit, text: ".info breakpoints");
442 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .info breakpoints\nNo breakpoints set."));
443
444 outputEdit->clear();
445 executeConsoleCommand(inputEdit, outputEdit, text: ".tbreak bar.qs:456");
446 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .tbreak bar.qs:456\nBreakpoint 2: bar.qs, line 456."));
447
448 {
449 QString script;
450 script.append(s: "function foo(i) {\n");
451 for (int i = 0; i < 100; ++i)
452 script.append(s: QString::fromLatin1(str: " i = i + %0;\n").arg(a: i));
453 script.append(s: " return i;\n}");
454 engine.evaluate(program: script, fileName: "foo.qs");
455 }
456 outputEdit->clear();
457 executeConsoleCommand(inputEdit, outputEdit, text: ".info scripts");
458 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .info scripts\n\tfoo.qs"));
459
460 outputEdit->clear();
461 executeConsoleCommand(inputEdit, outputEdit, text: ".list foo.qs");
462 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .list foo.qs\n"
463 "1\tfunction foo(i) {\n"
464 "2\t i = i + 0;\n"
465 "3\t i = i + 1;\n"
466 "4\t i = i + 2;\n"
467 "5\t i = i + 3;\n"
468 "6\t i = i + 4;\n"
469 "7\t i = i + 5;\n"
470 "8\t i = i + 6;\n"
471 "9\t i = i + 7;\n"
472 "10\t i = i + 8;"));
473 outputEdit->clear();
474 executeConsoleCommand(inputEdit, outputEdit, text: ".list");
475 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .list\n"
476 "No script."));
477
478 outputEdit->clear();
479 executeConsoleCommand(inputEdit, outputEdit, text: ".backtrace");
480 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .backtrace\n#0 <global>() at -1"));
481
482 outputEdit->clear();
483 executeConsoleCommand(inputEdit, outputEdit, text: ".down");
484 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .down\nAlready at bottom (innermost) frame."));
485
486 outputEdit->clear();
487 executeConsoleCommand(inputEdit, outputEdit, text: ".up");
488 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .up\nAlready at top (outermost) frame."));
489
490 outputEdit->clear();
491 executeConsoleCommand(inputEdit, outputEdit, text: ".frame");
492 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .frame\n#0 <global>() at -1"));
493
494 outputEdit->clear();
495 executeConsoleCommand(inputEdit, outputEdit, text: ".break foo.qs:789");
496 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .break foo.qs:789\nBreakpoint 3: foo.qs, line 789."));
497
498 outputEdit->clear();
499 executeConsoleCommand(inputEdit, outputEdit, text: ".clear foo.qs:789");
500 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .clear foo.qs:789\nDeleted breakpoint 3."));
501
502 outputEdit->clear();
503 executeConsoleCommand(inputEdit, outputEdit, text: ".info breakpoints");
504 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .info breakpoints\nId\tEnabled\tWhere\n2\tyes\tbar.qs:456"));
505
506 outputEdit->clear();
507 executeConsoleCommand(inputEdit, outputEdit, text: ".info locals");
508 QVERIFY(outputEdit->toPlainText().startsWith("qsdb> .info locals\n"
509 "NaN : NaN\n"
510 "Infinity : Infinity\n"
511 "undefined : undefined\n"
512 "print : function () { [native] }\n"
513 "parseInt : function () { [native] }\n"
514 "parseFloat : function () { [native] }\n"
515 "isNaN : function () { [native] }\n"
516 "isFinite : function () { [native] }\n"
517 "decodeURI : function () { [native] }\n"
518 "decodeURIComponent : function () { [native] }\n"
519 "encodeURI : function () { [native] }\n"
520 "encodeURIComponent : function () { [native] }\n"
521 "escape : function () { [native] }\n"
522 "unescape : function () { [native] }\n"
523 "version : function () { [native] }\n"
524 "gc : function () { [native] }\n"
525 "Object : function () { [native] }\n"
526 "Function : function () { [native] }\n"
527 "Number : function () { [native] }\n"
528 "Boolean : function () { [native] }"));
529
530 outputEdit->clear();
531 QVERIFY(!engine.globalObject().property("a").isValid());
532 executeConsoleCommand(inputEdit, outputEdit, text: ".eval a = 123");
533 QVERIFY(engine.globalObject().property("a").isNumber());
534 QCOMPARE(engine.globalObject().property("a").toInt32(), 123);
535
536 outputEdit->clear();
537 QVERIFY(!engine.globalObject().property("b").isValid());
538 executeConsoleCommand(inputEdit, outputEdit, text: "b = 456");
539 QVERIFY(engine.globalObject().property("b").isNumber());
540 QCOMPARE(engine.globalObject().property("b").toInt32(), 456);
541
542 outputEdit->clear();
543 executeConsoleCommand(inputEdit, outputEdit, text: ".break myscript.qs:1");
544 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .break myscript.qs:1\nBreakpoint 4: myscript.qs, line 1."));
545
546 {
547 DebuggerCommandExecutor executor(inputEdit, outputEdit, ".continue");
548 QObject::connect(sender: &debugger, SIGNAL(evaluationSuspended()), receiver: &executor, SLOT(execute()), Qt::QueuedConnection);
549 outputEdit->clear();
550 engine.evaluate(program: "void(123);", fileName: "myscript.qs");
551 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("Breakpoint 4 at myscript.qs, line 1.\n1\tvoid(123);\nqsdb> .continue"));
552 }
553
554 {
555 DebuggerCommandExecutor executor(inputEdit, outputEdit, ".step");
556 QObject::connect(sender: &debugger, SIGNAL(evaluationSuspended()), receiver: &executor, SLOT(execute()), Qt::QueuedConnection);
557 outputEdit->clear();
558 engine.evaluate(program: "void(123);\nvoid(456);", fileName: "myscript.qs");
559 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("Breakpoint 4 at myscript.qs, line 1.\n"
560 "1\tvoid(123);\n"
561 "qsdb> .step\n"
562 "2\tvoid(456);\n"
563 "qsdb> .step"));
564 }
565
566 {
567 DebuggerCommandExecutor executor(inputEdit, outputEdit, ".step 2");
568 QObject::connect(sender: &debugger, SIGNAL(evaluationSuspended()), receiver: &executor, SLOT(execute()), Qt::QueuedConnection);
569 outputEdit->clear();
570 engine.evaluate(program: "void(123);\nvoid(456);", fileName: "myscript.qs");
571 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("Breakpoint 4 at myscript.qs, line 1.\n1\tvoid(123);\nqsdb> .step 2"));
572 }
573
574 {
575 DebuggerCommandExecutor executor(inputEdit, outputEdit, ".next");
576 QObject::connect(sender: &debugger, SIGNAL(evaluationSuspended()), receiver: &executor, SLOT(execute()), Qt::QueuedConnection);
577 outputEdit->clear();
578 engine.evaluate(program: "void(123);\nvoid(456);", fileName: "myscript.qs");
579 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("Breakpoint 4 at myscript.qs, line 1.\n"
580 "1\tvoid(123);\n"
581 "qsdb> .next\n"
582 "2\tvoid(456);\n"
583 "qsdb> .next"));
584 }
585
586 {
587 DebuggerCommandExecutor executor(inputEdit, outputEdit, ".next 2");
588 QObject::connect(sender: &debugger, SIGNAL(evaluationSuspended()), receiver: &executor, SLOT(execute()), Qt::QueuedConnection);
589 outputEdit->clear();
590 engine.evaluate(program: "void(123);\nvoid(456);", fileName: "myscript.qs");
591 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("Breakpoint 4 at myscript.qs, line 1.\n"
592 "1\tvoid(123);\n"
593 "qsdb> .next 2"));
594 }
595
596 {
597 DebuggerCommandExecutor executor(inputEdit, outputEdit, ".finish");
598 QObject::connect(sender: &debugger, SIGNAL(evaluationSuspended()), receiver: &executor, SLOT(execute()), Qt::QueuedConnection);
599 outputEdit->clear();
600 engine.evaluate(program: "void(123);\nvoid(456);", fileName: "myscript.qs");
601 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("Breakpoint 4 at myscript.qs, line 1.\n1\tvoid(123);\nqsdb> .finish"));
602 }
603
604 {
605 DebuggerCommandExecutor executor(inputEdit, outputEdit, ".return");
606 QObject::connect(sender: &debugger, SIGNAL(evaluationSuspended()), receiver: &executor, SLOT(execute()), Qt::QueuedConnection);
607 outputEdit->clear();
608 engine.evaluate(program: "void(123);\nvoid(456);\n789;", fileName: "myscript.qs");
609 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("Breakpoint 4 at myscript.qs, line 1.\n1\tvoid(123);\nqsdb> .return"));
610 }
611
612 {
613 DebuggerCommandExecutor executor(inputEdit, outputEdit, QStringList() << ".list" << ".continue");
614 QObject::connect(sender: &debugger, SIGNAL(evaluationSuspended()), receiver: &executor, SLOT(execute()), Qt::QueuedConnection);
615 outputEdit->clear();
616 engine.evaluate(program: "void(123);\nvoid(456);\n789;", fileName: "myscript.qs");
617 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("Breakpoint 4 at myscript.qs, line 1.\n"
618 "1\tvoid(123);\n"
619 "qsdb> .list\n"
620 "1\tvoid(123);\n"
621 "2\tvoid(456);\n"
622 "3\t789;\n"
623 "4\n"
624 "5\n"
625 "6\n"
626 "7\n"
627 "8\n"
628 "9\n"
629 "10\n"
630 "qsdb> .continue"));
631 }
632
633 {
634 QString script;
635 script.append(s: "function bar(i) {\n");
636 for (int i = 0; i < 10; ++i)
637 script.append(s: QString::fromLatin1(str: " i = i + %0;\n").arg(a: i));
638 script.append(s: " return i;\n}");
639 engine.evaluate(program: script, fileName: "bar.qs");
640 }
641
642 outputEdit->clear();
643 executeConsoleCommand(inputEdit, outputEdit, text: ".break bar.qs:7");
644
645 {
646 DebuggerCommandExecutor executor(inputEdit, outputEdit, QStringList()
647 << ".list"
648 << ".up"
649 << ".list"
650 << ".frame"
651 << ".down"
652 << ".list"
653 << ".continue");
654 QObject::connect(sender: &debugger, SIGNAL(evaluationSuspended()), receiver: &executor, SLOT(execute()), Qt::QueuedConnection);
655 outputEdit->clear();
656 engine.evaluate(program: "bar(123);", fileName: "testscript.qs");
657 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("Breakpoint 5 at bar.qs, line 7.\n"
658 "7\t i = i + 5;\n"
659 "qsdb> .list\n"
660 "2\t i = i + 0;\n"
661 "3\t i = i + 1;\n"
662 "4\t i = i + 2;\n"
663 "5\t i = i + 3;\n"
664 "6\t i = i + 4;\n"
665 "7\t i = i + 5;\n"
666 "8\t i = i + 6;\n"
667 "9\t i = i + 7;\n"
668 "10\t i = i + 8;\n"
669 "11\t i = i + 9;\n"
670 "qsdb> .up\n"
671 "#1 <global>()@testscript.qs:1\n"
672 "qsdb> .list\n"
673 "1\tbar(123);\n"
674 "2\n"
675 "3\n"
676 "4\n"
677 "5\n"
678 "6\n"
679 "7\n"
680 "8\n"
681 "9\n"
682 "10\n"
683 "qsdb> .frame\n"
684 "#1 <global>()@testscript.qs:1\n"
685 "qsdb> .down\n"
686 "#0 bar(123)@bar.qs:7\n"
687 "qsdb> .list\n"
688 "2\t i = i + 0;\n"
689 "3\t i = i + 1;\n"
690 "4\t i = i + 2;\n"
691 "5\t i = i + 3;\n"
692 "6\t i = i + 4;\n"
693 "7\t i = i + 5;\n"
694 "8\t i = i + 6;\n"
695 "9\t i = i + 7;\n"
696 "10\t i = i + 8;\n"
697 "11\t i = i + 9;\n"
698 "qsdb> .continue"));
699 }
700
701 {
702 DebuggerCommandExecutor executor(inputEdit, outputEdit, QStringList()
703 << ".list 9"
704 << ".continue");
705 QObject::connect(sender: &debugger, SIGNAL(evaluationSuspended()), receiver: &executor, SLOT(execute()), Qt::QueuedConnection);
706 outputEdit->clear();
707 engine.evaluate(program: "bar(123);", fileName: "testscript.qs");
708 QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("Breakpoint 5 at bar.qs, line 7.\n"
709 "7\t i = i + 5;\n"
710 "qsdb> .list 9\n"
711 "4\t i = i + 2;\n"
712 "5\t i = i + 3;\n"
713 "6\t i = i + 4;\n"
714 "7\t i = i + 5;\n"
715 "8\t i = i + 6;\n"
716 "9\t i = i + 7;\n"
717 "10\t i = i + 8;\n"
718 "11\t i = i + 9;\n"
719 "12\t return i;\n"
720 "13\t}\n"
721 "qsdb> .continue"));
722 }
723}
724
725class ScriptEvaluator : public QObject
726{
727 Q_OBJECT
728public:
729 ScriptEvaluator(QObject *parent = 0)
730 : QObject(parent) {
731 m_engine = new QScriptEngine(this);
732 }
733 QScriptEngine *engine() const {
734 return m_engine;
735 }
736public Q_SLOTS:
737 QScriptValue evaluate(const QString &program) {
738 return m_engine->evaluate(program);
739 }
740private:
741 QScriptEngine *m_engine;
742};
743
744void tst_QScriptEngineDebugger::multithreadedDebugging()
745{
746#ifdef Q_OS_WINCE
747 QSKIP("This tests uses too much memory for Windows CE");
748#endif
749 ScriptEvaluator eval;
750 QThread thread;
751 eval.moveToThread(thread: &thread);
752 eval.engine()->moveToThread(thread: &thread);
753 QScriptEngineDebugger debugger;
754 QSignalSpy evaluationSuspendedSpy(&debugger, SIGNAL(evaluationSuspended()));
755 QSignalSpy evaluationResumedSpy(&debugger, SIGNAL(evaluationResumed()));
756 debugger.attachTo(engine: eval.engine());
757 QMetaObject::invokeMethod(obj: &eval, member: "evaluate", type: Qt::QueuedConnection, Q_ARG(QString, "debugger"));
758 QSignalSpy threadFinishedSpy(&thread, SIGNAL(finished()));
759 thread.start();
760 QTRY_COMPARE(evaluationSuspendedSpy.count(), 1);
761 QTRY_COMPARE(evaluationResumedSpy.count(), 0);
762 thread.quit();
763 QTRY_COMPARE(threadFinishedSpy.count(), 1);
764}
765
766void tst_QScriptEngineDebugger::autoShowStandardWindow()
767{
768 {
769 QScriptEngine engine;
770 QScriptEngineDebugger debugger;
771 QCOMPARE(debugger.autoShowStandardWindow(), true);
772 debugger.attachTo(engine: &engine);
773 QObject::connect(sender: &debugger, SIGNAL(evaluationSuspended()),
774 receiver: debugger.action(action: QScriptEngineDebugger::ContinueAction),
775 SLOT(trigger()));
776 engine.evaluate(program: "debugger");
777 QTRY_VERIFY(debugger.standardWindow()->isVisible());
778
779 debugger.setAutoShowStandardWindow(true);
780 QCOMPARE(debugger.autoShowStandardWindow(), true);
781
782 debugger.setAutoShowStandardWindow(false);
783 QCOMPARE(debugger.autoShowStandardWindow(), false);
784
785 debugger.setAutoShowStandardWindow(true);
786 QCOMPARE(debugger.autoShowStandardWindow(), true);
787
788 debugger.standardWindow()->hide();
789
790 engine.evaluate(program: "debugger");
791 QTRY_VERIFY(debugger.standardWindow()->isVisible());
792 }
793
794 {
795 QScriptEngine engine;
796 QScriptEngineDebugger debugger;
797 debugger.setAutoShowStandardWindow(false);
798 debugger.attachTo(engine: &engine);
799 QObject::connect(sender: &debugger, SIGNAL(evaluationSuspended()),
800 receiver: debugger.action(action: QScriptEngineDebugger::ContinueAction),
801 SLOT(trigger()));
802 QSignalSpy evaluationResumedSpy(&debugger, SIGNAL(evaluationResumed()));
803 engine.evaluate(program: "debugger");
804 QTRY_COMPARE(evaluationResumedSpy.count(), 1);
805 QVERIFY(!debugger.standardWindow()->isVisible());
806 }
807}
808
809void tst_QScriptEngineDebugger::standardWindowOwnership()
810{
811 QScriptEngine engine;
812 QPointer<QMainWindow> win;
813 {
814 QScriptEngineDebugger debugger;
815 win = debugger.standardWindow();
816 }
817 QVERIFY(win == 0);
818
819 // Reparent the window.
820 QWidget widget;
821 {
822 QScriptEngineDebugger debugger;
823 win = debugger.standardWindow();
824 win->setParent(&widget);
825 }
826 QVERIFY(win != 0);
827}
828
829void tst_QScriptEngineDebugger::engineDeleted()
830{
831 QScriptEngine* engine = new QScriptEngine;
832 QScriptEngineDebugger *debugger = new QScriptEngineDebugger;
833 debugger->attachTo(engine);
834
835 debugger->standardWindow()->show();
836 QTest::qWaitForWindowExposed(widget: debugger->standardWindow());
837
838 QSignalSpy destroyedSpy(engine, SIGNAL(destroyed()));
839 engine->deleteLater();
840 QTRY_COMPARE(destroyedSpy.count(), 1);
841
842 // Shouldn't crash (QTBUG-21548)
843 debugger->action(action: QScriptEngineDebugger::ContinueAction)->trigger();
844}
845
846QTEST_MAIN(tst_QScriptEngineDebugger)
847#include "tst_qscriptenginedebugger.moc"
848

source code of qtscript/tests/auto/qscriptenginedebugger/tst_qscriptenginedebugger.cpp