tests/auto/qscriptengineagent/tst_qscriptengineagent.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/auto/qscriptengineagent/tst_qscriptengineagent.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,2224 @@
+/****************************************************************************
+**
+** 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 <QtScript/qscriptengineagent.h>
+#include <QtScript/qscriptengine.h>
+#include <qscriptvalueiterator.h>
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+QT_BEGIN_NAMESPACE
+extern bool qt_script_isJITEnabled();
+QT_END_NAMESPACE
+
+class tst_QScriptEngineAgent : public QObject
+{
+    Q_OBJECT
+    Q_PROPERTY(double testProperty READ testProperty WRITE setTestProperty)
+
+public:
+    tst_QScriptEngineAgent();
+    virtual ~tst_QScriptEngineAgent();
+
+    double testProperty() const { return m_testProperty; }
+    void setTestProperty(double val) { m_testProperty = val; }
+
+public slots:
+    double testSlot(double arg) { return arg; }
+
+signals:
+    void testSignal(double arg);
+
+private slots:
+    void scriptLoadAndUnload_statement();
+    void scriptLoadAndUnload();
+    void scriptLoadAndUnload_eval();
+    void contextPushAndPop();
+    void functionEntryAndExit_semicolon();
+    void functionEntryAndExit_expression();
+    void functionEntryAndExit_functionCall();
+    void functionEntryAndExit_functionCallWithoutReturn();
+    void functionEntryAndExit_functionDefinition();
+    void functionEntryAndExit_native();
+    void functionEntryAndExit_native2();
+    void functionEntryAndExit_nativeThrowing();
+    void functionEntryAndExit_builtin();
+    void functionEntryAndExit_objects();
+    void functionEntryAndExit_slots();
+    void functionEntryAndExit_property_set();
+    void functionEntryAndExit_property_get();
+    void functionEntryAndExit_call();
+    void functionEntryAndExit_functionReturn_construct();
+    void functionEntryAndExit_functionReturn_call();
+    void functionEntryAndExit_objectCall();
+    void positionChange_1();
+    void positionChange_2();
+    void exceptionThrowAndCatch();
+    void eventOrder_assigment();
+    void eventOrder_functionDefinition();
+    void eventOrder_throwError();
+    void eventOrder_throwAndCatch();
+    void eventOrder_functions();
+    void eventOrder_throwCatchFinally();
+    void eventOrder_signalsHandling();
+    void recursiveObserve();
+    void multipleAgents();
+    void syntaxError();
+    void extension_invoctaion();
+    void extension();
+    void isEvaluatingInExtension();
+    void hasUncaughtException();
+
+private:
+    double m_testProperty;
+};
+
+tst_QScriptEngineAgent::tst_QScriptEngineAgent()
+{
+}
+
+tst_QScriptEngineAgent::~tst_QScriptEngineAgent()
+{
+}
+
+struct ScriptEngineEvent
+{
+    enum Type {
+        ScriptLoad,
+        ScriptUnload,//1
+        ContextPush,
+        ContextPop,  //3
+        FunctionEntry, //4
+        FunctionExit, //5
+        PositionChange,
+        ExceptionThrow,//7
+        ExceptionCatch,
+        DebuggerInvocationRequest
+    };
+
+    Type type;
+
+    qint64 scriptId;
+    QString script;
+    QString fileName;
+    int lineNumber;
+    int columnNumber;
+    QScriptValue value;
+    bool hasExceptionHandler;
+
+    ScriptEngineEvent(qint64 scriptId,
+                      const QString &script, const QString &fileName,
+                      int lineNumber)
+        : type(ScriptLoad), scriptId(scriptId),
+          script(script), fileName(fileName),
+          lineNumber(lineNumber)
+        { }
+
+    ScriptEngineEvent(Type type, qint64 scriptId = -777)
+        : type(type), scriptId(scriptId)
+        { }
+
+    ScriptEngineEvent(Type type, qint64 scriptId,
+                      const QScriptValue &value)
+        : type(type), scriptId(scriptId),
+          value(value)
+        { }
+
+    ScriptEngineEvent(qint64 scriptId,
+                      int lineNumber, int columnNumber)
+        : type(PositionChange), scriptId(scriptId),
+          lineNumber(lineNumber), columnNumber(columnNumber)
+        { }
+
+    ScriptEngineEvent(qint64 scriptId,
+                      const QScriptValue &exception, bool hasHandler)
+        : type(ExceptionThrow), scriptId(scriptId),
+          value(exception), hasExceptionHandler(hasHandler)
+        { }
+
+    static QString typeToQString(Type t)
+    {
+        switch (t) {
+            case ScriptEngineEvent::ScriptLoad: return "ScriptLoad";
+            case ScriptEngineEvent::ScriptUnload: return "ScriptUnload";
+            case ScriptEngineEvent::ContextPush: return "ContextPush";
+            case ScriptEngineEvent::ContextPop: return "ContextPop";
+            case ScriptEngineEvent::FunctionEntry: return "FunctionEntry";
+            case ScriptEngineEvent::FunctionExit: return "FunctionExit";
+            case ScriptEngineEvent::PositionChange: return "PositionChange";
+            case ScriptEngineEvent::ExceptionThrow: return "ExceptionThrow";
+            case ScriptEngineEvent::ExceptionCatch: return "ExceptionCatch";
+            case ScriptEngineEvent::DebuggerInvocationRequest: return "DebuggerInvocationRequest";
+            }
+    }
+};
+
+class ScriptEngineSpy : public QScriptEngineAgent, public QList<ScriptEngineEvent>
+{
+public:
+    enum IgnoreFlag {
+        IgnoreScriptLoad = 0x001,
+        IgnoreScriptUnload = 0x002,
+        IgnoreFunctionEntry = 0x004,
+        IgnoreFunctionExit = 0x008,
+        IgnorePositionChange = 0x010,
+        IgnoreExceptionThrow = 0x020,
+        IgnoreExceptionCatch = 0x040,
+        IgnoreContextPush = 0x0100,
+        IgnoreContextPop = 0x0200,
+        IgnoreDebuggerInvocationRequest = 0x0400
+    };
+
+    ScriptEngineSpy(QScriptEngine *engine, int ignores = 0);
+    ~ScriptEngineSpy();
+
+    void enableIgnoreFlags(int flags)
+        { m_ignores |= flags; }
+    void disableIgnoreFlags(int flags)
+        { m_ignores &= ~flags; }
+
+protected:
+    void scriptLoad(qint64 id, const QString &script,
+                    const QString &fileName, int lineNumber);
+    void scriptUnload(qint64 id);
+
+    void contextPush();
+    void contextPop();
+
+    void functionEntry(qint64 scriptId);
+    void functionExit(qint64 scriptId, const QScriptValue &returnValue);
+
+    void positionChange(qint64 scriptId,
+                        int lineNumber, int columnNumber);
+
+    void exceptionThrow(qint64 scriptId, const QScriptValue &exception,
+                        bool hasHandler);
+    void exceptionCatch(qint64 scriptId, const QScriptValue &exception);
+
+    bool supportsExtension(Extension ext) const;
+    QVariant extension(Extension ext, const QVariant &arg);
+
+private:
+    int m_ignores;
+};
+
+ScriptEngineSpy::ScriptEngineSpy(QScriptEngine *engine, int ignores)
+    : QScriptEngineAgent(engine)
+{
+    m_ignores = ignores;
+    engine->setAgent(this);
+}
+
+ScriptEngineSpy::~ScriptEngineSpy()
+{
+}
+
+void ScriptEngineSpy::scriptLoad(qint64 id, const QString &script,
+                                 const QString &fileName, int lineNumber)
+{
+    if (!(m_ignores & IgnoreScriptLoad))
+        append(ScriptEngineEvent(id, script, fileName, lineNumber));
+}
+
+void ScriptEngineSpy::scriptUnload(qint64 id)
+{
+    if (!(m_ignores & IgnoreScriptUnload))
+        append(ScriptEngineEvent(ScriptEngineEvent::ScriptUnload, id));
+}
+
+void ScriptEngineSpy::contextPush()
+{
+    if (!(m_ignores & IgnoreContextPush))
+        append(ScriptEngineEvent(ScriptEngineEvent::ContextPush));
+}
+
+void ScriptEngineSpy::contextPop()
+{
+    if (!(m_ignores & IgnoreContextPop))
+        append(ScriptEngineEvent(ScriptEngineEvent::ContextPop));
+}
+
+void ScriptEngineSpy::functionEntry(qint64 scriptId)
+{
+    if (!(m_ignores & IgnoreFunctionEntry))
+        append(ScriptEngineEvent(ScriptEngineEvent::FunctionEntry, scriptId));
+}
+
+void ScriptEngineSpy::functionExit(qint64 scriptId,
+                                   const QScriptValue &returnValue)
+{
+    if (!(m_ignores & IgnoreFunctionExit))
+        append(ScriptEngineEvent(ScriptEngineEvent::FunctionExit, scriptId, returnValue));
+}
+
+void ScriptEngineSpy::positionChange(qint64 scriptId,
+                                     int lineNumber, int columnNumber)
+{
+    if (!(m_ignores & IgnorePositionChange))
+        append(ScriptEngineEvent(scriptId, lineNumber, columnNumber));
+}
+
+void ScriptEngineSpy::exceptionThrow(qint64 scriptId,
+                                     const QScriptValue &exception, bool hasHandler)
+{
+    if (!(m_ignores & IgnoreExceptionThrow))
+        append(ScriptEngineEvent(scriptId, exception, hasHandler));
+}
+
+void ScriptEngineSpy::exceptionCatch(qint64 scriptId,
+                                     const QScriptValue &exception)
+{
+    if (!(m_ignores & IgnoreExceptionCatch))
+        append(ScriptEngineEvent(ScriptEngineEvent::ExceptionCatch, scriptId, exception));
+}
+
+bool ScriptEngineSpy::supportsExtension(Extension ext) const
+{
+    if (ext == DebuggerInvocationRequest)
+        return !(m_ignores & IgnoreDebuggerInvocationRequest);
+    return false;
+}
+
+QVariant ScriptEngineSpy::extension(Extension ext, const QVariant &arg)
+{
+    if (ext == DebuggerInvocationRequest) {
+        QVariantList lst = arg.toList();
+        qint64 scriptId = lst.at(0).toLongLong();
+        int lineNumber = lst.at(1).toInt();
+        int columnNumber = lst.at(2).toInt();
+        ScriptEngineEvent evt(scriptId, lineNumber, columnNumber);
+        evt.type = ScriptEngineEvent::DebuggerInvocationRequest;
+        append(evt);
+        return QString::fromLatin1("extension(DebuggerInvocationRequest)");
+    }
+    return QVariant();
+}
+
+void tst_QScriptEngineAgent::scriptLoadAndUnload_statement()
+{
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreScriptLoad
+                                                       | ScriptEngineSpy::IgnoreScriptUnload));
+    QCOMPARE(eng.agent(), (QScriptEngineAgent*)spy);
+    {
+        spy->clear();
+        QString code = ";";
+        QString fileName = "foo.qs";
+        int lineNumber = 123;
+        eng.evaluate(code, fileName, lineNumber);
+
+        QCOMPARE(spy->count(), 2);
+
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::ScriptLoad);
+        QVERIFY(spy->at(0).scriptId != -1);
+        QCOMPARE(spy->at(0).script, code);
+        QCOMPARE(spy->at(0).fileName, fileName);
+        QCOMPARE(spy->at(0).lineNumber, lineNumber);
+
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::ScriptUnload);
+        QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId);
+    }
+
+    {
+        spy->clear();
+        QString code = ";";
+        QString fileName = "bar.qs";
+        int lineNumber = 456;
+        eng.evaluate(code, fileName, lineNumber);
+
+        QCOMPARE(spy->count(), 2);
+
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::ScriptLoad);
+        QVERIFY(spy->at(0).scriptId != -1);
+        QCOMPARE(spy->at(0).script, code);
+        QCOMPARE(spy->at(0).fileName, fileName);
+        QCOMPARE(spy->at(0).lineNumber, lineNumber);
+
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::ScriptUnload);
+        QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId);
+    }
+    delete spy;
+}
+
+void tst_QScriptEngineAgent::scriptLoadAndUnload()
+{
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreScriptLoad
+                                                       | ScriptEngineSpy::IgnoreScriptUnload));
+    QCOMPARE(eng.agent(), (QScriptEngineAgent*)spy);
+    {
+        spy->clear();
+        QString code = "function foo() { print('ciao'); }";
+        QString fileName = "baz.qs";
+        int lineNumber = 789;
+        eng.evaluate(code, fileName, lineNumber);
+
+        QCOMPARE(spy->count(), 1);
+
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::ScriptLoad);
+        QVERIFY(spy->at(0).scriptId != -1);
+        QCOMPARE(spy->at(0).script, code);
+        QCOMPARE(spy->at(0).fileName, fileName);
+        QCOMPARE(spy->at(0).lineNumber, lineNumber);
+
+        code = "foo = null";
+        eng.evaluate(code);
+        QCOMPARE(spy->count(), 3);
+
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::ScriptLoad);
+        QVERIFY(spy->at(1).scriptId != -1);
+        QVERIFY(spy->at(1).scriptId != spy->at(0).scriptId);
+        QCOMPARE(spy->at(1).script, code);
+        QCOMPARE(spy->at(1).lineNumber, 1);
+
+        QCOMPARE(spy->at(2).type, ScriptEngineEvent::ScriptUnload);
+        QCOMPARE(spy->at(2).scriptId, spy->at(1).scriptId);
+
+        eng.collectGarbage(); // foo() is GC'ed
+        QCOMPARE(spy->count(), 4);
+        QCOMPARE(spy->at(3).type, ScriptEngineEvent::ScriptUnload);
+        QCOMPARE(spy->at(3).scriptId, spy->at(0).scriptId);
+    }
+
+    {
+        spy->clear();
+        QString code = "function foo() { return function() { print('ciao'); } }";
+        QString fileName = "foo.qs";
+        int lineNumber = 123;
+        eng.evaluate(code, fileName, lineNumber);
+
+        QCOMPARE(spy->count(), 1);
+
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::ScriptLoad);
+        QVERIFY(spy->at(0).scriptId != -1);
+        QCOMPARE(spy->at(0).script, code);
+        QCOMPARE(spy->at(0).fileName, fileName);
+        QCOMPARE(spy->at(0).lineNumber, lineNumber);
+
+        code = "bar = foo(); foo = null";
+        eng.evaluate(code);
+        QCOMPARE(spy->count(), 3);
+
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::ScriptLoad);
+        QVERIFY(spy->at(1).scriptId != -1);
+        QVERIFY(spy->at(1).scriptId != spy->at(0).scriptId);
+        QCOMPARE(spy->at(1).script, code);
+
+        QCOMPARE(spy->at(2).type, ScriptEngineEvent::ScriptUnload);
+        QCOMPARE(spy->at(2).scriptId, spy->at(1).scriptId);
+
+        eng.collectGarbage(); // foo() is not GC'ed
+        QCOMPARE(spy->count(), 3);
+
+        code = "bar = null";
+        eng.evaluate(code);
+        QCOMPARE(spy->count(), 5);
+
+        eng.collectGarbage(); // foo() is GC'ed
+        QCOMPARE(spy->count(), 6);
+    }
+    delete spy;
+}
+
+void tst_QScriptEngineAgent::scriptLoadAndUnload_eval()
+{
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreScriptLoad
+                                                       | ScriptEngineSpy::IgnoreScriptUnload));
+    {
+        spy->clear();
+        eng.evaluate("eval('function foo() { print(123); }')");
+
+        QEXPECT_FAIL("","Eval is threaded in different way that in old backend", Abort);
+        QCOMPARE(spy->count(), 3);
+
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::ScriptLoad);
+        QVERIFY(spy->at(0).scriptId != -1);
+
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::ScriptLoad);
+        QVERIFY(spy->at(1).scriptId != -1);
+        QVERIFY(spy->at(1).scriptId != spy->at(0).scriptId);
+
+        QCOMPARE(spy->at(2).type, ScriptEngineEvent::ScriptUnload);
+        QCOMPARE(spy->at(2).scriptId, spy->at(0).scriptId);
+    }
+    delete spy;
+}
+
+void tst_QScriptEngineAgent::contextPushAndPop()
+{
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreContextPush
+                                                       | ScriptEngineSpy::IgnoreContextPop));
+
+    {
+        spy->clear();
+        eng.pushContext();
+        eng.popContext();
+        QCOMPARE(spy->count(), 2);
+
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::ContextPush);
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::ContextPop);
+    }
+}
+
+static QScriptValue nativeFunctionReturningArg(QScriptContext *ctx, QScriptEngine *)
+{
+    return ctx->argument(0);
+}
+
+static QScriptValue nativeFunctionThrowingError(QScriptContext *ctx, QScriptEngine *)
+{
+    return ctx->throwError(ctx->argument(0).toString());
+}
+
+static QScriptValue nativeFunctionCallingArg(QScriptContext *ctx, QScriptEngine *)
+{
+    return ctx->argument(0).call();
+}
+
+/** check behaiviour of ';' */
+void tst_QScriptEngineAgent::functionEntryAndExit_semicolon()
+{
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry
+                                                       | ScriptEngineSpy::IgnoreFunctionExit));
+    {
+        spy->clear();
+        eng.evaluate(";");
+
+        QCOMPARE(spy->count(), 2);
+
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry);
+        QVERIFY(spy->at(0).scriptId != -1);
+
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId);
+        QVERIFY(spy->at(1).value.isUndefined());
+    }
+    delete spy;
+}
+
+/** check behaiviour of expression */
+void tst_QScriptEngineAgent::functionEntryAndExit_expression()
+{
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry
+                                                       | ScriptEngineSpy::IgnoreFunctionExit));
+    {
+        spy->clear();
+        eng.evaluate("1 + 2");
+
+        QCOMPARE(spy->count(), 2);
+
+        // evaluate() entry
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry);
+        QVERIFY(spy->at(0).scriptId != -1);
+
+        // evaluate() exit
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId);
+        QVERIFY(spy->at(1).value.isNumber());
+        QCOMPARE(spy->at(1).value.toNumber(), qsreal(3));
+    }
+    delete spy;
+}
+
+/** check behaiviour of standard function call */
+void tst_QScriptEngineAgent::functionEntryAndExit_functionCall()
+{
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry
+                                                       | ScriptEngineSpy::IgnoreFunctionExit));
+    {
+        spy->clear();
+        QVERIFY(eng.evaluate("(function() { return 123; } )()").toNumber()==123);
+
+        QCOMPARE(spy->count(), 4);
+
+        // evaluate() entry
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry);
+        QVERIFY(spy->at(0).scriptId != -1);
+
+        // anonymous function entry
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId);
+
+        // anonymous function exit
+        QCOMPARE(spy->at(2).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(2).scriptId, spy->at(0).scriptId);
+        QVERIFY(spy->at(2).value.isNumber());
+        QCOMPARE(spy->at(2).value.toNumber(), qsreal(123));
+
+        // evaluate() exit
+        QCOMPARE(spy->at(3).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(3).scriptId, spy->at(0).scriptId);
+        QVERIFY(spy->at(3).value.isNumber());
+        QCOMPARE(spy->at(3).value.toNumber(), qsreal(123));
+    }
+    delete spy;
+}
+
+/** check behaiviour of standard function call */
+void tst_QScriptEngineAgent::functionEntryAndExit_functionCallWithoutReturn()
+{
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry
+                                                       | ScriptEngineSpy::IgnoreFunctionExit));
+    {
+        spy->clear();
+        eng.evaluate("(function() { var a = 123; } )()");
+
+        QCOMPARE(spy->count(), 4);
+
+        // evaluate() entry
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry);
+        QVERIFY(spy->at(0).scriptId != -1);
+
+        // anonymous function entry
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId);
+
+        // anonymous function exit
+        QCOMPARE(spy->at(2).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(2).scriptId, spy->at(0).scriptId);
+
+        // evaluate() exit
+        QCOMPARE(spy->at(3).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(3).scriptId, spy->at(0).scriptId);
+    }
+    delete spy;
+}
+
+/** check behaiviour of function definition */
+void tst_QScriptEngineAgent::functionEntryAndExit_functionDefinition()
+{
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry
+                                                       | ScriptEngineSpy::IgnoreFunctionExit));
+    {
+        spy->clear();
+        eng.evaluate("function foo() { return 456; }");
+        QCOMPARE(spy->count(), 2);
+
+        // evaluate() entry
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry);
+        QVERIFY(spy->at(0).scriptId != -1);
+
+        // evaluate() exit
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId);
+        QVERIFY(spy->at(1).value.isUndefined());
+
+        eng.evaluate("foo()");
+        QCOMPARE(spy->count(), 6);
+
+        // evaluate() entry
+        QCOMPARE(spy->at(2).type, ScriptEngineEvent::FunctionEntry);
+        QVERIFY(spy->at(2).scriptId != spy->at(0).scriptId);
+
+        // foo() entry
+        QCOMPARE(spy->at(3).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(3).scriptId, spy->at(0).scriptId);
+
+        // foo() exit
+        QCOMPARE(spy->at(4).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(4).scriptId, spy->at(0).scriptId);
+        QVERIFY(spy->at(4).value.isNumber());
+        QCOMPARE(spy->at(4).value.toNumber(), qsreal(456));
+
+        // evaluate() exit
+        QCOMPARE(spy->at(5).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(5).scriptId, spy->at(2).scriptId);
+        QVERIFY(spy->at(5).value.isNumber());
+        QCOMPARE(spy->at(5).value.toNumber(), qsreal(456));
+    }
+    delete spy;
+}
+
+/** check behaiviour of native function */
+void tst_QScriptEngineAgent::functionEntryAndExit_native()
+{
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry
+                                                       | ScriptEngineSpy::IgnoreFunctionExit));
+    // native functions
+    {
+        QScriptValue fun = eng.newFunction(nativeFunctionReturningArg);
+        eng.globalObject().setProperty("nativeFunctionReturningArg", fun);
+
+        spy->clear();
+        eng.evaluate("nativeFunctionReturningArg(123)");
+
+        QCOMPARE(spy->count(), 4);
+
+        // evaluate() entry
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry);
+
+        // native function entry
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(1).scriptId, qint64(-1));
+
+        // native function exit
+        QCOMPARE(spy->at(2).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(2).scriptId, qint64(-1));
+        QVERIFY(spy->at(2).value.isNumber());
+        QCOMPARE(spy->at(2).value.toNumber(), qsreal(123));
+
+        // evaluate() exit
+        QCOMPARE(spy->at(3).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(3).scriptId, spy->at(0).scriptId);
+        QVERIFY(spy->at(3).value.isNumber());
+        QCOMPARE(spy->at(3).value.toNumber(), qsreal(123));
+    }
+    delete spy;
+}
+
+/** check behaiviour of native function */
+void tst_QScriptEngineAgent::functionEntryAndExit_native2()
+{
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry
+                                                       | ScriptEngineSpy::IgnoreFunctionExit));
+    {
+        QScriptValue fun = eng.newFunction(nativeFunctionCallingArg);
+        eng.globalObject().setProperty("nativeFunctionCallingArg", fun);
+
+        spy->clear();
+        eng.evaluate("nativeFunctionCallingArg(function() { return 123; })");
+        QCOMPARE(spy->count(), 6);
+
+        // evaluate() entry
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry);
+
+        // native function entry
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(1).scriptId, qint64(-1));
+
+        // script function entry
+        QCOMPARE(spy->at(2).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(2).scriptId, spy->at(0).scriptId);
+
+        // script function exit
+        QCOMPARE(spy->at(3).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(3).scriptId, spy->at(0).scriptId);
+
+        // native function exit
+        QCOMPARE(spy->at(4).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(4).scriptId, qint64(-1));
+        QVERIFY(spy->at(4).value.isNumber());
+        QCOMPARE(spy->at(4).value.toNumber(), qsreal(123));
+
+        // evaluate() exit
+        QCOMPARE(spy->at(5).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(5).scriptId, spy->at(0).scriptId);
+        QVERIFY(spy->at(5).value.isNumber());
+        QCOMPARE(spy->at(5).value.toNumber(), qsreal(123));
+    }
+    delete spy;
+}
+
+/** check behaiviour of native function throwing error*/
+void tst_QScriptEngineAgent::functionEntryAndExit_nativeThrowing()
+{
+    /* This function was changed from old backend. JSC return more Entrys / Exits, (exactly +1)
+       in exception creation time */
+
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry
+                                                       | ScriptEngineSpy::IgnoreFunctionExit));
+    {
+        QScriptValue fun = eng.newFunction(nativeFunctionThrowingError);
+        eng.globalObject().setProperty("nativeFunctionThrowingError", fun);
+
+        spy->clear();
+        eng.evaluate("nativeFunctionThrowingError('ciao')");
+        QCOMPARE(spy->count(), 6);
+
+        // evaluate() entry
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry);
+
+        // native function entry
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(1).scriptId, qint64(-1));
+
+        // Exception constructor entry
+        QCOMPARE(spy->at(2).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(2).scriptId, qint64(-1));
+
+        // Exception constructor exit
+        QCOMPARE(spy->at(3).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(3).scriptId, qint64(-1));
+        QVERIFY(spy->at(3).value.isError());
+
+        // native function exit
+        QCOMPARE(spy->at(4).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(4).scriptId, qint64(-1));
+        QVERIFY(spy->at(4).value.isError());
+
+        // evaluate() exit
+        QCOMPARE(spy->at(5).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(5).scriptId, spy->at(0).scriptId);
+        QVERIFY(spy->at(5).value.isError());
+    }
+    delete spy;
+}
+
+/** check behaiviour of built-in function */
+void tst_QScriptEngineAgent::functionEntryAndExit_builtin()
+{
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry
+                                                       | ScriptEngineSpy::IgnoreFunctionExit));
+    {
+        spy->clear();
+        eng.evaluate("'ciao'.toString()");
+
+        if (qt_script_isJITEnabled())
+            QEXPECT_FAIL("", "Some events are missing when JIT is enabled", Abort);
+        QCOMPARE(spy->count(), 4);
+
+        // evaluate() entry
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry);
+
+        // built-in native function entry
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(1).scriptId, qint64(-1));
+
+        // built-in native function exit
+        QCOMPARE(spy->at(2).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(2).scriptId, qint64(-1));
+        QVERIFY(spy->at(2).value.isString());
+        QCOMPARE(spy->at(2).value.toString(), QString("ciao"));
+
+        // evaluate() exit
+        QCOMPARE(spy->at(3).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(3).scriptId, spy->at(0).scriptId);
+        QVERIFY(spy->at(3).value.isString());
+        QCOMPARE(spy->at(3).value.toString(), QString("ciao"));
+    }
+    delete spy;
+}
+
+/** check behaiviour of object creation*/
+void tst_QScriptEngineAgent::functionEntryAndExit_objects()
+{
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry
+                                                       | ScriptEngineSpy::IgnoreFunctionExit));
+    {
+        spy->clear();
+        eng.evaluate("Array(); Boolean(); Date(); Function(); Number(); Object(); RegExp(); String()");
+        QCOMPARE(spy->count(), 18);
+
+        // evaluate() entry
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry);
+
+        // Array constructor entry
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(1).scriptId, qint64(-1));
+
+        // Array constructor exit
+        QCOMPARE(spy->at(2).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(2).scriptId, qint64(-1));
+        QVERIFY(spy->at(2).value.isArray());
+
+        // Boolean constructor entry
+        QCOMPARE(spy->at(3).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(3).scriptId, qint64(-1));
+
+        // Boolean constructor exit
+        QCOMPARE(spy->at(4).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(4).scriptId, qint64(-1));
+        QVERIFY(spy->at(4).value.isBoolean());
+
+        // Date constructor entry
+        QCOMPARE(spy->at(5).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(5).scriptId, qint64(-1));
+
+        // Date constructor exit
+        QCOMPARE(spy->at(6).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(6).scriptId, qint64(-1));
+        QVERIFY(spy->at(6).value.isString());
+
+        // Function constructor entry
+        QCOMPARE(spy->at(7).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(7).scriptId, qint64(-1));
+
+        // Function constructor exit
+        QCOMPARE(spy->at(8).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(8).scriptId, qint64(-1));
+        QVERIFY(spy->at(8).value.isFunction());
+
+        // Number constructor entry
+        QCOMPARE(spy->at(9).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(9).scriptId, qint64(-1));
+
+        // Number constructor exit
+        QCOMPARE(spy->at(10).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(10).scriptId, qint64(-1));
+        QVERIFY(spy->at(10).value.isNumber());
+
+        // Object constructor entry
+        QCOMPARE(spy->at(11).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(11).scriptId, qint64(-1));
+
+        // Object constructor exit
+        QCOMPARE(spy->at(12).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(12).scriptId, qint64(-1));
+        QVERIFY(spy->at(12).value.isObject());
+
+        // RegExp constructor entry
+        QCOMPARE(spy->at(13).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(13).scriptId, qint64(-1));
+
+        // RegExp constructor exit
+        QCOMPARE(spy->at(14).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(14).scriptId, qint64(-1));
+        QVERIFY(spy->at(14).value.isRegExp());
+
+        // String constructor entry
+        QCOMPARE(spy->at(15).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(15).scriptId, qint64(-1));
+
+        // String constructor exit
+        QCOMPARE(spy->at(16).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(16).scriptId, qint64(-1));
+        QVERIFY(spy->at(16).value.isString());
+
+        // evaluate() exit
+        QCOMPARE(spy->at(17).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(17).scriptId, spy->at(0).scriptId);
+        QVERIFY(spy->at(17).value.isString());
+        QCOMPARE(spy->at(17).value.toString(), QString());
+    }
+    delete spy;
+}
+
+/** check behaiviour of slots*/
+void tst_QScriptEngineAgent::functionEntryAndExit_slots()
+{
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry
+                                                       | ScriptEngineSpy::IgnoreFunctionExit));
+    // slots
+    {
+        eng.globalObject().setProperty("qobj", eng.newQObject(this));
+        spy->clear();
+        eng.evaluate("qobj.testSlot(123)");
+        QCOMPARE(spy->count(), 4);
+        // evaluate() entry
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry);
+        // testSlot() entry
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(1).scriptId, qint64(-1));
+        // testSlot() exit
+        QCOMPARE(spy->at(2).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(2).scriptId, qint64(-1));
+        QVERIFY(spy->at(2).value.isNumber());
+        QCOMPARE(spy->at(2).value.toNumber(), qsreal(123));
+        // evaluate() exit
+        QCOMPARE(spy->at(3).type, ScriptEngineEvent::FunctionExit);
+    }
+    delete spy;
+}
+
+/** check behaiviour of property accessors*/
+void tst_QScriptEngineAgent::functionEntryAndExit_property_set()
+{
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry
+                                                       | ScriptEngineSpy::IgnoreFunctionExit));
+    // property accessors
+    {
+        eng.globalObject().setProperty("qobj", eng.newQObject(this));
+        // set
+        spy->clear();
+        eng.evaluate("qobj.testProperty = 456");
+        QCOMPARE(spy->count(), 4);
+        // evaluate() entry
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry);
+        // setTestProperty() entry
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(1).scriptId, qint64(-1));
+        // setTestProperty() exit
+        QCOMPARE(spy->at(2).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(2).scriptId, qint64(-1));
+        QVERIFY(spy->at(2).value.isNumber());
+        QCOMPARE(spy->at(2).value.toNumber(), testProperty());
+        // evaluate() exit
+        QCOMPARE(spy->at(3).type, ScriptEngineEvent::FunctionExit);
+        QVERIFY(spy->at(3).value.strictlyEquals(spy->at(2).value));
+    }
+    delete spy;
+}
+
+/** check behaiviour of property accessors*/
+void tst_QScriptEngineAgent::functionEntryAndExit_property_get()
+{
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry
+                                                       | ScriptEngineSpy::IgnoreFunctionExit));
+    // property accessors
+    {
+        eng.globalObject().setProperty("qobj", eng.newQObject(this));
+        // set
+        eng.evaluate("qobj.testProperty = 456");
+        // get
+        spy->clear();
+        eng.evaluate("qobj.testProperty");
+        QCOMPARE(spy->count(), 4);
+        // evaluate() entry
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry);
+        // testProperty() entry
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(1).scriptId, qint64(-1));
+        // testProperty() exit
+        QCOMPARE(spy->at(2).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(2).scriptId, qint64(-1));
+        QVERIFY(spy->at(2).value.isNumber());
+        QCOMPARE(spy->at(2).value.toNumber(), testProperty());
+        // evaluate() exit
+        QCOMPARE(spy->at(3).type, ScriptEngineEvent::FunctionExit);
+        QVERIFY(spy->at(3).value.strictlyEquals(spy->at(2).value));
+    }
+    delete spy;
+}
+
+
+/** check behaiviour of calling script functions from c++*/
+void tst_QScriptEngineAgent::functionEntryAndExit_call()
+{
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry
+                                                       | ScriptEngineSpy::IgnoreFunctionExit));
+    // calling script functions from C++
+
+    {
+        QScriptValue fun = eng.evaluate("function foo() { return 123; }; foo");
+        QVERIFY(fun.isFunction());
+
+        spy->clear();
+        fun.call();
+        QCOMPARE(spy->count(), 2);
+
+        // entry
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry);
+        QVERIFY(spy->at(0).scriptId != -1);
+
+        // exit
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId);
+        QVERIFY(spy->at(1).value.isNumber());
+        QCOMPARE(spy->at(1).value.toNumber(), qsreal(123));
+    }
+    delete spy;
+}
+
+/** check behaiviour of native function returnning arg*/
+void tst_QScriptEngineAgent::functionEntryAndExit_functionReturn_call()
+{
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry
+                                                       | ScriptEngineSpy::IgnoreFunctionExit));
+    {
+        QScriptValue fun = eng.newFunction(nativeFunctionReturningArg);
+
+        spy->clear();
+        QScriptValueList args;
+        args << QScriptValue(&eng, 123);
+        fun.call(QScriptValue(), args);
+        QCOMPARE(spy->count(), 2);
+
+        // entry
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry);
+        QVERIFY(spy->at(0).scriptId == -1);
+
+        // exit
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId);
+        QVERIFY(spy->at(1).value.strictlyEquals(args.at(0)));
+    }
+    delete spy;
+}
+
+void tst_QScriptEngineAgent::functionEntryAndExit_functionReturn_construct()
+{
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry
+                                                       | ScriptEngineSpy::IgnoreFunctionExit));
+    {
+        QScriptValue fun = eng.newFunction(nativeFunctionReturningArg);
+
+        spy->clear();
+        QScriptValueList args;
+        args << QScriptValue(&eng, 123);
+        QScriptValue obj = fun.construct(args);
+
+        QVERIFY(args.at(0).isValid());
+        QVERIFY(args.at(0).isNumber());
+        QVERIFY(args.at(0).toNumber() == 123);
+
+        QCOMPARE(spy->count(), 2);
+
+        // entry
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry);
+        QVERIFY(spy->at(0).scriptId == -1);
+
+        // exit
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId);
+
+        QVERIFY(spy->at(1).value.strictlyEquals(args.at(0)));
+    }
+
+    delete spy;
+}
+
+/** check behaiviour of object creation with args (?)*/
+void tst_QScriptEngineAgent::functionEntryAndExit_objectCall()
+{
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreFunctionEntry
+                                                       | ScriptEngineSpy::IgnoreFunctionExit));
+    for (int x = 0; x < 2; ++x) {
+        QScriptValue fun = eng.evaluate("Boolean");
+
+        QVERIFY(!fun.isError());
+
+        spy->clear();
+        QScriptValueList args;
+        args << QScriptValue(&eng, true);
+        if (x)
+            fun.construct(args);
+        else
+            fun.call(QScriptValue(), args);
+        QCOMPARE(spy->count(), 2);
+
+        // entry
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry);
+        QVERIFY(spy->at(0).scriptId == -1);
+
+        // exit
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId);
+        QVERIFY(spy->at(1).value.equals(args.at(0)));
+    }
+    delete spy;
+}
+
+void tst_QScriptEngineAgent::positionChange_1()
+{
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnorePositionChange));
+    {
+        spy->clear();
+        eng.evaluate(";");
+        QEXPECT_FAIL("","JSC do not evaluate ';' to statemant",Continue);
+        QCOMPARE(spy->count(), 1);
+        if (spy->count()) {
+            QEXPECT_FAIL("","JSC do not evaluate ';' to statemant",Continue);
+            QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange);
+            QEXPECT_FAIL("","JSC do not evaluate ';' to statemant",Continue);
+            QVERIFY(spy->at(0).scriptId != -1);
+            QEXPECT_FAIL("","JSC do not evaluate ';' to statemant",Continue);
+            QCOMPARE(spy->at(0).lineNumber, 1);
+            QEXPECT_FAIL("","JSC do not evaluate ';' to statemant",Continue);
+            QCOMPARE(spy->at(0).columnNumber, 1);
+        }
+    }
+
+    {
+        spy->clear();
+        int lineNumber = 123;
+        eng.evaluate("1 + 2", "foo.qs", lineNumber);
+        QCOMPARE(spy->count(), 1);
+
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange);
+        QVERIFY(spy->at(0).scriptId != -1);
+        QCOMPARE(spy->at(0).lineNumber, lineNumber);
+        QCOMPARE(spy->at(0).columnNumber, 1);
+    }
+
+    {
+        spy->clear();
+        int lineNumber = 123;
+        eng.evaluate("var i = 0", "foo.qs", lineNumber);
+        QCOMPARE(spy->count(), 1);
+
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange);
+        QVERIFY(spy->at(0).scriptId != -1);
+        QCOMPARE(spy->at(0).lineNumber, lineNumber);
+        QCOMPARE(spy->at(0).columnNumber, 1);
+    }
+
+    QStringList lineTerminators;
+    lineTerminators << "\n" << "\r" << "\n\r" << "\r\n";
+    for (int i = 0; i < lineTerminators.size(); ++i) {
+        spy->clear();
+        int lineNumber = 456;
+        QString code = "1 + 2; 3 + 4;";
+        code.append(lineTerminators.at(i));
+        code.append("5 + 6");
+        eng.evaluate(code, "foo.qs", lineNumber);
+        QCOMPARE(spy->count(), 3);
+
+        // 1 + 2
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange);
+        QVERIFY(spy->at(0).scriptId != -1);
+        QCOMPARE(spy->at(0).lineNumber, lineNumber);
+        QCOMPARE(spy->at(0).columnNumber, 1);
+
+        // 3 + 4
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::PositionChange);
+        QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId);
+        QCOMPARE(spy->at(1).lineNumber, lineNumber);
+        QCOMPARE(spy->at(1).columnNumber, 8);
+
+        // 5 + 6
+        QCOMPARE(spy->at(2).type, ScriptEngineEvent::PositionChange);
+        QCOMPARE(spy->at(2).scriptId, spy->at(0).scriptId);
+        QCOMPARE(spy->at(2).lineNumber, lineNumber + 1);
+        QCOMPARE(spy->at(2).columnNumber, 1);
+    }
+    delete spy;
+}
+
+void tst_QScriptEngineAgent::positionChange_2()
+{
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnorePositionChange));
+    {
+        spy->clear();
+        int lineNumber = 789;
+        eng.evaluate("function foo() { return 123; }", "foo.qs", lineNumber);
+        QCOMPARE(spy->count(), 0);
+
+        eng.evaluate("foo()");
+        QCOMPARE(spy->count(), 2);
+
+        // foo()
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange);
+        QVERIFY(spy->at(0).scriptId != -1);
+        QCOMPARE(spy->at(0).lineNumber, 1);
+        QCOMPARE(spy->at(0).columnNumber, 1);
+
+        // return 123
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::PositionChange);
+        QVERIFY(spy->at(1).scriptId != spy->at(0).scriptId);
+        QCOMPARE(spy->at(1).lineNumber, lineNumber);
+        QCOMPARE(spy->at(1).columnNumber, 18);
+    }
+
+    {
+        spy->clear();
+        eng.evaluate("if (true) i = 1; else i = 0;");
+        QCOMPARE(spy->count(), 2);
+
+        // if
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange);
+        QVERIFY(spy->at(0).scriptId != -1);
+        QCOMPARE(spy->at(0).lineNumber, 1);
+        QCOMPARE(spy->at(0).columnNumber, 1);
+
+        // i = 1
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::PositionChange);
+        QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId);
+        QCOMPARE(spy->at(1).lineNumber, 1);
+        QCOMPARE(spy->at(1).columnNumber, 11);
+    }
+
+    {
+        spy->clear();
+        eng.evaluate("for (var i = 0; i < 2; ++i) { }");
+        QCOMPARE(spy->count(), 1);
+
+        // for
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange);
+        QVERIFY(spy->at(0).scriptId != -1);
+        QCOMPARE(spy->at(0).lineNumber, 1);
+        QCOMPARE(spy->at(0).columnNumber, 1);
+    }
+
+    {
+        spy->clear();
+        eng.evaluate("for (var i = 0; i < 2; ++i) { void(i); }");
+        QCOMPARE(spy->count(), 3);
+
+        // for
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange);
+        QVERIFY(spy->at(0).scriptId != -1);
+        QCOMPARE(spy->at(0).lineNumber, 1);
+        QCOMPARE(spy->at(0).columnNumber, 1);
+
+        // void(i)
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::PositionChange);
+        QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId);
+        QCOMPARE(spy->at(1).lineNumber, 1);
+        QCOMPARE(spy->at(1).columnNumber, 31);
+
+        // void(i)
+        QCOMPARE(spy->at(2).type, ScriptEngineEvent::PositionChange);
+        QCOMPARE(spy->at(2).scriptId, spy->at(0).scriptId);
+        QCOMPARE(spy->at(2).lineNumber, 1);
+        QCOMPARE(spy->at(2).columnNumber, 31);
+    }
+
+    {
+        spy->clear();
+        eng.evaluate("var i = 0; while (i < 2) { ++i; }");
+        QCOMPARE(spy->count(), 4);
+
+        // i = 0
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange);
+        QVERIFY(spy->at(0).scriptId != -1);
+        QCOMPARE(spy->at(0).lineNumber, 1);
+        QCOMPARE(spy->at(0).columnNumber, 1);
+
+        // while
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::PositionChange);
+        QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId);
+        QCOMPARE(spy->at(1).lineNumber, 1);
+        QCOMPARE(spy->at(1).columnNumber, 12);
+
+        // ++i
+        QCOMPARE(spy->at(2).type, ScriptEngineEvent::PositionChange);
+        QCOMPARE(spy->at(2).scriptId, spy->at(0).scriptId);
+        QCOMPARE(spy->at(2).lineNumber, 1);
+        QCOMPARE(spy->at(2).columnNumber, 28);
+
+        // ++i
+        QCOMPARE(spy->at(3).type, ScriptEngineEvent::PositionChange);
+        QCOMPARE(spy->at(3).scriptId, spy->at(0).scriptId);
+        QCOMPARE(spy->at(3).lineNumber, 1);
+        QCOMPARE(spy->at(3).columnNumber, 28);
+    }
+
+    {
+        spy->clear();
+        eng.evaluate("var i = 0; do { ++i; } while (i < 2)");
+        QCOMPARE(spy->count(), 5);
+
+        // i = 0
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange);
+        QVERIFY(spy->at(0).scriptId != -1);
+        QCOMPARE(spy->at(0).lineNumber, 1);
+        QCOMPARE(spy->at(0).columnNumber, 1);
+
+        // do
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::PositionChange);
+        QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId);
+        QCOMPARE(spy->at(1).lineNumber, 1);
+        QCOMPARE(spy->at(1).columnNumber, 12);
+
+        // ++i
+        QCOMPARE(spy->at(2).type, ScriptEngineEvent::PositionChange);
+        QCOMPARE(spy->at(2).scriptId, spy->at(0).scriptId);
+        QCOMPARE(spy->at(2).lineNumber, 1);
+        QCOMPARE(spy->at(2).columnNumber, 17);
+
+        // do
+        QCOMPARE(spy->at(3).type, ScriptEngineEvent::PositionChange);
+        QCOMPARE(spy->at(3).scriptId, spy->at(0).scriptId);
+        QCOMPARE(spy->at(3).lineNumber, 1);
+        QCOMPARE(spy->at(3).columnNumber, 12);
+
+        // ++i
+        QCOMPARE(spy->at(4).type, ScriptEngineEvent::PositionChange);
+        QCOMPARE(spy->at(4).scriptId, spy->at(0).scriptId);
+        QCOMPARE(spy->at(4).lineNumber, 1);
+        QCOMPARE(spy->at(4).columnNumber, 17);
+    }
+
+    {
+        spy->clear();
+        eng.evaluate("for (var i in { }) { void(i); }");
+        QCOMPARE(spy->count(), 1);
+
+        // for
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange);
+        QVERIFY(spy->at(0).scriptId != -1);
+        QCOMPARE(spy->at(0).lineNumber, 1);
+        QCOMPARE(spy->at(0).columnNumber, 1);
+    }
+
+    {
+        spy->clear();
+        eng.evaluate("for ( ; ; ) { break; }");
+        QCOMPARE(spy->count(), 2);
+
+        // for
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange);
+        QVERIFY(spy->at(0).scriptId != -1);
+        QCOMPARE(spy->at(0).lineNumber, 1);
+        QCOMPARE(spy->at(0).columnNumber, 1);
+
+        // break
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::PositionChange);
+        QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId);
+        QCOMPARE(spy->at(1).lineNumber, 1);
+        QCOMPARE(spy->at(1).columnNumber, 15);
+    }
+
+    {
+        spy->clear();
+        eng.evaluate("for (var i = 0 ; i < 2; ++i) { continue; }");
+        QCOMPARE(spy->count(), 3);
+
+        // for
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange);
+        QVERIFY(spy->at(0).scriptId != -1);
+        QCOMPARE(spy->at(0).lineNumber, 1);
+        QCOMPARE(spy->at(0).columnNumber, 1);
+
+        // continue
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::PositionChange);
+        QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId);
+        QCOMPARE(spy->at(1).lineNumber, 1);
+        QCOMPARE(spy->at(1).columnNumber, 32);
+
+        // continue
+        QCOMPARE(spy->at(2).type, ScriptEngineEvent::PositionChange);
+        QCOMPARE(spy->at(2).scriptId, spy->at(0).scriptId);
+        QCOMPARE(spy->at(2).lineNumber, 1);
+        QCOMPARE(spy->at(2).columnNumber, 32);
+    }
+
+    {
+        spy->clear();
+        eng.evaluate("with (this) { }");
+        QCOMPARE(spy->count(), 1);
+
+        // with
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange);
+        QVERIFY(spy->at(0).scriptId != -1);
+        QCOMPARE(spy->at(0).lineNumber, 1);
+        QCOMPARE(spy->at(0).columnNumber, 1);
+    }
+
+    {
+        spy->clear();
+        eng.evaluate("switch (undefined) { }");
+        QCOMPARE(spy->count(), 1);
+
+        // switch
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange);
+        QVERIFY(spy->at(0).scriptId != -1);
+        QCOMPARE(spy->at(0).lineNumber, 1);
+        QCOMPARE(spy->at(0).columnNumber, 1);
+    }
+
+    {
+        spy->clear();
+        eng.evaluate("switch (undefined) { default: i = 5; }");
+        QCOMPARE(spy->count(), 2);
+
+        // switch
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange);
+        QVERIFY(spy->at(0).scriptId != -1);
+        QCOMPARE(spy->at(0).lineNumber, 1);
+        QCOMPARE(spy->at(0).columnNumber, 1);
+
+        // i = 5
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::PositionChange);
+        QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId);
+        QCOMPARE(spy->at(1).lineNumber, 1);
+        QCOMPARE(spy->at(1).columnNumber, 31);
+    }
+
+    {
+        spy->clear();
+        eng.evaluate("switch (undefined) { case undefined: i = 5; break; }");
+        QCOMPARE(spy->count(), 3);
+
+        // switch
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange);
+        QVERIFY(spy->at(0).scriptId != -1);
+        QCOMPARE(spy->at(0).lineNumber, 1);
+        QCOMPARE(spy->at(0).columnNumber, 1);
+
+        // i = 5
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::PositionChange);
+        QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId);
+        QCOMPARE(spy->at(1).lineNumber, 1);
+        QCOMPARE(spy->at(1).columnNumber, 38);
+
+        // break
+        QCOMPARE(spy->at(2).type, ScriptEngineEvent::PositionChange);
+        QCOMPARE(spy->at(2).scriptId, spy->at(0).scriptId);
+        QCOMPARE(spy->at(2).lineNumber, 1);
+        QCOMPARE(spy->at(2).columnNumber, 45);
+    }
+
+    {
+        spy->clear();
+        eng.evaluate("throw 1");
+        QCOMPARE(spy->count(), 1);
+
+        // throw
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange);
+        QVERIFY(spy->at(0).scriptId != -1);
+        QCOMPARE(spy->at(0).lineNumber, 1);
+        QCOMPARE(spy->at(0).columnNumber, 1);
+    }
+
+    {
+        spy->clear();
+        eng.evaluate("try { throw 1; } catch(e) { i = e; } finally { i = 2; }");
+        QCOMPARE(spy->count(), 3);
+
+        // throw 1
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange);
+        QVERIFY(spy->at(0).scriptId != -1);
+        QCOMPARE(spy->at(0).lineNumber, 1);
+        QCOMPARE(spy->at(0).columnNumber, 7);
+
+        // i = e
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::PositionChange);
+        QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId);
+        QCOMPARE(spy->at(1).lineNumber, 1);
+        QCOMPARE(spy->at(1).columnNumber, 29);
+
+        // i = 2
+        QCOMPARE(spy->at(2).type, ScriptEngineEvent::PositionChange);
+        QCOMPARE(spy->at(2).scriptId, spy->at(0).scriptId);
+        QCOMPARE(spy->at(2).lineNumber, 1);
+        QCOMPARE(spy->at(2).columnNumber, 48);
+    }
+
+    {
+        spy->clear();
+        eng.evaluate("try { i = 1; } catch(e) { i = 2; } finally { i = 3; }");
+        QCOMPARE(spy->count(), 2);
+
+        // i = 1
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange);
+        QVERIFY(spy->at(0).scriptId != -1);
+        QCOMPARE(spy->at(0).lineNumber, 1);
+        QCOMPARE(spy->at(0).columnNumber, 7);
+
+        // i = 3
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::PositionChange);
+        QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId);
+        QCOMPARE(spy->at(1).lineNumber, 1);
+        QCOMPARE(spy->at(1).columnNumber, 46);
+    }
+
+    {
+        QEXPECT_FAIL("","I believe the test is wrong. Expressions shouldn't call positionChange "
+                     "because statement '1+2' will call it at least twice, why debugger have to "
+                     "stop here so many times?", Abort);
+        spy->clear();
+        eng.evaluate("c = {a: 10, b: 20}");
+        QCOMPARE(spy->count(), 2);
+
+        // a: 10
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::PositionChange);
+        QVERIFY(spy->at(0).scriptId != -1);
+        QCOMPARE(spy->at(0).lineNumber, 1);
+        QCOMPARE(spy->at(0).columnNumber, 1);
+
+        // b: 20
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::PositionChange);
+        QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId);
+        QCOMPARE(spy->at(1).lineNumber, 1);
+        QCOMPARE(spy->at(1).columnNumber, 20);
+    }
+    delete spy;
+}
+
+void tst_QScriptEngineAgent::exceptionThrowAndCatch()
+{
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreExceptionThrow
+                                                       | ScriptEngineSpy::IgnoreExceptionCatch));
+    {
+        spy->clear();
+        eng.evaluate(";");
+        QCOMPARE(spy->count(), 0);
+    }
+
+    {
+        spy->clear();
+        eng.evaluate("try { i = 5; } catch (e) { }");
+        QCOMPARE(spy->count(), 0);
+    }
+
+    {
+        spy->clear();
+        eng.evaluate("throw new Error('ciao');");
+        QCOMPARE(spy->count(), 1);
+
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::ExceptionThrow);
+        QVERIFY(spy->at(0).scriptId != -1);
+        QVERIFY(!spy->at(0).hasExceptionHandler);
+        QVERIFY(spy->at(0).value.isError());
+        QCOMPARE(spy->at(0).value.toString(), QString("Error: ciao"));
+    }
+
+    {
+        spy->clear();
+        eng.evaluate("try { throw new Error('ciao'); } catch (e) { }");
+        QCOMPARE(spy->count(), 2);
+
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::ExceptionThrow);
+        QVERIFY(spy->at(0).scriptId != -1);
+        QVERIFY(spy->at(0).hasExceptionHandler);
+        QVERIFY(spy->at(0).value.isError());
+        QCOMPARE(spy->at(0).value.toString(), QString("Error: ciao"));
+        QVERIFY(spy->at(0).scriptId != -1);
+
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::ExceptionCatch);
+        QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId);
+        QVERIFY(spy->at(1).value.strictlyEquals(spy->at(0).value));
+    }
+}
+
+void tst_QScriptEngineAgent::eventOrder_assigment()
+{
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng);
+    {
+        spy->clear();
+        eng.evaluate("i = 3; i = 5");
+        QCOMPARE(spy->count(), 6);
+        // load
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::ScriptLoad);
+        // evaluate() entry
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId);
+        // i = 3
+        QCOMPARE(spy->at(2).type, ScriptEngineEvent::PositionChange);
+        QCOMPARE(spy->at(2).scriptId, spy->at(0).scriptId);
+        // i = 5
+        QCOMPARE(spy->at(3).type, ScriptEngineEvent::PositionChange);
+        QCOMPARE(spy->at(3).scriptId, spy->at(0).scriptId);
+        // evaluate() exit
+        QCOMPARE(spy->at(4).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(4).scriptId, spy->at(0).scriptId);
+        // unload
+        QCOMPARE(spy->at(5).type, ScriptEngineEvent::ScriptUnload);
+        QCOMPARE(spy->at(5).scriptId, spy->at(0).scriptId);
+    }
+    delete spy;
+}
+
+void tst_QScriptEngineAgent::eventOrder_functionDefinition()
+{
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng);
+    {
+        spy->clear();
+        eng.evaluate("function foo(arg) { void(arg); }");
+        QCOMPARE(spy->count(), 3);
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::ScriptLoad);
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(2).type, ScriptEngineEvent::FunctionExit);
+
+        eng.evaluate("foo(123)");
+        QCOMPARE(spy->count(), 13);
+        // load
+        QCOMPARE(spy->at(3).type, ScriptEngineEvent::ScriptLoad);
+        QVERIFY(spy->at(3).scriptId != spy->at(0).scriptId);
+        // evaluate() entry
+        QCOMPARE(spy->at(4).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(4).scriptId, spy->at(3).scriptId);
+        // foo()
+        QCOMPARE(spy->at(5).type, ScriptEngineEvent::PositionChange);
+        QCOMPARE(spy->at(5).scriptId, spy->at(3).scriptId);
+        // new context
+        QCOMPARE(spy->at(6).type, ScriptEngineEvent::ContextPush);
+        // foo() entry
+        QCOMPARE(spy->at(7).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(7).scriptId, spy->at(0).scriptId);
+        // void(arg)
+        QCOMPARE(spy->at(8).type, ScriptEngineEvent::PositionChange);
+        QCOMPARE(spy->at(8).scriptId, spy->at(0).scriptId);
+        // foo() exit
+        QCOMPARE(spy->at(9).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(9).scriptId, spy->at(0).scriptId);
+        // restore context
+        QCOMPARE(spy->at(10).type, ScriptEngineEvent::ContextPop);
+        // evaluate() exit
+        QCOMPARE(spy->at(11).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(11).scriptId, spy->at(3).scriptId);
+        // unload
+        QCOMPARE(spy->at(12).type, ScriptEngineEvent::ScriptUnload);
+        QCOMPARE(spy->at(12).scriptId, spy->at(3).scriptId);
+
+        eng.evaluate("foo = null");
+        eng.collectGarbage();
+    }
+    delete spy;
+}
+
+void tst_QScriptEngineAgent::eventOrder_throwError()
+{
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng);
+    {
+        spy->clear();
+        eng.evaluate("throw new Error('ciao')");
+        QCOMPARE(spy->count(), 10);
+        // load
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::ScriptLoad);
+        // evaluate() entry
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry);
+        // throw
+        QCOMPARE(spy->at(2).type, ScriptEngineEvent::PositionChange);
+        // new context
+        QCOMPARE(spy->at(3).type, ScriptEngineEvent::ContextPush);
+        // Error constructor entry
+        QCOMPARE(spy->at(4).type, ScriptEngineEvent::FunctionEntry);
+        // Error constructor exit
+        QCOMPARE(spy->at(5).type, ScriptEngineEvent::FunctionExit);
+        // restore context
+        QCOMPARE(spy->at(6).type, ScriptEngineEvent::ContextPop);
+        // exception
+        QCOMPARE(spy->at(7).type, ScriptEngineEvent::ExceptionThrow);
+        QVERIFY(!spy->at(7).hasExceptionHandler);
+        // evaluate() exit
+        QCOMPARE(spy->at(8).type, ScriptEngineEvent::FunctionExit);
+        // unload
+        QCOMPARE(spy->at(9).type, ScriptEngineEvent::ScriptUnload);
+    }
+    delete spy;
+}
+
+void tst_QScriptEngineAgent::eventOrder_throwAndCatch()
+{
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng);
+    {
+        spy->clear();
+        eng.evaluate("try { throw new Error('ciao') } catch (e) { void(e); }");
+        QCOMPARE(spy->count(), 12);
+        // load
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::ScriptLoad);
+        // evaluate() entry
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry);
+        // throw
+        QCOMPARE(spy->at(2).type, ScriptEngineEvent::PositionChange);
+        // new context
+        QCOMPARE(spy->at(3).type, ScriptEngineEvent::ContextPush);
+        // Error constructor entry
+        QCOMPARE(spy->at(4).type, ScriptEngineEvent::FunctionEntry);
+        // Error constructor exit
+        QCOMPARE(spy->at(5).type, ScriptEngineEvent::FunctionExit);
+        // restore context
+        QCOMPARE(spy->at(6).type, ScriptEngineEvent::ContextPop);
+        // exception
+        QCOMPARE(spy->at(7).type, ScriptEngineEvent::ExceptionThrow);
+        QVERIFY(spy->at(7).value.isError());
+        QVERIFY(spy->at(7).hasExceptionHandler);
+        // catch
+        QCOMPARE(spy->at(8).type, ScriptEngineEvent::ExceptionCatch);
+        QVERIFY(spy->at(8).value.isError());
+        // void(e)
+        QCOMPARE(spy->at(9).type, ScriptEngineEvent::PositionChange);
+        // evaluate() exit
+        QCOMPARE(spy->at(10).type, ScriptEngineEvent::FunctionExit);
+        QVERIFY(spy->at(10).value.isUndefined());
+        // unload
+        QCOMPARE(spy->at(11).type, ScriptEngineEvent::ScriptUnload);
+    }
+    delete spy;
+}
+
+void tst_QScriptEngineAgent::eventOrder_functions()
+{
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng);
+    {
+        spy->clear();
+        eng.evaluate("function foo(arg) { return bar(arg); }");
+        eng.evaluate("function bar(arg) { return arg; }");
+        QCOMPARE(spy->count(), 6);
+
+        eng.evaluate("foo(123)");
+        QCOMPARE(spy->count(), 21);
+
+        // load
+        QCOMPARE(spy->at(6).type, ScriptEngineEvent::ScriptLoad);
+        // evaluate() entry
+        QCOMPARE(spy->at(7).type, ScriptEngineEvent::FunctionEntry);
+        // foo(123)
+        QCOMPARE(spy->at(8).type, ScriptEngineEvent::PositionChange);
+        // new context
+        QCOMPARE(spy->at(9).type, ScriptEngineEvent::ContextPush);
+        // foo() entry
+        QCOMPARE(spy->at(10).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(10).scriptId, spy->at(0).scriptId);
+        // return bar(arg)
+        QCOMPARE(spy->at(11).type, ScriptEngineEvent::PositionChange);
+        QCOMPARE(spy->at(11).scriptId, spy->at(0).scriptId);
+        // new context
+        QCOMPARE(spy->at(12).type, ScriptEngineEvent::ContextPush);
+        // bar() entry
+        QCOMPARE(spy->at(13).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(13).scriptId, spy->at(3).scriptId);
+        // return arg
+        QCOMPARE(spy->at(14).type, ScriptEngineEvent::PositionChange);
+        QCOMPARE(spy->at(14).scriptId, spy->at(3).scriptId);
+        // bar() exit
+        QCOMPARE(spy->at(15).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(15).scriptId, spy->at(3).scriptId);
+        QVERIFY(spy->at(15).value.isNumber());
+        // restore context
+        QCOMPARE(spy->at(16).type, ScriptEngineEvent::ContextPop);
+        // foo() exit
+        QCOMPARE(spy->at(17).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(17).scriptId, spy->at(0).scriptId);
+        QVERIFY(spy->at(17).value.isNumber());
+        // restore context
+        QCOMPARE(spy->at(18).type, ScriptEngineEvent::ContextPop);
+        // evaluate() exit
+        QCOMPARE(spy->at(19).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(19).scriptId, spy->at(6).scriptId);
+        QVERIFY(spy->at(19).value.isNumber());
+        // unload
+        QCOMPARE(spy->at(20).type, ScriptEngineEvent::ScriptUnload);
+        QCOMPARE(spy->at(20).scriptId, spy->at(6).scriptId);
+
+        // redefine bar()
+        eng.evaluate("function bar(arg) { throw new Error(arg); }");
+        eng.collectGarbage();
+        QCOMPARE(spy->count(), 25);
+        QCOMPARE(spy->at(21).type, ScriptEngineEvent::ScriptLoad);
+        QCOMPARE(spy->at(22).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(23).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(24).type, ScriptEngineEvent::ScriptUnload);
+        QCOMPARE(spy->at(24).scriptId, spy->at(3).scriptId);
+
+        eng.evaluate("foo('ciao')");
+
+        QCOMPARE(spy->count(), 45);
+
+        // load
+        QCOMPARE(spy->at(25).type, ScriptEngineEvent::ScriptLoad);
+        // evaluate() entry
+        QCOMPARE(spy->at(26).type, ScriptEngineEvent::FunctionEntry);
+        // foo('ciao')
+        QCOMPARE(spy->at(27).type, ScriptEngineEvent::PositionChange);
+        // new context
+        QCOMPARE(spy->at(28).type, ScriptEngineEvent::ContextPush);
+        // foo() entry
+        QCOMPARE(spy->at(29).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(29).scriptId, spy->at(0).scriptId);
+        // return bar(arg)
+        QCOMPARE(spy->at(30).type, ScriptEngineEvent::PositionChange);
+        QCOMPARE(spy->at(30).scriptId, spy->at(0).scriptId);
+        // new context
+        QCOMPARE(spy->at(31).type, ScriptEngineEvent::ContextPush);
+        // bar() entry
+        QCOMPARE(spy->at(32).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(32).scriptId, spy->at(21).scriptId);
+        // throw
+        QCOMPARE(spy->at(33).type, ScriptEngineEvent::PositionChange);
+        QCOMPARE(spy->at(33).scriptId, spy->at(21).scriptId);
+        // new context
+        QCOMPARE(spy->at(34).type, ScriptEngineEvent::ContextPush);
+        // Error constructor entry
+        QCOMPARE(spy->at(35).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(35).scriptId, qint64(-1));
+        // Error constructor exit
+        QCOMPARE(spy->at(36).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(36).scriptId, qint64(-1));
+        // restore context
+        QCOMPARE(spy->at(37).type, ScriptEngineEvent::ContextPop);
+        // exception
+        QCOMPARE(spy->at(38).type, ScriptEngineEvent::ExceptionThrow);
+        QCOMPARE(spy->at(38).scriptId, spy->at(21).scriptId);
+        QVERIFY(!spy->at(38).hasExceptionHandler);
+        // bar() exit
+        QCOMPARE(spy->at(39).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(39).scriptId, spy->at(21).scriptId);
+        QVERIFY(spy->at(39).value.isError());
+        // restore context
+        QCOMPARE(spy->at(40).type, ScriptEngineEvent::ContextPop);
+        // foo() exit
+        QCOMPARE(spy->at(41).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(41).scriptId, spy->at(0).scriptId);
+        QVERIFY(spy->at(41).value.isError());
+        // restore context
+        QCOMPARE(spy->at(42).type, ScriptEngineEvent::ContextPop);
+        // evaluate() exit
+        QCOMPARE(spy->at(43).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(43).scriptId, spy->at(26).scriptId);
+        QVERIFY(spy->at(43).value.isError());
+        // unload
+        QCOMPARE(spy->at(44).type, ScriptEngineEvent::ScriptUnload);
+        QCOMPARE(spy->at(44).scriptId, spy->at(25).scriptId);
+    }
+    delete spy;
+}
+
+void tst_QScriptEngineAgent::eventOrder_throwCatchFinally()
+{
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng);
+    {
+        spy->clear();
+        eng.evaluate("try { throw 1; } catch(e) { i = e; } finally { i = 2; }");
+        QCOMPARE(spy->count(), 9);
+
+        // load
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::ScriptLoad);
+        // evaluate() entry
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry);
+        // throw 1
+        QCOMPARE(spy->at(2).type, ScriptEngineEvent::PositionChange);
+        // i = e
+        QCOMPARE(spy->at(3).type, ScriptEngineEvent::ExceptionThrow);
+        // catch
+        QCOMPARE(spy->at(4).type, ScriptEngineEvent::ExceptionCatch);
+        // i = e
+        QCOMPARE(spy->at(5).type, ScriptEngineEvent::PositionChange);
+        // i = 2
+        QCOMPARE(spy->at(6).type, ScriptEngineEvent::PositionChange);
+        // evaluate() exit
+        QCOMPARE(spy->at(7).type, ScriptEngineEvent::FunctionExit);
+        // unload
+        QCOMPARE(spy->at(8).type, ScriptEngineEvent::ScriptUnload);
+    }
+    delete spy;
+}
+
+void tst_QScriptEngineAgent::eventOrder_signalsHandling()
+{
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng);
+    // signal handling
+    {
+        spy->clear();
+        QScriptValue fun = eng.evaluate("(function(arg) { throw Error(arg); })");
+        QVERIFY(fun.isFunction());
+        QCOMPARE(spy->count(), 4);
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::ScriptLoad);
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(2).type, ScriptEngineEvent::PositionChange);
+        QCOMPARE(spy->at(3).type, ScriptEngineEvent::FunctionExit);
+
+        qScriptConnect(this, SIGNAL(testSignal(double)),
+                       QScriptValue(), fun);
+
+        emit testSignal(123);
+
+        QCOMPARE(spy->count(), 14);
+        // new context
+        QCOMPARE(spy->at(4).type, ScriptEngineEvent::ContextPush);
+        // anonymous function entry
+        QCOMPARE(spy->at(5).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(5).scriptId, spy->at(0).scriptId);
+        // throw statement
+        QCOMPARE(spy->at(6).type, ScriptEngineEvent::PositionChange);
+        QCOMPARE(spy->at(6).scriptId, spy->at(0).scriptId);
+        // new context
+        QCOMPARE(spy->at(7).type, ScriptEngineEvent::ContextPush);
+        // Error constructor entry
+        QCOMPARE(spy->at(8).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(8).scriptId, qint64(-1));
+        // Error constructor exit
+        QCOMPARE(spy->at(9).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(9).scriptId, qint64(-1));
+        // restore context
+        QCOMPARE(spy->at(10).type, ScriptEngineEvent::ContextPop);
+        // exception
+        QCOMPARE(spy->at(11).type, ScriptEngineEvent::ExceptionThrow);
+        QCOMPARE(spy->at(11).scriptId, spy->at(0).scriptId);
+        QVERIFY(spy->at(11).value.isError());
+        QVERIFY(!spy->at(11).hasExceptionHandler);
+        // anonymous function exit
+        QCOMPARE(spy->at(12).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(12).scriptId, spy->at(0).scriptId);
+        QVERIFY(spy->at(12).value.isError());
+        // restore context
+        QCOMPARE(spy->at(13).type, ScriptEngineEvent::ContextPop);
+    }
+    delete spy;
+}
+
+class DoubleAgent : public ScriptEngineSpy
+{
+public:
+    DoubleAgent(QScriptEngine *engine) : ScriptEngineSpy(engine) { }
+    ~DoubleAgent() { }
+
+    void positionChange(qint64 scriptId, int lineNumber, int columnNumber)
+    {
+        if (lineNumber == 123)
+            engine()->evaluate("1 + 2");
+        ScriptEngineSpy::positionChange(scriptId, lineNumber, columnNumber);
+    }
+};
+
+void tst_QScriptEngineAgent::recursiveObserve()
+{
+    QScriptEngine eng;
+    DoubleAgent *spy = new DoubleAgent(&eng);
+
+    eng.evaluate("3 + 4", "foo.qs", 123);
+
+    QCOMPARE(spy->count(), 10);
+
+    int i = 0;
+    // load "3 + 4"    
+    QCOMPARE(spy->at(i).type, ScriptEngineEvent::ScriptLoad);
+    i++;
+    // evaluate() entry
+    QCOMPARE(spy->at(i).type, ScriptEngineEvent::FunctionEntry);
+    i++;
+    // load "1 + 2"
+    QCOMPARE(spy->at(i).type, ScriptEngineEvent::ScriptLoad);
+    i++;
+    // evaluate() entry
+    QCOMPARE(spy->at(i).type, ScriptEngineEvent::FunctionEntry);
+    i++;
+    // 1 + 2
+    QCOMPARE(spy->at(i).type, ScriptEngineEvent::PositionChange);
+    QCOMPARE(spy->at(i).scriptId, spy->at(2).scriptId);
+    i++;
+    // evaluate() exit
+    QCOMPARE(spy->at(i).type, ScriptEngineEvent::FunctionExit);
+    i++;
+    // unload "1 + 2"
+    QCOMPARE(spy->at(i).type, ScriptEngineEvent::ScriptUnload);
+    QCOMPARE(spy->at(i).scriptId, spy->at(2).scriptId);
+    i++;
+    // 3 + 4
+    QCOMPARE(spy->at(i).type, ScriptEngineEvent::PositionChange);
+    QCOMPARE(spy->at(i).scriptId, spy->at(0).scriptId);
+    i++;
+    // evaluate() exit
+    QCOMPARE(spy->at(i).type, ScriptEngineEvent::FunctionExit);
+    i++;
+    // unload "3 + 4"
+    QCOMPARE(spy->at(i).type, ScriptEngineEvent::ScriptUnload);
+    QCOMPARE(spy->at(i).scriptId, spy->at(0).scriptId);
+}
+
+
+/** When second agent is attached to Engine the first one should be deatached */
+void tst_QScriptEngineAgent::multipleAgents()
+{
+    QScriptEngine eng;
+    QCOMPARE(eng.agent(), (QScriptEngineAgent *)0);
+    ScriptEngineSpy *spy1 = new ScriptEngineSpy(&eng);
+    QCOMPARE(eng.agent(), (QScriptEngineAgent*)spy1);
+    ScriptEngineSpy *spy2 = new ScriptEngineSpy(&eng);
+    QCOMPARE(eng.agent(), (QScriptEngineAgent*)spy2);
+
+    eng.evaluate("1 + 2");
+    QCOMPARE(spy1->count(), 0);
+    QCOMPARE(spy2->count(), 5);
+
+    spy2->clear();
+    eng.setAgent(spy1);
+    eng.evaluate("1 + 2");
+    QCOMPARE(spy2->count(), 0);
+    QCOMPARE(spy1->count(), 5);
+}
+
+void tst_QScriptEngineAgent::syntaxError()
+{
+    /* This test was changed. Old backend didn't generate events in exception objects creation time
+        JSC does */
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng);
+    {
+        int i = 0;
+        spy->clear();
+        eng.evaluate("{");
+        
+        QCOMPARE(spy->count(), 9);
+
+        QCOMPARE(spy->at(i).type, ScriptEngineEvent::ScriptLoad);
+        QVERIFY(spy->at(i).scriptId != -1);
+        i = 1;
+        QCOMPARE(spy->at(i).type, ScriptEngineEvent::FunctionEntry);
+        QCOMPARE(spy->at(i).scriptId, spy->at(0).scriptId);
+
+        //create exception
+
+        i = 2;
+        QCOMPARE(spy->at(i).type, ScriptEngineEvent::ContextPush);
+        QEXPECT_FAIL("","The test is broken, contextPush event do not provide scriptId", Continue);
+        QVERIFY(spy->at(i).scriptId == -1);
+        i = 3;
+        QCOMPARE(spy->at(i).type, ScriptEngineEvent::FunctionEntry);
+        QVERIFY(spy->at(i).scriptId == -1);
+        i = 4;
+        QCOMPARE(spy->at(i).type, ScriptEngineEvent::FunctionExit);
+        QVERIFY(spy->at(i).scriptId == -1);
+        i = 5;
+        QCOMPARE(spy->at(i).type, ScriptEngineEvent::ContextPop);
+        QEXPECT_FAIL("","The test is broken, contextPop event do not provide scriptId", Continue);
+        QVERIFY(spy->at(i).scriptId == -1);
+        i = 6;
+        QCOMPARE(spy->at(i).type, ScriptEngineEvent::ExceptionThrow);
+        QCOMPARE(spy->at(i).scriptId, spy->at(0).scriptId);
+        QVERIFY(!spy->at(i).hasExceptionHandler);
+        QVERIFY(spy->at(i).value.isError());
+        QEXPECT_FAIL("","There are other messages in JSC",Continue);
+        QCOMPARE(spy->at(i).value.toString(), QString("SyntaxError: Expected `}'"));
+        QCOMPARE(spy->at(i).scriptId, spy->at(0).scriptId);
+        i = 7;
+        //exit script
+        QCOMPARE(spy->at(i).type, ScriptEngineEvent::FunctionExit);
+        QCOMPARE(spy->at(i).scriptId, spy->at(0).scriptId);
+        i = 8;
+        QCOMPARE(spy->at(i).type, ScriptEngineEvent::ScriptUnload);
+        QCOMPARE(spy->at(i).scriptId, spy->at(0).scriptId);
+    }
+}
+
+void tst_QScriptEngineAgent::extension_invoctaion()
+{
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreDebuggerInvocationRequest
+                                                   | ScriptEngineSpy::IgnoreScriptLoad));
+    // DebuggerInvocationRequest
+    {
+        spy->clear();
+
+        QString fileName = "foo.qs";
+        int lineNumber = 123;
+        QScriptValue ret = eng.evaluate("debugger", fileName, lineNumber);
+
+        QCOMPARE(spy->count(), 2);
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::ScriptLoad);
+        QCOMPARE(spy->at(1).type, ScriptEngineEvent::DebuggerInvocationRequest);
+        QCOMPARE(spy->at(1).scriptId, spy->at(0).scriptId);
+        QCOMPARE(spy->at(1).lineNumber, lineNumber);
+        QCOMPARE(spy->at(1).columnNumber, 1);
+
+        QEXPECT_FAIL("","In JSC Eval('debugger') returns undefined",Abort);
+        QVERIFY(ret.isString());
+        QCOMPARE(ret.toString(), QString::fromLatin1("extension(DebuggerInvocationRequest)"));
+    }
+    delete spy;
+}
+
+void tst_QScriptEngineAgent::extension()
+{
+    QScriptEngine eng;
+    ScriptEngineSpy *spy = new ScriptEngineSpy(&eng, ~(ScriptEngineSpy::IgnoreDebuggerInvocationRequest
+                                                   | ScriptEngineSpy::IgnoreScriptLoad));
+
+    {
+        spy->clear();
+        spy->enableIgnoreFlags(ScriptEngineSpy::IgnoreDebuggerInvocationRequest);
+
+        QScriptValue ret = eng.evaluate("debugger");
+
+        QCOMPARE(spy->count(), 1);
+        QCOMPARE(spy->at(0).type, ScriptEngineEvent::ScriptLoad);
+        QVERIFY(ret.isUndefined());
+    }
+    delete spy;
+}
+
+class TestIsEvaluatingAgent : public QScriptEngineAgent
+{
+public:
+    TestIsEvaluatingAgent(QScriptEngine *engine)
+        : QScriptEngineAgent(engine), wasEvaluating(false)
+    { engine->setAgent(this); }
+    bool supportsExtension(Extension ext) const
+    { return ext == DebuggerInvocationRequest; }
+    QVariant extension(Extension, const QVariant &)
+    { wasEvaluating = engine()->isEvaluating(); return QVariant(); }
+
+    bool wasEvaluating;
+};
+
+void tst_QScriptEngineAgent::isEvaluatingInExtension()
+{
+    QScriptEngine eng;
+    TestIsEvaluatingAgent *spy = new TestIsEvaluatingAgent(&eng);
+    QVERIFY(!spy->wasEvaluating);
+    eng.evaluate("debugger");
+    QVERIFY(spy->wasEvaluating);
+}
+
+class NewSpy :public QScriptEngineAgent
+{
+    bool m_result;
+public:
+  NewSpy(QScriptEngine* eng) : QScriptEngineAgent(eng), m_result(false) {}
+  void functionExit (qint64, const QScriptValue &scriptValue)
+  {
+      if (engine()->hasUncaughtException()) m_result = true;
+  }
+
+  bool isPass() { return m_result; }
+  void reset() { m_result =  false; }
+};
+
+void tst_QScriptEngineAgent::hasUncaughtException()
+{
+  QScriptEngine eng;
+  NewSpy* spy = new NewSpy(&eng);
+  eng.setAgent(spy);
+  QScriptValue scriptValue;
+
+  // Check unhandled exception.
+  eng.evaluate("function init () {Unknown.doSth ();}");
+  scriptValue = QScriptValue(eng.globalObject().property("init")).call();
+  QVERIFY(eng.hasUncaughtException());
+  QVERIFY2(spy->isPass(), "At least one of a functionExit event should set hasUncaughtException flag.");
+  spy->reset();
+
+  // Check catched exception.
+  eng.evaluate("function innerFoo() { throw new Error('ciao') }");
+  eng.evaluate("function foo() {try { innerFoo() } catch (e) {} }");
+  scriptValue = QScriptValue(eng.globalObject().property("foo")).call();
+  QVERIFY(!eng.hasUncaughtException());
+  QVERIFY2(spy->isPass(), "At least one of a functionExit event should set hasUncaughtException flag.");
+}
+
+
+QTEST_MAIN(tst_QScriptEngineAgent)
+#include "tst_qscriptengineagent.moc"