#include #include #include #include #include #include #include #include #include #include "testtypes.h" /* This test covers evaluation of ECMAScript expressions and bindings from within QML. This does not include static QML language issues. Static QML language issues are covered in qmllanguage */ inline QUrl TEST_FILE(const QString &filename) { QFileInfo fileInfo(__FILE__); return QUrl::fromLocalFile(fileInfo.absoluteDir().filePath("data/" + filename)); } inline QUrl TEST_FILE(const char *filename) { return TEST_FILE(QLatin1String(filename)); } class tst_qmlecmascript : public QObject { Q_OBJECT public: tst_qmlecmascript() {} private slots: void idShortcutInvalidates(); void boolPropertiesEvaluateAsBool(); void methods(); void signalAssignment(); void bindingLoop(); void basicExpressions(); void basicExpressions_data(); void arrayExpressions(); void contextPropertiesTriggerReeval(); void objectPropertiesTriggerReeval(); void deferredProperties(); void extensionObjects(); void attachedProperties(); void enums(); void valueTypeFunctions(); void constantsOverrideBindings(); void outerBindingOverridesInnerBinding(); void aliasPropertyAndBinding(); void nonExistantAttachedObject(); void scope(); void signalParameterTypes(); void objectsCompareAsEqual(); void scriptAccess(); void dynamicCreation(); void dynamicDestruction(); void objectToString(); void selfDeletingBinding(); void extendedObjectPropertyLookup(); void scriptErrors(); void signalTriggeredBindings(); private: QmlEngine engine; }; void tst_qmlecmascript::idShortcutInvalidates() { { QmlComponent component(&engine, TEST_FILE("idShortcutInvalidates.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); QVERIFY(object->objectProperty() != 0); delete object->objectProperty(); QVERIFY(object->objectProperty() == 0); } { QmlComponent component(&engine, TEST_FILE("idShortcutInvalidates.1.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); QVERIFY(object->objectProperty() != 0); delete object->objectProperty(); QVERIFY(object->objectProperty() == 0); } } void tst_qmlecmascript::boolPropertiesEvaluateAsBool() { { QmlComponent component(&engine, TEST_FILE("boolPropertiesEvaluateAsBool.1.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); QCOMPARE(object->stringProperty(), QLatin1String("pass")); } { QmlComponent component(&engine, TEST_FILE("boolPropertiesEvaluateAsBool.2.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); QCOMPARE(object->stringProperty(), QLatin1String("pass")); } } void tst_qmlecmascript::signalAssignment() { { QmlComponent component(&engine, TEST_FILE("signalAssignment.1.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); QCOMPARE(object->string(), QString()); emit object->basicSignal(); QCOMPARE(object->string(), QString("pass")); } { QmlComponent component(&engine, TEST_FILE("signalAssignment.2.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); QCOMPARE(object->string(), QString()); emit object->argumentSignal(19, "Hello world!", 10.3); QCOMPARE(object->string(), QString("pass 19 Hello world! 10.3")); } } void tst_qmlecmascript::methods() { { QmlComponent component(&engine, TEST_FILE("methods.1.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); QCOMPARE(object->methodCalled(), false); QCOMPARE(object->methodIntCalled(), false); emit object->basicSignal(); QCOMPARE(object->methodCalled(), true); QCOMPARE(object->methodIntCalled(), false); } { QmlComponent component(&engine, TEST_FILE("methods.2.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); QCOMPARE(object->methodCalled(), false); QCOMPARE(object->methodIntCalled(), false); emit object->basicSignal(); QCOMPARE(object->methodCalled(), false); QCOMPARE(object->methodIntCalled(), true); } } void tst_qmlecmascript::bindingLoop() { QmlComponent component(&engine, TEST_FILE("bindingLoop.qml")); QString warning = "QML MyQmlObject (" + component.url().toString() + ":9:9) Binding loop detected for property \"stringProperty\" "; QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); QObject *object = component.create(); QVERIFY(object != 0); } void tst_qmlecmascript::basicExpressions_data() { QTest::addColumn("expression"); QTest::addColumn("result"); QTest::addColumn("nest"); QTest::newRow("Context property") << "a" << QVariant(1944) << false; QTest::newRow("Context property") << "a" << QVariant(1944) << true; QTest::newRow("Context property expression") << "a * 2" << QVariant(3888) << false; QTest::newRow("Context property expression") << "a * 2" << QVariant(3888) << true; QTest::newRow("Overridden context property") << "b" << QVariant("Milk") << false; QTest::newRow("Overridden context property") << "b" << QVariant("Cow") << true; QTest::newRow("Object property") << "object.stringProperty" << QVariant("Object1") << false; QTest::newRow("Object property") << "object.stringProperty" << QVariant("Object1") << true; QTest::newRow("Overridden object property") << "objectOverride.stringProperty" << QVariant("Object2") << false; QTest::newRow("Overridden object property") << "objectOverride.stringProperty" << QVariant("Object3") << true; QTest::newRow("Default object property") << "horseLegs" << QVariant(4) << false; QTest::newRow("Default object property") << "antLegs" << QVariant(6) << false; QTest::newRow("Default object property") << "emuLegs" << QVariant(2) << false; QTest::newRow("Nested default object property") << "horseLegs" << QVariant(4) << true; QTest::newRow("Nested default object property") << "antLegs" << QVariant(7) << true; QTest::newRow("Nested default object property") << "emuLegs" << QVariant(2) << true; QTest::newRow("Nested default object property") << "humanLegs" << QVariant(2) << true; QTest::newRow("Context property override default object property") << "millipedeLegs" << QVariant(100) << true; } void tst_qmlecmascript::basicExpressions() { QFETCH(QString, expression); QFETCH(QVariant, result); QFETCH(bool, nest); MyQmlObject object1; MyQmlObject object2; MyQmlObject object3; MyDefaultObject1 default1; MyDefaultObject2 default2; MyDefaultObject3 default3; object1.setStringProperty("Object1"); object2.setStringProperty("Object2"); object3.setStringProperty("Object3"); QmlContext context(engine.rootContext()); QmlContext nestedContext(&context); context.addDefaultObject(&default1); context.addDefaultObject(&default2); context.setContextProperty("a", QVariant(1944)); context.setContextProperty("b", QVariant("Milk")); context.setContextProperty("object", &object1); context.setContextProperty("objectOverride", &object2); nestedContext.addDefaultObject(&default3); nestedContext.setContextProperty("b", QVariant("Cow")); nestedContext.setContextProperty("objectOverride", &object3); nestedContext.setContextProperty("millipedeLegs", QVariant(100)); MyExpression expr(nest?&nestedContext:&context, expression); QCOMPARE(expr.value(), result); } Q_DECLARE_METATYPE(QList); void tst_qmlecmascript::arrayExpressions() { QObject obj1; QObject obj2; QObject obj3; QmlContext context(engine.rootContext()); context.setContextProperty("a", &obj1); context.setContextProperty("b", &obj2); context.setContextProperty("c", &obj3); MyExpression expr(&context, "[a, b, c, 10]"); QVariant result = expr.value(); QCOMPARE(result.userType(), qMetaTypeId >()); QList list = qvariant_cast >(result); QCOMPARE(list.count(), 4); QCOMPARE(list.at(0), &obj1); QCOMPARE(list.at(1), &obj2); QCOMPARE(list.at(2), &obj3); QCOMPARE(list.at(3), (QObject *)0); } // Tests that modifying a context property will reevaluate expressions void tst_qmlecmascript::contextPropertiesTriggerReeval() { QmlContext context(engine.rootContext()); MyQmlObject object1; MyQmlObject object2; MyQmlObject *object3 = new MyQmlObject; object1.setStringProperty("Hello"); object2.setStringProperty("World"); context.setContextProperty("testProp", QVariant(1)); context.setContextProperty("testObj", &object1); context.setContextProperty("testObj2", object3); { MyExpression expr(&context, "testProp + 1"); QCOMPARE(expr.changed, false); QCOMPARE(expr.value(), QVariant(2)); context.setContextProperty("testProp", QVariant(2)); QCOMPARE(expr.changed, true); QCOMPARE(expr.value(), QVariant(3)); } { MyExpression expr(&context, "testProp + testProp + testProp"); QCOMPARE(expr.changed, false); QCOMPARE(expr.value(), QVariant(6)); context.setContextProperty("testProp", QVariant(4)); QCOMPARE(expr.changed, true); QCOMPARE(expr.value(), QVariant(12)); } { MyExpression expr(&context, "testObj.stringProperty"); QCOMPARE(expr.changed, false); QCOMPARE(expr.value(), QVariant("Hello")); context.setContextProperty("testObj", &object2); QCOMPARE(expr.changed, true); QCOMPARE(expr.value(), QVariant("World")); } { MyExpression expr(&context, "testObj.stringProperty /**/"); QCOMPARE(expr.changed, false); QCOMPARE(expr.value(), QVariant("World")); context.setContextProperty("testObj", &object1); QCOMPARE(expr.changed, true); QCOMPARE(expr.value(), QVariant("Hello")); } { MyExpression expr(&context, "testObj2"); QCOMPARE(expr.changed, false); QCOMPARE(expr.value(), QVariant::fromValue((QObject *)object3)); } } void tst_qmlecmascript::objectPropertiesTriggerReeval() { QmlContext context(engine.rootContext()); MyQmlObject object1; MyQmlObject object2; MyQmlObject object3; context.setContextProperty("testObj", &object1); object1.setStringProperty(QLatin1String("Hello")); object2.setStringProperty(QLatin1String("Dog")); object3.setStringProperty(QLatin1String("Cat")); { MyExpression expr(&context, "testObj.stringProperty"); QCOMPARE(expr.changed, false); QCOMPARE(expr.value(), QVariant("Hello")); object1.setStringProperty(QLatin1String("World")); QCOMPARE(expr.changed, true); QCOMPARE(expr.value(), QVariant("World")); } { MyExpression expr(&context, "testObj.objectProperty.stringProperty"); QCOMPARE(expr.changed, false); QCOMPARE(expr.value(), QVariant()); object1.setObjectProperty(&object2); QCOMPARE(expr.changed, true); expr.changed = false; QCOMPARE(expr.value(), QVariant("Dog")); object1.setObjectProperty(&object3); QCOMPARE(expr.changed, true); expr.changed = false; QCOMPARE(expr.value(), QVariant("Cat")); object1.setObjectProperty(0); QCOMPARE(expr.changed, true); expr.changed = false; QCOMPARE(expr.value(), QVariant()); object1.setObjectProperty(&object3); QCOMPARE(expr.changed, true); expr.changed = false; QCOMPARE(expr.value(), QVariant("Cat")); object3.setStringProperty("Donkey"); QCOMPARE(expr.changed, true); expr.changed = false; QCOMPARE(expr.value(), QVariant("Donkey")); } } void tst_qmlecmascript::deferredProperties() { QmlComponent component(&engine, TEST_FILE("deferredProperties.qml")); MyDeferredObject *object = qobject_cast(component.create()); QVERIFY(object != 0); QCOMPARE(object->value(), 0); QVERIFY(object->objectProperty() == 0); QVERIFY(object->objectProperty2() != 0); qmlExecuteDeferred(object); QCOMPARE(object->value(), 10); QVERIFY(object->objectProperty() != 0); MyQmlObject *qmlObject = qobject_cast(object->objectProperty()); QVERIFY(qmlObject != 0); } void tst_qmlecmascript::extensionObjects() { QmlComponent component(&engine, TEST_FILE("extensionObjects.qml")); MyExtendedObject *object = qobject_cast(component.create()); QVERIFY(object != 0); QCOMPARE(object->baseProperty(), 13); QCOMPARE(object->coreProperty(), 9); object->setProperty("extendedProperty", QVariant(11)); object->setProperty("baseExtendedProperty", QVariant(92)); QCOMPARE(object->coreProperty(), 11); QCOMPARE(object->baseProperty(), 92); } void tst_qmlecmascript::attachedProperties() { QmlComponent component(&engine, TEST_FILE("attachedProperty.qml")); QObject *object = component.create(); QVERIFY(object != 0); QCOMPARE(object->property("a").toInt(), 19); QCOMPARE(object->property("b").toInt(), 19); QCOMPARE(object->property("c").toInt(), 19); QCOMPARE(object->property("d").toInt(), 19); // ### Need to test attached property assignment } void tst_qmlecmascript::enums() { // Existant enums { QmlComponent component(&engine, TEST_FILE("enums.1.qml")); QObject *object = component.create(); QVERIFY(object != 0); QCOMPARE(object->property("a").toInt(), 0); QCOMPARE(object->property("b").toInt(), 1); QCOMPARE(object->property("c").toInt(), 2); QCOMPARE(object->property("d").toInt(), 3); QCOMPARE(object->property("e").toInt(), 0); QCOMPARE(object->property("f").toInt(), 1); QCOMPARE(object->property("g").toInt(), 2); QCOMPARE(object->property("h").toInt(), 3); QCOMPARE(object->property("i").toInt(), 19); QCOMPARE(object->property("j").toInt(), 19); } // Non-existant enums { QmlComponent component(&engine, TEST_FILE("enums.2.qml")); QObject *object = component.create(); QVERIFY(object != 0); QCOMPARE(object->property("a").toInt(), 0); QCOMPARE(object->property("b").toInt(), 0); } } void tst_qmlecmascript::valueTypeFunctions() { QmlComponent component(&engine, TEST_FILE("valueTypeFunctions.qml")); MyTypeObject *obj = qobject_cast(component.create()); QVERIFY(obj != 0); QCOMPARE(obj->rectProperty(), QRect(0,0,100,100)); QCOMPARE(obj->rectFProperty(), QRectF(0,0.5,100,99.5)); } /* Tests that writing a constant to a property with a binding on it disables the binding. */ void tst_qmlecmascript::constantsOverrideBindings() { // From ECMAScript { QmlComponent component(&engine, TEST_FILE("constantsOverrideBindings.1.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); QCOMPARE(object->property("c2").toInt(), 0); object->setProperty("c1", QVariant(9)); QCOMPARE(object->property("c2").toInt(), 9); emit object->basicSignal(); QCOMPARE(object->property("c2").toInt(), 13); object->setProperty("c1", QVariant(8)); QCOMPARE(object->property("c2").toInt(), 13); } // During construction { QmlComponent component(&engine, TEST_FILE("constantsOverrideBindings.2.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); QCOMPARE(object->property("c1").toInt(), 0); QCOMPARE(object->property("c2").toInt(), 10); object->setProperty("c1", QVariant(9)); QCOMPARE(object->property("c1").toInt(), 9); QCOMPARE(object->property("c2").toInt(), 10); } #if 0 // From C++ { QmlComponent component(&engine, TEST_FILE("constantsOverrideBindings.3.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); QCOMPARE(object->property("c2").toInt(), 0); object->setProperty("c1", QVariant(9)); QCOMPARE(object->property("c2").toInt(), 9); object->setProperty("c2", QVariant(13)); QCOMPARE(object->property("c2").toInt(), 13); object->setProperty("c1", QVariant(7)); QCOMPARE(object->property("c1").toInt(), 7); QCOMPARE(object->property("c2").toInt(), 13); } #endif } /* Tests that assigning a binding to a property that already has a binding causes the original binding to be disabled. */ void tst_qmlecmascript::outerBindingOverridesInnerBinding() { QmlComponent component(&engine, TEST_FILE("outerBindingOverridesInnerBinding.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); QCOMPARE(object->property("c1").toInt(), 0); QCOMPARE(object->property("c2").toInt(), 0); QCOMPARE(object->property("c3").toInt(), 0); object->setProperty("c1", QVariant(9)); QCOMPARE(object->property("c1").toInt(), 9); QCOMPARE(object->property("c2").toInt(), 0); QCOMPARE(object->property("c3").toInt(), 0); object->setProperty("c3", QVariant(8)); QCOMPARE(object->property("c1").toInt(), 9); QCOMPARE(object->property("c2").toInt(), 8); QCOMPARE(object->property("c3").toInt(), 8); } /* Access a non-existant attached object. Tests for a regression where this used to crash. */ void tst_qmlecmascript::nonExistantAttachedObject() { QmlComponent component(&engine, TEST_FILE("nonExistantAttachedObject.qml")); QObject *object = component.create(); QVERIFY(object != 0); } void tst_qmlecmascript::scope() { { QmlComponent component(&engine, TEST_FILE("scope.qml")); QObject *object = component.create(); QVERIFY(object != 0); QCOMPARE(object->property("test1").toInt(), 1); QCOMPARE(object->property("test2").toInt(), 2); QCOMPARE(object->property("test3").toString(), QString("1Test")); QCOMPARE(object->property("test4").toString(), QString("2Test")); QCOMPARE(object->property("test5").toInt(), 1); QCOMPARE(object->property("test6").toInt(), 1); QCOMPARE(object->property("test7").toInt(), 2); QCOMPARE(object->property("test8").toInt(), 2); QCOMPARE(object->property("test9").toInt(), 1); QCOMPARE(object->property("test10").toInt(), 3); } { QmlComponent component(&engine, TEST_FILE("scope.2.qml")); QObject *object = component.create(); QVERIFY(object != 0); QCOMPARE(object->property("test1").toInt(), 19); QCOMPARE(object->property("test2").toInt(), 19); QCOMPARE(object->property("test3").toInt(), 11); QCOMPARE(object->property("test4").toInt(), 11); QCOMPARE(object->property("test5").toInt(), 24); QCOMPARE(object->property("test6").toInt(), 24); } } /* Tests that "any" type passes through a synthesized signal parameter. This is essentially a test of QmlMetaType::copy() */ void tst_qmlecmascript::signalParameterTypes() { QmlComponent component(&engine, TEST_FILE("signalParameterTypes.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); emit object->basicSignal(); QCOMPARE(object->property("intProperty").toInt(), 10); QCOMPARE(object->property("realProperty").toReal(), 19.2); QVERIFY(object->property("colorProperty").value() == QColor(255, 255, 0, 255)); QVERIFY(object->property("variantProperty") == QVariant::fromValue(QColor(255, 0, 255, 255))); } /* Test that two JS objects for the same QObject compare as equal. */ void tst_qmlecmascript::objectsCompareAsEqual() { QmlComponent component(&engine, TEST_FILE("objectsCompareAsEqual.qml")); QObject *object = component.create(); QVERIFY(object != 0); QCOMPARE(object->property("test1").toBool(), true); QCOMPARE(object->property("test2").toBool(), true); QCOMPARE(object->property("test3").toBool(), true); QCOMPARE(object->property("test4").toBool(), true); QCOMPARE(object->property("test5").toBool(), true); } /* Confirm bindings and alias properties can coexist. Tests for a regression where the binding would not reevaluate. */ void tst_qmlecmascript::aliasPropertyAndBinding() { QmlComponent component(&engine, TEST_FILE("aliasPropertyAndBinding.qml")); QObject *object = component.create(); QVERIFY(object != 0); QCOMPARE(object->property("c2").toInt(), 3); QCOMPARE(object->property("c3").toInt(), 3); object->setProperty("c2", QVariant(19)); QCOMPARE(object->property("c2").toInt(), 19); QCOMPARE(object->property("c3").toInt(), 19); } /* Tests that only methods of Script {} blocks are exposed. */ void tst_qmlecmascript::scriptAccess() { QmlComponent component(&engine, TEST_FILE("scriptAccess.qml")); QObject *object = component.create(); QVERIFY(object != 0); QCOMPARE(object->property("test1").toInt(), 10); QCOMPARE(object->property("test2").toInt(), 19); QCOMPARE(object->property("test3").toInt(), 0); } /* Test using createQmlObject to dynamically generate an item Also using createComponent is tested. */ void tst_qmlecmascript::dynamicCreation() { QmlComponent component(&engine, TEST_FILE("dynamicCreation.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); QObject *createdQmlObject = 0; QObject *createdComponent = 0; QMetaObject::invokeMethod(object, "createOne"); createdQmlObject = object->objectProperty(); QVERIFY(createdQmlObject); QCOMPARE(createdQmlObject->objectName(), QString("objectOne")); QMetaObject::invokeMethod(object, "createTwo"); createdComponent = object->objectProperty(); QVERIFY(createdComponent); QCOMPARE(createdComponent->objectName(), QString("objectTwo")); } /* Tests the destroy function */ void tst_qmlecmascript::dynamicDestruction() { QmlComponent component(&engine, TEST_FILE("dynamicDeletion.qml")); QGuard object = qobject_cast(component.create()); QVERIFY(object != 0); QGuard createdQmlObject = 0; QMetaObject::invokeMethod(object, "create"); createdQmlObject = object->objectProperty(); QVERIFY(createdQmlObject); QCOMPARE(createdQmlObject->objectName(), QString("emptyObject")); QMetaObject::invokeMethod(object, "killOther"); QVERIFY(createdQmlObject); QTest::qWait(0); QCoreApplication::instance()->processEvents(QEventLoop::DeferredDeletion); QVERIFY(createdQmlObject); QTest::qWait(100); QCoreApplication::instance()->processEvents(QEventLoop::DeferredDeletion); QVERIFY(!createdQmlObject); QMetaObject::invokeMethod(object, "killMe"); QVERIFY(object); QTest::qWait(0); QCoreApplication::instance()->processEvents(QEventLoop::DeferredDeletion); QVERIFY(!object); } /* tests that id.toString() works */ void tst_qmlecmascript::objectToString() { QmlComponent component(&engine, TEST_FILE("qmlToString.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); QMetaObject::invokeMethod(object, "testToString"); QVERIFY(object->stringProperty().startsWith("MyQmlObject_QML_")); QVERIFY(object->stringProperty().endsWith(", \"objName\")")); } /* Tests bindings that indirectly cause their own deletion work. This test is best run under valgrind to ensure no invalid memory access occur. */ void tst_qmlecmascript::selfDeletingBinding() { { QmlComponent component(&engine, TEST_FILE("selfDeletingBinding.qml")); QObject *object = component.create(); QVERIFY(object != 0); object->setProperty("triggerDelete", true); } { QmlComponent component(&engine, TEST_FILE("selfDeletingBinding.2.qml")); QObject *object = component.create(); QVERIFY(object != 0); object->setProperty("triggerDelete", true); } } /* Test that extended object properties can be accessed. This test a regression where this used to crash. The issue was specificially for extended objects that did not include a synthesized meta object (so non-root and no synthesiszed properties). */ void tst_qmlecmascript::extendedObjectPropertyLookup() { QmlComponent component(&engine, TEST_FILE("extendedObjectPropertyLookup.qml")); QObject *object = component.create(); QVERIFY(object != 0); } /* Test file/lineNumbers for binding/Script errors. */ void tst_qmlecmascript::scriptErrors() { QmlComponent component(&engine, TEST_FILE("scriptErrors.qml")); QString url = component.url().toString(); QString warning1 = url.left(url.length() - 3) + "js:2: Error: Invalid write to global property \"a\""; QString warning2 = url + ":7: TypeError: Result of expression 'a' [undefined] is not an object."; QString warning3 = url + ":5: Error: Invalid write to global property \"a\""; QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData()); QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData()); QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData()); QObject *object = component.create(); QVERIFY(object != 0); } /* Test bindings still work when the reeval is triggered from within a signal script. */ void tst_qmlecmascript::signalTriggeredBindings() { QmlComponent component(&engine, TEST_FILE("signalTriggeredBindings.qml")); MyQmlObject *object = qobject_cast(component.create()); QVERIFY(object != 0); QCOMPARE(object->property("base").toReal(), 50.); QCOMPARE(object->property("test1").toReal(), 50.); QCOMPARE(object->property("test2").toReal(), 50.); object->basicSignal(); QCOMPARE(object->property("base").toReal(), 200.); QCOMPARE(object->property("test1").toReal(), 200.); QCOMPARE(object->property("test2").toReal(), 200.); object->argumentSignal(10, QString(), 10); QCOMPARE(object->property("base").toReal(), 400.); QCOMPARE(object->property("test1").toReal(), 400.); QCOMPARE(object->property("test2").toReal(), 400.); } QTEST_MAIN(tst_qmlecmascript) #include "tst_qmlecmascript.moc"