2 Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
20 #include "qscriptengine.h"
21 #include "qscriptprogram.h"
22 #include "qscriptsyntaxcheckresult.h"
23 #include "qscriptvalue.h"
24 #include <QtTest/qtest.h>
26 class tst_QScriptEngine
: public QObject
{
30 tst_QScriptEngine() {}
31 virtual ~tst_QScriptEngine() {}
40 void collectGarbage();
41 void reportAdditionalMemoryCost();
43 void undefinedValue();
44 void evaluateProgram();
45 void checkSyntax_data();
49 /* Evaluating a script that throw an unhandled exception should return an invalid value. */
50 void tst_QScriptEngine::evaluate()
53 QVERIFY2(engine
.evaluate("1+1").isValid(), "the expression should be evaluated and an valid result should be returned");
54 QVERIFY2(engine
.evaluate("ping").isValid(), "Script throwing an unhandled exception should return an exception value");
57 void tst_QScriptEngine::globalObject()
60 QScriptValue global
= engine
.globalObject();
61 QScriptValue self
= engine
.evaluate("this");
62 QVERIFY(global
.isObject());
63 QVERIFY(engine
.globalObject().equals(engine
.evaluate("this")));
64 QEXPECT_FAIL("", "strictlyEquals is broken - bug 36600 in bugs.webkit.org", Continue
);
65 QVERIFY(engine
.globalObject().strictlyEquals(self
));
68 /* Test garbage collection, at least try to not crash. */
69 void tst_QScriptEngine::collectGarbage()
72 QScriptValue foo
= engine
.evaluate("( function foo() {return 'pong';} )");
73 QVERIFY(foo
.isFunction());
74 engine
.collectGarbage();
75 QCOMPARE(foo
.call().toString(), QString::fromAscii("pong"));
78 void tst_QScriptEngine::reportAdditionalMemoryCost()
80 // There isn't any easy way to test the responsiveness of the GC;
81 // just try to call the function a few times with various sizes.
83 for (int i
= 0; i
< 100; ++i
) {
84 eng
.reportAdditionalMemoryCost(0);
85 eng
.reportAdditionalMemoryCost(10);
86 eng
.reportAdditionalMemoryCost(1000);
87 eng
.reportAdditionalMemoryCost(10000);
88 eng
.reportAdditionalMemoryCost(100000);
89 eng
.reportAdditionalMemoryCost(1000000);
90 eng
.reportAdditionalMemoryCost(10000000);
91 eng
.reportAdditionalMemoryCost(-1);
92 eng
.reportAdditionalMemoryCost(-1000);
93 QScriptValue obj
= eng
.evaluate("new Object");
98 void tst_QScriptEngine::nullValue()
100 QScriptEngine engine
;
101 QScriptValue value
= engine
.nullValue();
102 QVERIFY(value
.isValid());
103 QVERIFY(value
.isNull());
106 void tst_QScriptEngine::undefinedValue()
108 QScriptEngine engine
;
109 QScriptValue value
= engine
.undefinedValue();
110 QVERIFY(value
.isValid());
111 QVERIFY(value
.isUndefined());
114 void tst_QScriptEngine::evaluateProgram()
118 QString
code("1 + 2");
119 QString
fileName("hello.js");
120 int lineNumber
= 123;
121 QScriptProgram
program(code
, fileName
, lineNumber
);
122 QVERIFY(!program
.isNull());
123 QCOMPARE(program
.sourceCode(), code
);
124 QCOMPARE(program
.fileName(), fileName
);
125 QCOMPARE(program
.firstLineNumber(), lineNumber
);
127 QScriptValue expected
= eng
.evaluate(code
);
128 for (int x
= 0; x
< 10; ++x
) {
129 QScriptValue ret
= eng
.evaluate(program
);
130 QVERIFY(ret
.equals(expected
));
134 QScriptProgram sameProgram
= program
;
135 QVERIFY(sameProgram
== program
);
136 QVERIFY(eng
.evaluate(sameProgram
).equals(expected
));
139 QScriptProgram
sameProgram2(program
);
140 QVERIFY(sameProgram2
== program
);
141 QVERIFY(eng
.evaluate(sameProgram2
).equals(expected
));
143 QScriptProgram
differentProgram("2 + 3");
144 QVERIFY(differentProgram
!= program
);
145 QVERIFY(!eng
.evaluate(differentProgram
).equals(expected
));
148 // Program that accesses variable in the scope
150 QScriptProgram
program("a");
151 QVERIFY(!program
.isNull());
153 QScriptValue ret
= eng
.evaluate(program
);
154 QVERIFY(ret
.isError());
155 QCOMPARE(ret
.toString(), QString::fromLatin1("ReferenceError: Can't find variable: a"));
158 QScriptValue ret
= eng
.evaluate(program
);
159 QVERIFY(ret
.isError());
161 eng
.evaluate("a = 456");
163 QScriptValue ret
= eng
.evaluate(program
);
164 QVERIFY(!ret
.isError());
165 QCOMPARE(ret
.toNumber(), 456.0);
169 // Program that creates closure
171 QScriptProgram
program("(function() { var count = 0; return function() { return count++; }; })");
172 QVERIFY(!program
.isNull());
173 QScriptValue createCounter
= eng
.evaluate(program
);
174 QVERIFY(createCounter
.isFunction());
175 QScriptValue counter
= createCounter
.call();
176 QVERIFY(counter
.isFunction());
178 QScriptValue ret
= counter
.call();
179 QVERIFY(ret
.isNumber());
181 QScriptValue counter2
= createCounter
.call();
182 QVERIFY(counter2
.isFunction());
183 QVERIFY(!counter2
.equals(counter
));
185 QScriptValue ret
= counter2
.call();
186 QVERIFY(ret
.isNumber());
190 // Same program run in different engines
192 QString
code("1 + 2");
193 QScriptProgram
program(code
);
194 QVERIFY(!program
.isNull());
195 double expected
= eng
.evaluate(program
).toNumber();
196 for (int x
= 0; x
< 2; ++x
) {
198 for (int y
= 0; y
< 2; ++y
) {
199 double ret
= eng2
.evaluate(program
).toNumber();
200 QCOMPARE(ret
, expected
);
207 QScriptProgram program
;
208 QVERIFY(program
.isNull());
209 QScriptValue ret
= eng
.evaluate(program
);
210 QVERIFY(!ret
.isValid());
214 void tst_QScriptEngine::checkSyntax_data()
216 QTest::addColumn
<QString
>("code");
217 QTest::addColumn
<int>("expectedState");
218 QTest::addColumn
<int>("errorLineNumber");
219 QTest::addColumn
<int>("errorColumnNumber");
220 QTest::addColumn
<QString
>("errorMessage");
223 << QString("0") << int(QScriptSyntaxCheckResult::Valid
)
225 QTest::newRow("if (")
226 << QString("if (\n") << int(QScriptSyntaxCheckResult::Intermediate
)
228 QTest::newRow("if else")
229 << QString("\nif else") << int(QScriptSyntaxCheckResult::Error
)
230 << 2 << 4 << "SyntaxError: Parse error";
231 QTest::newRow("{if}")
232 << QString("{\n{\nif\n}\n") << int(QScriptSyntaxCheckResult::Error
)
233 << 4 << 1 << "SyntaxError: Parse error";
234 QTest::newRow("foo[")
235 << QString("foo[") << int(QScriptSyntaxCheckResult::Error
)
236 << 1 << 4 << "SyntaxError: Parse error";
237 QTest::newRow("foo['bar']")
238 << QString("foo['bar']") << int(QScriptSyntaxCheckResult::Valid
)
242 << QString("/*") << int(QScriptSyntaxCheckResult::Intermediate
)
243 << 1 << 1 << "Unclosed comment at end of file";
244 QTest::newRow("/*\nMy comment")
245 << QString("/*\nMy comment") << int(QScriptSyntaxCheckResult::Intermediate
)
246 << 1 << 1 << "Unclosed comment at end of file";
247 QTest::newRow("/*\nMy comment */\nfoo = 10")
248 << QString("/*\nMy comment */\nfoo = 10") << int(QScriptSyntaxCheckResult::Valid
)
250 QTest::newRow("foo = 10 /*")
251 << QString("foo = 10 /*") << int(QScriptSyntaxCheckResult::Intermediate
)
253 QTest::newRow("foo = 10; /*")
254 << QString("foo = 10; /*") << int(QScriptSyntaxCheckResult::Intermediate
)
255 << 1 << 11 << "Expected `end of file'";
256 QTest::newRow("foo = 10 /* My comment */")
257 << QString("foo = 10 /* My comment */") << int(QScriptSyntaxCheckResult::Valid
)
261 << QString("/=/") << int(QScriptSyntaxCheckResult::Valid
) << -1 << -1 << "";
262 QTest::newRow("/=/g")
263 << QString("/=/g") << int(QScriptSyntaxCheckResult::Valid
) << -1 << -1 << "";
265 << QString("/a/") << int(QScriptSyntaxCheckResult::Valid
) << -1 << -1 << "";
266 QTest::newRow("/a/g")
267 << QString("/a/g") << int(QScriptSyntaxCheckResult::Valid
) << -1 << -1 << "";
270 void tst_QScriptEngine::checkSyntax()
272 QFETCH(QString
, code
);
273 QFETCH(int, expectedState
);
274 QFETCH(int, errorLineNumber
);
275 QFETCH(int, errorColumnNumber
);
276 QFETCH(QString
, errorMessage
);
278 QScriptSyntaxCheckResult result
= QScriptEngine::checkSyntax(code
);
282 QScriptSyntaxCheckResult copy
= result
;
283 QCOMPARE(copy
.state(), result
.state());
284 QCOMPARE(copy
.errorLineNumber(), result
.errorLineNumber());
285 QCOMPARE(copy
.errorColumnNumber(), result
.errorColumnNumber());
286 QCOMPARE(copy
.errorMessage(), result
.errorMessage());
289 QScriptSyntaxCheckResult
copy(result
);
290 QCOMPARE(copy
.state(), result
.state());
291 QCOMPARE(copy
.errorLineNumber(), result
.errorLineNumber());
292 QCOMPARE(copy
.errorColumnNumber(), result
.errorColumnNumber());
293 QCOMPARE(copy
.errorMessage(), result
.errorMessage());
296 if (expectedState
== QScriptSyntaxCheckResult::Intermediate
)
297 QEXPECT_FAIL("", "QScriptSyntaxCheckResult::state() doesn't return the Intermediate state", Abort
);
298 QCOMPARE(result
.state(), QScriptSyntaxCheckResult::State(expectedState
));
299 QCOMPARE(result
.errorLineNumber(), errorLineNumber
);
300 if (expectedState
!= QScriptSyntaxCheckResult::Valid
&& errorColumnNumber
!= 1)
301 QEXPECT_FAIL("", "QScriptSyntaxCheckResult::errorColumnNumber() doesn't return correct value", Continue
);
302 QCOMPARE(result
.errorColumnNumber(), errorColumnNumber
);
303 QCOMPARE(result
.errorMessage(), errorMessage
);
307 QTEST_MAIN(tst_QScriptEngine
)
308 #include "tst_qscriptengine.moc"