tests/auto/qscriptengine/tst_qscriptengine.cpp
changeset 22 79de32ba3296
parent 19 fcece45ef507
child 23 89e065397ea6
equal deleted inserted replaced
19:fcece45ef507 22:79de32ba3296
   102     void newQMetaObject();
   102     void newQMetaObject();
   103     void newActivationObject();
   103     void newActivationObject();
   104     void getSetGlobalObject();
   104     void getSetGlobalObject();
   105     void globalObjectProperties();
   105     void globalObjectProperties();
   106     void globalObjectGetterSetterProperty();
   106     void globalObjectGetterSetterProperty();
       
   107     void customGlobalObjectWithPrototype();
       
   108     void globalObjectWithCustomPrototype();
   107     void builtinFunctionNames_data();
   109     void builtinFunctionNames_data();
   108     void builtinFunctionNames();
   110     void builtinFunctionNames();
   109     void checkSyntax_data();
   111     void checkSyntax_data();
   110     void checkSyntax();
   112     void checkSyntax();
   111     void canEvaluate_data();
   113     void canEvaluate_data();
   150     void getSetAgent();
   152     void getSetAgent();
   151     void reentrancy();
   153     void reentrancy();
   152     void incDecNonObjectProperty();
   154     void incDecNonObjectProperty();
   153     void installTranslatorFunctions_data();
   155     void installTranslatorFunctions_data();
   154     void installTranslatorFunctions();
   156     void installTranslatorFunctions();
       
   157     void translateScript();
       
   158     void translateWithInvalidArgs_data();
       
   159     void translateWithInvalidArgs();
   155     void functionScopes();
   160     void functionScopes();
   156     void nativeFunctionScopes();
   161     void nativeFunctionScopes();
   157     void evaluateProgram();
   162     void evaluateProgram();
   158     void collectGarbageAfterConnect();
   163     void collectGarbageAfterConnect();
   159 
   164 
  1170     QVERIFY(global.property("bar").equals(global.property("foo")));
  1175     QVERIFY(global.property("bar").equals(global.property("foo")));
  1171 
  1176 
  1172     engine.evaluate("__defineGetter__('baz', function() { return 789; })");
  1177     engine.evaluate("__defineGetter__('baz', function() { return 789; })");
  1173     QVERIFY(engine.evaluate("baz").equals(789));
  1178     QVERIFY(engine.evaluate("baz").equals(789));
  1174     QVERIFY(global.property("baz").equals(789));
  1179     QVERIFY(global.property("baz").equals(789));
       
  1180 }
       
  1181 
       
  1182 void tst_QScriptEngine::customGlobalObjectWithPrototype()
       
  1183 {
       
  1184     for (int x = 0; x < 2; ++x) {
       
  1185         QScriptEngine engine;
       
  1186         QScriptValue wrap = engine.newObject();
       
  1187         QScriptValue global = engine.globalObject();
       
  1188         QScriptValue originalGlobalProto = global.prototype();
       
  1189         if (!x) {
       
  1190             // Set prototype before setting global object
       
  1191             wrap.setPrototype(global);
       
  1192             QVERIFY(wrap.prototype().strictlyEquals(global));
       
  1193             engine.setGlobalObject(wrap);
       
  1194         } else {
       
  1195             // Set prototype after setting global object
       
  1196             engine.setGlobalObject(wrap);
       
  1197             wrap.setPrototype(global);
       
  1198             QVERIFY(wrap.prototype().strictlyEquals(global));
       
  1199         }
       
  1200         {
       
  1201             QScriptValue ret = engine.evaluate("print");
       
  1202             QVERIFY(ret.isFunction());
       
  1203             QVERIFY(ret.strictlyEquals(wrap.property("print")));
       
  1204         }
       
  1205         {
       
  1206             QScriptValue ret = engine.evaluate("this.print");
       
  1207             QVERIFY(ret.isFunction());
       
  1208             QVERIFY(ret.strictlyEquals(wrap.property("print")));
       
  1209         }
       
  1210         {
       
  1211             QScriptValue ret = engine.evaluate("hasOwnProperty('print')");
       
  1212             QVERIFY(ret.isBool());
       
  1213             QVERIFY(!ret.toBool());
       
  1214         }
       
  1215         {
       
  1216             QScriptValue ret = engine.evaluate("this.hasOwnProperty('print')");
       
  1217             QVERIFY(ret.isBool());
       
  1218             QVERIFY(!ret.toBool());
       
  1219         }
       
  1220 
       
  1221         QScriptValue anotherProto = engine.newObject();
       
  1222         anotherProto.setProperty("anotherProtoProperty", 123);
       
  1223         global.setPrototype(anotherProto);
       
  1224         {
       
  1225             QScriptValue ret = engine.evaluate("print");
       
  1226             QVERIFY(ret.isFunction());
       
  1227             QVERIFY(ret.strictlyEquals(wrap.property("print")));
       
  1228         }
       
  1229         {
       
  1230             QScriptValue ret = engine.evaluate("anotherProtoProperty");
       
  1231             QVERIFY(ret.isNumber());
       
  1232             QVERIFY(ret.strictlyEquals(wrap.property("anotherProtoProperty")));
       
  1233         }
       
  1234         {
       
  1235             QScriptValue ret = engine.evaluate("this.anotherProtoProperty");
       
  1236             QVERIFY(ret.isNumber());
       
  1237             QVERIFY(ret.strictlyEquals(wrap.property("anotherProtoProperty")));
       
  1238         }
       
  1239 
       
  1240         wrap.setPrototype(anotherProto);
       
  1241         {
       
  1242             QScriptValue ret = engine.evaluate("print");
       
  1243             QVERIFY(ret.isError());
       
  1244             QCOMPARE(ret.toString(), QString::fromLatin1("ReferenceError: Can't find variable: print"));
       
  1245         }
       
  1246         {
       
  1247             QScriptValue ret = engine.evaluate("anotherProtoProperty");
       
  1248             QVERIFY(ret.isNumber());
       
  1249             QVERIFY(ret.strictlyEquals(wrap.property("anotherProtoProperty")));
       
  1250         }
       
  1251         QVERIFY(global.prototype().strictlyEquals(anotherProto));
       
  1252 
       
  1253         global.setPrototype(originalGlobalProto);
       
  1254         engine.setGlobalObject(global);
       
  1255         {
       
  1256             QScriptValue ret = engine.evaluate("anotherProtoProperty");
       
  1257             QVERIFY(ret.isError());
       
  1258             QCOMPARE(ret.toString(), QString::fromLatin1("ReferenceError: Can't find variable: anotherProtoProperty"));
       
  1259         }
       
  1260         {
       
  1261             QScriptValue ret = engine.evaluate("print");
       
  1262             QVERIFY(ret.isFunction());
       
  1263             QVERIFY(ret.strictlyEquals(global.property("print")));
       
  1264         }
       
  1265         QVERIFY(!anotherProto.property("print").isValid());
       
  1266     }
       
  1267 }
       
  1268 
       
  1269 void tst_QScriptEngine::globalObjectWithCustomPrototype()
       
  1270 {
       
  1271     QScriptEngine engine;
       
  1272     QScriptValue proto = engine.newObject();
       
  1273     proto.setProperty("protoProperty", 123);
       
  1274     QScriptValue global = engine.globalObject();
       
  1275     QScriptValue originalProto = global.prototype();
       
  1276     global.setPrototype(proto);
       
  1277     {
       
  1278         QScriptValue ret = engine.evaluate("protoProperty");
       
  1279         QVERIFY(ret.isNumber());
       
  1280         QVERIFY(ret.strictlyEquals(global.property("protoProperty")));
       
  1281     }
       
  1282     {
       
  1283         QScriptValue ret = engine.evaluate("this.protoProperty");
       
  1284         QVERIFY(ret.isNumber());
       
  1285         QVERIFY(ret.strictlyEquals(global.property("protoProperty")));
       
  1286     }
       
  1287     {
       
  1288         QScriptValue ret = engine.evaluate("hasOwnProperty('protoProperty')");
       
  1289         QVERIFY(ret.isBool());
       
  1290         QVERIFY(!ret.toBool());
       
  1291     }
       
  1292     {
       
  1293         QScriptValue ret = engine.evaluate("this.hasOwnProperty('protoProperty')");
       
  1294         QVERIFY(ret.isBool());
       
  1295         QVERIFY(!ret.toBool());
       
  1296     }
       
  1297 
       
  1298     // Custom prototype set from JS
       
  1299     {
       
  1300         QScriptValue ret = engine.evaluate("this.__proto__ = { 'a': 123 }; a");
       
  1301         QVERIFY(ret.isNumber());
       
  1302         QEXPECT_FAIL("", "QTBUG-9737", Continue);
       
  1303         QVERIFY(ret.strictlyEquals(global.property("a")));
       
  1304     }
  1175 }
  1305 }
  1176 
  1306 
  1177 void tst_QScriptEngine::builtinFunctionNames_data()
  1307 void tst_QScriptEngine::builtinFunctionNames_data()
  1178 {
  1308 {
  1179     QTest::addColumn<QString>("expression");
  1309     QTest::addColumn<QString>("expression");
  4185         QVERIFY(ret.isString());
  4315         QVERIFY(ret.isString());
  4186         QCOMPARE(ret.toString(), QString::fromLatin1("foobar"));
  4316         QCOMPARE(ret.toString(), QString::fromLatin1("foobar"));
  4187     }
  4317     }
  4188 }
  4318 }
  4189 
  4319 
       
  4320 static QScriptValue callQsTr(QScriptContext *ctx, QScriptEngine *eng)
       
  4321 {
       
  4322     return eng->globalObject().property("qsTr").call(ctx->thisObject(), ctx->argumentsObject());
       
  4323 }
       
  4324 
       
  4325 void tst_QScriptEngine::translateScript()
       
  4326 {
       
  4327     QScriptEngine engine;
       
  4328 
       
  4329     QTranslator translator;
       
  4330     translator.load(":/translations/translatable_la");
       
  4331     QCoreApplication::instance()->installTranslator(&translator);
       
  4332     engine.installTranslatorFunctions();
       
  4333 
       
  4334     QString fileName = QString::fromLatin1("translatable.js");
       
  4335     // Top-level
       
  4336     QCOMPARE(engine.evaluate("qsTr('One')", fileName).toString(), QString::fromLatin1("En"));
       
  4337     QCOMPARE(engine.evaluate("qsTr('Hello')", fileName).toString(), QString::fromLatin1("Hallo"));
       
  4338     // From function
       
  4339     QCOMPARE(engine.evaluate("(function() { return qsTr('One'); })()", fileName).toString(), QString::fromLatin1("En"));
       
  4340     QCOMPARE(engine.evaluate("(function() { return qsTr('Hello'); })()", fileName).toString(), QString::fromLatin1("Hallo"));
       
  4341     // From eval
       
  4342     QCOMPARE(engine.evaluate("eval('qsTr(\\'One\\')')", fileName).toString(), QString::fromLatin1("En"));
       
  4343     QCOMPARE(engine.evaluate("eval('qsTr(\\'Hello\\')')", fileName).toString(), QString::fromLatin1("Hallo"));
       
  4344 
       
  4345     QCOMPARE(engine.evaluate("qsTranslate('FooContext', 'Two')", fileName).toString(), QString::fromLatin1("To"));
       
  4346     QCOMPARE(engine.evaluate("qsTranslate('FooContext', 'Goodbye')", fileName).toString(), QString::fromLatin1("Farvel"));
       
  4347     // From eval
       
  4348     QCOMPARE(engine.evaluate("eval('qsTranslate(\\'FooContext\\', \\'Two\\')')", fileName).toString(), QString::fromLatin1("To"));
       
  4349     QCOMPARE(engine.evaluate("eval('qsTranslate(\\'FooContext\\', \\'Goodbye\\')')", fileName).toString(), QString::fromLatin1("Farvel"));
       
  4350 
       
  4351     QCOMPARE(engine.evaluate("qsTranslate('FooContext', 'Goodbye', '', 'UnicodeUTF8')", fileName).toString(), QString::fromLatin1("Farvel"));
       
  4352 
       
  4353     QCOMPARE(engine.evaluate("qsTr('One', 'not the same one')", fileName).toString(), QString::fromLatin1("Enda en"));
       
  4354 
       
  4355     QVERIFY(engine.evaluate("QT_TR_NOOP()").isUndefined());
       
  4356     QCOMPARE(engine.evaluate("QT_TR_NOOP('One')").toString(), QString::fromLatin1("One"));
       
  4357 
       
  4358     QVERIFY(engine.evaluate("QT_TRANSLATE_NOOP()").isUndefined());
       
  4359     QVERIFY(engine.evaluate("QT_TRANSLATE_NOOP('FooContext')").isUndefined());
       
  4360     QCOMPARE(engine.evaluate("QT_TRANSLATE_NOOP('FooContext', 'Two')").toString(), QString::fromLatin1("Two"));
       
  4361 
       
  4362     // Don't exist in translation
       
  4363     QCOMPARE(engine.evaluate("qsTr('Three')", fileName).toString(), QString::fromLatin1("Three"));
       
  4364     QCOMPARE(engine.evaluate("qsTranslate('FooContext', 'So long')", fileName).toString(), QString::fromLatin1("So long"));
       
  4365     QCOMPARE(engine.evaluate("qsTranslate('BarContext', 'Goodbye')", fileName).toString(), QString::fromLatin1("Goodbye"));
       
  4366 
       
  4367     // From C++
       
  4368     // There is no context, but it shouldn't crash
       
  4369     QCOMPARE(engine.globalObject().property("qsTr").call(
       
  4370                  QScriptValue(), QScriptValueList() << "One").toString(), QString::fromLatin1("One"));
       
  4371 
       
  4372     // Translate strings from the second script (translatable2.js)
       
  4373 
       
  4374     QString fileName2 = QString::fromLatin1("translatable2.js");
       
  4375 
       
  4376     QCOMPARE(engine.evaluate("qsTr('Three')", fileName2).toString(), QString::fromLatin1("Tre"));
       
  4377     QCOMPARE(engine.evaluate("qsTr('Happy birthday!')", fileName2).toString(), QString::fromLatin1("Gratulerer med dagen!"));
       
  4378 
       
  4379     // Not translated because translation is only in translatable.js
       
  4380     QCOMPARE(engine.evaluate("qsTr('One')", fileName2).toString(), QString::fromLatin1("One"));
       
  4381     QCOMPARE(engine.evaluate("(function() { return qsTr('One'); })()", fileName2).toString(), QString::fromLatin1("One"));
       
  4382 
       
  4383     // For qsTranslate() the filename shouldn't matter
       
  4384     QCOMPARE(engine.evaluate("qsTranslate('FooContext', 'Two')", fileName2).toString(), QString::fromLatin1("To"));
       
  4385     QCOMPARE(engine.evaluate("qsTranslate('BarContext', 'Congratulations!')", fileName).toString(), QString::fromLatin1("Gratulerer!"));
       
  4386 
       
  4387     // qsTr() should use the innermost filename as context
       
  4388     engine.evaluate("function foo(s) { return bar(s); }", fileName);
       
  4389     engine.evaluate("function bar(s) { return qsTr(s); }", fileName2);
       
  4390     QCOMPARE(engine.evaluate("bar('Three')", fileName2).toString(), QString::fromLatin1("Tre"));
       
  4391     QCOMPARE(engine.evaluate("bar('Three')", fileName).toString(), QString::fromLatin1("Tre"));
       
  4392     QCOMPARE(engine.evaluate("bar('One')", fileName2).toString(), QString::fromLatin1("One"));
       
  4393 
       
  4394     engine.evaluate("function foo(s) { return bar(s); }", fileName2);
       
  4395     engine.evaluate("function bar(s) { return qsTr(s); }", fileName);
       
  4396     QCOMPARE(engine.evaluate("bar('Three')", fileName2).toString(), QString::fromLatin1("Three"));
       
  4397     QCOMPARE(engine.evaluate("bar('One')", fileName).toString(), QString::fromLatin1("En"));
       
  4398     QCOMPARE(engine.evaluate("bar('One')", fileName2).toString(), QString::fromLatin1("En"));
       
  4399 
       
  4400     // Calling qsTr() from a native function
       
  4401     engine.globalObject().setProperty("qsTrProxy", engine.newFunction(callQsTr));
       
  4402     QCOMPARE(engine.evaluate("qsTrProxy('One')", fileName).toString(), QString::fromLatin1("En"));
       
  4403     QCOMPARE(engine.evaluate("qsTrProxy('One')", fileName2).toString(), QString::fromLatin1("One"));
       
  4404     QCOMPARE(engine.evaluate("qsTrProxy('Three')", fileName).toString(), QString::fromLatin1("Three"));
       
  4405     QCOMPARE(engine.evaluate("qsTrProxy('Three')", fileName2).toString(), QString::fromLatin1("Tre"));
       
  4406 
       
  4407     QCoreApplication::instance()->removeTranslator(&translator);
       
  4408 }
       
  4409 
       
  4410 void tst_QScriptEngine::translateWithInvalidArgs_data()
       
  4411 {
       
  4412     QTest::addColumn<QString>("expression");
       
  4413     QTest::addColumn<QString>("expectedError");
       
  4414 
       
  4415     QTest::newRow("qsTr()")  << "qsTr()" << "Error: qsTr() requires at least one argument";
       
  4416     QTest::newRow("qsTr(123)")  << "qsTr(123)" << "Error: qsTr(): first argument (text) must be a string";
       
  4417     QTest::newRow("qsTr('foo', 123)")  << "qsTr('foo', 123)" << "Error: qsTr(): second argument (comment) must be a string";
       
  4418     QTest::newRow("qsTr('foo', 'bar', 'baz')")  << "qsTr('foo', 'bar', 'baz')" << "Error: qsTr(): third argument (n) must be a number";
       
  4419     QTest::newRow("qsTr('foo', 'bar', true)")  << "qsTr('foo', 'bar', true)" << "Error: qsTr(): third argument (n) must be a number";
       
  4420 
       
  4421     QTest::newRow("qsTranslate()")  << "qsTranslate()" << "Error: qsTranslate() requires at least two arguments";
       
  4422     QTest::newRow("qsTranslate('foo')")  << "qsTranslate('foo')" << "Error: qsTranslate() requires at least two arguments";
       
  4423     QTest::newRow("qsTranslate('foo', 123)")  << "qsTranslate('foo', 123)" << "Error: qsTranslate(): second argument (text) must be a string";
       
  4424     QTest::newRow("qsTranslate('foo', 'bar', 123)")  << "qsTranslate('foo', 'bar', 123)" << "Error: qsTranslate(): third argument (comment) must be a string";
       
  4425     QTest::newRow("qsTranslate('foo', 'bar', 'baz', 123)")  << "qsTranslate('foo', 'bar', 'baz', 123)" << "Error: qsTranslate(): fourth argument (encoding) must be a string";
       
  4426     QTest::newRow("qsTranslate('foo', 'bar', 'baz', 'zab', 'rab')")  << "qsTranslate('foo', 'bar', 'baz', 'zab', 'rab')" << "Error: qsTranslate(): fifth argument (n) must be a number";
       
  4427     QTest::newRow("qsTranslate('foo', 'bar', 'baz', 'zab', 123)")  << "qsTranslate('foo', 'bar', 'baz', 'zab', 123)" << "Error: qsTranslate(): invalid encoding 'zab'";
       
  4428 }
       
  4429 
       
  4430 void tst_QScriptEngine::translateWithInvalidArgs()
       
  4431 {
       
  4432     QFETCH(QString, expression);
       
  4433     QFETCH(QString, expectedError);
       
  4434     QScriptEngine engine;
       
  4435     engine.installTranslatorFunctions();
       
  4436     QScriptValue result = engine.evaluate(expression);
       
  4437     QVERIFY(result.isError());
       
  4438     QCOMPARE(result.toString(), expectedError);
       
  4439 }
       
  4440 
  4190 void tst_QScriptEngine::functionScopes()
  4441 void tst_QScriptEngine::functionScopes()
  4191 {
  4442 {
  4192     QScriptEngine eng;
  4443     QScriptEngine eng;
  4193     {
  4444     {
  4194         // top-level functions have only the global object in their scope
  4445         // top-level functions have only the global object in their scope