/**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the either Technology Preview License Agreement or the ** Beta Release License Agreement. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain ** additional rights. These rights are described in the Nokia Qt LGPL ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this ** package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** If you are unsure which license is appropriate for your use, please ** contact the sales department at http://www.qtsoftware.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include #include #include #include //TESTED_CLASS= //TESTED_FILES= // Will try to wait for the condition while allowing event processing #define QTRY_COMPARE(__expr, __expected) \ do { \ const int __step = 50; \ const int __timeout = 5000; \ if ((__expr) != (__expected)) { \ QTest::qWait(0); \ } \ for (int __i = 0; __i < __timeout && ((__expr) != (__expected)); __i+=__step) { \ QTest::qWait(__step); \ } \ QCOMPARE(__expr, __expected); \ } while(0) // Can't use QTest::qWait() because it causes event loop to hang on some platforms static void qsWait(int ms) { QTimer timer; timer.setSingleShot(true); timer.setInterval(ms); timer.start(); QEventLoop loop; QObject::connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); loop.exec(); QCoreApplication::processEvents(); } class tst_QScriptEngineDebugger : public QObject { Q_OBJECT public: tst_QScriptEngineDebugger(); virtual ~tst_QScriptEngineDebugger(); private slots: void attachAndDetach(); void action(); void widget(); void standardObjects(); void debuggerSignals(); void consoleCommands(); void multithreadedDebugging(); }; tst_QScriptEngineDebugger::tst_QScriptEngineDebugger() { } tst_QScriptEngineDebugger::~tst_QScriptEngineDebugger() { } void tst_QScriptEngineDebugger::attachAndDetach() { { QScriptEngineDebugger debugger; QCOMPARE(debugger.state(), QScriptEngineDebugger::SuspendedState); debugger.attachTo(0); QScriptEngine engine; debugger.attachTo(&engine); QCOMPARE(debugger.state(), QScriptEngineDebugger::SuspendedState); } { QScriptEngineDebugger debugger; QScriptEngine engine; QScriptValue oldPrint = engine.globalObject().property("print"); QVERIFY(oldPrint.isFunction()); QVERIFY(!engine.globalObject().property("__FILE__").isValid()); QVERIFY(!engine.globalObject().property("__LINE__").isValid()); debugger.attachTo(&engine); QVERIFY(engine.globalObject().property("__FILE__").isUndefined()); QVERIFY(engine.globalObject().property("__LINE__").isNumber()); QVERIFY(!engine.globalObject().property("print").strictlyEquals(oldPrint)); debugger.detach(); QVERIFY(engine.globalObject().property("print").strictlyEquals(oldPrint)); QVERIFY(!engine.globalObject().property("__FILE__").isValid()); QVERIFY(!engine.globalObject().property("__LINE__").isValid()); } { QScriptEngineDebugger debugger; QScriptEngine engine; debugger.attachTo(&engine); debugger.detach(); QScriptEngine engine2; debugger.attachTo(&engine2); } { QScriptEngineDebugger debugger; QScriptEngine engine; debugger.attachTo(&engine); debugger.detach(); QScriptEngine engine2; debugger.attachTo(&engine2); debugger.detach(); } #ifndef Q_OS_WINCE // demands too much memory for WinCE { QScriptEngineDebugger debugger; QScriptEngine engine; debugger.attachTo(&engine); QScriptEngineDebugger debugger2; debugger2.attachTo(&engine); } #endif { QScriptEngine *engine = new QScriptEngine; QScriptEngineDebugger debugger; debugger.attachTo(engine); delete engine; QScriptEngine engine2; debugger.attachTo(&engine2); } } void tst_QScriptEngineDebugger::action() { QScriptEngine engine; QScriptEngineDebugger debugger; debugger.attachTo(&engine); QList actions; actions << QScriptEngineDebugger::InterruptAction << QScriptEngineDebugger::ContinueAction << QScriptEngineDebugger::StepIntoAction << QScriptEngineDebugger::StepOverAction << QScriptEngineDebugger::StepOutAction << QScriptEngineDebugger::RunToCursorAction << QScriptEngineDebugger::RunToNewScriptAction << QScriptEngineDebugger::ToggleBreakpointAction << QScriptEngineDebugger::ClearDebugOutputAction << QScriptEngineDebugger::ClearErrorLogAction << QScriptEngineDebugger::ClearConsoleAction << QScriptEngineDebugger::FindInScriptAction << QScriptEngineDebugger::FindNextInScriptAction << QScriptEngineDebugger::FindPreviousInScriptAction << QScriptEngineDebugger::GoToLineAction; QList lst; for (int i = 0; i < actions.size(); ++i) { QScriptEngineDebugger::DebuggerAction da = actions.at(i); QAction *act = debugger.action(da); QVERIFY(act != 0); QCOMPARE(act, debugger.action(da)); QCOMPARE(act->parent(), (QObject*)&debugger); QVERIFY(lst.indexOf(act) == -1); lst.append(act); } } void tst_QScriptEngineDebugger::widget() { QScriptEngine engine; QScriptEngineDebugger debugger; debugger.attachTo(&engine); QList widgets; widgets << QScriptEngineDebugger::ConsoleWidget << QScriptEngineDebugger::StackWidget << QScriptEngineDebugger::ScriptsWidget << QScriptEngineDebugger::LocalsWidget << QScriptEngineDebugger::CodeWidget << QScriptEngineDebugger::CodeFinderWidget << QScriptEngineDebugger::BreakpointsWidget << QScriptEngineDebugger::DebugOutputWidget << QScriptEngineDebugger::ErrorLogWidget; QList lst; for (int i = 0; i < widgets.size(); ++i) { QScriptEngineDebugger::DebuggerWidget dw = widgets.at(i); QWidget *wid = debugger.widget(dw); QVERIFY(wid != 0); QCOMPARE(wid, debugger.widget(dw)); QVERIFY(lst.indexOf(wid) == -1); lst.append(wid); QCOMPARE(static_cast(wid->parent()), (QWidget*)0); } } void tst_QScriptEngineDebugger::standardObjects() { QScriptEngine engine; QScriptEngineDebugger debugger; debugger.attachTo(&engine); QMainWindow *win = debugger.standardWindow(); QCOMPARE(static_cast(win->parent()), (QWidget*)0); QMenu *menu = debugger.createStandardMenu(); QCOMPARE(static_cast(menu->parent()), (QWidget*)0); QToolBar *toolBar = debugger.createStandardToolBar(); QCOMPARE(static_cast(toolBar->parent()), (QWidget*)0); QMenu *menu2 = debugger.createStandardMenu(win); QCOMPARE(static_cast(menu2->parent()), (QWidget*)win); QVERIFY(menu2 != menu); QToolBar *toolBar2 = debugger.createStandardToolBar(win); QCOMPARE(static_cast(toolBar2->parent()), (QWidget*)win); QVERIFY(toolBar2 != toolBar); delete menu; delete toolBar; } void tst_QScriptEngineDebugger::debuggerSignals() { QScriptEngine engine; QScriptEngineDebugger debugger; debugger.attachTo(&engine); debugger.setAutoShowStandardWindow(false); QSignalSpy evaluationSuspendedSpy(&debugger, SIGNAL(evaluationSuspended())); QSignalSpy evaluationResumedSpy(&debugger, SIGNAL(evaluationResumed())); QObject::connect(&debugger, SIGNAL(evaluationSuspended()), debugger.action(QScriptEngineDebugger::ContinueAction), SLOT(trigger())); engine.evaluate("123"); QCOMPARE(evaluationSuspendedSpy.count(), 0); QCOMPARE(evaluationResumedSpy.count(), 0); engine.evaluate("debugger"); QCoreApplication::processEvents(); QCOMPARE(evaluationSuspendedSpy.count(), 1); QCOMPARE(evaluationResumedSpy.count(), 1); } static void executeConsoleCommand(QLineEdit *inputEdit, QPlainTextEdit *outputEdit, const QString &text) { QString before = outputEdit->toPlainText(); inputEdit->setText(text); QTest::keyPress(inputEdit, Qt::Key_Enter); const int delay = 100; qsWait(delay); QString after = outputEdit->toPlainText(); int retryCount = 10; LAgain: while ((before == after) && (retryCount != 0)) { qsWait(delay); after = outputEdit->toPlainText(); --retryCount; } if (before != after) { before = after; qsWait(delay); after = outputEdit->toPlainText(); if (before != after) { retryCount = 10; goto LAgain; } } } class DebuggerCommandExecutor : public QObject { Q_OBJECT public: DebuggerCommandExecutor(QLineEdit *inputEdit, QPlainTextEdit *outputEdit, const QString &text, QObject *parent = 0) : QObject(parent), m_inputEdit(inputEdit), m_outputEdit(outputEdit), m_commands(text) {} DebuggerCommandExecutor(QLineEdit *inputEdit, QPlainTextEdit *outputEdit, const QStringList &commands, QObject *parent = 0) : QObject(parent), m_inputEdit(inputEdit), m_outputEdit(outputEdit), m_commands(commands) {} public Q_SLOTS: void execute() { for (int i = 0; i < m_commands.size(); ++i) executeConsoleCommand(m_inputEdit, m_outputEdit, m_commands.at(i)); } private: QLineEdit *m_inputEdit; QPlainTextEdit *m_outputEdit; QStringList m_commands; }; void tst_QScriptEngineDebugger::consoleCommands() { QSKIP("This test can hang / misbehave because of timing/event loop issues (task 241300)", SkipAll); QScriptEngine engine; QScriptEngineDebugger debugger; debugger.setAutoShowStandardWindow(false); debugger.attachTo(&engine); QWidget *consoleWidget = debugger.widget(QScriptEngineDebugger::ConsoleWidget); QLineEdit *inputEdit = qFindChild(consoleWidget); QVERIFY(inputEdit != 0); QPlainTextEdit *outputEdit = qFindChild(consoleWidget); QVERIFY(outputEdit != 0); QVERIFY(outputEdit->toPlainText().startsWith("Welcome to the Qt Script debugger.")); outputEdit->clear(); // print() { QWidget *debugOutputWidget = debugger.widget(QScriptEngineDebugger::DebugOutputWidget); QPlainTextEdit *debugOutputEdit = qFindChild(debugOutputWidget); QVERIFY(debugOutputEdit != 0); QVERIFY(debugOutputEdit->toPlainText().isEmpty()); executeConsoleCommand(inputEdit, outputEdit, "print('Test of debug output')"); QCOMPARE(debugOutputEdit->toPlainText(), QString::fromLatin1("Test of debug output")); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> print('Test of debug output')")); } outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".info scripts"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .info scripts\nNo scripts loaded.")); outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".break foo.qs:123"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .break foo.qs:123\nBreakpoint 1: foo.qs, line 123.")); outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".break 123"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .break 123\nNo script.")); outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".info breakpoints"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .info breakpoints\nId\tEnabled\tWhere\n1\tyes\tfoo.qs:123")); outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".disable 1"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .disable 1")); outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".info breakpoints"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .info breakpoints\nId\tEnabled\tWhere\n1\tno\tfoo.qs:123")); outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".disable 1"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .disable 1")); outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".disable 123"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .disable 123\nNo breakpoint number 123.")); outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".enable 1"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .enable 1")); outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".info breakpoints"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .info breakpoints\nId\tEnabled\tWhere\n1\tyes\tfoo.qs:123")); outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".enable 123"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .enable 123\nNo breakpoint number 123.")); outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".condition 1 i > 456"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .condition 1 i > 456")); outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".info breakpoints"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .info breakpoints\nId\tEnabled\tWhere\n1\tyes\tfoo.qs:123\n\tstop only if i > 456")); outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".condition 1"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .condition 1\nBreakpoint 1 now unconditional.")); outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".info breakpoints"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .info breakpoints\nId\tEnabled\tWhere\n1\tyes\tfoo.qs:123")); outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".condition 123"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .condition 123\nNo breakpoint number 123.")); outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".ignore 1"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .ignore 1\nMissing argument (ignore-count).")); outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".ignore 1 10"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .ignore 1 10\nBreakpoint 1 will be ignored the next 10 time(s).")); outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".delete 1"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .delete 1")); outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".info breakpoints"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .info breakpoints\nNo breakpoints set.")); outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".tbreak bar.qs:456"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .tbreak bar.qs:456\nBreakpoint 2: bar.qs, line 456.")); { QString script; script.append("function foo(i) {\n"); for (int i = 0; i < 100; ++i) script.append(QString::fromLatin1(" i = i + %0;\n").arg(i)); script.append(" return i;\n}"); engine.evaluate(script, "foo.qs"); } outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".info scripts"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .info scripts\n\tfoo.qs")); outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".list foo.qs"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .list foo.qs\n" "1\tfunction foo(i) {\n" "2\t i = i + 0;\n" "3\t i = i + 1;\n" "4\t i = i + 2;\n" "5\t i = i + 3;\n" "6\t i = i + 4;\n" "7\t i = i + 5;\n" "8\t i = i + 6;\n" "9\t i = i + 7;\n" "10\t i = i + 8;")); outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".list"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .list\n" "No script.")); outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".backtrace"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .backtrace\n#0 ()@:-1")); outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".down"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .down\nAlready at bottom (innermost) frame.")); outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".up"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .up\nAlready at top (outermost) frame.")); outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".frame"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .frame\n#0 ()@:-1")); outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".break foo.qs:789"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .break foo.qs:789\nBreakpoint 3: foo.qs, line 789.")); outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".clear foo.qs:789"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .clear foo.qs:789\nDeleted breakpoint 3.")); outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".info breakpoints"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .info breakpoints\nId\tEnabled\tWhere\n2\tyes\tbar.qs:456")); outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".info locals"); QVERIFY(outputEdit->toPlainText().startsWith("qsdb> .info locals\n" "NaN : NaN\n" "Infinity : Infinity\n" "undefined : undefined\n" "print : function () { [native] }\n" "parseInt : function () { [native] }\n" "parseFloat : function () { [native] }\n" "isNaN : function () { [native] }\n" "isFinite : function () { [native] }\n" "decodeURI : function () { [native] }\n" "decodeURIComponent : function () { [native] }\n" "encodeURI : function () { [native] }\n" "encodeURIComponent : function () { [native] }\n" "escape : function () { [native] }\n" "unescape : function () { [native] }\n" "version : function () { [native] }\n" "gc : function () { [native] }\n" "Object : function () { [native] }\n" "Function : function () { [native] }\n" "Number : function () { [native] }\n" "Boolean : function () { [native] }")); outputEdit->clear(); QVERIFY(!engine.globalObject().property("a").isValid()); executeConsoleCommand(inputEdit, outputEdit, ".eval a = 123"); QVERIFY(engine.globalObject().property("a").isNumber()); QCOMPARE(engine.globalObject().property("a").toInt32(), 123); outputEdit->clear(); QVERIFY(!engine.globalObject().property("b").isValid()); executeConsoleCommand(inputEdit, outputEdit, "b = 456"); QVERIFY(engine.globalObject().property("b").isNumber()); QCOMPARE(engine.globalObject().property("b").toInt32(), 456); outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".break myscript.qs:1"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("qsdb> .break myscript.qs:1\nBreakpoint 4: myscript.qs, line 1.")); { DebuggerCommandExecutor executor(inputEdit, outputEdit, ".continue"); QObject::connect(&debugger, SIGNAL(evaluationSuspended()), &executor, SLOT(execute()), Qt::QueuedConnection); outputEdit->clear(); engine.evaluate("void(123);", "myscript.qs"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("Breakpoint 4 at myscript.qs, line 1.\n1\tvoid(123);\nqsdb> .continue")); } { DebuggerCommandExecutor executor(inputEdit, outputEdit, ".step"); QObject::connect(&debugger, SIGNAL(evaluationSuspended()), &executor, SLOT(execute()), Qt::QueuedConnection); outputEdit->clear(); engine.evaluate("void(123);\nvoid(456);", "myscript.qs"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("Breakpoint 4 at myscript.qs, line 1.\n" "1\tvoid(123);\n" "qsdb> .step\n" "2\tvoid(456);\n" "qsdb> .step")); } { DebuggerCommandExecutor executor(inputEdit, outputEdit, ".step 2"); QObject::connect(&debugger, SIGNAL(evaluationSuspended()), &executor, SLOT(execute()), Qt::QueuedConnection); outputEdit->clear(); engine.evaluate("void(123);\nvoid(456);", "myscript.qs"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("Breakpoint 4 at myscript.qs, line 1.\n1\tvoid(123);\nqsdb> .step 2")); } { DebuggerCommandExecutor executor(inputEdit, outputEdit, ".next"); QObject::connect(&debugger, SIGNAL(evaluationSuspended()), &executor, SLOT(execute()), Qt::QueuedConnection); outputEdit->clear(); engine.evaluate("void(123);\nvoid(456);", "myscript.qs"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("Breakpoint 4 at myscript.qs, line 1.\n" "1\tvoid(123);\n" "qsdb> .next\n" "2\tvoid(456);\n" "qsdb> .next")); } { DebuggerCommandExecutor executor(inputEdit, outputEdit, ".next 2"); QObject::connect(&debugger, SIGNAL(evaluationSuspended()), &executor, SLOT(execute()), Qt::QueuedConnection); outputEdit->clear(); engine.evaluate("void(123);\nvoid(456);", "myscript.qs"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("Breakpoint 4 at myscript.qs, line 1.\n" "1\tvoid(123);\n" "qsdb> .next 2")); } { DebuggerCommandExecutor executor(inputEdit, outputEdit, ".finish"); QObject::connect(&debugger, SIGNAL(evaluationSuspended()), &executor, SLOT(execute()), Qt::QueuedConnection); outputEdit->clear(); engine.evaluate("void(123);\nvoid(456);", "myscript.qs"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("Breakpoint 4 at myscript.qs, line 1.\n1\tvoid(123);\nqsdb> .finish")); } { DebuggerCommandExecutor executor(inputEdit, outputEdit, ".return"); QObject::connect(&debugger, SIGNAL(evaluationSuspended()), &executor, SLOT(execute()), Qt::QueuedConnection); outputEdit->clear(); engine.evaluate("void(123);\nvoid(456);\n789;", "myscript.qs"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("Breakpoint 4 at myscript.qs, line 1.\n1\tvoid(123);\nqsdb> .return")); } { DebuggerCommandExecutor executor(inputEdit, outputEdit, QStringList() << ".list" << ".continue"); QObject::connect(&debugger, SIGNAL(evaluationSuspended()), &executor, SLOT(execute()), Qt::QueuedConnection); outputEdit->clear(); engine.evaluate("void(123);\nvoid(456);\n789;", "myscript.qs"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("Breakpoint 4 at myscript.qs, line 1.\n" "1\tvoid(123);\n" "qsdb> .list\n" "1\tvoid(123);\n" "2\tvoid(456);\n" "3\t789;\n" "4\n" "5\n" "6\n" "7\n" "8\n" "9\n" "10\n" "qsdb> .continue")); } { QString script; script.append("function bar(i) {\n"); for (int i = 0; i < 10; ++i) script.append(QString::fromLatin1(" i = i + %0;\n").arg(i)); script.append(" return i;\n}"); engine.evaluate(script, "bar.qs"); } outputEdit->clear(); executeConsoleCommand(inputEdit, outputEdit, ".break bar.qs:7"); { DebuggerCommandExecutor executor(inputEdit, outputEdit, QStringList() << ".list" << ".up" << ".list" << ".frame" << ".down" << ".list" << ".continue"); QObject::connect(&debugger, SIGNAL(evaluationSuspended()), &executor, SLOT(execute()), Qt::QueuedConnection); outputEdit->clear(); engine.evaluate("bar(123);", "testscript.qs"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("Breakpoint 5 at bar.qs, line 7.\n" "7\t i = i + 5;\n" "qsdb> .list\n" "2\t i = i + 0;\n" "3\t i = i + 1;\n" "4\t i = i + 2;\n" "5\t i = i + 3;\n" "6\t i = i + 4;\n" "7\t i = i + 5;\n" "8\t i = i + 6;\n" "9\t i = i + 7;\n" "10\t i = i + 8;\n" "11\t i = i + 9;\n" "qsdb> .up\n" "#1 ()@testscript.qs:1\n" "qsdb> .list\n" "1\tbar(123);\n" "2\n" "3\n" "4\n" "5\n" "6\n" "7\n" "8\n" "9\n" "10\n" "qsdb> .frame\n" "#1 ()@testscript.qs:1\n" "qsdb> .down\n" "#0 bar(123)@bar.qs:7\n" "qsdb> .list\n" "2\t i = i + 0;\n" "3\t i = i + 1;\n" "4\t i = i + 2;\n" "5\t i = i + 3;\n" "6\t i = i + 4;\n" "7\t i = i + 5;\n" "8\t i = i + 6;\n" "9\t i = i + 7;\n" "10\t i = i + 8;\n" "11\t i = i + 9;\n" "qsdb> .continue")); } { DebuggerCommandExecutor executor(inputEdit, outputEdit, QStringList() << ".list 9" << ".continue"); QObject::connect(&debugger, SIGNAL(evaluationSuspended()), &executor, SLOT(execute()), Qt::QueuedConnection); outputEdit->clear(); engine.evaluate("bar(123);", "testscript.qs"); QCOMPARE(outputEdit->toPlainText(), QString::fromLatin1("Breakpoint 5 at bar.qs, line 7.\n" "7\t i = i + 5;\n" "qsdb> .list 9\n" "4\t i = i + 2;\n" "5\t i = i + 3;\n" "6\t i = i + 4;\n" "7\t i = i + 5;\n" "8\t i = i + 6;\n" "9\t i = i + 7;\n" "10\t i = i + 8;\n" "11\t i = i + 9;\n" "12\t return i;\n" "13\t}\n" "qsdb> .continue")); } } class ScriptEvaluator : public QObject { Q_OBJECT public: ScriptEvaluator(QObject *parent = 0) : QObject(parent) { m_engine = new QScriptEngine(this); } QScriptEngine *engine() const { return m_engine; } public Q_SLOTS: QScriptValue evaluate(const QString &program) { return m_engine->evaluate(program); } private: QScriptEngine *m_engine; }; void tst_QScriptEngineDebugger::multithreadedDebugging() { ScriptEvaluator eval; QThread thread; eval.moveToThread(&thread); eval.engine()->moveToThread(&thread); QScriptEngineDebugger debugger; QSignalSpy evaluationSuspendedSpy(&debugger, SIGNAL(evaluationSuspended())); QSignalSpy evaluationResumedSpy(&debugger, SIGNAL(evaluationResumed())); debugger.attachTo(eval.engine()); QMetaObject::invokeMethod(&eval, "evaluate", Qt::QueuedConnection, Q_ARG(QString, "debugger")); QSignalSpy threadFinishedSpy(&thread, SIGNAL(finished())); thread.start(); QTRY_COMPARE(evaluationSuspendedSpy.count(), 1); QTRY_COMPARE(evaluationResumedSpy.count(), 0); thread.quit(); QTRY_COMPARE(threadFinishedSpy.count(), 1); } QTEST_MAIN(tst_QScriptEngineDebugger) #include "tst_qscriptenginedebugger.moc"