diff -r 000000000000 -r 1918ee327afb tests/auto/qscriptenginedebugger/tst_qscriptenginedebugger.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/auto/qscriptenginedebugger/tst_qscriptenginedebugger.cpp Mon Jan 11 14:00:40 2010 +0000 @@ -0,0 +1,788 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** 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 Technology Preview License Agreement accompanying +** this package. +** +** 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.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $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() +{ +#if defined(Q_OS_WINCE) && _WIN32_WCE < 0x600 + QSKIP("skipped due to high mem usage until task 261062 is fixed", SkipAll); +#endif + { + 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() +{ +#if defined(Q_OS_WINCE) && _WIN32_WCE < 0x600 + QSKIP("skipped due to high mem usage until task 261062 is fixed", SkipAll); +#endif + + 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() +{ +#if defined(Q_OS_WINCE) && _WIN32_WCE < 0x600 + QSKIP("skipped due to high mem usage until task 261062 is fixed", SkipAll); +#endif + + 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() +{ +#if defined(Q_OS_WINCE) && _WIN32_WCE < 0x600 + QSKIP("skipped due to high mem usage until task 261062 is fixed", SkipAll); +#endif + + 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() +{ +#if defined(Q_OS_WINCE) && _WIN32_WCE < 0x600 + QSKIP("skipped due to high mem usage until task 261062 is fixed", SkipAll); +#endif + + 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 () at -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 () at -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() +{ +#ifdef Q_OS_WINCE + QSKIP("This tests uses too much memory for Windows CE", SkipAll); +#endif + 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"