diff options
Diffstat (limited to 'tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp')
-rw-r--r-- | tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 278 |
1 files changed, 234 insertions, 44 deletions
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index e764ae783c..6b19c13109 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -304,9 +304,21 @@ private slots: void propertyOverride(); void concatenatedStringPropertyAccess(); void jsOwnedObjectsDeletedOnEngineDestroy(); + void updateCall(); void numberParsing(); void stringParsing(); + void push_and_shift(); void qtbug_32801(); + void thisObject(); + void qtbug_33754(); + void qtbug_34493(); + void singletonFromQMLToCpp(); + void singletonFromQMLAndBackAndCompare(); + void setPropertyOnInvalid(); + void miscTypeTest(); + void stackLimits(); + void idsAsLValues(); + void qtbug_34792(); private: // static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter); @@ -1304,6 +1316,7 @@ void tst_qqmlecmascript::scope() QVERIFY(object != 0); QCOMPARE(object->property("test1").toBool(), true); + QEXPECT_FAIL("", "Properties resolvable at compile time come before the global object, which is not 100% compatible with older QML versions", Continue); QCOMPARE(object->property("test2").toBool(), true); QCOMPARE(object->property("test3").toBool(), true); @@ -2257,15 +2270,16 @@ static inline bool evaluate_error(QV8Engine *engine, const QV4::ValueRef o, cons QV4::ExecutionContext *ctx = QV8Engine::getV4(engine)->current; QV4::Scope scope(ctx); - try { - QV4::Scoped<QV4::FunctionObject> function(scope, program.run()); - if (!function) - return false; - QV4::ScopedCallData d(scope, 1); - d->args[0] = o; - d->thisObject = engine->global(); - function->call(d); - } catch (...) { + QV4::Scoped<QV4::FunctionObject> function(scope, program.run()); + if (scope.engine->hasException) { + ctx->catchException(); + return true; + } + QV4::ScopedCallData d(scope, 1); + d->args[0] = o; + d->thisObject = engine->global(); + function->call(d); + if (scope.engine->hasException) { ctx->catchException(); return true; } @@ -2284,21 +2298,24 @@ static inline bool evaluate_value(QV8Engine *engine, const QV4::ValueRef o, QV4::ExecutionContext *ctx = QV8Engine::getV4(engine)->current; QV4::Scope scope(ctx); - try { - QV4::Scoped<QV4::FunctionObject> function(scope, program.run()); - if (!function) - return false; - - QV4::ScopedValue value(scope); - QV4::ScopedCallData d(scope, 1); - d->args[0] = o; - d->thisObject = engine->global(); - value = function->call(d); - return __qmljs_strict_equal(value, result); - } catch (...) { + QV4::Scoped<QV4::FunctionObject> function(scope, program.run()); + if (scope.engine->hasException) { ctx->catchException(); + return false; } - return false; + if (!function) + return false; + + QV4::ScopedValue value(scope); + QV4::ScopedCallData d(scope, 1); + d->args[0] = o; + d->thisObject = engine->global(); + value = function->call(d); + if (scope.engine->hasException) { + ctx->catchException(); + return false; + } + return __qmljs_strict_equal(value, result); } static inline QV4::ReturnedValue evaluate(QV8Engine *engine, const QV4::ValueRef o, @@ -2312,18 +2329,23 @@ static inline QV4::ReturnedValue evaluate(QV8Engine *engine, const QV4::ValueRef QV4::Script program(QV8Engine::getV4(engine)->rootContext, functionSource); program.inheritContext = true; - try { - QV4::Scoped<QV4::FunctionObject> function(scope, program.run()); - if (!function) - return QV4::Encode::undefined(); - QV4::ScopedCallData d(scope, 1); - d->args[0] = o; - d->thisObject = engine->global(); - return function->call(d); - } catch (...) { + + QV4::Scoped<QV4::FunctionObject> function(scope, program.run()); + if (scope.engine->hasException) { + ctx->catchException(); + return QV4::Encode::undefined(); + } + if (!function) + return QV4::Encode::undefined(); + QV4::ScopedCallData d(scope, 1); + d->args[0] = o; + d->thisObject = engine->global(); + QV4::ScopedValue result(scope, function->call(d)); + if (scope.engine->hasException) { ctx->catchException(); + return QV4::Encode::undefined(); } - return QV4::Encode::undefined(); + return result.asReturnedValue(); } #define EVALUATE_ERROR(source) evaluate_error(engine, object, source) @@ -3479,9 +3501,9 @@ void tst_qqmlecmascript::compiled() QCOMPARE(object->property("test15").toBool(), false); QCOMPARE(object->property("test16").toBool(), true); - QCOMPARE(object->property("test17").toInt(), 5); + QCOMPARE(object->property("test17").toInt(), 4); QCOMPARE(object->property("test18").toReal(), qreal(176)); - QCOMPARE(object->property("test19").toInt(), 7); + QCOMPARE(object->property("test19").toInt(), 6); QCOMPARE(object->property("test20").toReal(), qreal(6.7)); QCOMPARE(object->property("test21").toString(), QLatin1String("6.7")); QCOMPARE(object->property("test22").toString(), QLatin1String("!")); @@ -3506,7 +3528,7 @@ void tst_qqmlecmascript::numberAssignment() QCOMPARE(object->property("test3"), QVariant((qreal)6)); QCOMPARE(object->property("test4"), QVariant((qreal)6)); - QCOMPARE(object->property("test5"), QVariant((int)7)); + QCOMPARE(object->property("test5"), QVariant((int)6)); QCOMPARE(object->property("test6"), QVariant((int)7)); QCOMPARE(object->property("test7"), QVariant((int)6)); QCOMPARE(object->property("test8"), QVariant((int)6)); @@ -3880,15 +3902,15 @@ void tst_qqmlecmascript::singletonTypeResolution() void tst_qqmlecmascript::verifyContextLifetime(QQmlContextData *ctxt) { QQmlContextData *childCtxt = ctxt->childContexts; - if (!ctxt->importedScripts.isEmpty()) { + if (!ctxt->importedScripts.isNullOrUndefined()) { QV8Engine *engine = QV8Engine::get(ctxt->engine); - foreach (const QV4::PersistentValue& qmlglobal, ctxt->importedScripts) { + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); + QV4::Scope scope(v4); + QV4::ScopedArrayObject scripts(scope, ctxt->importedScripts); + QV4::ScopedValue qml(scope); + for (quint32 i = 0; i < scripts->arrayLength(); ++i) { QQmlContextData *scriptContext, *newContext; - - if (qmlglobal.isUndefined()) - continue; - QV4::Scope scope(QV8Engine::getV4((engine))); - QV4::ScopedValue qml(scope, qmlglobal.value()); + qml = scripts->getIndexed(i); scriptContext = QV4::QmlContextWrapper::getContext(qml); qml = QV4::Encode::undefined(); @@ -3900,7 +3922,7 @@ void tst_qqmlecmascript::verifyContextLifetime(QQmlContextData *ctxt) { } engine->gc(); - qml = qmlglobal.value(); + qml = scripts->getIndexed(i); newContext = QV4::QmlContextWrapper::getContext(qml); QVERIFY(scriptContext == newContext); } @@ -5411,6 +5433,8 @@ void tst_qqmlecmascript::sequenceConversionIndexes() QTest::ignoreMessage(QtWarningMsg, qPrintable(w3)); QMetaObject::invokeMethod(object, "indexedAccess"); QVERIFY(object->property("success").toBool()); + QMetaObject::invokeMethod(object, "indexOf"); + QVERIFY(object->property("success").toBool()); delete object; } @@ -6435,7 +6459,7 @@ void tst_qqmlecmascript::realToInt() QMetaObject::invokeMethod(object, "test1"); QCOMPARE(object->value(), int(4)); QMetaObject::invokeMethod(object, "test2"); - QCOMPARE(object->value(), int(8)); + QCOMPARE(object->value(), int(7)); } void tst_qqmlecmascript::urlProperty() @@ -7261,6 +7285,17 @@ void tst_qqmlecmascript::jsOwnedObjectsDeletedOnEngineDestroy() delete object; } +void tst_qqmlecmascript::updateCall() +{ + // update is a slot on QQuickItem. Even though it's not + // documented it can be called from within QML. Make sure + // we don't crash when calling it. + QString file("updateCall.qml"); + QQmlComponent component(&engine, testFileUrl(file)); + QObject *object = component.create(); + QVERIFY(object != 0); +} + void tst_qqmlecmascript::numberParsing() { for (int i = 1; i < 8; ++i) { @@ -7289,6 +7324,18 @@ void tst_qqmlecmascript::stringParsing() } } +void tst_qqmlecmascript::push_and_shift() +{ + QJSEngine e; + const QString program = + "var array = []; " + "for (var i = 0; i < 10000; i++) {" + " array.push(5); array.unshift(5); array.push(5);" + "}" + "array.length;"; + QVERIFY(e.evaluate(program).toNumber() == 30000); +} + void tst_qqmlecmascript::qtbug_32801() { QQmlComponent component(&engine, testFileUrl("qtbug_32801.qml")); @@ -7301,6 +7348,149 @@ void tst_qqmlecmascript::qtbug_32801() QVERIFY(QMetaObject::invokeMethod(obj.data(), "emitTestSignal")); } +void tst_qqmlecmascript::thisObject() +{ + QQmlComponent component(&engine, testFileUrl("thisObject.qml")); + QObject *object = component.create(); + QVERIFY(object); + QCOMPARE(qvariant_cast<QObject*>(object->property("subObject"))->property("test").toInt(), 2); + delete object; +} + +void tst_qqmlecmascript::qtbug_33754() +{ + QQmlComponent component(&engine, testFileUrl("qtbug_33754.qml")); + + QScopedPointer<QObject> obj(component.create()); + QVERIFY(obj != 0); +} + +void tst_qqmlecmascript::qtbug_34493() +{ + QQmlComponent component(&engine, testFileUrl("qtbug_34493.qml")); + + QScopedPointer<QObject> obj(component.create()); + if (component.errors().size()) + qDebug() << component.errors(); + QVERIFY(component.errors().isEmpty()); + QVERIFY(obj != 0); + QVERIFY(QMetaObject::invokeMethod(obj.data(), "doIt")); + QTRY_VERIFY(obj->property("prop").toString() == QLatin1String("Hello World!")); +} + +// Check that a Singleton can be passed from QML to C++ +// as its type*, it's parent type* and as QObject* +void tst_qqmlecmascript::singletonFromQMLToCpp() +{ + QQmlComponent component(&engine, testFile("singletonTest.qml")); + QScopedPointer<QObject> obj(component.create()); + if (component.errors().size()) + qDebug() << component.errors(); + QVERIFY(component.errors().isEmpty()); + QVERIFY(obj != 0); + + QCOMPARE(obj->property("qobjectTest"), QVariant(true)); + QCOMPARE(obj->property("myQmlObjectTest"), QVariant(true)); + QCOMPARE(obj->property("myInheritedQmlObjectTest"), QVariant(true)); +} + +// Check that a Singleton can be passed from QML to C++ +// as its type*, it's parent type* and as QObject* +// and correctly compares to itself +void tst_qqmlecmascript::singletonFromQMLAndBackAndCompare() +{ + QQmlComponent component(&engine, testFile("singletonTest2.qml")); + QScopedPointer<QObject> o(component.create()); + if (component.errors().size()) + qDebug() << component.errors(); + QVERIFY(component.errors().isEmpty()); + QVERIFY(o != 0); + + QCOMPARE(o->property("myInheritedQmlObjectTest1"), QVariant(true)); + QCOMPARE(o->property("myInheritedQmlObjectTest2"), QVariant(true)); + QCOMPARE(o->property("myInheritedQmlObjectTest3"), QVariant(true)); + + QCOMPARE(o->property("myQmlObjectTest1"), QVariant(true)); + QCOMPARE(o->property("myQmlObjectTest2"), QVariant(true)); + QCOMPARE(o->property("myQmlObjectTest3"), QVariant(true)); + + QCOMPARE(o->property("qobjectTest1"), QVariant(true)); + QCOMPARE(o->property("qobjectTest2"), QVariant(true)); + QCOMPARE(o->property("qobjectTest3"), QVariant(true)); + + QCOMPARE(o->property("singletonEqualToItself"), QVariant(true)); +} + +void tst_qqmlecmascript::setPropertyOnInvalid() +{ + { + QQmlComponent component(&engine, testFileUrl("setPropertyOnNull.qml")); + QString warning = component.url().toString() + ":4: TypeError: Type error"; + QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); + QObject *object = component.create(); + QVERIFY(object); + delete object; + } + + { + QQmlComponent component(&engine, testFileUrl("setPropertyOnUndefined.qml")); + QString warning = component.url().toString() + ":4: TypeError: Type error"; + QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); + QObject *object = component.create(); + QVERIFY(object); + delete object; + } +} + +void tst_qqmlecmascript::miscTypeTest() +{ + QQmlComponent component(&engine, testFileUrl("misctypetest.qml")); + + QObject *object = component.create(); + if (object == 0) + qDebug() << component.errorString(); + QVERIFY(object != 0); + + QVariant q; + QMetaObject::invokeMethod(object, "test_invalid_url_equal", Q_RETURN_ARG(QVariant, q)); + QVERIFY(q.toBool() == true); + QMetaObject::invokeMethod(object, "test_invalid_url_strictequal", Q_RETURN_ARG(QVariant, q)); + QVERIFY(q.toBool() == true); + QMetaObject::invokeMethod(object, "test_valid_url_equal", Q_RETURN_ARG(QVariant, q)); + QVERIFY(q.toBool() == true); + QMetaObject::invokeMethod(object, "test_valid_url_strictequal", Q_RETURN_ARG(QVariant, q)); + QVERIFY(q.toBool() == true); + + delete object; +} + +void tst_qqmlecmascript::stackLimits() +{ + QJSEngine engine; + engine.evaluate(QStringLiteral("function foo() {foo();} try {foo()} catch(e) { }")); +} + +void tst_qqmlecmascript::idsAsLValues() +{ + QString err = QString(QLatin1String("%1:5 left-hand side of assignment operator is not an lvalue\n")).arg(testFileUrl("idAsLValue.qml").toString()); + QQmlComponent component(&engine, testFileUrl("idAsLValue.qml")); + QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); + MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); + QVERIFY(!object); + QCOMPARE(component.errorString(), err); +} + +void tst_qqmlecmascript::qtbug_34792() +{ + QQmlComponent component(&engine, testFileUrl("qtbug34792.qml")); + + QObject *object = component.create(); + if (object == 0) + qDebug() << component.errorString(); + QVERIFY(object != 0); + delete object; +} + QTEST_MAIN(tst_qqmlecmascript) #include "tst_qqmlecmascript.moc" |