tests/auto/qscriptenginedebugger/tst_qscriptenginedebugger.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /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 <QtTest/QtTest>
+
+#include <qscriptengine.h>
+#include <qscriptenginedebugger.h>
+#include <qaction.h>
+#include <qlineedit.h>
+#include <qmainwindow.h>
+#include <qmenu.h>
+#include <qplaintextedit.h>
+#include <qtoolbar.h>
+
+//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<QScriptEngineDebugger::DebuggerAction> 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<QAction*> 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<QScriptEngineDebugger::DebuggerWidget> widgets;
+    widgets
+        << QScriptEngineDebugger::ConsoleWidget
+        << QScriptEngineDebugger::StackWidget
+        << QScriptEngineDebugger::ScriptsWidget
+        << QScriptEngineDebugger::LocalsWidget
+        << QScriptEngineDebugger::CodeWidget
+        << QScriptEngineDebugger::CodeFinderWidget
+        << QScriptEngineDebugger::BreakpointsWidget
+        << QScriptEngineDebugger::DebugOutputWidget
+        << QScriptEngineDebugger::ErrorLogWidget;
+    QList<QWidget*> 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<QWidget *>(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<QWidget *>(win->parent()), (QWidget*)0);
+
+    QMenu *menu = debugger.createStandardMenu();
+    QCOMPARE(static_cast<QWidget *>(menu->parent()), (QWidget*)0);
+    QToolBar *toolBar = debugger.createStandardToolBar();
+    QCOMPARE(static_cast<QWidget *>(toolBar->parent()), (QWidget*)0);
+
+    QMenu *menu2 = debugger.createStandardMenu(win);
+    QCOMPARE(static_cast<QWidget *>(menu2->parent()), (QWidget*)win);
+    QVERIFY(menu2 != menu);
+    QToolBar *toolBar2 = debugger.createStandardToolBar(win);
+    QCOMPARE(static_cast<QWidget *>(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<QLineEdit*>(consoleWidget);
+    QVERIFY(inputEdit != 0);
+    QPlainTextEdit *outputEdit = qFindChild<QPlainTextEdit*>(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<QPlainTextEdit*>(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  <global>() 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  <global>() 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  <global>()@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  <global>()@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"