diff options
Diffstat (limited to 'Source/JavaScriptCore/qt/tests/qscriptengine')
-rw-r--r-- | Source/JavaScriptCore/qt/tests/qscriptengine/qscriptengine.pro | 8 | ||||
-rw-r--r-- | Source/JavaScriptCore/qt/tests/qscriptengine/tst_qscriptengine.cpp | 734 |
2 files changed, 742 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/qt/tests/qscriptengine/qscriptengine.pro b/Source/JavaScriptCore/qt/tests/qscriptengine/qscriptengine.pro new file mode 100644 index 0000000..d521dd8 --- /dev/null +++ b/Source/JavaScriptCore/qt/tests/qscriptengine/qscriptengine.pro @@ -0,0 +1,8 @@ +TEMPLATE = app +TARGET = tst_qscriptengine +QT += testlib +isEmpty(OUTPUT_DIR): OUTPUT_DIR = ../../../.. +include(../tests.pri) + +SOURCES += tst_qscriptengine.cpp + diff --git a/Source/JavaScriptCore/qt/tests/qscriptengine/tst_qscriptengine.cpp b/Source/JavaScriptCore/qt/tests/qscriptengine/tst_qscriptengine.cpp new file mode 100644 index 0000000..dabcfb2 --- /dev/null +++ b/Source/JavaScriptCore/qt/tests/qscriptengine/tst_qscriptengine.cpp @@ -0,0 +1,734 @@ +/* + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "qscriptengine.h" +#include "qscriptprogram.h" +#include "qscriptsyntaxcheckresult.h" +#include "qscriptvalue.h" +#include <QtCore/qnumeric.h> +#include <QtTest/qtest.h> + +class tst_QScriptEngine : public QObject { + Q_OBJECT + +public: + tst_QScriptEngine() {} + virtual ~tst_QScriptEngine() {} + +public slots: + void init() {} + void cleanup() {} + +private slots: + void newFunction(); + void newObject(); + void globalObject(); + void evaluate(); + void collectGarbage(); + void reportAdditionalMemoryCost(); + void nullValue(); + void undefinedValue(); + void evaluateProgram(); + void checkSyntax_data(); + void checkSyntax(); + void toObject(); + void toObjectTwoEngines(); + void newArray(); + void uncaughtException(); + void newDate(); +}; + +/* Evaluating a script that throw an unhandled exception should return an invalid value. */ +void tst_QScriptEngine::evaluate() +{ + QScriptEngine engine; + QVERIFY2(engine.evaluate("1+1").isValid(), "the expression should be evaluated and an valid result should be returned"); + QVERIFY2(engine.evaluate("ping").isValid(), "Script throwing an unhandled exception should return an exception value"); +} + +static QScriptValue myFunction(QScriptContext*, QScriptEngine* eng) +{ + return eng->nullValue(); +} + +static QScriptValue myFunctionWithArg(QScriptContext*, QScriptEngine* eng, void* arg) +{ + int* result = reinterpret_cast<int*>(arg); + return QScriptValue(eng, *result); +} + +static QScriptValue myFunctionThatReturns(QScriptContext*, QScriptEngine* eng) +{ + return QScriptValue(eng, 42); +} + +static QScriptValue myFunctionThatReturnsWithoutEngine(QScriptContext*, QScriptEngine*) +{ + return QScriptValue(1024); +} + +static QScriptValue myFunctionThatReturnsWrongEngine(QScriptContext*, QScriptEngine*, void* arg) +{ + QScriptEngine* wrongEngine = reinterpret_cast<QScriptEngine*>(arg); + return QScriptValue(wrongEngine, 42); +} + +void tst_QScriptEngine::newFunction() +{ + QScriptEngine eng; + { + QScriptValue fun = eng.newFunction(myFunction); + QCOMPARE(fun.isValid(), true); + QCOMPARE(fun.isFunction(), true); + QCOMPARE(fun.isObject(), true); + // QCOMPARE(fun.scriptClass(), (QScriptClass*)0); + // a prototype property is automatically constructed + { + QScriptValue prot = fun.property("prototype", QScriptValue::ResolveLocal); + QVERIFY(prot.isObject()); + QVERIFY(prot.property("constructor").strictlyEquals(fun)); + QEXPECT_FAIL("", "JSCallbackObject::getOwnPropertyDescriptor() doesn't return correct information yet", Continue); + QCOMPARE(fun.propertyFlags("prototype"), QScriptValue::Undeletable); + QEXPECT_FAIL("", "WebKit bug: 40613 (The JSObjectSetProperty doesn't overwrite property flags)", Continue); + QCOMPARE(prot.propertyFlags("constructor"), QScriptValue::PropertyFlags(QScriptValue::Undeletable | QScriptValue::SkipInEnumeration)); + } + // prototype should be Function.prototype + QCOMPARE(fun.prototype().isValid(), true); + QCOMPARE(fun.prototype().isFunction(), true); + QCOMPARE(fun.prototype().strictlyEquals(eng.evaluate("Function.prototype")), true); + + QCOMPARE(fun.call().isNull(), true); + // QCOMPARE(fun.construct().isObject(), true); + } + // the overload that takes an extra argument + { + int expectedResult = 42; + QScriptValue fun = eng.newFunction(myFunctionWithArg, reinterpret_cast<void*>(&expectedResult)); + QVERIFY(fun.isFunction()); + // QCOMPARE(fun.scriptClass(), (QScriptClass*)0); + // a prototype property is automatically constructed + { + QScriptValue prot = fun.property("prototype", QScriptValue::ResolveLocal); + QVERIFY(prot.isObject()); + QVERIFY(prot.property("constructor").strictlyEquals(fun)); + QEXPECT_FAIL("", "JSCallbackObject::getOwnPropertyDescriptor() doesn't return correct information yet", Continue); + QCOMPARE(fun.propertyFlags("prototype"), QScriptValue::Undeletable); + QEXPECT_FAIL("", "WebKit bug: 40613 (The JSObjectSetProperty doesn't overwrite property flags)", Continue); + QCOMPARE(prot.propertyFlags("constructor"), QScriptValue::PropertyFlags(QScriptValue::Undeletable | QScriptValue::SkipInEnumeration)); + } + // prototype should be Function.prototype + QCOMPARE(fun.prototype().isValid(), true); + QCOMPARE(fun.prototype().isFunction(), true); + QCOMPARE(fun.prototype().strictlyEquals(eng.evaluate("Function.prototype")), true); + + QScriptValue result = fun.call(); + QCOMPARE(result.isNumber(), true); + QCOMPARE(result.toInt32(), expectedResult); + } + // the overload that takes a prototype + { + QScriptValue proto = eng.newObject(); + QScriptValue fun = eng.newFunction(myFunction, proto); + QCOMPARE(fun.isValid(), true); + QCOMPARE(fun.isFunction(), true); + QCOMPARE(fun.isObject(), true); + // internal prototype should be Function.prototype + QCOMPARE(fun.prototype().isValid(), true); + QCOMPARE(fun.prototype().isFunction(), true); + QCOMPARE(fun.prototype().strictlyEquals(eng.evaluate("Function.prototype")), true); + // public prototype should be the one we passed + QCOMPARE(fun.property("prototype").strictlyEquals(proto), true); + QEXPECT_FAIL("", "JSCallbackObject::getOwnPropertyDescriptor() doesn't return correct information yet", Continue); + QCOMPARE(fun.propertyFlags("prototype"), QScriptValue::Undeletable); + QCOMPARE(proto.property("constructor").strictlyEquals(fun), true); + QEXPECT_FAIL("", "WebKit bug: 40613 (The JSObjectSetProperty doesn't overwrite property flags)", Continue); + QCOMPARE(proto.propertyFlags("constructor"), QScriptValue::PropertyFlags(QScriptValue::Undeletable | QScriptValue::SkipInEnumeration)); + + QCOMPARE(fun.call().isNull(), true); + // QCOMPARE(fun.construct().isObject(), true); + } + // whether the return value is correct + { + QScriptValue fun = eng.newFunction(myFunctionThatReturns); + QCOMPARE(fun.isValid(), true); + QCOMPARE(fun.isFunction(), true); + QCOMPARE(fun.isObject(), true); + + QScriptValue result = fun.call(); + QCOMPARE(result.isNumber(), true); + QCOMPARE(result.toInt32(), 42); + } + // whether the return value is assigned to the correct engine + { + QScriptValue fun = eng.newFunction(myFunctionThatReturnsWithoutEngine); + QCOMPARE(fun.isValid(), true); + QCOMPARE(fun.isFunction(), true); + QCOMPARE(fun.isObject(), true); + + QScriptValue result = fun.call(); + QCOMPARE(result.engine(), &eng); + QCOMPARE(result.isNumber(), true); + QCOMPARE(result.toInt32(), 1024); + } + // whether the return value is undefined when returning a value with wrong engine + { + QScriptEngine wrongEngine; + + QScriptValue fun = eng.newFunction(myFunctionThatReturnsWrongEngine, reinterpret_cast<void*>(&wrongEngine)); + QCOMPARE(fun.isValid(), true); + QCOMPARE(fun.isFunction(), true); + QCOMPARE(fun.isObject(), true); + + QTest::ignoreMessage(QtWarningMsg, "Value from different engine returned from native function, returning undefined value instead."); + QScriptValue result = fun.call(); + QCOMPARE(result.isValid(), true); + QCOMPARE(result.isUndefined(), true); + } +} + +void tst_QScriptEngine::newObject() +{ + QScriptEngine engine; + QScriptValue object = engine.newObject(); + QVERIFY(object.isObject()); + QVERIFY(object.engine() == &engine); + QVERIFY(!object.isError()); + QVERIFY(!object.equals(engine.newObject())); + QVERIFY(!object.strictlyEquals(engine.newObject())); + QCOMPARE(object.toString(), QString::fromAscii("[object Object]")); +} + +void tst_QScriptEngine::globalObject() +{ + QScriptEngine engine; + QScriptValue global = engine.globalObject(); + QScriptValue self = engine.evaluate("this"); + QVERIFY(global.isObject()); + QVERIFY(engine.globalObject().equals(engine.evaluate("this"))); + QVERIFY(engine.globalObject().strictlyEquals(self)); +} + +/* Test garbage collection, at least try to not crash. */ +void tst_QScriptEngine::collectGarbage() +{ + QScriptEngine engine; + QScriptValue foo = engine.evaluate("( function foo() {return 'pong';} )"); + QVERIFY(foo.isFunction()); + engine.collectGarbage(); + QCOMPARE(foo.call().toString(), QString::fromAscii("pong")); +} + +void tst_QScriptEngine::reportAdditionalMemoryCost() +{ + // There isn't any easy way to test the responsiveness of the GC; + // just try to call the function a few times with various sizes. + QScriptEngine eng; + for (int i = 0; i < 100; ++i) { + eng.reportAdditionalMemoryCost(0); + eng.reportAdditionalMemoryCost(10); + eng.reportAdditionalMemoryCost(1000); + eng.reportAdditionalMemoryCost(10000); + eng.reportAdditionalMemoryCost(100000); + eng.reportAdditionalMemoryCost(1000000); + eng.reportAdditionalMemoryCost(10000000); + eng.reportAdditionalMemoryCost(-1); + eng.reportAdditionalMemoryCost(-1000); + QScriptValue obj = eng.evaluate("new Object"); + eng.collectGarbage(); + } +} + +void tst_QScriptEngine::nullValue() +{ + QScriptEngine engine; + QScriptValue value = engine.nullValue(); + QVERIFY(value.isValid()); + QVERIFY(value.isNull()); +} + +void tst_QScriptEngine::undefinedValue() +{ + QScriptEngine engine; + QScriptValue value = engine.undefinedValue(); + QVERIFY(value.isValid()); + QVERIFY(value.isUndefined()); +} + +void tst_QScriptEngine::evaluateProgram() +{ + QScriptEngine eng; + { + QString code("1 + 2"); + QString fileName("hello.js"); + int lineNumber = 123; + QScriptProgram program(code, fileName, lineNumber); + QVERIFY(!program.isNull()); + QCOMPARE(program.sourceCode(), code); + QCOMPARE(program.fileName(), fileName); + QCOMPARE(program.firstLineNumber(), lineNumber); + + QScriptValue expected = eng.evaluate(code); + for (int x = 0; x < 10; ++x) { + QScriptValue ret = eng.evaluate(program); + QVERIFY(ret.equals(expected)); + } + + // operator= + QScriptProgram sameProgram = program; + QVERIFY(sameProgram == program); + QVERIFY(eng.evaluate(sameProgram).equals(expected)); + + // copy constructor + QScriptProgram sameProgram2(program); + QVERIFY(sameProgram2 == program); + QVERIFY(eng.evaluate(sameProgram2).equals(expected)); + + QScriptProgram differentProgram("2 + 3"); + QVERIFY(differentProgram != program); + QVERIFY(!eng.evaluate(differentProgram).equals(expected)); + } + + // Program that accesses variable in the scope + { + QScriptProgram program("a"); + QVERIFY(!program.isNull()); + { + QScriptValue ret = eng.evaluate(program); + QVERIFY(ret.isError()); + QCOMPARE(ret.toString(), QString::fromLatin1("ReferenceError: Can't find variable: a")); + } + { + QScriptValue ret = eng.evaluate(program); + QVERIFY(ret.isError()); + } + eng.evaluate("a = 456"); + { + QScriptValue ret = eng.evaluate(program); + QVERIFY(!ret.isError()); + QCOMPARE(ret.toNumber(), 456.0); + } + } + + // Program that creates closure + { + QScriptProgram program("(function() { var count = 0; return function() { return count++; }; })"); + QVERIFY(!program.isNull()); + QScriptValue createCounter = eng.evaluate(program); + QVERIFY(createCounter.isFunction()); + QScriptValue counter = createCounter.call(); + QVERIFY(counter.isFunction()); + { + QScriptValue ret = counter.call(); + QVERIFY(ret.isNumber()); + } + QScriptValue counter2 = createCounter.call(); + QVERIFY(counter2.isFunction()); + QVERIFY(!counter2.equals(counter)); + { + QScriptValue ret = counter2.call(); + QVERIFY(ret.isNumber()); + } + } + + // Same program run in different engines + { + QString code("1 + 2"); + QScriptProgram program(code); + QVERIFY(!program.isNull()); + double expected = eng.evaluate(program).toNumber(); + for (int x = 0; x < 2; ++x) { + QScriptEngine eng2; + for (int y = 0; y < 2; ++y) { + double ret = eng2.evaluate(program).toNumber(); + QCOMPARE(ret, expected); + } + } + } + + // No program + { + QScriptProgram program; + QVERIFY(program.isNull()); + QScriptValue ret = eng.evaluate(program); + QVERIFY(!ret.isValid()); + } +} + +void tst_QScriptEngine::checkSyntax_data() +{ + QTest::addColumn<QString>("code"); + QTest::addColumn<int>("expectedState"); + QTest::addColumn<int>("errorLineNumber"); + QTest::addColumn<int>("errorColumnNumber"); + QTest::addColumn<QString>("errorMessage"); + + QTest::newRow("0") + << QString("0") << int(QScriptSyntaxCheckResult::Valid) + << -1 << -1 << ""; + QTest::newRow("if (") + << QString("if (\n") << int(QScriptSyntaxCheckResult::Intermediate) + << 1 << 4 << ""; + QTest::newRow("if else") + << QString("\nif else") << int(QScriptSyntaxCheckResult::Error) + << 2 << 4 << "SyntaxError: Parse error"; + QTest::newRow("{if}") + << QString("{\n{\nif\n}\n") << int(QScriptSyntaxCheckResult::Error) + << 4 << 1 << "SyntaxError: Parse error"; + QTest::newRow("foo[") + << QString("foo[") << int(QScriptSyntaxCheckResult::Error) + << 1 << 4 << "SyntaxError: Parse error"; + QTest::newRow("foo['bar']") + << QString("foo['bar']") << int(QScriptSyntaxCheckResult::Valid) + << -1 << -1 << ""; + + QTest::newRow("/*") + << QString("/*") << int(QScriptSyntaxCheckResult::Intermediate) + << 1 << 1 << "Unclosed comment at end of file"; + QTest::newRow("/*\nMy comment") + << QString("/*\nMy comment") << int(QScriptSyntaxCheckResult::Intermediate) + << 1 << 1 << "Unclosed comment at end of file"; + QTest::newRow("/*\nMy comment */\nfoo = 10") + << QString("/*\nMy comment */\nfoo = 10") << int(QScriptSyntaxCheckResult::Valid) + << -1 << -1 << ""; + QTest::newRow("foo = 10 /*") + << QString("foo = 10 /*") << int(QScriptSyntaxCheckResult::Intermediate) + << -1 << -1 << ""; + QTest::newRow("foo = 10; /*") + << QString("foo = 10; /*") << int(QScriptSyntaxCheckResult::Intermediate) + << 1 << 11 << "Expected `end of file'"; + QTest::newRow("foo = 10 /* My comment */") + << QString("foo = 10 /* My comment */") << int(QScriptSyntaxCheckResult::Valid) + << -1 << -1 << ""; + + QTest::newRow("/=/") + << QString("/=/") << int(QScriptSyntaxCheckResult::Valid) << -1 << -1 << ""; + QTest::newRow("/=/g") + << QString("/=/g") << int(QScriptSyntaxCheckResult::Valid) << -1 << -1 << ""; + QTest::newRow("/a/") + << QString("/a/") << int(QScriptSyntaxCheckResult::Valid) << -1 << -1 << ""; + QTest::newRow("/a/g") + << QString("/a/g") << int(QScriptSyntaxCheckResult::Valid) << -1 << -1 << ""; +} + +void tst_QScriptEngine::checkSyntax() +{ + QFETCH(QString, code); + QFETCH(int, expectedState); + QFETCH(int, errorLineNumber); + QFETCH(int, errorColumnNumber); + QFETCH(QString, errorMessage); + + QScriptSyntaxCheckResult result = QScriptEngine::checkSyntax(code); + + // assignment + { + QScriptSyntaxCheckResult copy = result; + QCOMPARE(copy.state(), result.state()); + QCOMPARE(copy.errorLineNumber(), result.errorLineNumber()); + QCOMPARE(copy.errorColumnNumber(), result.errorColumnNumber()); + QCOMPARE(copy.errorMessage(), result.errorMessage()); + } + { + QScriptSyntaxCheckResult copy(result); + QCOMPARE(copy.state(), result.state()); + QCOMPARE(copy.errorLineNumber(), result.errorLineNumber()); + QCOMPARE(copy.errorColumnNumber(), result.errorColumnNumber()); + QCOMPARE(copy.errorMessage(), result.errorMessage()); + } + + if (expectedState == QScriptSyntaxCheckResult::Intermediate) + QEXPECT_FAIL("", "QScriptSyntaxCheckResult::state() doesn't return the Intermediate state", Abort); + QCOMPARE(result.state(), QScriptSyntaxCheckResult::State(expectedState)); + QCOMPARE(result.errorLineNumber(), errorLineNumber); + if (expectedState != QScriptSyntaxCheckResult::Valid && errorColumnNumber != 1) + QEXPECT_FAIL("", "QScriptSyntaxCheckResult::errorColumnNumber() doesn't return correct value", Continue); + QCOMPARE(result.errorColumnNumber(), errorColumnNumber); + QCOMPARE(result.errorMessage(), errorMessage); +} + +void tst_QScriptEngine::toObject() +{ + QScriptEngine eng; + QVERIFY(!eng.toObject(eng.undefinedValue()).isValid()); + QVERIFY(!eng.toObject(eng.nullValue()).isValid()); + QVERIFY(!eng.toObject(QScriptValue()).isValid()); + + QScriptValue falskt(false); + { + QScriptValue tmp = eng.toObject(falskt); + QVERIFY(tmp.isObject()); + QVERIFY(!falskt.isObject()); + QVERIFY(!falskt.engine()); + QCOMPARE(tmp.toNumber(), falskt.toNumber()); + } + + QScriptValue sant(true); + { + QScriptValue tmp = eng.toObject(sant); + QVERIFY(tmp.isObject()); + QVERIFY(!sant.isObject()); + QVERIFY(!sant.engine()); + QCOMPARE(tmp.toNumber(), sant.toNumber()); + } + + QScriptValue number(123.0); + { + QScriptValue tmp = eng.toObject(number); + QVERIFY(tmp.isObject()); + QVERIFY(!number.isObject()); + QVERIFY(!number.engine()); + QCOMPARE(tmp.toNumber(), number.toNumber()); + } + + QScriptValue str = QScriptValue(&eng, QString("ciao")); + { + QScriptValue tmp = eng.toObject(str); + QVERIFY(tmp.isObject()); + QVERIFY(!str.isObject()); + QCOMPARE(tmp.toString(), str.toString()); + } + + QScriptValue object = eng.evaluate("new Object"); + { + QScriptValue tmp = eng.toObject(object); + QVERIFY(tmp.isObject()); + QVERIFY(object.isObject()); + QVERIFY(tmp.strictlyEquals(object)); + } +} + +void tst_QScriptEngine::toObjectTwoEngines() +{ + QScriptEngine engine1; + QScriptEngine engine2; + + { + QScriptValue null = engine1.nullValue(); + QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::toObject: cannot convert value created in a different engine"); + QVERIFY(!engine2.toObject(null).isValid()); + QVERIFY(null.isValid()); + QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::toObject: cannot convert value created in a different engine"); + QVERIFY(engine2.toObject(null).engine() != &engine2); + } + { + QScriptValue undefined = engine1.undefinedValue(); + QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::toObject: cannot convert value created in a different engine"); + QVERIFY(!engine2.toObject(undefined).isValid()); + QVERIFY(undefined.isValid()); + QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::toObject: cannot convert value created in a different engine"); + QVERIFY(engine2.toObject(undefined).engine() != &engine2); + } + { + QScriptValue value = engine1.evaluate("1"); + QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::toObject: cannot convert value created in a different engine"); + QVERIFY(engine2.toObject(value).engine() != &engine2); + QVERIFY(!value.isObject()); + } + { + QScriptValue string = engine1.evaluate("'Qt'"); + QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::toObject: cannot convert value created in a different engine"); + QVERIFY(engine2.toObject(string).engine() != &engine2); + QVERIFY(!string.isObject()); + } + { + QScriptValue object = engine1.evaluate("new Object"); + QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::toObject: cannot convert value created in a different engine"); + QVERIFY(engine2.toObject(object).engine() != &engine2); + QVERIFY(object.isObject()); + } +} + +void tst_QScriptEngine::newArray() +{ + QScriptEngine eng; + QScriptValue array = eng.newArray(); + QCOMPARE(array.isValid(), true); + QCOMPARE(array.isArray(), true); + QCOMPARE(array.isObject(), true); + QVERIFY(!array.isFunction()); + // QCOMPARE(array.scriptClass(), (QScriptClass*)0); + + // Prototype should be Array.prototype. + QCOMPARE(array.prototype().isValid(), true); + QCOMPARE(array.prototype().isArray(), true); + QCOMPARE(array.prototype().strictlyEquals(eng.evaluate("Array.prototype")), true); + + QScriptValue arrayWithSize = eng.newArray(42); + QCOMPARE(arrayWithSize.isValid(), true); + QCOMPARE(arrayWithSize.isArray(), true); + QCOMPARE(arrayWithSize.isObject(), true); + QCOMPARE(arrayWithSize.property("length").toInt32(), 42); + + // task 218092 + { + QScriptValue ret = eng.evaluate("[].splice(0, 0, 'a')"); + QVERIFY(ret.isArray()); + QCOMPARE(ret.property("length").toInt32(), 0); + } + { + QScriptValue ret = eng.evaluate("['a'].splice(0, 1, 'b')"); + QVERIFY(ret.isArray()); + QCOMPARE(ret.property("length").toInt32(), 1); + } + { + QScriptValue ret = eng.evaluate("['a', 'b'].splice(0, 1, 'c')"); + QVERIFY(ret.isArray()); + QCOMPARE(ret.property("length").toInt32(), 1); + } + { + QScriptValue ret = eng.evaluate("['a', 'b', 'c'].splice(0, 2, 'd')"); + QVERIFY(ret.isArray()); + QCOMPARE(ret.property("length").toInt32(), 2); + } + { + QScriptValue ret = eng.evaluate("['a', 'b', 'c'].splice(1, 2, 'd', 'e', 'f')"); + QVERIFY(ret.isArray()); + QCOMPARE(ret.property("length").toInt32(), 2); + } +} + +void tst_QScriptEngine::uncaughtException() +{ + QScriptEngine eng; + QScriptValue fun = eng.evaluate("(function foo () { return null; });"); + QVERIFY(!eng.uncaughtException().isValid()); + QVERIFY(fun.isFunction()); + QScriptValue throwFun = eng.evaluate("( function() { throw new Error('Pong'); });"); + QVERIFY(throwFun.isFunction()); + { + eng.evaluate("a = 10"); + QVERIFY(!eng.hasUncaughtException()); + QVERIFY(!eng.uncaughtException().isValid()); + } + { + eng.evaluate("1 = 2"); + QVERIFY(eng.hasUncaughtException()); + eng.clearExceptions(); + QVERIFY(!eng.hasUncaughtException()); + } + { + // Check if the call or toString functions can remove the last exception. + QVERIFY(throwFun.call().isError()); + QVERIFY(eng.hasUncaughtException()); + QScriptValue exception = eng.uncaughtException(); + fun.call(); + exception.toString(); + QVERIFY(eng.hasUncaughtException()); + QVERIFY(eng.uncaughtException().strictlyEquals(exception)); + } + eng.clearExceptions(); + { + // Check if in the call function a new exception can override an existing one. + throwFun.call(); + QVERIFY(eng.hasUncaughtException()); + QScriptValue exception = eng.uncaughtException(); + throwFun.call(); + QVERIFY(eng.hasUncaughtException()); + QVERIFY(!exception.strictlyEquals(eng.uncaughtException())); + } + { + eng.evaluate("throwFun = (function foo () { throw new Error('bla') });"); + eng.evaluate("1;\nthrowFun();"); + QVERIFY(eng.hasUncaughtException()); + QCOMPARE(eng.uncaughtExceptionLineNumber(), 1); + eng.clearExceptions(); + QVERIFY(!eng.hasUncaughtException()); + } + for (int x = 1; x < 4; ++x) { + QScriptValue ret = eng.evaluate("a = 10;\nb = 20;\n0 = 0;\n", + QString::fromLatin1("FooScript") + QString::number(x), + /* lineNumber */ x); + QVERIFY(eng.hasUncaughtException()); + QCOMPARE(eng.uncaughtExceptionLineNumber(), x + 2); + QVERIFY(eng.uncaughtException().strictlyEquals(ret)); + QVERIFY(eng.hasUncaughtException()); + QVERIFY(eng.uncaughtException().strictlyEquals(ret)); + QString backtrace = QString::fromLatin1("<anonymous>()@FooScript") + QString::number(x) + ":" + QString::number(x + 2); + QCOMPARE(eng.uncaughtExceptionBacktrace().join(""), backtrace); + QVERIFY(fun.call().isNull()); + QVERIFY(eng.hasUncaughtException()); + QCOMPARE(eng.uncaughtExceptionLineNumber(), x + 2); + QVERIFY(eng.uncaughtException().strictlyEquals(ret)); + eng.clearExceptions(); + QVERIFY(!eng.hasUncaughtException()); + QCOMPARE(eng.uncaughtExceptionLineNumber(), -1); + QVERIFY(!eng.uncaughtException().isValid()); + eng.evaluate("2 = 3"); + QVERIFY(eng.hasUncaughtException()); + QScriptValue ret2 = throwFun.call(); + QVERIFY(ret2.isError()); + QVERIFY(eng.hasUncaughtException()); + QVERIFY(eng.uncaughtException().strictlyEquals(ret2)); + QCOMPARE(eng.uncaughtExceptionLineNumber(), 1); + eng.clearExceptions(); + QVERIFY(!eng.hasUncaughtException()); + eng.evaluate("1 + 2"); + QVERIFY(!eng.hasUncaughtException()); + } +} + +void tst_QScriptEngine::newDate() +{ + QScriptEngine eng; + { + QScriptValue date = eng.newDate(0); + QCOMPARE(date.isValid(), true); + QCOMPARE(date.isDate(), true); + QCOMPARE(date.isObject(), true); + QVERIFY(!date.isFunction()); + // prototype should be Date.prototype + QCOMPARE(date.prototype().isValid(), true); + QCOMPARE(date.prototype().isDate(), true); + QCOMPARE(date.prototype().strictlyEquals(eng.evaluate("Date.prototype")), true); + } + { + QDateTime dt = QDateTime(QDate(1, 2, 3), QTime(4, 5, 6, 7), Qt::LocalTime); + QScriptValue date = eng.newDate(dt); + QCOMPARE(date.isValid(), true); + QCOMPARE(date.isDate(), true); + QCOMPARE(date.isObject(), true); + // prototype should be Date.prototype + QCOMPARE(date.prototype().isValid(), true); + QCOMPARE(date.prototype().isDate(), true); + QCOMPARE(date.prototype().strictlyEquals(eng.evaluate("Date.prototype")), true); + + QCOMPARE(date.toDateTime(), dt); + } + { + QDateTime dt = QDateTime(QDate(1, 2, 3), QTime(4, 5, 6, 7), Qt::UTC); + QScriptValue date = eng.newDate(dt); + // toDateTime() result should be in local time + QCOMPARE(date.toDateTime(), dt.toLocalTime()); + } + // Date.parse() should return NaN when it fails + { + QScriptValue ret = eng.evaluate("Date.parse()"); + QVERIFY(ret.isNumber()); + QVERIFY(qIsNaN(ret.toNumber())); + } + // Date.parse() should be able to parse the output of Date().toString() + { + QScriptValue ret = eng.evaluate("var x = new Date(); var s = x.toString(); s == new Date(Date.parse(s)).toString()"); + QVERIFY(ret.isBoolean()); + QCOMPARE(ret.toBoolean(), true); + } +} + +QTEST_MAIN(tst_QScriptEngine) +#include "tst_qscriptengine.moc" |