diff options
Diffstat (limited to 'tests/auto/qml/qqmlecmascript')
374 files changed, 13974 insertions, 0 deletions
diff --git a/tests/auto/qml/qqmlecmascript/data/AliasBindingsAssignCorrectlyType.qml b/tests/auto/qml/qqmlecmascript/data/AliasBindingsAssignCorrectlyType.qml new file mode 100644 index 0000000000..e8e108fa44 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/AliasBindingsAssignCorrectlyType.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 + +QtObject { + id: root + + property real realProperty + property alias aliasProperty: root.realProperty +} + diff --git a/tests/auto/qml/qqmlecmascript/data/AliasBindingsOverrideTargetType.qml b/tests/auto/qml/qqmlecmascript/data/AliasBindingsOverrideTargetType.qml new file mode 100644 index 0000000000..062772106b --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/AliasBindingsOverrideTargetType.qml @@ -0,0 +1,14 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +MyTypeObject { + id: root + + property int data: 7 + + property int targetProperty: root.data * 43 - root.data + property alias aliasProperty: root.targetProperty + + pointProperty: Qt.point(data, data); + property alias pointAliasProperty: root.pointProperty +} diff --git a/tests/auto/qml/qqmlecmascript/data/AliasBindingsOverrideTargetType3.qml b/tests/auto/qml/qqmlecmascript/data/AliasBindingsOverrideTargetType3.qml new file mode 100644 index 0000000000..823c0ef367 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/AliasBindingsOverrideTargetType3.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 + +QtObject { + id: root + + property int testProperty + property alias aliasProperty: root.testProperty +} + diff --git a/tests/auto/qml/qqmlecmascript/data/AliasToCompositeElementType1.qml b/tests/auto/qml/qqmlecmascript/data/AliasToCompositeElementType1.qml new file mode 100644 index 0000000000..cef8ae09ea --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/AliasToCompositeElementType1.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 + +QtObject { + property alias group: obj + property variant foo: AliasToCompositeElementType2 { id: obj } +} diff --git a/tests/auto/qml/qqmlecmascript/data/AliasToCompositeElementType2.qml b/tests/auto/qml/qqmlecmascript/data/AliasToCompositeElementType2.qml new file mode 100644 index 0000000000..4a45535a50 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/AliasToCompositeElementType2.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +QtObject { + property int value +} diff --git a/tests/auto/qml/qqmlecmascript/data/ConstantsOverrideBindings.qml b/tests/auto/qml/qqmlecmascript/data/ConstantsOverrideBindings.qml new file mode 100644 index 0000000000..07bb16b0d8 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/ConstantsOverrideBindings.qml @@ -0,0 +1,12 @@ +import Qt.test 1.0 + +MyQmlObject { + property int c1: 0 + property int c2: c1 + property alias c3: inner.ic1 + + objectProperty: MyQmlObject { + id: inner + property int ic1: c1 + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/CustomObject.qml b/tests/auto/qml/qqmlecmascript/data/CustomObject.qml new file mode 100644 index 0000000000..aa1a1d6061 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/CustomObject.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +QtObject { + property string greeting: "hello world" +} diff --git a/tests/auto/qml/qqmlecmascript/data/ElementAssignType.qml b/tests/auto/qml/qqmlecmascript/data/ElementAssignType.qml new file mode 100644 index 0000000000..4a45535a50 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/ElementAssignType.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +QtObject { + property int value +} diff --git a/tests/auto/qml/qqmlecmascript/data/MethodsObject.qml b/tests/auto/qml/qqmlecmascript/data/MethodsObject.qml new file mode 100644 index 0000000000..eaca0a7f92 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/MethodsObject.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 + +QtObject { + function testFunction() { return 19; } + function testFunction2() { return 18; } +} diff --git a/tests/auto/qml/qqmlecmascript/data/NestedTypeTransientErrors.qml b/tests/auto/qml/qqmlecmascript/data/NestedTypeTransientErrors.qml new file mode 100644 index 0000000000..3b3e84a900 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/NestedTypeTransientErrors.qml @@ -0,0 +1,11 @@ +import QtQuick 2.0 + +QtObject { + property int b: obj.prop.a + + property variant prop; + prop: QtObject { + property int a: 10 + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/PropertyVarBaseItem.qml b/tests/auto/qml/qqmlecmascript/data/PropertyVarBaseItem.qml new file mode 100644 index 0000000000..5f28833fe7 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/PropertyVarBaseItem.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +Item { + property var random: null +} diff --git a/tests/auto/qml/qqmlecmascript/data/PropertyVarCircularComponent.qml b/tests/auto/qml/qqmlecmascript/data/PropertyVarCircularComponent.qml new file mode 100644 index 0000000000..36c025401f --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/PropertyVarCircularComponent.qml @@ -0,0 +1,23 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: first + property var vp: Item { + id: second + property var vp: Item { + id: third + property var vp: Item { + id: fourth + property var vp: Item { + id: fifth + property int fifthCanary: 5 + property var circ: third.vp + property MyScarceResourceObject srp; + srp: MyScarceResourceObject { id: scarceResourceProvider } + property variant memoryHog: scarceResourceProvider.newScarceResource() + } + } + } + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/PropertyVarCircularComponent2.qml b/tests/auto/qml/qqmlecmascript/data/PropertyVarCircularComponent2.qml new file mode 100644 index 0000000000..6a49cb9317 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/PropertyVarCircularComponent2.qml @@ -0,0 +1,26 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +// Similar to PVCC.qml except that it has another var property +// It will have a different metaobject. +Item { + id: first + property var anotherVp: 6 + property var vp: Item { + id: second + property var vp: Item { + id: third + property var vp: Item { + id: fourth + property var vp: Item { + id: fifth + property int fifthCanary: 5 + property var circ: third.vp + property MyScarceResourceObject srp; + srp: MyScarceResourceObject { id: scarceResourceProvider } + property variant memoryHog2: scarceResourceProvider.newScarceResource() + } + } + } + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/PropertyVarCircularComponent3.qml b/tests/auto/qml/qqmlecmascript/data/PropertyVarCircularComponent3.qml new file mode 100644 index 0000000000..a90725016e --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/PropertyVarCircularComponent3.qml @@ -0,0 +1,16 @@ +import QtQuick 2.0 + +Rectangle { + id: rectangle // will have JS ownership + objectName: "rectangle" + width: 10 + height: 10 + property var rectCanary: 5 + + Text { + id: text // will have Eventual-JS ownership + objectName: "text" + property var vp: rectangle + property var textCanary: 10 + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/PropertyVarCircularComponent4.qml b/tests/auto/qml/qqmlecmascript/data/PropertyVarCircularComponent4.qml new file mode 100644 index 0000000000..9273a52f54 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/PropertyVarCircularComponent4.qml @@ -0,0 +1,28 @@ +import QtQuick 2.0 + +Rectangle { + id: rectangle // will have JS ownership + objectName: "rectangle" + width: 10 + height: 10 + property var rectCanary: 5 + + Text { + id: text // will have Eventual-JS ownership + objectName: "text" + property var vp + property var textCanary: 10 + + // The varProperties array of "text" is weak + // (due to eventual JS ownership since parent is JS owned) + // but nonetheless, the reference to the created QObject + // should cause that QObject to NOT be collected. + function constructQObject() { + var component = Qt.createComponent("PropertyVarCircularComponent5.qml"); + if (component.status == Component.Ready) { + text.vp = component.createObject(null); // has JavaScript ownership + } + gc(); + } + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/PropertyVarCircularComponent5.qml b/tests/auto/qml/qqmlecmascript/data/PropertyVarCircularComponent5.qml new file mode 100644 index 0000000000..94ef338792 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/PropertyVarCircularComponent5.qml @@ -0,0 +1,7 @@ +import QtQuick 2.0 + +Image { + id: image + objectName: "image" + property var imageCanary: 13 +} diff --git a/tests/auto/qml/qqmlecmascript/data/PropertyVarInheritanceComponent.qml b/tests/auto/qml/qqmlecmascript/data/PropertyVarInheritanceComponent.qml new file mode 100644 index 0000000000..b01cf6ed84 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/PropertyVarInheritanceComponent.qml @@ -0,0 +1,22 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +PropertyVarCircularComponent { + id: inheritanceComponent + property int inheritanceIntProperty: 6 + property var inheritanceVarProperty + + function constructGarbage() { + var retn = 1; + var component = Qt.createComponent("PropertyVarCircularComponent2.qml"); + if (component.status == Component.Ready) { + retn = component.createObject(null); // has JavaScript ownership + } + return retn; + } + + Component.onCompleted: { + inheritanceVarProperty = constructGarbage(); + gc(); + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/PropertyVarOwnershipComponent.qml b/tests/auto/qml/qqmlecmascript/data/PropertyVarOwnershipComponent.qml new file mode 100644 index 0000000000..c1f73d3bac --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/PropertyVarOwnershipComponent.qml @@ -0,0 +1,37 @@ +import QtQuick 2.0 + +Rectangle { + id: rectangle // will have JS ownership + objectName: "rectangle" + width: 10 + height: 10 + property var rectCanary: 5 + + Text { + id: textOne // will have Eventual-JS ownership + objectName: "textOne" + property var textCanary: 11 + property var vp + } + + Text { + id: textTwo + objectName: "textTwo" + property var textCanary: 12 + property var vp + + function constructQObject() { + var component = Qt.createComponent("PropertyVarCircularComponent5.qml"); + if (component.status == Component.Ready) { + textTwo.vp = component.createObject(null); // has JavaScript ownership + } + gc(); + } + + function deassignVp() { + textTwo.textCanary = 22; + textTwo.vp = textTwo.textCanary; + gc(); + } + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/ScarceResourceSignalComponentVar.qml b/tests/auto/qml/qqmlecmascript/data/ScarceResourceSignalComponentVar.qml new file mode 100644 index 0000000000..d56bd41a99 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/ScarceResourceSignalComponentVar.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 + +QtObject { + property var scarceResourceCopy + property int width: 5 + signal testSignal + signal testSignal2 +} + diff --git a/tests/auto/qml/qqmlecmascript/data/ScarceResourceSignalComponentVariant.qml b/tests/auto/qml/qqmlecmascript/data/ScarceResourceSignalComponentVariant.qml new file mode 100644 index 0000000000..e10fcfe36a --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/ScarceResourceSignalComponentVariant.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 + +QtObject { + property variant scarceResourceCopy + property int width: 5 + signal testSignal + signal testSignal2 +} + diff --git a/tests/auto/qml/qqmlecmascript/data/ScarceResourceVarComponent.qml b/tests/auto/qml/qqmlecmascript/data/ScarceResourceVarComponent.qml new file mode 100644 index 0000000000..2cf6b4223b --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/ScarceResourceVarComponent.qml @@ -0,0 +1,13 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: first + property var vp: Item { + id: second + property MyScarceResourceObject srp; + srp: MyScarceResourceObject { id: scarceResourceProvider } + property var sr: scarceResourceProvider.scarceResource + property var canary: 5 + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/Scope6Nested.qml b/tests/auto/qml/qqmlecmascript/data/Scope6Nested.qml new file mode 100644 index 0000000000..a3794df22b --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/Scope6Nested.qml @@ -0,0 +1,7 @@ +import Qt.test 1.0 + +MyQmlObject { + function runtest(obj) { + return obj.MyQmlObject.value == 19; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/ScopeObject.qml b/tests/auto/qml/qqmlecmascript/data/ScopeObject.qml new file mode 100644 index 0000000000..f341cce3c9 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/ScopeObject.qml @@ -0,0 +1,12 @@ +import QtQuick 2.0 + +Item { + property int a: 3 + property int binding: myFunction(); + property int binding2: myCompFunction(); + + function myCompFunction() { + return a; + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/SequenceConversionComponent.qml b/tests/auto/qml/qqmlecmascript/data/SequenceConversionComponent.qml new file mode 100644 index 0000000000..0c7f60b062 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/SequenceConversionComponent.qml @@ -0,0 +1,7 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +MySequenceConversionObject { + id: sccmsco + objectName: "sccmsco" +} diff --git a/tests/auto/qml/qqmlecmascript/data/SpuriousWarning.qml b/tests/auto/qml/qqmlecmascript/data/SpuriousWarning.qml new file mode 100644 index 0000000000..f6398d254d --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/SpuriousWarning.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +Item { + property int children: root.children.length +} diff --git a/tests/auto/qml/qqmlecmascript/data/TypeForDynamicCreation.qml b/tests/auto/qml/qqmlecmascript/data/TypeForDynamicCreation.qml new file mode 100644 index 0000000000..56e06252c4 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/TypeForDynamicCreation.qml @@ -0,0 +1,2 @@ +import Qt.test 1.0 +MyQmlObject{objectName:"objectThree"} diff --git a/tests/auto/qml/qqmlecmascript/data/aliasBindingsAssignCorrectly.qml b/tests/auto/qml/qqmlecmascript/data/aliasBindingsAssignCorrectly.qml new file mode 100644 index 0000000000..ff6c553c31 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/aliasBindingsAssignCorrectly.qml @@ -0,0 +1,59 @@ +import QtQuick 2.0 + +Item { + id: root + + property bool test: false + + property real testData: 9 + property real testData2: 9 + + states: State { + name: "change" + PropertyChanges { + target: myType + realProperty: if (testData2 > 3) 9; else 11; + } + } + + AliasBindingsAssignCorrectlyType { + id: myType + + aliasProperty: if (testData > 3) 14; else 12; + } + + Component.onCompleted: { + // Check original binding works + if (myType.aliasProperty != 14) return; + + testData = 2; + if (myType.aliasProperty != 12) return; + + // Change binding indirectly by modifying the "realProperty" + root.state = "change"; + if (myType.aliasProperty != 9) return; + + // Check the new binding works + testData2 = 1; + if (myType.aliasProperty != 11) return; + + // Try and trigger the old binding (that should have been removed) + testData = 6; + if (myType.aliasProperty != 11) return; + + // Restore the original binding + root.state = ""; + if (myType.aliasProperty != 14) return; + + // Test the restored binding works + testData = 0; + if (myType.aliasProperty != 12) return; + + // Test the old binding isn't somehow hanging around and still in effect + testData2 = 13; + if (myType.aliasProperty != 12) return; + + test = true; + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/aliasBindingsOverrideTarget.2.qml b/tests/auto/qml/qqmlecmascript/data/aliasBindingsOverrideTarget.2.qml new file mode 100644 index 0000000000..bba9033235 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/aliasBindingsOverrideTarget.2.qml @@ -0,0 +1,29 @@ +import QtQuick 2.0 + +Item { + id: me + property bool test: false + + property int value: 9 + + AliasBindingsOverrideTargetType { + id: aliasType + pointAliasProperty.x: me.value + } + + Component.onCompleted: { + if (aliasType.pointAliasProperty.x != 9) return; + + me.value = 11; + if (aliasType.pointAliasProperty.x != 11) return; + + aliasType.data = 8; + if (aliasType.pointAliasProperty.x != 11) return; + + me.value = 4; + if (aliasType.pointAliasProperty.x != 4) return; + + test = true; + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/aliasBindingsOverrideTarget.3.qml b/tests/auto/qml/qqmlecmascript/data/aliasBindingsOverrideTarget.3.qml new file mode 100644 index 0000000000..3e4cda6ba3 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/aliasBindingsOverrideTarget.3.qml @@ -0,0 +1,24 @@ +import QtQuick 2.0 + +Item { + id: root + property bool test: false; + + property int value1: 10 + property int value2: 11 + + AliasBindingsOverrideTargetType3 { + id: obj + + testProperty: root.value1 * 9 + aliasProperty: root.value2 * 10 + } + + Component.onCompleted: { + if (obj.testProperty != 110) return; + if (obj.aliasProperty != 110) return; + + test = true; + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/aliasBindingsOverrideTarget.qml b/tests/auto/qml/qqmlecmascript/data/aliasBindingsOverrideTarget.qml new file mode 100644 index 0000000000..de5f49ffc5 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/aliasBindingsOverrideTarget.qml @@ -0,0 +1,28 @@ +import QtQuick 2.0 + +Item { + id: me + property bool test: false + + property int value: 9 + + AliasBindingsOverrideTargetType { + id: aliasType + aliasProperty: me.value + } + + Component.onCompleted: { + if (aliasType.aliasProperty != 9) return; + + me.value = 11; + if (aliasType.aliasProperty != 11) return; + + aliasType.data = 8; + if (aliasType.aliasProperty != 11) return; + + me.value = 4; + if (aliasType.aliasProperty != 4) return; + + test = true; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/aliasPropertyAndBinding.qml b/tests/auto/qml/qqmlecmascript/data/aliasPropertyAndBinding.qml new file mode 100644 index 0000000000..f228b2c19f --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/aliasPropertyAndBinding.qml @@ -0,0 +1,14 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +MyQmlObject { + property alias c1: myObject.c1 + property int c2: 3 + property int c3: c2 + objectProperty: QtObject { + id: myObject + property int c1 + } +} + + diff --git a/tests/auto/qml/qqmlecmascript/data/aliasToCompositeElement.qml b/tests/auto/qml/qqmlecmascript/data/aliasToCompositeElement.qml new file mode 100644 index 0000000000..79d6e6887c --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/aliasToCompositeElement.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 + +AliasToCompositeElementType1 { + group.value: 13 +} + diff --git a/tests/auto/qml/qqmlecmascript/data/aliasWritesOverrideBindings.2.qml b/tests/auto/qml/qqmlecmascript/data/aliasWritesOverrideBindings.2.qml new file mode 100644 index 0000000000..b5bc280d11 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/aliasWritesOverrideBindings.2.qml @@ -0,0 +1,29 @@ +import QtQuick 2.0 + +Item { + id: me + property bool test: false + + property int value: 9 + + AliasBindingsOverrideTargetType { + id: aliasType + } + + Component.onCompleted: { + if (aliasType.aliasProperty != 294) return; + + aliasType.data = 8; + if (aliasType.aliasProperty != 336) return; + + aliasType.aliasProperty = 4; + if (aliasType.aliasProperty != 4) return; + + aliasType.data = 7; + if (aliasType.aliasProperty != 4) return; + + test = true; + } +} + + diff --git a/tests/auto/qml/qqmlecmascript/data/aliasWritesOverrideBindings.3.qml b/tests/auto/qml/qqmlecmascript/data/aliasWritesOverrideBindings.3.qml new file mode 100644 index 0000000000..6c16ff5604 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/aliasWritesOverrideBindings.3.qml @@ -0,0 +1,23 @@ +import QtQuick 2.0 + +Item { + id: me + property bool test: false + + property int value: 9 + + AliasBindingsOverrideTargetType { + id: aliasType + pointAliasProperty.x: 9 + } + + Component.onCompleted: { + if (aliasType.pointAliasProperty.x != 9) return; + + aliasType.data = 8; + if (aliasType.pointAliasProperty.x != 9) return; + + test = true; + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/aliasWritesOverrideBindings.qml b/tests/auto/qml/qqmlecmascript/data/aliasWritesOverrideBindings.qml new file mode 100644 index 0000000000..441098bd39 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/aliasWritesOverrideBindings.qml @@ -0,0 +1,23 @@ +import QtQuick 2.0 + +Item { + id: me + property bool test: false + + property int value: 9 + + AliasBindingsOverrideTargetType { + id: aliasType + aliasProperty: 11 + } + + Component.onCompleted: { + if (aliasType.aliasProperty != 11) return; + + aliasType.data = 8; + if (aliasType.aliasProperty != 11) return; + + test = true; + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/aliasreset/AliasPropertyComponent.qml b/tests/auto/qml/qqmlecmascript/data/aliasreset/AliasPropertyComponent.qml new file mode 100644 index 0000000000..9135e79469 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/aliasreset/AliasPropertyComponent.qml @@ -0,0 +1,17 @@ +import QtQuick 2.0 + +Item { + id: apc + property alias sourceComponent: loader.sourceComponent + + Component { + id: redSquare + Rectangle { color: "red"; width: 10; height: 10 } + } + + Loader { + id: loader + objectName: "loader" + sourceComponent: redSquare + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/aliasreset/aliasPropertyReset.1.qml b/tests/auto/qml/qqmlecmascript/data/aliasreset/aliasPropertyReset.1.qml new file mode 100644 index 0000000000..b855a183ee --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/aliasreset/aliasPropertyReset.1.qml @@ -0,0 +1,23 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: first + property bool aliasIsUndefined: false + property alias sourceComponentAlias: loader.sourceComponent + + Component { + id: redSquare + Rectangle { color: "red"; width: 10; height: 10 } + } + + Loader { + id: loader + sourceComponent: redSquare + } + + function resetAliased() { + loader.sourceComponent = undefined; + aliasIsUndefined = (sourceComponentAlias == undefined); + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/aliasreset/aliasPropertyReset.2.qml b/tests/auto/qml/qqmlecmascript/data/aliasreset/aliasPropertyReset.2.qml new file mode 100644 index 0000000000..b0bb3681cf --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/aliasreset/aliasPropertyReset.2.qml @@ -0,0 +1,23 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: first + property bool loaderSourceComponentIsUndefined: false + property alias sourceComponentAlias: loader.sourceComponent + + Component { + id: redSquare + Rectangle { color: "red"; width: 10; height: 10 } + } + + Loader { + id: loader + sourceComponent: redSquare + } + + function resetAlias() { + sourceComponentAlias = undefined; + loaderSourceComponentIsUndefined = (loader.sourceComponent == undefined); + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/aliasreset/aliasPropertyReset.3.qml b/tests/auto/qml/qqmlecmascript/data/aliasreset/aliasPropertyReset.3.qml new file mode 100644 index 0000000000..b318af0138 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/aliasreset/aliasPropertyReset.3.qml @@ -0,0 +1,31 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: first + property bool loaderTwoSourceComponentIsUndefined: false + property bool loaderOneSourceComponentIsUndefined: false + property alias sourceComponentAlias: loaderOne.sourceComponent + + Component { + id: redSquare + Rectangle { color: "red"; width: 10; height: 10 } + } + + Loader { + id: loaderOne + sourceComponent: loaderTwo.sourceComponent + } + + Loader { + id: loaderTwo + sourceComponent: redSquare + x: 15 + } + + function resetAlias() { + sourceComponentAlias = undefined; // loaderOne.sourceComponent should be set to undefined instead of l2.sc + loaderOneSourceComponentIsUndefined = (loaderOne.sourceComponent == undefined); // should be true + loaderTwoSourceComponentIsUndefined = (loaderTwo.sourceComponent == undefined); // should be false + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/aliasreset/aliasPropertyReset.4.qml b/tests/auto/qml/qqmlecmascript/data/aliasreset/aliasPropertyReset.4.qml new file mode 100644 index 0000000000..c5f56a8798 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/aliasreset/aliasPropertyReset.4.qml @@ -0,0 +1,26 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: first + property alias sourceComponentAlias: loader.sourceComponent + + Component { + id: redSquare + Rectangle { color: "red"; width: 10; height: 10 } + } + + Loader { + id: loader + objectName: "loader" + sourceComponent: redSquare + } + + function resetAlias() { + sourceComponentAlias = undefined; // ensure we don't crash after deletion of loader. + } + + function setAlias() { + sourceComponentAlias = redSquare; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/aliasreset/aliasPropertyReset.5.qml b/tests/auto/qml/qqmlecmascript/data/aliasreset/aliasPropertyReset.5.qml new file mode 100644 index 0000000000..b07db8ba40 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/aliasreset/aliasPropertyReset.5.qml @@ -0,0 +1,14 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: root + + AliasPropertyComponent { + sourceComponent: returnsUndefined() + } + + function returnsUndefined() { + return undefined; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/aliasreset/aliasPropertyReset.error.1.qml b/tests/auto/qml/qqmlecmascript/data/aliasreset/aliasPropertyReset.error.1.qml new file mode 100644 index 0000000000..35c9d6fd5d --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/aliasreset/aliasPropertyReset.error.1.qml @@ -0,0 +1,18 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +MyQmlObject { + id: first + property bool aliasedIntIsUndefined: false + property alias intAlias: objprop.intp + + objectProperty: QtObject { + id: objprop + property int intp: 12 + } + + function resetAlias() { + intAlias = undefined; // should error + aliasedIntIsUndefined = (objprop.intp == undefined); + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/assignBasicTypes.2.qml b/tests/auto/qml/qqmlecmascript/data/assignBasicTypes.2.qml new file mode 100644 index 0000000000..2c79729651 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/assignBasicTypes.2.qml @@ -0,0 +1,26 @@ +import Qt.test 1.0 + +MyTypeObject { + flagProperty: if(1) "FlagVal1 | FlagVal3" + enumProperty: if(1) "EnumVal2" + stringProperty: if(1) "Hello World!" + uintProperty: if(1) 10 + intProperty: if(1) -19 + realProperty: if(1) 23.2 + doubleProperty: if(1) -19.75 + floatProperty: if(1) 8.5 + colorProperty: if(1) "red" + dateProperty: if(1) "1982-11-25" + timeProperty: if(1) "11:11:32" + dateTimeProperty: if(1) "2009-05-12T13:22:01" + pointProperty: if(1) "99,13" + pointFProperty: if(1) "-10.1,12.3" + sizeProperty: if(1) "99x13" + sizeFProperty: if(1) "0.1x0.2" + rectProperty: if(1) "9,7,100x200" + rectFProperty: if(1) "1000.1,-10.9,400x90.99" + boolProperty: if(1) true + variantProperty: if(1) "Hello World!" + vectorProperty: if(1) "10,1,2.2" + urlProperty: if(1) "main.qml" +} diff --git a/tests/auto/qml/qqmlecmascript/data/assignBasicTypes.qml b/tests/auto/qml/qqmlecmascript/data/assignBasicTypes.qml new file mode 100644 index 0000000000..86ff6b6bb3 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/assignBasicTypes.qml @@ -0,0 +1,29 @@ +import Qt.test 1.0 +import QtQuick 2.0 + +MyTypeObject { + Component.onCompleted: { + flagProperty = "FlagVal1 | FlagVal3" + enumProperty = "EnumVal2" + stringProperty = "Hello World!" + uintProperty = 10 + intProperty = -19 + realProperty = 23.2 + doubleProperty = -19.75 + floatProperty = 8.5 + colorProperty = "red" + dateProperty = "1982-11-25" + timeProperty = "11:11:32" + dateTimeProperty = "2009-05-12T13:22:01" + pointProperty = "99,13" + pointFProperty = "-10.1,12.3" + sizeProperty = "99x13" + sizeFProperty = "0.1x0.2" + rectProperty = "9,7,100x200" + rectFProperty = "1000.1,-10.9,400x90.99" + boolProperty = true + variantProperty = "Hello World!" + vectorProperty = "10,1,2.2" + urlProperty = "main.qml" + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/assignSequenceTypes.1.qml b/tests/auto/qml/qqmlecmascript/data/assignSequenceTypes.1.qml new file mode 100644 index 0000000000..be283fdda1 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/assignSequenceTypes.1.qml @@ -0,0 +1,11 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +MySequenceConversionObject { + intListProperty: [1, 2] + qrealListProperty: [1.1, 2.2] + boolListProperty: [false, true] + urlListProperty: [ "http://www.example1.com", "http://www.example2.com" ] + stringListProperty: [ "one", "two" ] + qstringListProperty: [ "one", "two" ] +} diff --git a/tests/auto/qml/qqmlecmascript/data/assignSequenceTypes.2.qml b/tests/auto/qml/qqmlecmascript/data/assignSequenceTypes.2.qml new file mode 100644 index 0000000000..c8fb28b04e --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/assignSequenceTypes.2.qml @@ -0,0 +1,11 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +MySequenceConversionObject { + intListProperty: 1 + qrealListProperty: 1.1 + boolListProperty: false + urlListProperty: "http://www.example1.com" + stringListProperty: "one" + qstringListProperty: "two" +} diff --git a/tests/auto/qml/qqmlecmascript/data/assignSequenceTypes.3.qml b/tests/auto/qml/qqmlecmascript/data/assignSequenceTypes.3.qml new file mode 100644 index 0000000000..ad8a92e317 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/assignSequenceTypes.3.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +MySequenceConversionObject { + intListProperty: 1 + qrealListProperty: 1.1 + boolListProperty: false + urlListProperty: Qt.resolvedUrl("example.html") +} diff --git a/tests/auto/qml/qqmlecmascript/data/assignSequenceTypes.4.qml b/tests/auto/qml/qqmlecmascript/data/assignSequenceTypes.4.qml new file mode 100644 index 0000000000..a9f2e642d1 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/assignSequenceTypes.4.qml @@ -0,0 +1,13 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +MySequenceConversionObject { + Component.onCompleted: { + intListProperty = [1, 2] + qrealListProperty = [1.1, 2.2] + boolListProperty = [false, true] + urlListProperty = [ "http://www.example1.com", "http://www.example2.com" ] + stringListProperty = [ "one", "two" ] + qstringListProperty = [ "one", "two" ] + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/assignSequenceTypes.5.qml b/tests/auto/qml/qqmlecmascript/data/assignSequenceTypes.5.qml new file mode 100644 index 0000000000..b8697e4290 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/assignSequenceTypes.5.qml @@ -0,0 +1,13 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +MySequenceConversionObject { + Component.onCompleted: { + intListProperty = 1; + qrealListProperty = 1.1; + boolListProperty = false; + urlListProperty = "http://www.example1.com"; + stringListProperty = "one"; + qstringListProperty = "two"; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/assignSequenceTypes.6.qml b/tests/auto/qml/qqmlecmascript/data/assignSequenceTypes.6.qml new file mode 100644 index 0000000000..7a794eb694 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/assignSequenceTypes.6.qml @@ -0,0 +1,11 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +MySequenceConversionObject { + Component.onCompleted: { + intListProperty = 1; + qrealListProperty = 1.1; + boolListProperty = false; + urlListProperty = Qt.resolvedUrl("example.html"); + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/assignSequenceTypes.7.qml b/tests/auto/qml/qqmlecmascript/data/assignSequenceTypes.7.qml new file mode 100644 index 0000000000..96c0684939 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/assignSequenceTypes.7.qml @@ -0,0 +1,42 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + // single url assignment to url list property + MySequenceConversionObject { + id: msco1 + objectName: "msco1" + } + + // single url binding to url list property + MySequenceConversionObject { + id: msco2 + objectName: "msco2" + urlListProperty: "example.html" + } + + // multiple url assignment to url list property + MySequenceConversionObject { + id: msco3 + objectName: "msco3" + } + + // multiple url binding to url list property + MySequenceConversionObject { + id: msco4 + objectName: "msco4" + urlListProperty: [ "example.html", "example2.html" ] + } + + // multiple url binding to url list property - already resolved + MySequenceConversionObject { + id: msco5 + objectName: "msco5" + urlListProperty: [ Qt.resolvedUrl("example.html"), Qt.resolvedUrl("example2.html") ] + } + + Component.onCompleted: { + msco1.urlListProperty = "example.html"; + msco3.urlListProperty = [ "example.html", "example2.html" ]; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/attachedProperty.2.qml b/tests/auto/qml/qqmlecmascript/data/attachedProperty.2.qml new file mode 100644 index 0000000000..a7184c9200 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/attachedProperty.2.qml @@ -0,0 +1,22 @@ +import Qt.test 1.0 +import Qt.test 1.0 as Namespace + +MyQmlObject { + property alias a: me.a + property alias b: me.a + property alias c: me.a + property alias d: me.a + + property MyQmlObject obj + obj: MyQmlObject { + MyQmlObject.value2: 13 + + id: me + property int a: MyQmlObject.value2 * 2 + property int b: Namespace.MyQmlObject.value2 * 2 + property int c: me.Namespace.MyQmlObject.value * 2 + property int d: me.Namespace.MyQmlObject.value * 2 + } +} + + diff --git a/tests/auto/qml/qqmlecmascript/data/attachedProperty.qml b/tests/auto/qml/qqmlecmascript/data/attachedProperty.qml new file mode 100644 index 0000000000..061eda0e54 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/attachedProperty.qml @@ -0,0 +1,11 @@ +import Qt.test 1.0 +import Qt.test 1.0 as Namespace + +MyQmlObject { + id: me + property int a: MyQmlObject.value + property int b: Namespace.MyQmlObject.value + property int c: me.Namespace.MyQmlObject.value + property int d: me.Namespace.MyQmlObject.value +} + diff --git a/tests/auto/qml/qqmlecmascript/data/attachedPropertyScope.qml b/tests/auto/qml/qqmlecmascript/data/attachedPropertyScope.qml new file mode 100644 index 0000000000..11fb7ccad2 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/attachedPropertyScope.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +QtObject { + property int value: 9 + property int value2 + + MyQmlObject.onMySignal: value2 = value +} diff --git a/tests/auto/qml/qqmlecmascript/data/automaticSemicolon.qml b/tests/auto/qml/qqmlecmascript/data/automaticSemicolon.qml new file mode 100644 index 0000000000..6db68f2328 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/automaticSemicolon.qml @@ -0,0 +1,11 @@ + +import QtQuick 2.0 + +QtObject { + function code() { + if (1) { + var a; + function f1(){}a=1; + } + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/bindingLoop.qml b/tests/auto/qml/qqmlecmascript/data/bindingLoop.qml new file mode 100644 index 0000000000..80545cf72b --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/bindingLoop.qml @@ -0,0 +1,14 @@ +import Qt.test 1.0 + +MyQmlContainer { + children : [ + MyQmlObject { + id: object1 + stringProperty: "hello" + object2.stringProperty + }, + MyQmlObject { + id: object2 + stringProperty: "hello" + object1.stringProperty + } + ] +} diff --git a/tests/auto/qml/qqmlecmascript/data/blank.js b/tests/auto/qml/qqmlecmascript/data/blank.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/blank.js diff --git a/tests/auto/qml/qqmlecmascript/data/boolPropertiesEvaluateAsBool.1.qml b/tests/auto/qml/qqmlecmascript/data/boolPropertiesEvaluateAsBool.1.qml new file mode 100644 index 0000000000..3147f63989 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/boolPropertiesEvaluateAsBool.1.qml @@ -0,0 +1,5 @@ +import Qt.test 1.0 + +MyQmlObject { + stringProperty: trueProperty?'pass':'fail' +} diff --git a/tests/auto/qml/qqmlecmascript/data/boolPropertiesEvaluateAsBool.2.qml b/tests/auto/qml/qqmlecmascript/data/boolPropertiesEvaluateAsBool.2.qml new file mode 100644 index 0000000000..c89bb49b45 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/boolPropertiesEvaluateAsBool.2.qml @@ -0,0 +1,5 @@ +import Qt.test 1.0 + +MyQmlObject { + stringProperty: falseProperty?'fail':'pass' +} diff --git a/tests/auto/qml/qqmlecmascript/data/booleanConversion.qml b/tests/auto/qml/qqmlecmascript/data/booleanConversion.qml new file mode 100644 index 0000000000..a363cf4dd1 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/booleanConversion.qml @@ -0,0 +1,28 @@ +import QtQuick 2.0 + +QtObject { + id: root + + property bool test_true1: false + property bool test_true2: false + property bool test_true3: false + property bool test_true4: false + property bool test_true5: false + + property bool test_false1: true + property bool test_false2: true + property bool test_false3: true + + + Component.onCompleted: { + test_true1 = 11 + test_true2 = "Hello" + test_true3 = root + test_true4 = { a: 10, b: 11 } + test_true5 = true + + test_false1 = 0 + test_false2 = null + test_false3 = false + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/bug.1.qml b/tests/auto/qml/qqmlecmascript/data/bug.1.qml new file mode 100644 index 0000000000..31f7c44fcc --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/bug.1.qml @@ -0,0 +1,10 @@ +import QtQuick 2.0 + +QtObject { + property int a: 10 + property bool b: false + + property int test + + test: ((a == 10)?(a + 1):0) + ((b == true)?9:3) +} diff --git a/tests/auto/qml/qqmlecmascript/data/canAssignNullToQObject.1.qml b/tests/auto/qml/qqmlecmascript/data/canAssignNullToQObject.1.qml new file mode 100644 index 0000000000..3fd9131b2f --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/canAssignNullToQObject.1.qml @@ -0,0 +1,9 @@ +import Qt.test 1.0 + +MyQmlObject { + property bool runTest: false + + property variant a: MyQmlObject {} + + objectProperty: (runTest == false)?a:null +} diff --git a/tests/auto/qml/qqmlecmascript/data/canAssignNullToQObject.2.qml b/tests/auto/qml/qqmlecmascript/data/canAssignNullToQObject.2.qml new file mode 100644 index 0000000000..3fbf931fca --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/canAssignNullToQObject.2.qml @@ -0,0 +1,11 @@ +import Qt.test 1.0 +import QtQuick 2.0 + +MyQmlObject { + objectProperty: MyQmlObject {} + + Component.onCompleted: { + objectProperty = null; + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlotErrors.1.qml b/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlotErrors.1.qml new file mode 100644 index 0000000000..1e92aca825 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlotErrors.1.qml @@ -0,0 +1,12 @@ +import QtQuick 2.0 + +Item { + property int changeCount: 0 + + property bool _nameWithUnderscore: false + + // this should error, since the first alpha isn't capitalised. + on_nameWithUnderscoreChanged: { + changeCount = changeCount + 2; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlotErrors.2.qml b/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlotErrors.2.qml new file mode 100644 index 0000000000..3549d8c556 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlotErrors.2.qml @@ -0,0 +1,12 @@ +import QtQuick 2.0 + +Item { + property int changeCount: 0 + + property bool ____nameWithUnderscores: false + + // this should error, since the first alpha isn't capitalised + on____nameWithUnderscoresChanged: { + changeCount = changeCount + 3; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlotErrors.3.qml b/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlotErrors.3.qml new file mode 100644 index 0000000000..d611e0fe30 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlotErrors.3.qml @@ -0,0 +1,12 @@ +import QtQuick 2.0 + +Item { + property int changeCount: 0 + + // invalid property name - we don't allow $ + property bool $nameWithDollarsign: false + + on$NameWithDollarsignChanged: { + changeCount = changeCount + 4; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlotErrors.4.qml b/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlotErrors.4.qml new file mode 100644 index 0000000000..a6862517c6 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlotErrors.4.qml @@ -0,0 +1,12 @@ +import QtQuick 2.0 + +Item { + property int changeCount: 0 + + property bool _6nameWithUnderscoreNumber: false + + // invalid property name - the first character after an underscore must be a letter + on_6NameWithUnderscoreNumberChanged: { + changeCount = changeCount + 3; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlots.qml b/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlots.qml new file mode 100644 index 0000000000..f91fb71f1f --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlots.qml @@ -0,0 +1,27 @@ +import QtQuick 2.0 + +Item { + property int changeCount: 0 + + property bool normalName: false + property bool _nameWithUnderscore: false + property bool ____nameWithUnderscores: false + + onNormalNameChanged: { + changeCount = changeCount + 1; + } + + on_NameWithUnderscoreChanged: { + changeCount = changeCount + 2; + } + + on____NameWithUnderscoresChanged: { + changeCount = changeCount + 3; + } + + Component.onCompleted: { + normalName = true; + _nameWithUnderscore = true; + ____nameWithUnderscores = true; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/compiled.qml b/tests/auto/qml/qqmlecmascript/data/compiled.qml new file mode 100644 index 0000000000..7c46306772 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/compiled.qml @@ -0,0 +1,48 @@ +import QtQuick 2.0 + +QtObject { + //real + property real test1: a + b + property real test2: a - b + property bool test3: (a < b) + property bool test4: (a > b) + property bool test5: (a == b) + property bool test6: (a != b) + + //int + property int test7: c + d + property int test8: d - c + property bool test9: (c < d) + property bool test10: (c > d) + property bool test11: (c == d) + property bool test12: (c != d) + + //string + property string test13: e + f + property string test14: e + " " + f + property bool test15: (e == f) + property bool test16: (e != f) + + //type conversion + property int test17: a + property real test18: d + property int test19: g + property real test20: g + property string test21: g + property string test22: h + property bool test23: i + property color test24: j + property color test25: k + + property real a: 4.5 + property real b: 11.2 + property int c: 9 + property int d: 176 + property string e: "Hello" + property string f: "World" + property variant g: 6.7 + property variant h: "!" + property variant i: true + property string j: "#112233" + property string k: "#aa112233" +} diff --git a/tests/auto/qml/qqmlecmascript/data/compositePropertyType.qml b/tests/auto/qml/qqmlecmascript/data/compositePropertyType.qml new file mode 100644 index 0000000000..e97b75c8d0 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/compositePropertyType.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 + +QtObject { + property CustomObject myObject + myObject: CustomObject { } + + Component.onCompleted: console.log(myObject.greeting) +} diff --git a/tests/auto/qml/qqmlecmascript/data/constantsOverrideBindings.1.qml b/tests/auto/qml/qqmlecmascript/data/constantsOverrideBindings.1.qml new file mode 100644 index 0000000000..13c5ae5fff --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/constantsOverrideBindings.1.qml @@ -0,0 +1,8 @@ +import Qt.test 1.0 + +MyQmlObject { + property int c1: 0 + property int c2: c1 + + onBasicSignal: c2 = 13 +} diff --git a/tests/auto/qml/qqmlecmascript/data/constantsOverrideBindings.2.qml b/tests/auto/qml/qqmlecmascript/data/constantsOverrideBindings.2.qml new file mode 100644 index 0000000000..207a06b700 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/constantsOverrideBindings.2.qml @@ -0,0 +1,11 @@ +import Qt.test 1.0 + +MyQmlObject { + property alias c1: myConstants.c1 + property alias c2: myConstants.c2 + + objectProperty: ConstantsOverrideBindings { + id: myConstants + c2: 10 + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/constantsOverrideBindings.3.qml b/tests/auto/qml/qqmlecmascript/data/constantsOverrideBindings.3.qml new file mode 100644 index 0000000000..ca9d1d8ab9 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/constantsOverrideBindings.3.qml @@ -0,0 +1,7 @@ +import Qt.test 1.0 + +MyQmlObject { + property int c1: 0 + property int c2: c1 +} + diff --git a/tests/auto/qml/qqmlecmascript/data/constantsOverrideBindings.4.qml b/tests/auto/qml/qqmlecmascript/data/constantsOverrideBindings.4.qml new file mode 100644 index 0000000000..5a2091f71c --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/constantsOverrideBindings.4.qml @@ -0,0 +1,11 @@ +import Qt.test 1.0 + +MyQmlObject { + property alias c1: myConstants.c1 + property alias c3: myConstants.c3 + + objectProperty: ConstantsOverrideBindings { + id: myConstants + c3: 10 + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/deferredProperties.qml b/tests/auto/qml/qqmlecmascript/data/deferredProperties.qml new file mode 100644 index 0000000000..e01f708a07 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/deferredProperties.qml @@ -0,0 +1,10 @@ +import Qt.test 1.0 + +MyDeferredObject { + id: root + value: 10 + objectProperty: MyQmlObject { + value: root.value + } + objectProperty2: MyQmlObject { id: blah } +} diff --git a/tests/auto/qml/qqmlecmascript/data/deferredPropertiesErrors.qml b/tests/auto/qml/qqmlecmascript/data/deferredPropertiesErrors.qml new file mode 100644 index 0000000000..308a01ce6f --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/deferredPropertiesErrors.qml @@ -0,0 +1,10 @@ +import Qt.test 1.0 +import QtQuick 2.0 + +MyDeferredObject { + value: undefined // error is resolved before complete + objectProperty: undefined // immediate error + objectProperty2: QtObject { + Component.onCompleted: value = 10 + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/deleteLater.qml b/tests/auto/qml/qqmlecmascript/data/deleteLater.qml new file mode 100644 index 0000000000..2a9ce44b20 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/deleteLater.qml @@ -0,0 +1,14 @@ +import QtQuick 2.0 + +QtObject { + id: root + property bool test: false + + Component.onCompleted: { + try { + root.deleteLater() + } catch(e) { + test = true; + } + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/deleteWhileBindingRunning.qml b/tests/auto/qml/qqmlecmascript/data/deleteWhileBindingRunning.qml new file mode 100644 index 0000000000..b5cc59e2c0 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/deleteWhileBindingRunning.qml @@ -0,0 +1,5 @@ +import Qt.test 1.0 + +MyDeleteObject { + property int result: nestedObject.intProperty + deleteNestedObject +} diff --git a/tests/auto/qml/qqmlecmascript/data/deletedEngine.qml b/tests/auto/qml/qqmlecmascript/data/deletedEngine.qml new file mode 100644 index 0000000000..97acddf5fc --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/deletedEngine.qml @@ -0,0 +1,11 @@ +import QtQuick 2.0 + +QtObject { + function calculate() { + return b * 13; + } + + property int a: calculate() + property int b: 3 +} + diff --git a/tests/auto/qml/qqmlecmascript/data/deletedObject.qml b/tests/auto/qml/qqmlecmascript/data/deletedObject.qml new file mode 100644 index 0000000000..24c12bf694 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/deletedObject.qml @@ -0,0 +1,25 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +QtObject { + property variant obj + obj: MyQmlObject { + id: myObject + value: 92 + } + + property bool test1: false + property bool test2: false + property bool test3: false + property bool test4: false + + Component.onCompleted: { + test1 = myObject.value == 92; + test2 = obj.value == 92; + + myObject.deleteOnSet = 1; + + test3 = myObject == null + test4 = obj == null + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/doubleEvaluate.qml b/tests/auto/qml/qqmlecmascript/data/doubleEvaluate.qml new file mode 100644 index 0000000000..0532715432 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/doubleEvaluate.qml @@ -0,0 +1,6 @@ +import Qt.test 1.0 + +WriteCounter { + property int x: 0 + value: if (1) x + x +} diff --git a/tests/auto/qml/qqmlecmascript/data/dynamicCreation.helper.qml b/tests/auto/qml/qqmlecmascript/data/dynamicCreation.helper.qml new file mode 100644 index 0000000000..d790d634e9 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/dynamicCreation.helper.qml @@ -0,0 +1,6 @@ +import Qt.test 1.0 + +MyQmlObject{ + objectName: "objectTwo" +} + diff --git a/tests/auto/qml/qqmlecmascript/data/dynamicCreation.qml b/tests/auto/qml/qqmlecmascript/data/dynamicCreation.qml new file mode 100644 index 0000000000..7b132e1edf --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/dynamicCreation.qml @@ -0,0 +1,27 @@ +import Qt.test 1.0 + +MyQmlObject{ + id: obj + objectName: "obj" + function createOne() + { + obj.objectProperty = Qt.createQmlObject('import Qt.test 1.0; MyQmlObject{objectName:"objectOne"}', obj); + } + + function createTwo() + { + var component = Qt.createComponent('dynamicCreation.helper.qml'); + obj.objectProperty = component.createObject(obj); + } + + function createThree() + { + obj.objectProperty = Qt.createQmlObject('TypeForDynamicCreation{}', obj); + } + + function dontCrash() + { + var component = Qt.createComponent('file-doesnt-exist.qml'); + obj.objectProperty = component.createObject(obj); + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/dynamicCreationOwnership.qml b/tests/auto/qml/qqmlecmascript/data/dynamicCreationOwnership.qml new file mode 100644 index 0000000000..ed396d49b0 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/dynamicCreationOwnership.qml @@ -0,0 +1,20 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: obj + objectName: "obj" + + MyDynamicCreationDestructionObject { + id: mdcdo + objectName: "mdcdo" + } + + function dynamicallyCreateJsOwnedObject() { + mdcdo.createNew(); + } + + function performGc() { + gc(); + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/dynamicDeletion.2.qml b/tests/auto/qml/qqmlecmascript/data/dynamicDeletion.2.qml new file mode 100644 index 0000000000..9a5732c194 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/dynamicDeletion.2.qml @@ -0,0 +1,21 @@ +import QtQuick 2.0 + +QtObject { + id: root + + property QtObject objectProperty + + property Component c: Component { + id: componentObject + QtObject { + } + } + + function create() { + objectProperty = c.createObject(root); + } + + function destroy() { + objectProperty.destroy(); + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/dynamicDeletion.qml b/tests/auto/qml/qqmlecmascript/data/dynamicDeletion.qml new file mode 100644 index 0000000000..f41e5262fd --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/dynamicDeletion.qml @@ -0,0 +1,20 @@ +import Qt.test 1.0 + +MyQmlObject{ + id: obj + objectName: "obj" + function create() + { + obj.objectProperty = Qt.createQmlObject('import Qt.test 1.0; MyQmlObject{objectName:"emptyObject"}', obj); + } + + function killOther() + { + obj.objectProperty.destroy(500); + } + + function killMe() + { + obj.destroy();//Must not segfault + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/dynamicString.qml b/tests/auto/qml/qqmlecmascript/data/dynamicString.qml new file mode 100644 index 0000000000..5693794c71 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/dynamicString.qml @@ -0,0 +1,16 @@ +import Qt.test 1.0 +import QtQuick 2.0 + +MyTypeObject { + stringProperty:"string:%0 false:%1 true:%2 uint32:%3 int32:%4 double:%5 date:%6!" + Component.onCompleted: { + var date = new Date(); + date.setDate(11); + date.setMonth(1); + date.setFullYear(2011); + date.setHours(5); + date.setMinutes(30); + date.setSeconds(50); + stringProperty = stringProperty.arg("Hello World").arg(false).arg(true).arg(100).arg(-100).arg(3.1415926).arg(Qt.formatDateTime(date, "yyyy-MM-dd hh::mm:ss")); + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/elementAssign.qml b/tests/auto/qml/qqmlecmascript/data/elementAssign.qml new file mode 100644 index 0000000000..0d75cbf6fc --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/elementAssign.qml @@ -0,0 +1,10 @@ +import QtQuick 2.0 + +QtObject { + id: root + + property bool test: bound.value == 1923 + + property ElementAssignType element: ElementAssignType { value: 1923 } + property ElementAssignType bound: root.element +} diff --git a/tests/auto/qml/qqmlecmascript/data/enums.1.qml b/tests/auto/qml/qqmlecmascript/data/enums.1.qml new file mode 100644 index 0000000000..6351823230 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/enums.1.qml @@ -0,0 +1,20 @@ +import Qt.test 1.0 +import Qt.test 1.0 as Namespace + +MyQmlObject { + // Enums from non-namespaced type + property int a: MyQmlObject.EnumValue1 + property int b: MyQmlObject.EnumValue2 + property int c: MyQmlObject.EnumValue3 + property int d: MyQmlObject.EnumValue4 + + // Enums from namespaced type + property int e: Namespace.MyQmlObject.EnumValue1 + property int f: Namespace.MyQmlObject.EnumValue2 + property int g: Namespace.MyQmlObject.EnumValue3 + property int h: Namespace.MyQmlObject.EnumValue4 + + // Test that enums don't mask attached properties + property int i: MyQmlObject.value + property int j: Namespace.MyQmlObject.value +} diff --git a/tests/auto/qml/qqmlecmascript/data/enums.2.qml b/tests/auto/qml/qqmlecmascript/data/enums.2.qml new file mode 100644 index 0000000000..bdc672fadc --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/enums.2.qml @@ -0,0 +1,8 @@ +import Qt.test 1.0 +import Qt.test 1.0 as Namespace + +MyQmlObject { + property int a: MyQmlObject.EnumValue10 + property int b: Namespace.MyQmlObject.EnumValue10 +} + diff --git a/tests/auto/qml/qqmlecmascript/data/eval.qml b/tests/auto/qml/qqmlecmascript/data/eval.qml new file mode 100644 index 0000000000..a752b8c0d3 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/eval.qml @@ -0,0 +1,27 @@ +import QtQuick 2.0 + +QtObject { + property bool test1: false; + property bool test2: false; + property bool test3: false; + property bool test4: false; + property bool test5: false; + + + property int a: 7 + property int b: 8 + + Component.onCompleted: { + var b = 9; + + test1 = (eval("a") == 7); + test2 = (eval("b") == 9); + try { + eval("c"); + } catch(e) { + test3 = true; + } + test4 = (eval("console") == console); + test5 = (eval("Qt") == Qt); + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/exception.js b/tests/auto/qml/qqmlecmascript/data/exception.js new file mode 100644 index 0000000000..160bbfa5b6 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/exception.js @@ -0,0 +1 @@ +throw("Whoops!"); diff --git a/tests/auto/qml/qqmlecmascript/data/exceptionClearsOnReeval.qml b/tests/auto/qml/qqmlecmascript/data/exceptionClearsOnReeval.qml new file mode 100644 index 0000000000..a2f0d1a8b7 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/exceptionClearsOnReeval.qml @@ -0,0 +1,6 @@ +import Qt.test 1.0 + +MyQmlObject { + property bool test: objectProperty.objectProperty.trueProperty +} + diff --git a/tests/auto/qml/qqmlecmascript/data/exceptionProducesWarning.qml b/tests/auto/qml/qqmlecmascript/data/exceptionProducesWarning.qml new file mode 100644 index 0000000000..b8d5e5e60f --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/exceptionProducesWarning.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +MyQmlObject { + Component.onCompleted: + { throw(new Error("JS exception")) } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/exceptionProducesWarning2.qml b/tests/auto/qml/qqmlecmascript/data/exceptionProducesWarning2.qml new file mode 100644 index 0000000000..a4ce55e245 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/exceptionProducesWarning2.qml @@ -0,0 +1,7 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +MyQmlObject { + value: { throw(new Error("JS exception")) } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/extendedObjectPropertyLookup.qml b/tests/auto/qml/qqmlecmascript/data/extendedObjectPropertyLookup.qml new file mode 100644 index 0000000000..2c382e871a --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/extendedObjectPropertyLookup.qml @@ -0,0 +1,8 @@ +import Qt.test 1.0 +import QtQuick 2.0 + +QtObject { + property MyExtendedObject a; + a: MyExtendedObject { id: root } + property int b: Math.max(root.extendedProperty, 0) +} diff --git a/tests/auto/qml/qqmlecmascript/data/extendedObjectPropertyLookup2.qml b/tests/auto/qml/qqmlecmascript/data/extendedObjectPropertyLookup2.qml new file mode 100644 index 0000000000..e4af3359d0 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/extendedObjectPropertyLookup2.qml @@ -0,0 +1,14 @@ +import Qt.test 1.0 +import QtQuick 2.0 + +QtObject { + id: root + property MyExtendedObject a; + a: MyExtendedObject { + id: obj + extendedProperty: 42; + } + function getValue() { + return obj.extendedProperty; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/extensionObjects.qml b/tests/auto/qml/qqmlecmascript/data/extensionObjects.qml new file mode 100644 index 0000000000..7734a11dd8 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/extensionObjects.qml @@ -0,0 +1,19 @@ +import Qt.test 1.0 +import QtQuick 2.0 + +MyExtendedObject +{ + baseProperty: baseExtendedProperty + baseExtendedProperty: 13 + + coreProperty: extendedProperty + extendedProperty: 9 + + property QtObject nested: MyExtendedObject { + baseProperty: baseExtendedProperty + baseExtendedProperty: 13 + + coreProperty: extendedProperty + extendedProperty: 9 + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/extensionObjectsPropertyOverride.qml b/tests/auto/qml/qqmlecmascript/data/extensionObjectsPropertyOverride.qml new file mode 100644 index 0000000000..3c443cb975 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/extensionObjectsPropertyOverride.qml @@ -0,0 +1,7 @@ +import Qt.test 1.0 + +OverrideDefaultPropertyObject +{ + MyBaseExtendedObject { + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/forInLoop.qml b/tests/auto/qml/qqmlecmascript/data/forInLoop.qml new file mode 100644 index 0000000000..f14367f177 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/forInLoop.qml @@ -0,0 +1,13 @@ +import QtQuick 2.0 + +QtObject { + property list<QtObject> objects + objects: [QtObject { objectName: "obj1" }, QtObject { objectName: "obj2" }, QtObject { objectName: "obj3" }] + property string listResult + + function listProperty() { + for (var i in objects) + listResult += i + "=" + objects[i].objectName + "|" + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/function.qml b/tests/auto/qml/qqmlecmascript/data/function.qml new file mode 100644 index 0000000000..af2da7023c --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/function.qml @@ -0,0 +1,23 @@ +import QtQuick 2.0 + +QtObject { + property bool test1: false; + property bool test2: false; + property bool test3: false; + + Component.onCompleted: { + var a = 10; + + var func1 = new Function("a", "return a + 7"); + var func2 = new Function("a", "return Qt.atob(a)"); + var func3 = new Function("return a"); + + test1 = (func1(4) == 11); + test2 = (func2("Hello World!") == Qt.atob("Hello World!")); + try { + func3(); + } catch(e) { + test3 = true; + } + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/functionAssignment.1.qml b/tests/auto/qml/qqmlecmascript/data/functionAssignment.1.qml new file mode 100644 index 0000000000..09540f1f6e --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/functionAssignment.1.qml @@ -0,0 +1,5 @@ +import Qt.test 1.0 + +MyQmlObject { + property variant a: function myFunction() { return 2; } +} diff --git a/tests/auto/qml/qqmlecmascript/data/functionAssignment.2.qml b/tests/auto/qml/qqmlecmascript/data/functionAssignment.2.qml new file mode 100644 index 0000000000..0f78eaf1dc --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/functionAssignment.2.qml @@ -0,0 +1,73 @@ +import Qt.test 1.0 +import QtQuick 2.0 + +import "functionAssignment.js" as Script + +MyQmlObject { + property variant a + property int aNumber: 10 + + property bool assignToProperty: false + property bool assignToPropertyFromJsFile: false + + property bool assignWithThis: false + property bool assignWithThisFromJsFile: false + + property bool assignToValueType: false + + property bool assignFuncWithoutReturn: false + property bool assignWrongType: false + property bool assignWrongTypeToValueType: false + + + onAssignToPropertyChanged: { + function myFunction() { + return aNumber * 10; + } + a = myFunction; + } + + property QtObject obj: QtObject { + property int aNumber: 4212 + function myFunction() { + return this.aNumber * 10; // should use the aNumber from root, not this object + } + } + onAssignWithThisChanged: { + a = obj.myFunction; + } + + onAssignToPropertyFromJsFileChanged: { + Script.bindPropertyWithThis() + } + + onAssignWithThisFromJsFileChanged: { + Script.bindProperty() + } + + property Text text: Text { } + onAssignToValueTypeChanged: { + text.font.pixelSize = (function() { return aNumber * 10; }) + a = (function() { return text.font.pixelSize; }) + } + + + // detecting errors: + + onAssignFuncWithoutReturnChanged: { + function myFunction() { + } + a = myFunction; + } + + onAssignWrongTypeChanged: { + function myFunction() { + return 'a string'; + } + aNumber = myFunction; + } + + onAssignWrongTypeToValueTypeChanged: { + text.font.pixelSize = (function() { return 'a string'; }) + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/functionAssignment.js b/tests/auto/qml/qqmlecmascript/data/functionAssignment.js new file mode 100644 index 0000000000..14daa7629f --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/functionAssignment.js @@ -0,0 +1,17 @@ +function bindProperty() +{ + a = (function(){ return aNumber * 10 }) +} + + +function TestObject() { } +TestObject.prototype.aNumber = 928349 +TestObject.prototype.bindFunction = function() { + return this.aNumber * 10 // this should not use the TestObject's aNumber +} +var testObj = new TestObject() + +function bindPropertyWithThis() +{ + a = testObj.bindFunction +} diff --git a/tests/auto/qml/qqmlecmascript/data/functionErrors.qml b/tests/auto/qml/qqmlecmascript/data/functionErrors.qml new file mode 100644 index 0000000000..230a626600 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/functionErrors.qml @@ -0,0 +1,10 @@ +import QtQuick 2.0 + +QtObject { + function myFunction() { + a = 10; + } + + Component.onCompleted: myFunction(); +} + diff --git a/tests/auto/qml/qqmlecmascript/data/handleReferenceManagement.handle.1.qml b/tests/auto/qml/qqmlecmascript/data/handleReferenceManagement.handle.1.qml new file mode 100644 index 0000000000..8a06c30d8c --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/handleReferenceManagement.handle.1.qml @@ -0,0 +1,21 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: obj + objectName: "obj" + property CircularReferenceHandle first + property CircularReferenceHandle second + + CircularReferenceHandle { + id: crh + objectName: "crh" + } + + function createReference() { + first = crh.generate(crh); + second = crh.generate(crh); + // NOTE: manually add reference from first to second + // in unit test prior reparenting and gc. + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/handleReferenceManagement.handle.2.qml b/tests/auto/qml/qqmlecmascript/data/handleReferenceManagement.handle.2.qml new file mode 100644 index 0000000000..91edc447e2 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/handleReferenceManagement.handle.2.qml @@ -0,0 +1,22 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: obj + objectName: "obj" + property CircularReferenceHandle first + property CircularReferenceHandle second + + CircularReferenceHandle { + id: crh + objectName: "crh" + } + + function circularReference() { + // generate the circularly referential pair + first = crh.generate(crh); + second = crh.generate(crh); + // note: must manually reparent in unit test + // after setting the handle references. + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/handleReferenceManagement.object.1.qml b/tests/auto/qml/qqmlecmascript/data/handleReferenceManagement.object.1.qml new file mode 100644 index 0000000000..70e8390677 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/handleReferenceManagement.object.1.qml @@ -0,0 +1,27 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: obj + objectName: "obj" + + property CircularReferenceObject first + property CircularReferenceObject second + + + CircularReferenceObject { + id: cro + objectName: "cro" + } + + function createReference() { + // generate the objects + first = cro.generate(cro); // has parent, so won't be collected + second = cro.generate(); // no parent, but will be kept alive by first's reference + first.addReference(second); + + // remove top level references + first = cro; + second = cro; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/handleReferenceManagement.object.2.qml b/tests/auto/qml/qqmlecmascript/data/handleReferenceManagement.object.2.qml new file mode 100644 index 0000000000..2ddb9253eb --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/handleReferenceManagement.object.2.qml @@ -0,0 +1,28 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: obj + objectName: "obj" + + property CircularReferenceObject first + property CircularReferenceObject second + + + CircularReferenceObject { + id: cro + objectName: "cro" + } + + function circularReference() { + // generate the circularly referential pair - they should still be collected + first = cro.generate(); // no parent, so should be collected + second = cro.generate(); // no parent, so should be collected + first.addReference(second); + second.addReference(first); + + // remove top level references + first = cro; + second = cro; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/idShortcutInvalidates.1.qml b/tests/auto/qml/qqmlecmascript/data/idShortcutInvalidates.1.qml new file mode 100644 index 0000000000..ece23269f1 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/idShortcutInvalidates.1.qml @@ -0,0 +1,13 @@ +import Qt.test 1.0 +import QtQuick 2.0 + +MyQmlObject { + objectProperty: if(1) otherObject + + property variant obj + + obj: QtObject { + id: otherObject + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/idShortcutInvalidates.qml b/tests/auto/qml/qqmlecmascript/data/idShortcutInvalidates.qml new file mode 100644 index 0000000000..650ed7c73e --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/idShortcutInvalidates.qml @@ -0,0 +1,12 @@ +import Qt.test 1.0 +import QtQuick 2.0 + +MyQmlObject { + objectProperty: otherObject + + property variant obj + + obj: QtObject { + id: otherObject + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/importScope.1.js b/tests/auto/qml/qqmlecmascript/data/importScope.1.js new file mode 100644 index 0000000000..4c556f9e96 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/importScope.1.js @@ -0,0 +1 @@ +var value = 240 diff --git a/tests/auto/qml/qqmlecmascript/data/importScope.2.js b/tests/auto/qml/qqmlecmascript/data/importScope.2.js new file mode 100644 index 0000000000..291fb9d2cc --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/importScope.2.js @@ -0,0 +1,3 @@ +function getValue() { + return ImportScope1.value +} diff --git a/tests/auto/qml/qqmlecmascript/data/importScope.qml b/tests/auto/qml/qqmlecmascript/data/importScope.qml new file mode 100644 index 0000000000..9b907f11f9 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/importScope.qml @@ -0,0 +1,7 @@ +import QtQuick 2.0 +import "importScope.1.js" as ImportScope1 +import "importScope.2.js" as ImportScope2 + +QtObject { + property int test: ImportScope2.getValue() +} diff --git a/tests/auto/qml/qqmlecmascript/data/in.qml b/tests/auto/qml/qqmlecmascript/data/in.qml new file mode 100644 index 0000000000..f84c9a1481 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/in.qml @@ -0,0 +1,7 @@ +import QtQuick 2.0 + +Item { + id: root + property bool test1: "x" in root + property bool test2: !("foo" in root) +} diff --git a/tests/auto/qml/qqmlecmascript/data/include.js b/tests/auto/qml/qqmlecmascript/data/include.js new file mode 100644 index 0000000000..232fd808f8 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/include.js @@ -0,0 +1,8 @@ +var test1 = true +var test2 = false +var test3 = false + +function go() { + Qt.include("js/include2.js"); +} + diff --git a/tests/auto/qml/qqmlecmascript/data/include.qml b/tests/auto/qml/qqmlecmascript/data/include.qml new file mode 100644 index 0000000000..5ce2ed78ec --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/include.qml @@ -0,0 +1,23 @@ +import QtQuick 2.0 +import "include.js" as IncludeTest + +QtObject { + property int test0: 0 + property bool test1: false + property bool test2: false + property bool test2_1: false + property bool test3: false + property bool test3_1: false + + property int testValue: 99 + + Component.onCompleted: { + IncludeTest.go(); + test0 = IncludeTest.value + test1 = IncludeTest.test1 + test2 = IncludeTest.test2 + test2_1 = IncludeTest.test2_1 + test3 = IncludeTest.test3 + test3_1 = IncludeTest.test3_1 + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/include_callback.js b/tests/auto/qml/qqmlecmascript/data/include_callback.js new file mode 100644 index 0000000000..ea19eba300 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/include_callback.js @@ -0,0 +1,11 @@ +function go() { + var a = Qt.include("missing.js", function(o) { test2 = o.status == o.NETWORK_ERROR }); + test1 = a.status == a.NETWORK_ERROR + + var b = Qt.include("blank.js", function(o) { test4 = o.status == o.OK }); + test3 = b.status == b.OK + + var c = Qt.include("exception.js", function(o) { test6 = o.status == o.EXCEPTION }); + test5 = c.status == c.EXCEPTION +} + diff --git a/tests/auto/qml/qqmlecmascript/data/include_callback.qml b/tests/auto/qml/qqmlecmascript/data/include_callback.qml new file mode 100644 index 0000000000..fbebcdcd58 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/include_callback.qml @@ -0,0 +1,15 @@ +import QtQuick 2.0 +import "include_callback.js" as IncludeTest + +QtObject { + property bool test1: false + property bool test2: false + property bool test3: false + property bool test4: false + property bool test5: false + property bool test6: false + + Component.onCompleted: { + IncludeTest.go(); + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/include_pragma.qml b/tests/auto/qml/qqmlecmascript/data/include_pragma.qml new file mode 100644 index 0000000000..7b23c76baa --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/include_pragma.qml @@ -0,0 +1,11 @@ +import QtQuick 2.0 +import "include_pragma_outer.js" as Script + +Item { + property int test1 + + Component.onCompleted: { + test1 = Script.callFunction() + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/include_pragma_inner.js b/tests/auto/qml/qqmlecmascript/data/include_pragma_inner.js new file mode 100644 index 0000000000..a0380a25df --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/include_pragma_inner.js @@ -0,0 +1,5 @@ +.pragma library + +function getValue() { + return 100; +} diff --git a/tests/auto/qml/qqmlecmascript/data/include_pragma_outer.js b/tests/auto/qml/qqmlecmascript/data/include_pragma_outer.js new file mode 100644 index 0000000000..d87bafc816 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/include_pragma_outer.js @@ -0,0 +1,6 @@ +Qt.include("include_pragma_inner.js") + +function callFunction() { + return getValue(); +} + diff --git a/tests/auto/qml/qqmlecmascript/data/include_remote.js b/tests/auto/qml/qqmlecmascript/data/include_remote.js new file mode 100644 index 0000000000..e6a4676819 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/include_remote.js @@ -0,0 +1,26 @@ +var myvar = 10; + +function go() +{ + var a = Qt.include("http://127.0.0.1:8111/remote_file.js", + function(o) { + test2 = o.status == o.OK + test3 = a.status == a.OK + test4 = myvar == 13 + + done = true; + }); + test1 = a.status == a.LOADING + + + var b = Qt.include("http://127.0.0.1:8111/exception.js", + function(o) { + test7 = o.status == o.EXCEPTION + test8 = b.status == a.EXCEPTION + test9 = b.exception.toString() == "Whoops!"; + test10 = o.exception.toString() == "Whoops!"; + + done2 = true; + }); + test6 = b.status == b.LOADING +} diff --git a/tests/auto/qml/qqmlecmascript/data/include_remote.qml b/tests/auto/qml/qqmlecmascript/data/include_remote.qml new file mode 100644 index 0000000000..fe020a55df --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/include_remote.qml @@ -0,0 +1,21 @@ +import QtQuick 2.0 +import "include_remote.js" as IncludeTest + +QtObject { + property bool done: false + property bool done2: false + + property bool test1: false + property bool test2: false + property bool test3: false + property bool test4: false + property bool test5: false + + property bool test6: false + property bool test7: false + property bool test8: false + property bool test9: false + property bool test10: false + + Component.onCompleted: IncludeTest.go(); +} diff --git a/tests/auto/qml/qqmlecmascript/data/include_remote_missing.js b/tests/auto/qml/qqmlecmascript/data/include_remote_missing.js new file mode 100644 index 0000000000..cc90860cc9 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/include_remote_missing.js @@ -0,0 +1,13 @@ +function go() +{ + var a = Qt.include("http://127.0.0.1:8111/missing.js", + function(o) { + test2 = o.status == o.NETWORK_ERROR + test3 = a.status == a.NETWORK_ERROR + + done = true; + }); + + test1 = a.status == a.LOADING +} + diff --git a/tests/auto/qml/qqmlecmascript/data/include_remote_missing.qml b/tests/auto/qml/qqmlecmascript/data/include_remote_missing.qml new file mode 100644 index 0000000000..e8ef609fed --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/include_remote_missing.qml @@ -0,0 +1,12 @@ +import QtQuick 2.0 +import "include_remote_missing.js" as IncludeTest + +QtObject { + property bool done: false + + property bool test1: false + property bool test2: false + property bool test3: false + + Component.onCompleted: IncludeTest.go(); +} diff --git a/tests/auto/qml/qqmlecmascript/data/include_shared.js b/tests/auto/qml/qqmlecmascript/data/include_shared.js new file mode 100644 index 0000000000..a49c07bbfc --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/include_shared.js @@ -0,0 +1,12 @@ +.pragma library + +var test1 = true +var test2 = false +var test3 = false + +var testValue = 99; + +function go() { + Qt.include("js/include2.js"); +} + diff --git a/tests/auto/qml/qqmlecmascript/data/include_shared.qml b/tests/auto/qml/qqmlecmascript/data/include_shared.qml new file mode 100644 index 0000000000..28b1003fd4 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/include_shared.qml @@ -0,0 +1,22 @@ +import QtQuick 2.0 +import "include_shared.js" as IncludeTest + +QtObject { + property int test0: 0 + property bool test1: false + property bool test2: false + property bool test2_1: false + property bool test3: false + property bool test3_1: false + + Component.onCompleted: { + IncludeTest.go(); + test0 = IncludeTest.value + test1 = IncludeTest.test1 + test2 = IncludeTest.test2 + test2_1 = IncludeTest.test2_1 + test3 = IncludeTest.test3 + test3_1 = IncludeTest.test3_1 + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/invokableObjectArg.qml b/tests/auto/qml/qqmlecmascript/data/invokableObjectArg.qml new file mode 100644 index 0000000000..160a90b574 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/invokableObjectArg.qml @@ -0,0 +1,9 @@ +import Qt.test 1.0 +import QtQuick 2.0 + +MyQmlObject { + id: root + Component.onCompleted: { + root.myinvokable(root); + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/invokableObjectRet.qml b/tests/auto/qml/qqmlecmascript/data/invokableObjectRet.qml new file mode 100644 index 0000000000..4612273727 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/invokableObjectRet.qml @@ -0,0 +1,11 @@ +import Qt.test 1.0 +import QtQuick 2.0 + +MyQmlObject { + id: root + property bool test: false + Component.onCompleted: { + test = (root.returnme() == root) + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/js/include2.js b/tests/auto/qml/qqmlecmascript/data/js/include2.js new file mode 100644 index 0000000000..2a0c039dfa --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/js/include2.js @@ -0,0 +1,4 @@ +test2 = true +var test2_1 = true + +Qt.include("include3.js"); diff --git a/tests/auto/qml/qqmlecmascript/data/js/include3.js b/tests/auto/qml/qqmlecmascript/data/js/include3.js new file mode 100644 index 0000000000..84b2770b6f --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/js/include3.js @@ -0,0 +1,3 @@ +test3 = true +var test3_1 = true +var value = testValue diff --git a/tests/auto/qml/qqmlecmascript/data/jsObject.qml b/tests/auto/qml/qqmlecmascript/data/jsObject.qml new file mode 100644 index 0000000000..4223c25f31 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/jsObject.qml @@ -0,0 +1,12 @@ +import QtQuick 2.0 + +QtObject { + property int test + + Component.onCompleted: { + var o = new Object; + o.test = 92; + test = o.test; + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/jsimport/SpecialRectangleOne.qml b/tests/auto/qml/qqmlecmascript/data/jsimport/SpecialRectangleOne.qml new file mode 100644 index 0000000000..97c72bd9a6 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/jsimport/SpecialRectangleOne.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 + +import "importPragmaLibrary.js" as TestPragmaLibraryImport + +Rectangle { + width: TestPragmaLibraryImport.importIncrementedValue() + height: width + 15 + color: "red" +} diff --git a/tests/auto/qml/qqmlecmascript/data/jsimport/SpecialRectangleTwo.qml b/tests/auto/qml/qqmlecmascript/data/jsimport/SpecialRectangleTwo.qml new file mode 100644 index 0000000000..d006343782 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/jsimport/SpecialRectangleTwo.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 + +import "importPragmaLibrary.js" as TestPragmaLibraryImport + +Rectangle { + width: TestPragmaLibraryImport.importIncrementedValue() + height: width + 5 + color: "blue" +} diff --git a/tests/auto/qml/qqmlecmascript/data/jsimport/importFive.js b/tests/auto/qml/qqmlecmascript/data/jsimport/importFive.js new file mode 100644 index 0000000000..e458094552 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/jsimport/importFive.js @@ -0,0 +1,3 @@ +function importFiveFunction() { + return '5'; +} diff --git a/tests/auto/qml/qqmlecmascript/data/jsimport/importFour.js b/tests/auto/qml/qqmlecmascript/data/jsimport/importFour.js new file mode 100644 index 0000000000..faddc15c9d --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/jsimport/importFour.js @@ -0,0 +1,9 @@ +.pragma library + +function importFourFunction() { + return '4'; +} + +function greetingString() { + return 'Hello, World!'; +} diff --git a/tests/auto/qml/qqmlecmascript/data/jsimport/importOne.js b/tests/auto/qml/qqmlecmascript/data/jsimport/importOne.js new file mode 100644 index 0000000000..338c4e042f --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/jsimport/importOne.js @@ -0,0 +1,13 @@ +.import "importTwo.js" as ImportTwoJs +.import "importThree.js" as ImportThreeJs + +function greetingString() { + if (ImportTwoJs.greetingString().length > 0) { + return ImportTwoJs.greetingString(); + } + return ImportThreeJs.greetingString(); +} + +function importOneFunction() { + return '1'; +} diff --git a/tests/auto/qml/qqmlecmascript/data/jsimport/importPragmaLibrary.js b/tests/auto/qml/qqmlecmascript/data/jsimport/importPragmaLibrary.js new file mode 100644 index 0000000000..c746fef14b --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/jsimport/importPragmaLibrary.js @@ -0,0 +1,9 @@ +.pragma library + +var i = 4; + +// .pragma library, so should be callable from multiple .qml with shared i. +function importIncrementedValue() { + i = i + 1; + return i; +} diff --git a/tests/auto/qml/qqmlecmascript/data/jsimport/importPragmaLibraryWithImports.js b/tests/auto/qml/qqmlecmascript/data/jsimport/importPragmaLibraryWithImports.js new file mode 100644 index 0000000000..3f2e6589dd --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/jsimport/importPragmaLibraryWithImports.js @@ -0,0 +1,9 @@ +.pragma library +.import "importFive.js" as ImportFive + +var i = 4; + +function importIncrementedValue() { + i = i + 1; + return (i + ImportFive.importFiveFunction()); // i + '5' (not i+5) +} diff --git a/tests/auto/qml/qqmlecmascript/data/jsimport/importPragmaLibraryWithPragmaLibraryImports.js b/tests/auto/qml/qqmlecmascript/data/jsimport/importPragmaLibraryWithPragmaLibraryImports.js new file mode 100644 index 0000000000..fa6497d99b --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/jsimport/importPragmaLibraryWithPragmaLibraryImports.js @@ -0,0 +1,11 @@ +.pragma library +.import "importPragmaLibrary.js" as LibraryImport + +var i = 10; + +function importIncrementedValue() { + i = i + 1; + // because LibraryImport is shared, and used in previous tests, + // the value will be large (already incremented a bunch of times). + return (i + LibraryImport.importIncrementedValue()); +} diff --git a/tests/auto/qml/qqmlecmascript/data/jsimport/importThree.js b/tests/auto/qml/qqmlecmascript/data/jsimport/importThree.js new file mode 100644 index 0000000000..3917134ee2 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/jsimport/importThree.js @@ -0,0 +1,9 @@ +.import "importFour.js" as ImportFourJs + +function greetingString() { + return ImportFourJs.greetingString(); +} + +function importThreeFunction() { + return '3'; +} diff --git a/tests/auto/qml/qqmlecmascript/data/jsimport/importTwo.js b/tests/auto/qml/qqmlecmascript/data/jsimport/importTwo.js new file mode 100644 index 0000000000..45b3c9a74d --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/jsimport/importTwo.js @@ -0,0 +1,10 @@ +.import "importFour.js" as ImportFourJs +.import "importFive.js" as ImportFiveJs + +function greetingString() { + return ImportFourJs.greetingString(); +} + +function importTwoFunction() { + return '2'; +} diff --git a/tests/auto/qml/qqmlecmascript/data/jsimport/importWithNoImports.js b/tests/auto/qml/qqmlecmascript/data/jsimport/importWithNoImports.js new file mode 100644 index 0000000000..83426c425c --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/jsimport/importWithNoImports.js @@ -0,0 +1,11 @@ +// This js file has no imports, and so should inherit +// scope from the QML file which includes it. + +function componentError() { + var i = 5; + var errorIsOne = Component.error == 1; + if (errorIsOne == true) { + i = i + 7; + } + return i; +} diff --git a/tests/auto/qml/qqmlecmascript/data/jsimport/testImport.qml b/tests/auto/qml/qqmlecmascript/data/jsimport/testImport.qml new file mode 100644 index 0000000000..456a10c7f0 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/jsimport/testImport.qml @@ -0,0 +1,14 @@ +import QtQuick 2.0 + +import "testScriptImport.js" as TestScriptImport +import "testModuleImport.js" as TestModuleImport + +QtObject { + id: testQtObject + + property string importedScriptStringValue: TestScriptImport.greetingText + property int importedScriptFunctionValue: TestScriptImport.randomInteger(1, 20) + + property int importedModuleAttachedPropertyValue: TestModuleImport.importedAttachedPropertyValue(testQtObject) + property int importedModuleEnumValue: TestModuleImport.importedEnumValue +} diff --git a/tests/auto/qml/qqmlecmascript/data/jsimport/testImportPragmaLibrary.qml b/tests/auto/qml/qqmlecmascript/data/jsimport/testImportPragmaLibrary.qml new file mode 100644 index 0000000000..29de15c197 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/jsimport/testImportPragmaLibrary.qml @@ -0,0 +1,20 @@ +import QtQuick 2.0 + +// We use the components specified in SpecialRectangleOne.qml and SpecialRectangleTwo.qml + +QtObject { + id: testQtObject + + property SpecialRectangleOne a; + property SpecialRectangleTwo b; + + a: SpecialRectangleOne { + id: rectangleOne + } + b: SpecialRectangleTwo { + id: rectangleTwo + } + + // this should be: (5 + 15) + (6 + 5) == 31 + property int testValue: rectangleOne.height + rectangleTwo.height +} diff --git a/tests/auto/qml/qqmlecmascript/data/jsimport/testImportPragmaLibraryWithImports.qml b/tests/auto/qml/qqmlecmascript/data/jsimport/testImportPragmaLibraryWithImports.qml new file mode 100644 index 0000000000..6a7459d3bb --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/jsimport/testImportPragmaLibraryWithImports.qml @@ -0,0 +1,7 @@ +import QtQuick 2.0 +import "importPragmaLibraryWithImports.js" as LibraryImport + +QtObject { + id: root + property int testValue: LibraryImport.importIncrementedValue(); // valueOf(4 + 1 + '5') = valueOf('55') = 55 +} diff --git a/tests/auto/qml/qqmlecmascript/data/jsimport/testImportPragmaLibraryWithPragmaLibraryImports.qml b/tests/auto/qml/qqmlecmascript/data/jsimport/testImportPragmaLibraryWithPragmaLibraryImports.qml new file mode 100644 index 0000000000..01f08dbdc3 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/jsimport/testImportPragmaLibraryWithPragmaLibraryImports.qml @@ -0,0 +1,7 @@ +import QtQuick 2.0 +import "importPragmaLibraryWithPragmaLibraryImports.js" as LibraryImport + +QtObject { + id: root + property int testValue: LibraryImport.importIncrementedValue(); // 10 + 1 + (7 due to previous tests) = 18 +} diff --git a/tests/auto/qml/qqmlecmascript/data/jsimport/testImportScoping.qml b/tests/auto/qml/qqmlecmascript/data/jsimport/testImportScoping.qml new file mode 100644 index 0000000000..aff61cc436 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/jsimport/testImportScoping.qml @@ -0,0 +1,11 @@ +import QtQuick 2.0 + +// For backward compatibility, importing a script which has no imports, +// should run the script in the parent context. See QTBUG-17518. + +import "importWithNoImports.js" as TestNoImportScoping + +QtObject { + id: testQtObject + property int componentError: TestNoImportScoping.componentError() +} diff --git a/tests/auto/qml/qqmlecmascript/data/jsimport/testModuleImport.js b/tests/auto/qml/qqmlecmascript/data/jsimport/testModuleImport.js new file mode 100644 index 0000000000..69bc1c9887 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/jsimport/testModuleImport.js @@ -0,0 +1,8 @@ +.import Qt.test 1.0 as JsQtTest // test that we can import elements from .js files + +function importedAttachedPropertyValue(obj) { + return obj.JsQtTest.MyQmlObject.value; // attached property, value = 19. +} + +var importedEnumValue = JsQtTest.MyQmlObject.EnumValue3 // the actual value of this enum value is "2" + diff --git a/tests/auto/qml/qqmlecmascript/data/jsimport/testScriptImport.js b/tests/auto/qml/qqmlecmascript/data/jsimport/testScriptImport.js new file mode 100644 index 0000000000..2ecccd8816 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/jsimport/testScriptImport.js @@ -0,0 +1,11 @@ +.import "importOne.js" as ImportOneJs // test that we can import scripts from .js files + +var greetingText = ImportOneJs.greetingString() + +function randomInteger(min, max) { + if (max > min) { + if (min > 10) return min; + return max; + } + return min; +} diff --git a/tests/auto/qml/qqmlecmascript/data/jsimportfail/failFive.qml b/tests/auto/qml/qqmlecmascript/data/jsimportfail/failFive.qml new file mode 100644 index 0000000000..73193a35a5 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/jsimportfail/failFive.qml @@ -0,0 +1,11 @@ +import QtQuick 2.0 + +// This should fail, since if the script does have imports +// of its own, it should run in its own context. + +import "importWithImports.js" as TestImportScoping + +QtObject { + id: testQtObject + property int componentError: TestImportScoping.componentError() +} diff --git a/tests/auto/qml/qqmlecmascript/data/jsimportfail/failFour.qml b/tests/auto/qml/qqmlecmascript/data/jsimportfail/failFour.qml new file mode 100644 index 0000000000..ef2fc591b3 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/jsimportfail/failFour.qml @@ -0,0 +1,7 @@ +import QtQuick 2.0 + +import "testModuleImport.js" as TestModuleImport + +QtObject { + property int importedModuleEnumValue: JsQtTest.MyQmlObject.EnumValue3 // should fail - the typenames available in TestModuleImport should not be available in this scope +} diff --git a/tests/auto/qml/qqmlecmascript/data/jsimportfail/failOne.qml b/tests/auto/qml/qqmlecmascript/data/jsimportfail/failOne.qml new file mode 100644 index 0000000000..d0c37ad9ba --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/jsimportfail/failOne.qml @@ -0,0 +1,7 @@ +import QtQuick 2.0 + +import "testScriptImport.js" as TestScriptImport + +QtObject { + property string importScriptFunctionValue: TestScriptImport.ImportOneJs.greetingString() // should fail - the context of TestScriptImport is private to TestScriptImport. +} diff --git a/tests/auto/qml/qqmlecmascript/data/jsimportfail/failThree.qml b/tests/auto/qml/qqmlecmascript/data/jsimportfail/failThree.qml new file mode 100644 index 0000000000..edd103bd82 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/jsimportfail/failThree.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 + +import "testModuleImport.js" as TestModuleImport + +QtObject { + id: testQtObject + property int importedModuleAttachedPropertyValue: testQtObject.TestModuleImport.JsQtTest.MyQmlObject.value // should fail - the context of TestScriptImport is private to TestScriptImport. +} diff --git a/tests/auto/qml/qqmlecmascript/data/jsimportfail/failTwo.qml b/tests/auto/qml/qqmlecmascript/data/jsimportfail/failTwo.qml new file mode 100644 index 0000000000..28e2026f8d --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/jsimportfail/failTwo.qml @@ -0,0 +1,7 @@ +import QtQuick 2.0 + +import "testScriptImport.js" as TestScriptImport + +QtObject { + property string importScriptFunctionValue: ImportOneJs.greetingString() // should fail - the typenames in TestScriptImport should not be visible from this scope +} diff --git a/tests/auto/qml/qqmlecmascript/data/jsimportfail/importOne.js b/tests/auto/qml/qqmlecmascript/data/jsimportfail/importOne.js new file mode 100644 index 0000000000..45fd9c75dd --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/jsimportfail/importOne.js @@ -0,0 +1,7 @@ +function greetingString() { + return 'Hello, World!'; +} + +function importOneFunction() { + return '1'; +} diff --git a/tests/auto/qml/qqmlecmascript/data/jsimportfail/importPragmaLibrary.js b/tests/auto/qml/qqmlecmascript/data/jsimportfail/importPragmaLibrary.js new file mode 100644 index 0000000000..ad0e6946a2 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/jsimportfail/importPragmaLibrary.js @@ -0,0 +1,11 @@ +.pragma library + +// .pragma library, so shouldn't inherit imports from any .qml file. +function importValue() { + var i = 3; + var errorIsOne = Component.error == 1; // this line should fail. + if (errorIsOne == true) { + i = i + 4; + } + return i; +} diff --git a/tests/auto/qml/qqmlecmascript/data/jsimportfail/importWithImports.js b/tests/auto/qml/qqmlecmascript/data/jsimportfail/importWithImports.js new file mode 100644 index 0000000000..6d77ceccb1 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/jsimportfail/importWithImports.js @@ -0,0 +1,13 @@ +.import "importOne.js" as ImportOne + +// This js file has imports, so should not inherit +// scope from the QML file which includes it. + +function componentError() { + var i = 3; + var errorIsOne = Component.error == 1; // this line should fail. + if (errorIsOne == true) { + i = i + 4; + } + return i; +} diff --git a/tests/auto/qml/qqmlecmascript/data/jsimportfail/testImportPragmaLibrary.qml b/tests/auto/qml/qqmlecmascript/data/jsimportfail/testImportPragmaLibrary.qml new file mode 100644 index 0000000000..f04ce007d8 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/jsimportfail/testImportPragmaLibrary.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 + +import "importPragmaLibrary.js" as ImportPragmaLibrary + +QtObject { + id: testQtObject + property int testValue: ImportPragmaLibrary.importValue() +} diff --git a/tests/auto/qml/qqmlecmascript/data/jsimportfail/testModuleImport.js b/tests/auto/qml/qqmlecmascript/data/jsimportfail/testModuleImport.js new file mode 100644 index 0000000000..69bc1c9887 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/jsimportfail/testModuleImport.js @@ -0,0 +1,8 @@ +.import Qt.test 1.0 as JsQtTest // test that we can import elements from .js files + +function importedAttachedPropertyValue(obj) { + return obj.JsQtTest.MyQmlObject.value; // attached property, value = 19. +} + +var importedEnumValue = JsQtTest.MyQmlObject.EnumValue3 // the actual value of this enum value is "2" + diff --git a/tests/auto/qml/qqmlecmascript/data/jsimportfail/testScriptImport.js b/tests/auto/qml/qqmlecmascript/data/jsimportfail/testScriptImport.js new file mode 100644 index 0000000000..2ecccd8816 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/jsimportfail/testScriptImport.js @@ -0,0 +1,11 @@ +.import "importOne.js" as ImportOneJs // test that we can import scripts from .js files + +var greetingText = ImportOneJs.greetingString() + +function randomInteger(min, max) { + if (max > min) { + if (min > 10) return min; + return max; + } + return min; +} diff --git a/tests/auto/qml/qqmlecmascript/data/libraryScriptAssert.js b/tests/auto/qml/qqmlecmascript/data/libraryScriptAssert.js new file mode 100644 index 0000000000..3ffdb339ad --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/libraryScriptAssert.js @@ -0,0 +1,6 @@ +.pragma library + +function test(target) +{ + var a = target.a; +} diff --git a/tests/auto/qml/qqmlecmascript/data/libraryScriptAssert.qml b/tests/auto/qml/qqmlecmascript/data/libraryScriptAssert.qml new file mode 100644 index 0000000000..5884e2719b --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/libraryScriptAssert.qml @@ -0,0 +1,7 @@ +import QtQuick 2.0 +import "libraryScriptAssert.js" as Test + +QtObject { + id: root + Component.onCompleted: Test.test(root); +} diff --git a/tests/auto/qml/qqmlecmascript/data/listAssignment.qml b/tests/auto/qml/qqmlecmascript/data/listAssignment.qml new file mode 100644 index 0000000000..6e6039715b --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/listAssignment.qml @@ -0,0 +1,20 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: root + width: 640 + height: 480 + + property int list1length: list1.length + + property list<MyQmlObject> list1 + property list<MyQmlObject> list2: [ + MyQmlObject { id: one; value: 100 }, + MyQmlObject { id: two; value: 300 } + ] + + Component.onCompleted: { + root.list1 = root.list2; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/listProperties.qml b/tests/auto/qml/qqmlecmascript/data/listProperties.qml new file mode 100644 index 0000000000..bdb1265a21 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/listProperties.qml @@ -0,0 +1,24 @@ +import Qt.test 1.0 +import QtQuick 2.0 + +MyQmlObject { + id: root + + objectListProperty: [ + QtObject { property int a: 10 }, + QtObject { property int a: 11 } + ] + + function calcTest1() { + var rv = 0; + for (var ii = 0; ii < root.objectListProperty.length; ++ii) { + rv += root.objectListProperty[ii].a; + } + return rv; + } + + property int test1: calcTest1(); + property int test2: root.objectListProperty.length + property bool test3: root.objectListProperty[1] != undefined + property bool test4: root.objectListProperty[100] == undefined +} diff --git a/tests/auto/qml/qqmlecmascript/data/listToVariant.qml b/tests/auto/qml/qqmlecmascript/data/listToVariant.qml new file mode 100644 index 0000000000..690024b928 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/listToVariant.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +QtObject { + property variant test: children +} diff --git a/tests/auto/qml/qqmlecmascript/data/metaobjectRevision.qml b/tests/auto/qml/qqmlecmascript/data/metaobjectRevision.qml new file mode 100644 index 0000000000..77accd80de --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/metaobjectRevision.qml @@ -0,0 +1,7 @@ +import Qt.test 1.1 + +MyRevisionedClass +{ + prop1: prop2 + onSignal1: method2() +} diff --git a/tests/auto/qml/qqmlecmascript/data/metaobjectRevision2.qml b/tests/auto/qml/qqmlecmascript/data/metaobjectRevision2.qml new file mode 100644 index 0000000000..36057cb902 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/metaobjectRevision2.qml @@ -0,0 +1,9 @@ +import Qt.test 1.1 + +MyRevisionedSubclass +{ + prop1: prop3 + onSignal1: method2() + prop3: prop4 + onSignal3: method4() +} diff --git a/tests/auto/qml/qqmlecmascript/data/metaobjectRevision3.qml b/tests/auto/qml/qqmlecmascript/data/metaobjectRevision3.qml new file mode 100644 index 0000000000..81769e98f7 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/metaobjectRevision3.qml @@ -0,0 +1,8 @@ +import Qt.test 1.0 + +MyRevisionedSubclass +{ + prop1: prop3 + onSignal1: method1() + onSignal3: method3() +} diff --git a/tests/auto/qml/qqmlecmascript/data/metaobjectRevision4.qml b/tests/auto/qml/qqmlecmascript/data/metaobjectRevision4.qml new file mode 100644 index 0000000000..6ebe4790bb --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/metaobjectRevision4.qml @@ -0,0 +1,14 @@ +import Qt.test 1.1 +import QtQuick 2.0 + +QtObject { + property variant a + property real test + + a: MyRevisionedClass { + prop2: 11 + + Component.onCompleted: test = prop2 + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/metaobjectRevisionErrors.qml b/tests/auto/qml/qqmlecmascript/data/metaobjectRevisionErrors.qml new file mode 100644 index 0000000000..8a7e24d788 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/metaobjectRevisionErrors.qml @@ -0,0 +1,14 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +MyRevisionedClass +{ + // Will not hit optimizer + property real p1: prop1 % 3 + property real p2: prop2 % 3 + + // Should hit optimizer + property real p3: prop2 + + Component.onCompleted: method2() +} diff --git a/tests/auto/qml/qqmlecmascript/data/metaobjectRevisionErrors2.qml b/tests/auto/qml/qqmlecmascript/data/metaobjectRevisionErrors2.qml new file mode 100644 index 0000000000..43e87948cd --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/metaobjectRevisionErrors2.qml @@ -0,0 +1,24 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +MyRevisionedSubclass +{ + // Will not hit optimizer + property real p1: prop1 % 3 + property real p2: prop2 % 3 + property real p3: prop3 % 3 + property real p4: prop4 % 3 + + // Should hit optimizer + property real p5: prop1 + property real p6: prop2 + property real p7: prop3 + property real p8: prop4 + + Component.onCompleted: { + method1() + method2() + method3() + method4() + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/metaobjectRevisionErrors3.qml b/tests/auto/qml/qqmlecmascript/data/metaobjectRevisionErrors3.qml new file mode 100644 index 0000000000..2f82d685fa --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/metaobjectRevisionErrors3.qml @@ -0,0 +1,36 @@ +import QtQuick 2.0 +import Qt.test 1.1 + +MyRevisionedSubclass +{ + // Will not hit optimizer + property real pA: propA % 3 + property real pB: propB % 3 + property real pC: propC % 3 + property real pD: propD % 3 + property real p1: prop1 % 3 + property real p2: prop2 % 3 + property real p3: prop3 % 3 + property real p4: prop4 % 3 + + // Should hit optimizer + property real pE: propA + property real pF: propB + property real pG: propC + property real pH: propD + property real p5: prop1 + property real p6: prop2 + property real p7: prop3 + property real p8: prop4 + + Component.onCompleted: { + methodA() + methodB() + methodC() + methodD() + method1() + method2() + method3() + method4() + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/methods.1.qml b/tests/auto/qml/qqmlecmascript/data/methods.1.qml new file mode 100644 index 0000000000..0bbee16df8 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/methods.1.qml @@ -0,0 +1,6 @@ +import Qt.test 1.0 + +MyQmlObject { + id: myObject + onBasicSignal: myObject.methodNoArgs() +} diff --git a/tests/auto/qml/qqmlecmascript/data/methods.2.qml b/tests/auto/qml/qqmlecmascript/data/methods.2.qml new file mode 100644 index 0000000000..9f0c6b15fe --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/methods.2.qml @@ -0,0 +1,6 @@ +import Qt.test 1.0 + +MyQmlObject { + id: myObject + onBasicSignal: myObject.method(163) +} diff --git a/tests/auto/qml/qqmlecmascript/data/methods.3.qml b/tests/auto/qml/qqmlecmascript/data/methods.3.qml new file mode 100644 index 0000000000..365780a560 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/methods.3.qml @@ -0,0 +1,7 @@ +import QtQuick 2.0 + +QtObject { + function testFunction() { return 19; } + + property int test: testFunction() +} diff --git a/tests/auto/qml/qqmlecmascript/data/methods.4.qml b/tests/auto/qml/qqmlecmascript/data/methods.4.qml new file mode 100644 index 0000000000..a3bd7bebf8 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/methods.4.qml @@ -0,0 +1,11 @@ +import QtQuick 2.0 + +MethodsObject { + function testFunction2() { return 17; } + function testFunction3() { return 16; } + + property int test: testFunction() + property int test2: testFunction2() + property int test3: testFunction3() +} + diff --git a/tests/auto/qml/qqmlecmascript/data/methods.5.qml b/tests/auto/qml/qqmlecmascript/data/methods.5.qml new file mode 100644 index 0000000000..ede2759e2e --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/methods.5.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 + +Item { + property alias blah: item.x + Item { id: item } + + function testFunction() { return 9; } + property int test: testFunction(); +} diff --git a/tests/auto/qml/qqmlecmascript/data/moduleapi/moduleApiMajorVersionFail.qml b/tests/auto/qml/qqmlecmascript/data/moduleapi/moduleApiMajorVersionFail.qml new file mode 100644 index 0000000000..fb050f65bc --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/moduleapi/moduleApiMajorVersionFail.qml @@ -0,0 +1,10 @@ +import QtQuick 2.0 + +// this qml file attempts to import an invalid version of a qobject module API. + +import Qt.test.qobjectApi 4.0 as QtTestMajorVersionQObjectApi // qobject module API installed into existing uri with nonexistent major version + +QtObject { + property int qobjectMajorVersionTest: QtTestMajorVersionQObjectApi.qobjectTestProperty +} + diff --git a/tests/auto/qml/qqmlecmascript/data/moduleapi/moduleApiMinorVersionFail.qml b/tests/auto/qml/qqmlecmascript/data/moduleapi/moduleApiMinorVersionFail.qml new file mode 100644 index 0000000000..e06be667f7 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/moduleapi/moduleApiMinorVersionFail.qml @@ -0,0 +1,10 @@ +import QtQuick 2.0 + +// this qml file attempts to import an invalid version of a qobject module API. + +import Qt.test.qobjectApi 1.2 as QtTestMinorVersionQObjectApi // qobject module API installed into existing uri with nonexistent minor version + +QtObject { + property int qobjectMinorVersionTest: QtTestMinorVersionedQObjectApi.qobjectTestProperty +} + diff --git a/tests/auto/qml/qqmlecmascript/data/moduleapi/qobjectModuleApi.qml b/tests/auto/qml/qqmlecmascript/data/moduleapi/qobjectModuleApi.qml new file mode 100644 index 0000000000..718a64652d --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/moduleapi/qobjectModuleApi.qml @@ -0,0 +1,21 @@ +import QtQuick 2.0 + +import Qt.test 1.0 as QtTest // module API installed into existing uri +import Qt.test.qobjectApi 1.0 as QtTestQObjectApi // qobject module API installed into new uri +import Qt.test.qobjectApi 1.3 as QtTestMinorVersionQObjectApi // qobject module API installed into existing uri with new minor version +import Qt.test.qobjectApi 2.0 as QtTestMajorVersionQObjectApi // qobject module API installed into existing uri with new major version +import Qt.test.qobjectApiParented 1.0 as QtTestParentedQObjectApi // qobject (with parent) module API installed into a new uri + +QtObject { + property int existingUriTest: QtTest.qobjectTestProperty + property int qobjectTest: QtTestQObjectApi.qobjectTestProperty + property int qobjectMethodTest: 2 + property int qobjectMinorVersionTest: QtTestMinorVersionQObjectApi.qobjectTestProperty + property int qobjectMajorVersionTest: QtTestMajorVersionQObjectApi.qobjectTestProperty + property int qobjectParentedTest: QtTestParentedQObjectApi.qobjectTestProperty + + Component.onCompleted: { + qobjectMethodTest = QtTestQObjectApi.qobjectTestMethod(); + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/moduleapi/qobjectModuleApiCaching.qml b/tests/auto/qml/qqmlecmascript/data/moduleapi/qobjectModuleApiCaching.qml new file mode 100644 index 0000000000..56a55e4e9b --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/moduleapi/qobjectModuleApiCaching.qml @@ -0,0 +1,10 @@ +import QtQuick 2.0 + +import Qt.test 1.0 as QtTest // module API installed into existing uri +import Qt.test.qobjectApiParented 1.0 as QtTestParentedQObjectApi // qobject (with parent) module API installed into a new uri + +QtObject { + property int existingUriTest: QtTest.qobjectTestProperty + property int qobjectParentedTest: QtTestParentedQObjectApi.qobjectTestProperty +} + diff --git a/tests/auto/qml/qqmlecmascript/data/moduleapi/qobjectModuleApiEnums.qml b/tests/auto/qml/qqmlecmascript/data/moduleapi/qobjectModuleApiEnums.qml new file mode 100644 index 0000000000..da5ffd5e76 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/moduleapi/qobjectModuleApiEnums.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import Qt.test.qobjectApi 1.0 as QtTestQObjectApi // qobject module API installed into new uri + +QtObject { + property int enumValue: QtTestQObjectApi.EnumValue2; + property int enumMethod: QtTestQObjectApi.qobjectEnumTestMethod(QtTestQObjectApi.EnumValue1); +} + diff --git a/tests/auto/qml/qqmlecmascript/data/moduleapi/qobjectModuleApiWriting.qml b/tests/auto/qml/qqmlecmascript/data/moduleapi/qobjectModuleApiWriting.qml new file mode 100644 index 0000000000..be647ca57f --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/moduleapi/qobjectModuleApiWriting.qml @@ -0,0 +1,26 @@ +import QtQuick 2.0 +import Qt.test 1.0 as QtTest // qobject module API installed into existing uri + +QtObject { + property int firstProperty: 1 + property int secondProperty: 2 + property int readOnlyProperty: QtTest.qobjectTestProperty + property int writableProperty: QtTest.qobjectTestWritableProperty + + onFirstPropertyChanged: { + // In this case, we want to attempt to set the module API property. + // This should fail, as the module API property is read only. + if (firstProperty != QtTest.qobjectTestProperty) { + QtTest.qobjectTestProperty = firstProperty; // should silently fail. + } + } + + onSecondPropertyChanged: { + // In this case, we want to attempt to set the module API property. + // This should succeed, as the module API property is writable. + if (secondProperty != QtTest.qobjectTestWritableProperty) { + QtTest.qobjectTestWritableProperty = secondProperty; // should succeed. + } + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/moduleapi/scriptModuleApi.qml b/tests/auto/qml/qqmlecmascript/data/moduleapi/scriptModuleApi.qml new file mode 100644 index 0000000000..7c4e20489d --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/moduleapi/scriptModuleApi.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 +import Qt.test.scriptApi 1.0 as QtTestScriptApi // script module API installed into new uri + +QtObject { + property int scriptTest: QtTestScriptApi.scriptTestProperty // script module api's only provide properties. +} diff --git a/tests/auto/qml/qqmlecmascript/data/moduleapi/scriptModuleApiCaching.qml b/tests/auto/qml/qqmlecmascript/data/moduleapi/scriptModuleApiCaching.qml new file mode 100644 index 0000000000..90974b5969 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/moduleapi/scriptModuleApiCaching.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 +import Qt.test.scriptApi 1.0 as QtTestScriptApi // script module API installed into new uri + +QtObject { + property int scriptTest: QtTestScriptApi.scriptTestProperty +} diff --git a/tests/auto/qml/qqmlecmascript/data/moduleapi/scriptModuleApiWriting.qml b/tests/auto/qml/qqmlecmascript/data/moduleapi/scriptModuleApiWriting.qml new file mode 100644 index 0000000000..02461d59ed --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/moduleapi/scriptModuleApiWriting.qml @@ -0,0 +1,32 @@ +import QtQuick 2.0 +import Qt.test.scriptApi 1.0 as QtTestScriptApi +import Qt.test.scriptApi 2.0 as QtTestScriptApi2 + +QtObject { + property int firstProperty + property int readBack + + property int secondProperty + property int unchanged + + onFirstPropertyChanged: { + if (QtTestScriptApi.scriptTestProperty != firstProperty) { + QtTestScriptApi.scriptTestProperty = firstProperty; + readBack = QtTestScriptApi.scriptTestProperty; + } + } + + onSecondPropertyChanged: { + if (QtTestScriptApi2.scriptTestProperty != secondProperty) { + QtTestScriptApi2.scriptTestProperty = secondProperty; + unchanged = QtTestScriptApi2.scriptTestProperty; + } + } + + Component.onCompleted: { + firstProperty = QtTestScriptApi.scriptTestProperty; + readBack = QtTestScriptApi.scriptTestProperty; + secondProperty = QtTestScriptApi2.scriptTestProperty; + unchanged = QtTestScriptApi2.scriptTestProperty; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/multiEngineObject.qml b/tests/auto/qml/qqmlecmascript/data/multiEngineObject.qml new file mode 100644 index 0000000000..e349ced98f --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/multiEngineObject.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +QtObject { + property string test: thing.stringProperty +} diff --git a/tests/auto/qml/qqmlecmascript/data/noSpuriousWarningsAtShutdown.2.qml b/tests/auto/qml/qqmlecmascript/data/noSpuriousWarningsAtShutdown.2.qml new file mode 100644 index 0000000000..23276f778d --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/noSpuriousWarningsAtShutdown.2.qml @@ -0,0 +1,10 @@ +import QtQuick 2.0 + +Item { + id: root + + Item {} + + SpuriousWarning {} +} + diff --git a/tests/auto/qml/qqmlecmascript/data/noSpuriousWarningsAtShutdown.qml b/tests/auto/qml/qqmlecmascript/data/noSpuriousWarningsAtShutdown.qml new file mode 100644 index 0000000000..b4a417e04e --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/noSpuriousWarningsAtShutdown.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 + +Item { + id: root + + property int childrenCount: root.children.length + + Item {} +} diff --git a/tests/auto/qml/qqmlecmascript/data/nonExistentAttachedObject.qml b/tests/auto/qml/qqmlecmascript/data/nonExistentAttachedObject.qml new file mode 100644 index 0000000000..f9585db009 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/nonExistentAttachedObject.qml @@ -0,0 +1,5 @@ +import Qt.test 1.0 + +MyQmlObject { + stringProperty: MyQmlContainer.prop +} diff --git a/tests/auto/qml/qqmlecmascript/data/nonNotifyable.qml b/tests/auto/qml/qqmlecmascript/data/nonNotifyable.qml new file mode 100644 index 0000000000..2b8b113c34 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/nonNotifyable.qml @@ -0,0 +1,6 @@ +import Qt.test 1.0 + +MyQmlObject { + id: root + property int test: root.value +} diff --git a/tests/auto/qml/qqmlecmascript/data/nonscriptable.qml b/tests/auto/qml/qqmlecmascript/data/nonscriptable.qml new file mode 100644 index 0000000000..e96df6b40e --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/nonscriptable.qml @@ -0,0 +1,19 @@ +import Qt.test 1.0 +import QtQuick 2.0 + +MyQmlObject { + id: root + + property bool readOk: false; + property bool writeOk: false + + Component.onCompleted: { + readOk = (root.nonscriptable == undefined); + + try { + root.nonscriptable = 10 + } catch (e) { + writeOk = true; + } + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/nullObjectBinding.qml b/tests/auto/qml/qqmlecmascript/data/nullObjectBinding.qml new file mode 100644 index 0000000000..1aee7a1670 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/nullObjectBinding.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 + +QtObject { + property QtObject test + test: if (1) model + property ListModel model +} + diff --git a/tests/auto/qml/qqmlecmascript/data/numberAssignment.qml b/tests/auto/qml/qqmlecmascript/data/numberAssignment.qml new file mode 100644 index 0000000000..30a77e8aed --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/numberAssignment.qml @@ -0,0 +1,18 @@ +import Qt.test 1.0 + +NumberAssignment { + test1: if (1) 6.7 + test2: if (1) "6.7" + test3: if (1) 6 + test4: if (1) "6" + + test5: if (1) 6.7 + test6: if (1) "6.7" + test7: if (1) 6 + test8: if (1) "6" + + test9: if (1) 6.7 + test10: if (1) "6.7" + test11: if (1) 6 + test12: if (1) "6" +} diff --git a/tests/auto/qml/qqmlecmascript/data/objectConversion.qml b/tests/auto/qml/qqmlecmascript/data/objectConversion.qml new file mode 100644 index 0000000000..67fc342db3 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/objectConversion.qml @@ -0,0 +1,16 @@ +import QtQuick 2.0 + +Rectangle { + width: 360 + height: 360 + + function circularObject() { + var a = {} + var b = {} + + a.test = 100; + a.c = b; + b.c = a; + return a; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/objectName.qml b/tests/auto/qml/qqmlecmascript/data/objectName.qml new file mode 100644 index 0000000000..20b9ec2935 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/objectName.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 + +QtObject { + objectName: "hello" + + property string test1: objectName + property string test2: objectName.substr(1, 3) +} diff --git a/tests/auto/qml/qqmlecmascript/data/objectsCompareAsEqual.qml b/tests/auto/qml/qqmlecmascript/data/objectsCompareAsEqual.qml new file mode 100644 index 0000000000..845f74b1aa --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/objectsCompareAsEqual.qml @@ -0,0 +1,15 @@ +import QtQuick 2.0 + +Item { + id: root + + property variant item: child + Item { id: child } + + property bool test1: child == child + property bool test2: child.parent == root + property bool test3: root != child + property bool test4: item == child + property bool test5: item != root +} + diff --git a/tests/auto/qml/qqmlecmascript/data/objectsPassThroughSignals.qml b/tests/auto/qml/qqmlecmascript/data/objectsPassThroughSignals.qml new file mode 100644 index 0000000000..98f9e05bdf --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/objectsPassThroughSignals.qml @@ -0,0 +1,18 @@ +import QtQuick 2.0 + +QtObject { + id: root + + property bool test: false + + signal mysignal(variant object); + function myslot(object) + { + test = (object == root); + } + + Component.onCompleted: { + mysignal.connect(this, myslot); + mysignal(root); + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/outerBindingOverridesInnerBinding.qml b/tests/auto/qml/qqmlecmascript/data/outerBindingOverridesInnerBinding.qml new file mode 100644 index 0000000000..090c948f26 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/outerBindingOverridesInnerBinding.qml @@ -0,0 +1,15 @@ +import Qt.test 1.0 + +MyQmlObject { + id: obj + property alias c1: myConstants.c1 + property alias c2: myConstants.c2 + property int c3: 0 + + objectProperty: ConstantsOverrideBindings { + id: myConstants + c2: obj.c3 + } + +} + diff --git a/tests/auto/qml/qqmlecmascript/data/ownership.qml b/tests/auto/qml/qqmlecmascript/data/ownership.qml new file mode 100644 index 0000000000..855a264995 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/ownership.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +QtObject { + Component.onCompleted: { var a = getObject(); a = null; } +} diff --git a/tests/auto/qml/qqmlecmascript/data/propertyAssignmentErrors.qml b/tests/auto/qml/qqmlecmascript/data/propertyAssignmentErrors.qml new file mode 100644 index 0000000000..34523ec1c7 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/propertyAssignmentErrors.qml @@ -0,0 +1,29 @@ +import QtQuick 2.0 + +QtObject { + id: root + + property int a + property variant b + + property bool test1: false; + property bool test2: false; + + Component.onCompleted: { + try { + root.a = undefined; + } catch(e) { + if (e.message == "Cannot assign [undefined] to int" && + e.stack.indexOf("propertyAssignmentErrors.qml:14") != -1) + root.test1 = true; + } + + try { + root.a = "Hello"; + } catch(e) { + if (e.message == "Cannot assign QString to int" && + e.stack.indexOf("propertyAssignmentErrors.qml:22") != -1) + root.test2 = true; + } + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/propertySplicing.qml b/tests/auto/qml/qqmlecmascript/data/propertySplicing.qml new file mode 100644 index 0000000000..53711db3f4 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/propertySplicing.qml @@ -0,0 +1,10 @@ +import Qt.test 1.0 +import QtQuick 2.0 + +MyDerivedObject { + property bool test: false + + Component.onCompleted: { + test = intProperty() + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVar.1.qml b/tests/auto/qml/qqmlecmascript/data/propertyVar.1.qml new file mode 100644 index 0000000000..219e61bf91 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/propertyVar.1.qml @@ -0,0 +1,22 @@ +import QtQuick 2.0 + +Item { + id: root + property bool test: false + + property var car: new vehicle(4); + property int wheelCount: car.wheels + + function vehicle(wheels) { + this.wheels = wheels; + } + + Component.onCompleted: { + car.wheels = 6; // not bindable, wheelCount shouldn't update + + if (car.wheels != 6) return; + if (wheelCount != 4) return; + + test = true; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVar.10.qml b/tests/auto/qml/qqmlecmascript/data/propertyVar.10.qml new file mode 100644 index 0000000000..ac7f2bed57 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/propertyVar.10.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 + +PropertyVarBaseItem { + property bool test: false + Component.onCompleted: { + test = true; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVar.2.qml b/tests/auto/qml/qqmlecmascript/data/propertyVar.2.qml new file mode 100644 index 0000000000..2ac4807ec5 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/propertyVar.2.qml @@ -0,0 +1,24 @@ +import QtQuick 2.0 + +Item { + id: root + property bool test: false + + property var truck: new vehicle(8); + property int wheelCount: truck.wheels + + function vehicle(wheels) { + this.wheels = wheels; + } + + Component.onCompleted: { + if (wheelCount != 8) return; + + // not bindable, but wheelCount will update because truck itself changed. + truck = new vehicle(12); + + if (wheelCount != 12) return; + + test = true; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVar.3.qml b/tests/auto/qml/qqmlecmascript/data/propertyVar.3.qml new file mode 100644 index 0000000000..cf6a651639 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/propertyVar.3.qml @@ -0,0 +1,19 @@ +import QtQuick 2.0 + +Item { + id: root + property bool test: false + + property var jsint: 4 + property int bound: jsint + 5 + + Component.onCompleted: { + if (bound != 9) return; + + jsint = jsint + 1; + + if (bound != 10) return; + + test = true; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVar.4.qml b/tests/auto/qml/qqmlecmascript/data/propertyVar.4.qml new file mode 100644 index 0000000000..82fc225e71 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/propertyVar.4.qml @@ -0,0 +1,18 @@ +import QtQuick 2.0 + +Item { + property bool test: false + + property var items: [1, 2, 3, "four", "five"] + property int bound: items[0] + + Component.onCompleted: { + if (bound != 1) return; + + items[0] = 10 // bound should remain 1 + + if (bound != 1) return; + + test = true; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVar.5.qml b/tests/auto/qml/qqmlecmascript/data/propertyVar.5.qml new file mode 100644 index 0000000000..a5c7812289 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/propertyVar.5.qml @@ -0,0 +1,18 @@ +import QtQuick 2.0 + +Item { + property bool test: false + + property var attributes: { 'color': 'red', 'width': 100 } + property int bound: attributes.width + + Component.onCompleted: { + if (bound != 100) return; + + attributes.width = 200 // bound should remain 100 + + if (bound != 100) return; + + test = true; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVar.6.qml b/tests/auto/qml/qqmlecmascript/data/propertyVar.6.qml new file mode 100644 index 0000000000..060d24e7bc --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/propertyVar.6.qml @@ -0,0 +1,27 @@ +import QtQuick 2.0 + +Item { + property bool test: false + + property var items: [1, 2, 3, "four", "five"] + property int bound: items[0] + property var funcs: [(function() { return 6; })] + property int bound2: funcs[0]() + + function returnTwenty() { + return 20; + } + + Component.onCompleted: { + if (bound != 1) return false; + if (bound2 != 6) return false; + + items = [10, 2, 3, "four", "five"] // bound should now be 10 + funcs = [returnTwenty] // bound2 should now be 20 + + if (bound != 10) return false; + if (bound2 != 20) return false; + + test = true; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVar.7.qml b/tests/auto/qml/qqmlecmascript/data/propertyVar.7.qml new file mode 100644 index 0000000000..1d6c8c0a37 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/propertyVar.7.qml @@ -0,0 +1,18 @@ +import QtQuick 2.0 + +Item { + property bool test: false + + property var attributes: { 'color': 'red', 'width': 100 } + property int bound: attributes.width + + Component.onCompleted: { + if (bound != 100) return; + + attributes = { 'color': 'blue', 'width': 200 } // bound should now be 200 + + if (bound != 200) return; + + test = true; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVar.8.qml b/tests/auto/qml/qqmlecmascript/data/propertyVar.8.qml new file mode 100644 index 0000000000..a9f73db402 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/propertyVar.8.qml @@ -0,0 +1,12 @@ +import QtQuick 2.0 + +Item { + property bool test: false + + property var literalValue: 6 + + Component.onCompleted: { + if (literalValue != 6) return; + test = true; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVar.9.qml b/tests/auto/qml/qqmlecmascript/data/propertyVar.9.qml new file mode 100644 index 0000000000..f5aca28417 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/propertyVar.9.qml @@ -0,0 +1,19 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + property bool test: false + + MyQmlObject { + id: qmlobject + intProperty: 5 + } + property var qobjectVar: qmlobject + property int bound: qobjectVar.intProperty + + Component.onCompleted: { + if (bound != 5) return; + + test = true; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVar.circular.2.qml b/tests/auto/qml/qqmlecmascript/data/propertyVar.circular.2.qml new file mode 100644 index 0000000000..93c44afcc9 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/propertyVar.circular.2.qml @@ -0,0 +1,26 @@ +import QtQuick 2.0 + +Item { + id: root + objectName: "separateRootObject" + property var vp + + function constructGarbage() { + var retn = 1; + var component = Qt.createComponent("PropertyVarCircularComponent3.qml"); + if (component.status == Component.Ready) { + retn = component.createObject(null); // has JavaScript ownership + } + return retn; + } + + function assignCircular() { + vp = constructGarbage(); + gc(); + } + + function deassignCircular() { + vp = 2; + gc(); + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVar.circular.qml b/tests/auto/qml/qqmlecmascript/data/propertyVar.circular.qml new file mode 100644 index 0000000000..171d7747cd --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/propertyVar.circular.qml @@ -0,0 +1,44 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: testCircular + + property var varProperty + property variant canaryResource + property int canaryInt + + function constructGarbage() { + var retn = 1; + var component = Qt.createComponent("PropertyVarCircularComponent.qml"); + if (component.status == Component.Ready) { + retn = component.createObject(null); // has JavaScript ownership + } + return retn; + } + + function deassignCanaryResource() { + canaryResource = 1; + gc(); + } + + function assignCircular() { + varProperty = constructGarbage(); + canaryResource = varProperty.vp.vp.vp.vp.memoryHog; + canaryInt = varProperty.vp.vp.vp.vp.fifthCanary; // == 5 + gc(); + } + + function deassignCircular() { + canaryInt = 2; + varProperty = 2; + gc(); + } + + function assignThenDeassign() { + varProperty = constructGarbage(); + varProperty = 2; + gc(); + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVar.inherit.qml b/tests/auto/qml/qqmlecmascript/data/propertyVar.inherit.qml new file mode 100644 index 0000000000..abd0dd7c04 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/propertyVar.inherit.qml @@ -0,0 +1,34 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: testInheritance + + property var varProperty + + function constructGarbage() { + var retn = 1; + var component = Qt.createComponent("PropertyVarInheritanceComponent.qml"); + if (component.status == Component.Ready) { + retn = component.createObject(null); // has JavaScript ownership + } + return retn; + } + + function assignCircular() { + varProperty = constructGarbage(); + gc(); + } + + function deassignCircular() { + varProperty = 2; + gc(); + } + + function assignThenDeassign() { + varProperty = constructGarbage(); + varProperty = 2; + gc(); + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVar.reparent.qml b/tests/auto/qml/qqmlecmascript/data/propertyVar.reparent.qml new file mode 100644 index 0000000000..7b3df674f1 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/propertyVar.reparent.qml @@ -0,0 +1,27 @@ +import QtQuick 2.0 + +Item { + id: root + objectName: "separateRootObject" + property var vp + + function constructGarbage() { + var retn = 1; + var component = Qt.createComponent("PropertyVarOwnershipComponent.qml"); + if (component.status == Component.Ready) { + retn = component.createObject(null); // has JavaScript ownership + } + return retn; + } + + function assignVarProp() { + vp = constructGarbage(); + gc(); + } + + function deassignVarProp() { + vp = 2; + gc(); + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVarCpp.qml b/tests/auto/qml/qqmlecmascript/data/propertyVarCpp.qml new file mode 100644 index 0000000000..cd3147f565 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/propertyVarCpp.qml @@ -0,0 +1,17 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: testOwnership + property int intProperty: 10 + property var varProperty: intProperty + property var varProperty2: false + property var varBound: varProperty + 5 + property int intBound: varProperty + 5 + property var jsobject: new vehicle(4) + + function vehicle(wheels) { + this.wheels = wheels; + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVarImplicitOwnership.qml b/tests/auto/qml/qqmlecmascript/data/propertyVarImplicitOwnership.qml new file mode 100644 index 0000000000..9cebded932 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/propertyVarImplicitOwnership.qml @@ -0,0 +1,26 @@ +import QtQuick 2.0 + +Item { + id: root + objectName: "separateRootObject" + property var vp + + function constructGarbage() { + var retn = 1; + var component = Qt.createComponent("PropertyVarCircularComponent4.qml"); + if (component.status == Component.Ready) { + retn = component.createObject(null); // has JavaScript ownership + } + return retn; + } + + function assignCircular() { + vp = constructGarbage(); + gc(); + } + + function deassignCircular() { + vp = 2; + gc(); + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVarOwnership.2.qml b/tests/auto/qml/qqmlecmascript/data/propertyVarOwnership.2.qml new file mode 100644 index 0000000000..14d4f9fd27 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/propertyVarOwnership.2.qml @@ -0,0 +1,24 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: testOwnership + property bool test: false + + property int dummyProperty // Tests for non-interference of other properties + property var varProperty + + function runTest() { + if (varProperty != undefined) return; + varProperty = { a: 10, b: 11 } + if (varProperty.a != 10) return; + + gc(); // Shouldn't collect + + if (varProperty.a != 10) return; + + test = true; + } +} + + diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVarOwnership.3.qml b/tests/auto/qml/qqmlecmascript/data/propertyVarOwnership.3.qml new file mode 100644 index 0000000000..d5b449c938 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/propertyVarOwnership.3.qml @@ -0,0 +1,31 @@ +import QtQuick 2.0 + +Item { + property var object + + property bool test1: false + property bool test2: false + + // Test methods are executed in sequential order + + function runTest() { + var c = Qt.createComponent("propertyVarOwnership.3.type.qml"); + object = c.createObject(); + + if (object.dummy != 10) return; + test1 = true; + } + + // Run gc() from C++ + + function runTest2() { + if (object.dummy != 10) return; + + object = undefined; + if (object != undefined) return; + + test2 = true; + } + + // Run gc() from C++ - QObject should be collected +} diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVarOwnership.3.type.qml b/tests/auto/qml/qqmlecmascript/data/propertyVarOwnership.3.type.qml new file mode 100644 index 0000000000..3406553b91 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/propertyVarOwnership.3.type.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +QtObject { + property int dummy: 10 +} diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVarOwnership.4.qml b/tests/auto/qml/qqmlecmascript/data/propertyVarOwnership.4.qml new file mode 100644 index 0000000000..1eba36ce81 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/propertyVarOwnership.4.qml @@ -0,0 +1,25 @@ +import QtQuick 2.0 + +Item { + id: root + + property var object + + property bool test: false + + Component.onCompleted: { + var c = Qt.createComponent("propertyVarOwnership.4.type1.qml"); + object = c.createObject(); + + if (object.dummy != 10) return; + if (object.test != true) return; + + object.creatorRef = root; + + test = true; + } + + function runTest() { + object = null; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVarOwnership.4.type1.qml b/tests/auto/qml/qqmlecmascript/data/propertyVarOwnership.4.type1.qml new file mode 100644 index 0000000000..9a29b6e17f --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/propertyVarOwnership.4.type1.qml @@ -0,0 +1,23 @@ +import QtQuick 2.0 + +// Has a self reference in selfRef, and a reference to propertyVarOwnership.4.qml in creatorRef +Item { + id: root + + property var creatorRef + property var selfRef + property var object + + property int dummy: 10 + property bool test: false + + Component.onCompleted: { + selfRef = root; + + var c = Qt.createComponent("propertyVarOwnership.4.type2.qml"); + object = c.createObject(); + object.creatorRef = root; + + test = true; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVarOwnership.4.type2.qml b/tests/auto/qml/qqmlecmascript/data/propertyVarOwnership.4.type2.qml new file mode 100644 index 0000000000..f82b8a1c1e --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/propertyVarOwnership.4.type2.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 + +// Has a reference to propertyVarOwnership.4.type1.qml in creatorRef +Item { + property var creatorRef +} diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVarOwnership.qml b/tests/auto/qml/qqmlecmascript/data/propertyVarOwnership.qml new file mode 100644 index 0000000000..7b99c4b6ad --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/propertyVarOwnership.qml @@ -0,0 +1,22 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: testOwnership + property bool test: false + + property var varProperty + + function runTest() { + if (varProperty != undefined) return; + varProperty = { a: 10, b: 11 } + if (varProperty.a != 10) return; + + gc(); // Shouldn't collect + + if (varProperty.a != 10) return; + + test = true; + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/qlistqobjectMethods.qml b/tests/auto/qml/qqmlecmascript/data/qlistqobjectMethods.qml new file mode 100644 index 0000000000..3c1986d721 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/qlistqobjectMethods.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 + +QtObject { + property int test: getObjects().length + property bool test2: getObjects()[0].trueProperty +} diff --git a/tests/auto/qml/qqmlecmascript/data/qmlHasOwnProperty.qml b/tests/auto/qml/qqmlecmascript/data/qmlHasOwnProperty.qml new file mode 100644 index 0000000000..12598b3b9f --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/qmlHasOwnProperty.qml @@ -0,0 +1,72 @@ +import QtQuick 2.0 +import Qt.test 1.0 +import Qt.test.qobjectApi 1.0 as QtTestQObjectApi + +Item { + id: obj + objectName: "objName" + property int someIntProperty: 10 + property bool result: false + + function testHasOwnPropertySuccess() + { + obj.result = obj.hasOwnProperty("someIntProperty"); + } + + function testHasOwnPropertyFailure() + { + obj.result = obj.hasOwnProperty("someNonexistentProperty"); + } + + MyTypeObject { + id: typeObj + objectName: "typeObj" + pointProperty: Qt.point(34, 29) + variantProperty: Qt.vector3d(1, 2, 3) + stringProperty: "test string" + property list<Rectangle> listProperty: [ Rectangle { width: 10; height: 10 } ] + property list<Rectangle> emptyListProperty + + property bool valueTypeHasOwnProperty + property bool valueTypeHasOwnProperty2 + property bool variantTypeHasOwnProperty + property bool stringTypeHasOwnProperty + property bool listTypeHasOwnProperty + property bool listAtValidHasOwnProperty + property bool emptyListTypeHasOwnProperty + property bool enumTypeHasOwnProperty + property bool typenameHasOwnProperty + property bool typenameHasOwnProperty2 + property bool moduleApiTypeHasOwnProperty + property bool moduleApiPropertyTypeHasOwnProperty + function testHasOwnPropertySuccess() { + valueTypeHasOwnProperty = !typeObj.pointProperty.hasOwnProperty("nonexistentpropertyname"); + valueTypeHasOwnProperty2 = typeObj.pointProperty.hasOwnProperty("x"); // should be true + variantTypeHasOwnProperty = !typeObj.variantProperty.hasOwnProperty("nonexistentpropertyname"); + stringTypeHasOwnProperty = !typeObj.stringProperty.hasOwnProperty("nonexistentpropertyname"); + listTypeHasOwnProperty = !typeObj.listProperty.hasOwnProperty("nonexistentpropertyname"); + listAtValidHasOwnProperty = !typeObj.listProperty[0].hasOwnProperty("nonexistentpropertyname"); + emptyListTypeHasOwnProperty = !typeObj.emptyListProperty.hasOwnProperty("nonexistentpropertyname"); + enumTypeHasOwnProperty = !MyTypeObject.EnumVal1.hasOwnProperty("nonexistentpropertyname"); + typenameHasOwnProperty = !MyTypeObject.hasOwnProperty("nonexistentpropertyname"); + typenameHasOwnProperty2 = MyTypeObject.hasOwnProperty("EnumVal1"); // should be true. + moduleApiTypeHasOwnProperty = !QtTestQObjectApi.hasOwnProperty("nonexistentpropertyname"); + moduleApiPropertyTypeHasOwnProperty = !QtTestQObjectApi.qobjectTestProperty.hasOwnProperty("nonexistentpropertyname"); + } + + property bool enumNonValueHasOwnProperty + function testHasOwnPropertyFailureOne() { + enumNonValueHasOwnProperty = !MyTypeObject.NonexistentEnumVal.hasOwnProperty("nonexistentpropertyname"); + } + + property bool moduleApiNonPropertyHasOwnProperty + function testHasOwnPropertyFailureTwo() { + moduleApiNonPropertyHasOwnProperty = !QtTestQObjectApi.someNonexistentProperty.hasOwnProperty("nonexistentpropertyname"); + } + + property bool listAtInvalidHasOwnProperty + function testHasOwnPropertyFailureThree() { + listAtInvalidHasOwnProperty = !typeObj.listProperty[5].hasOwnProperty("nonexistentpropertyname"); + } + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/qmlToString.qml b/tests/auto/qml/qqmlecmascript/data/qmlToString.qml new file mode 100644 index 0000000000..ac296ce293 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/qmlToString.qml @@ -0,0 +1,11 @@ +import Qt.test 1.0 + +MyQmlObject{ + id: obj + objectName: "objName" + function testToString() + { + obj.stringProperty = obj.toString(); + } + +} diff --git a/tests/auto/qml/qqmlecmascript/data/qobjectConnectionListExceptionHandling.qml b/tests/auto/qml/qqmlecmascript/data/qobjectConnectionListExceptionHandling.qml new file mode 100644 index 0000000000..acd512a2be --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/qobjectConnectionListExceptionHandling.qml @@ -0,0 +1,24 @@ +import QtQuick 2.0 + +Item { + id: root + property int first: 5 + property bool test: false + + Item { + id: exceptional + function exceptionalFunction() { + var obj = undefined; + var prop = undefined; + return obj[prop]; + } + } + + Component.onCompleted: { + root["firstChanged"].connect(exceptional.exceptionalFunction); + root["firstChanged"].connect(exceptional.exceptionalFunction); + root["firstChanged"].connect(exceptional.exceptionalFunction); + first = 6; + test = true; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/qobjectDerivedArgument.qml b/tests/auto/qml/qqmlecmascript/data/qobjectDerivedArgument.qml new file mode 100644 index 0000000000..bf4ab6fd7a --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/qobjectDerivedArgument.qml @@ -0,0 +1,17 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +MyQmlObject { + id: root + stringProperty: 'hello' + property var child + + property bool result: false + + Component.onCompleted: { + child = invokable.createMyQmlObject('goodbye'); + + result = (invokable.getStringProperty(root) == 'hello') && + (invokable.getStringProperty(child) == 'goodbye'); + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/qtbug_10696.qml b/tests/auto/qml/qqmlecmascript/data/qtbug_10696.qml new file mode 100644 index 0000000000..90263e5124 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/qtbug_10696.qml @@ -0,0 +1,26 @@ +import QtQuick 2.0 + +QtObject { + property string test: "aaaa" + + "bbbb" + + "cccc" + + "cccc" + + "cccc" + + "cccc" + + "cccc" + + "cccc" + + "cccc" + + "cccc" + + "cccc" + + "cccc" + + "cccc" + + "cccc" + + "cccc" + + "cccc" + + "cccc" + + "cccc" + + "cccc" + + "cccc" + + "cccc" + + "cccc"; +} diff --git a/tests/auto/qml/qqmlecmascript/data/qtbug_11600.js b/tests/auto/qml/qqmlecmascript/data/qtbug_11600.js new file mode 100644 index 0000000000..092bc2b041 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/qtbug_11600.js @@ -0,0 +1 @@ +; diff --git a/tests/auto/qml/qqmlecmascript/data/qtbug_11600.qml b/tests/auto/qml/qqmlecmascript/data/qtbug_11600.qml new file mode 100644 index 0000000000..6c7e8806e6 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/qtbug_11600.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import "qtbug_11600.js" as Test + +QtObject { + id: goo + + property bool test: undefined == goo.Test +} diff --git a/tests/auto/qml/qqmlecmascript/data/qtbug_11606.qml b/tests/auto/qml/qqmlecmascript/data/qtbug_11606.qml new file mode 100644 index 0000000000..b1b062ed35 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/qtbug_11606.qml @@ -0,0 +1,12 @@ +import QtQuick 2.0 + +QtObject { + property bool test: false + Component.onCompleted: { + try { + console.log(sorryNoSuchProperty); + } catch (e) { + test = true; + } + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/qtbug_20344.qml b/tests/auto/qml/qqmlecmascript/data/qtbug_20344.qml new file mode 100644 index 0000000000..f490848caf --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/qtbug_20344.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +MyQmlObject { + Component.onCompleted: v8function() +} diff --git a/tests/auto/qml/qqmlecmascript/data/qtbug_21580.qml b/tests/auto/qml/qqmlecmascript/data/qtbug_21580.qml new file mode 100644 index 0000000000..dc0066ba3f --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/qtbug_21580.qml @@ -0,0 +1,22 @@ +import QtQuick 2.0 + +QtObject { + property bool test: false + + property list<QtObject> objects: [ + QtObject { + id: first + property alias myAlias: other.myProperty + onMyAliasChanged: if (myAlias == 20) test = true + }, + QtObject { + id: other + property real myProperty + } + ] + + Component.onCompleted: { + other.myProperty = 20; + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/qtbug_21864.js b/tests/auto/qml/qqmlecmascript/data/qtbug_21864.js new file mode 100644 index 0000000000..e1a688ebbe --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/qtbug_21864.js @@ -0,0 +1,2 @@ +var a = { b: 10 } +var test = (typeof a) == "object" diff --git a/tests/auto/qml/qqmlecmascript/data/qtbug_21864.qml b/tests/auto/qml/qqmlecmascript/data/qtbug_21864.qml new file mode 100644 index 0000000000..0f972d5459 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/qtbug_21864.qml @@ -0,0 +1,6 @@ +import "qtbug_21864.js" as Test +import QtQuick 2.0 + +QtObject { + property bool test: Test.test +} diff --git a/tests/auto/qml/qqmlecmascript/data/qtbug_22464.qml b/tests/auto/qml/qqmlecmascript/data/qtbug_22464.qml new file mode 100644 index 0000000000..19f26736f1 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/qtbug_22464.qml @@ -0,0 +1,15 @@ +import QtQuick 2.0 + +QtObject { + property alias value: inner.value + property bool test: false + + property variant dummy: QtObject { + id: inner + property variant value: Qt.rgba(1, 1, 0, 1); + } + + Component.onCompleted: { + test = (value == Qt.rgba(1, 1, 0, 1)); + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/qtbug_22679.qml b/tests/auto/qml/qqmlecmascript/data/qtbug_22679.qml new file mode 100644 index 0000000000..b38a84b4c0 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/qtbug_22679.qml @@ -0,0 +1,14 @@ +import QtQuick 2.0 + +QtObject { + function accessContextProperty() { + for (var i = 0; i < contextProp.stringProperty.length; ++i) ; + } + + Component.onCompleted: { + for (var i = 0; i < 1000; ++i) + accessContextProperty(); + // Shouldn't cause "Illegal invocation" error. + gc(); + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/qtbug_22843.js b/tests/auto/qml/qqmlecmascript/data/qtbug_22843.js new file mode 100644 index 0000000000..6d19fe0571 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/qtbug_22843.js @@ -0,0 +1,5 @@ + +function func() +{ + isFinite() ) +} diff --git a/tests/auto/qml/qqmlecmascript/data/qtbug_22843.library.js b/tests/auto/qml/qqmlecmascript/data/qtbug_22843.library.js new file mode 100644 index 0000000000..1a7c8a2e6e --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/qtbug_22843.library.js @@ -0,0 +1,5 @@ +.pragma library +function func() +{ + isFinite() ) +} diff --git a/tests/auto/qml/qqmlecmascript/data/qtbug_22843.library.qml b/tests/auto/qml/qqmlecmascript/data/qtbug_22843.library.qml new file mode 100644 index 0000000000..281765bff6 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/qtbug_22843.library.qml @@ -0,0 +1,6 @@ +import "qtbug_22843.library.js" as MyScript +import QtQuick 2.0 + +QtObject { + Component.onCompleted: MyScript.func() +} diff --git a/tests/auto/qml/qqmlecmascript/data/qtbug_22843.qml b/tests/auto/qml/qqmlecmascript/data/qtbug_22843.qml new file mode 100644 index 0000000000..90a47c0f4b --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/qtbug_22843.qml @@ -0,0 +1,6 @@ +import "qtbug_22843.js" as MyScript +import QtQuick 2.0 + +QtObject { + Component.onCompleted: MyScript.func() +} diff --git a/tests/auto/qml/qqmlecmascript/data/qtbug_9792.qml b/tests/auto/qml/qqmlecmascript/data/qtbug_9792.qml new file mode 100644 index 0000000000..9ac44308c6 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/qtbug_9792.qml @@ -0,0 +1,5 @@ +import Qt.test 1.0 + +MyQmlObject { + onBasicSignal: print("Hello world!"); +} diff --git a/tests/auto/qml/qqmlecmascript/data/qtcreatorbug_1289.qml b/tests/auto/qml/qqmlecmascript/data/qtcreatorbug_1289.qml new file mode 100644 index 0000000000..90711c8d09 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/qtcreatorbug_1289.qml @@ -0,0 +1,13 @@ +import QtQuick 2.0 + +QtObject { + id: root + property QtObject object: QtObject { + id: nested + property QtObject nestedObject + } + + Component.onCompleted: { + nested.nestedObject = root; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/readonlyDeclaration.qml b/tests/auto/qml/qqmlecmascript/data/readonlyDeclaration.qml new file mode 100644 index 0000000000..5377d2dcbf --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/readonlyDeclaration.qml @@ -0,0 +1,45 @@ +import QtQuick 2.0 + +QtObject { + property int dummy: 13 + + readonly property int test1: 19 + readonly property int test2: dummy * 49 + readonly property alias test3: other.test + + property bool test: false + + property var dummyObj: QtObject { + id: other + property int test: 9 + } + + Component.onCompleted: { + if (test1 != 19) return; + if (test2 != 637) return; + if (test3 != 9) return; + + var caught = false; + + caught = false; + try { test1 = 13 } catch (e) { caught = true; } + if (!caught) return; + + caught = false; + try { test2 = 13 } catch (e) { caught = true; } + if (!caught) return; + + caught = false; + try { test3 = 13 } catch (e) { caught = true; } + if (!caught) return; + + other.test = 13; + dummy = 9; + + if (test1 != 19) return; + if (test2 != 441) return; + if (test3 != 13) return; + + test = true; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/realToInt.qml b/tests/auto/qml/qqmlecmascript/data/realToInt.qml new file mode 100644 index 0000000000..a9e7dd2a95 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/realToInt.qml @@ -0,0 +1,11 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +MyQmlObject { + function test1() { + value = 4.2 + } + function test2() { + value = 7.9 + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/regExp.2.qml b/tests/auto/qml/qqmlecmascript/data/regExp.2.qml new file mode 100644 index 0000000000..68cca5733b --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/regExp.2.qml @@ -0,0 +1,7 @@ +import Qt.test 1.0 + +MyQmlObject{ + id: obj + objectName: "obj" + regExp: "[a-zA-z]" +} diff --git a/tests/auto/qml/qqmlecmascript/data/regExp.qml b/tests/auto/qml/qqmlecmascript/data/regExp.qml new file mode 100644 index 0000000000..0dc404b5db --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/regExp.qml @@ -0,0 +1,7 @@ +import Qt.test 1.0 + +MyQmlObject{ + id: obj + objectName: "obj" + regExp: /[a-zA-z]/ +} diff --git a/tests/auto/qml/qqmlecmascript/data/remote_file.js b/tests/auto/qml/qqmlecmascript/data/remote_file.js new file mode 100644 index 0000000000..1b123aee61 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/remote_file.js @@ -0,0 +1,2 @@ +myvar = 13; +test5 = true; diff --git a/tests/auto/qml/qqmlecmascript/data/rewriteMultiLineStrings.qml b/tests/auto/qml/qqmlecmascript/data/rewriteMultiLineStrings.qml new file mode 100644 index 0000000000..1ae1b162b2 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/rewriteMultiLineStrings.qml @@ -0,0 +1,35 @@ +import QtQuick 2.0 + +Item { + id: root; + property bool test: str == str2 && (txt != null && txt.str == root.str) + property Text txt: null + //Constant doesn't hit rewriter + property string str: 'same +multiline +string 5 !' + property string str2: ''; + Component { + id: comp + Text { + property var value: 1 + property string str: 'same +multiline +string ' + value + " !" + Component.onCompleted: { //Separate codepath for signal handers in rewriter + root.str2 = 'same +multiline +string ' + value + " !" + } + } + } + Component.onCompleted: txt = comp.createObject(root,{"value" : 5}) + /* + Timer { + interval: 1000 + running: true + repeat: true + onTriggered: console.debug( "Test: " + test + '\n' + str + '\n:\n' + str2 + "\n:\n" + txt.str) + } + */ +} diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceCopy.var.qml b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopy.var.qml new file mode 100644 index 0000000000..805655fc17 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopy.var.qml @@ -0,0 +1,14 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +// Here we import a scarce resource directly. +// The instance has a property which is a copy +// of the scarce resource, so it should not be +// detached (but we should automatically release +// the resource from our engine internal list). + +QtObject { + property MyScarceResourceObject a; + a: MyScarceResourceObject { id: scarceResourceProvider } + property var scarceResourceCopy: scarceResourceProvider.scarceResource +}
\ No newline at end of file diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceCopy.variant.qml b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopy.variant.qml new file mode 100644 index 0000000000..ee5b05b28a --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopy.variant.qml @@ -0,0 +1,15 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +// Here we import a scarce resource directly. +// The instance has a property which is a copy +// of the scarce resource, so it should not be +// detached (but we should automatically release +// the resource from our engine internal list). + +QtObject { + property MyScarceResourceObject a; + a: MyScarceResourceObject { id: scarceResourceProvider } + property variant scarceResourceCopy: scarceResourceProvider.scarceResource +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyFromJs.var.qml b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyFromJs.var.qml new file mode 100644 index 0000000000..09868e5e7c --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyFromJs.var.qml @@ -0,0 +1,14 @@ +import QtQuick 2.0 +import Qt.test 1.0 +import "scarceResourceTest.var.js" as ScarceResourceProviderJs + +// Here we import a scarce resource directly, from JS module. +// It is not preserved or released manually, so it should be +// automatically released once evaluation of the binding +// is complete. + +QtObject { + property MyScarceResourceObject a; + a: MyScarceResourceObject { id: scarceResourceProvider } + property var scarceResourceCopy: ScarceResourceProviderJs.importScarceResource(scarceResourceProvider) +}
\ No newline at end of file diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyFromJs.variant.qml b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyFromJs.variant.qml new file mode 100644 index 0000000000..a1ebeb4073 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyFromJs.variant.qml @@ -0,0 +1,15 @@ +import QtQuick 2.0 +import Qt.test 1.0 +import "scarceResourceTest.variant.js" as ScarceResourceProviderJs + +// Here we import a scarce resource directly, from JS module. +// It is not preserved or released manually, so it should be +// automatically released once evaluation of the binding +// is complete. + +QtObject { + property MyScarceResourceObject a; + a: MyScarceResourceObject { id: scarceResourceProvider } + property variant scarceResourceCopy: ScarceResourceProviderJs.importScarceResource(scarceResourceProvider) +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImport.var.js b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImport.var.js new file mode 100644 index 0000000000..468a6b4f2e --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImport.var.js @@ -0,0 +1,25 @@ +.import Qt.test 1.0 as JsQtTest + +// In this case, the "retn" variable will be evaluated during import. +// Since the "importScarceResource()" function depends on this variable, +// we must explicitly preserve the "retn" variable or the scarce +// resource would automatically be released after import completes +// but before the binding is evaluated. + +var component = Qt.createComponent("scarceResourceCopy.var.qml"); +var scarceResourceElement = component.createObject(null); +var scarceResourceProvider = scarceResourceElement.a; +var retn = scarceResourceProvider.scarceResource; +retn.preserve(); // must preserve manually or it will be released! + +function importScarceResource() { + // if called prior to calling destroyScarceResource(), + // this function should return the preserved scarce resource. + // otherwise, it should return an invalid variant. + return retn; +} + +function destroyScarceResource() { + retn.destroy(); +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImport.var.qml b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImport.var.qml new file mode 100644 index 0000000000..9321481f45 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImport.var.qml @@ -0,0 +1,18 @@ +import QtQuick 2.0 +import Qt.test 1.0 +import "scarceResourceCopyImport.var.js" as ScarceResourceCopyImportJs + +QtObject { + // this binding is evaluated once, prior to the resource being released + property var scarceResourceImportedCopy: ScarceResourceCopyImportJs.importScarceResource() + + property bool arePropertiesEqual + property var scarceResourceAssignedCopyOne; + property var scarceResourceAssignedCopyTwo; + Component.onCompleted: { + scarceResourceAssignedCopyOne = ScarceResourceCopyImportJs.importScarceResource(); + arePropertiesEqual = (scarceResourceAssignedCopyOne == scarceResourceImportedCopy); + ScarceResourceCopyImportJs.destroyScarceResource(); // makes all properties invalid. + scarceResourceAssignedCopyTwo = ScarceResourceCopyImportJs.importScarceResource(); + } +}
\ No newline at end of file diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImport.variant.js b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImport.variant.js new file mode 100644 index 0000000000..9aeb507487 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImport.variant.js @@ -0,0 +1,25 @@ +.import Qt.test 1.0 as JsQtTest + +// In this case, the "retn" variable will be evaluated during import. +// Since the "importScarceResource()" function depends on this variable, +// we must explicitly preserve the "retn" variable or the scarce +// resource would automatically be released after import completes +// but before the binding is evaluated. + +var component = Qt.createComponent("scarceResourceCopy.variant.qml"); +var scarceResourceElement = component.createObject(null); +var scarceResourceProvider = scarceResourceElement.a; +var retn = scarceResourceProvider.scarceResource; +retn.preserve(); // must preserve manually or it will be released! + +function importScarceResource() { + // if called prior to calling destroyScarceResource(), + // this function should return the preserved scarce resource. + // otherwise, it should return an invalid variant. + return retn; +} + +function destroyScarceResource() { + retn.destroy(); +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImport.variant.qml b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImport.variant.qml new file mode 100644 index 0000000000..e8b53979dd --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImport.variant.qml @@ -0,0 +1,18 @@ +import QtQuick 2.0 +import Qt.test 1.0 +import "scarceResourceCopyImport.variant.js" as ScarceResourceCopyImportJs + +QtObject { + // this binding is evaluated once, prior to the resource being released + property variant scarceResourceImportedCopy: ScarceResourceCopyImportJs.importScarceResource() + + // this code is evaluated on completion, and so copy one should be valid, copy two invalid. + property variant scarceResourceAssignedCopyOne; + property variant scarceResourceAssignedCopyTwo; + Component.onCompleted: { + scarceResourceAssignedCopyOne = ScarceResourceCopyImportJs.importScarceResource(); + ScarceResourceCopyImportJs.destroyScarceResource(); + scarceResourceAssignedCopyTwo = ScarceResourceCopyImportJs.importScarceResource(); + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImportDifferent.var.js b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImportDifferent.var.js new file mode 100644 index 0000000000..000eeddb34 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImportDifferent.var.js @@ -0,0 +1,19 @@ +.import Qt.test 1.0 as JsQtTest + +// In this case, we create the returned scarce resource each call, +// so the object will be different every time it is returned. + +var mostRecent + +function importScarceResource() { + var component = Qt.createComponent("scarceResourceCopy.var.qml"); + var scarceResourceElement = component.createObject(null); + var scarceResourceProvider = scarceResourceElement.a; + var retn = scarceResourceProvider.scarceResource; + mostRecent = retn; + return retn; +} + +function destroyScarceResource() { + mostRecent.destroy(); +}
\ No newline at end of file diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImportDifferent.var.qml b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImportDifferent.var.qml new file mode 100644 index 0000000000..082d132c24 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImportDifferent.var.qml @@ -0,0 +1,22 @@ +import QtQuick 2.0 +import Qt.test 1.0 +import "scarceResourceCopyImportDifferent.var.js" as ScarceResourceCopyImportJs + +// in this case, the ScarceResourceCopyImportJs returns a _new_, different +// scarce resource each time. Invalidating one will not invalidate the others. + +QtObject { + // this binding is evaluated once, prior to the resource being released + property var scarceResourceImportedCopy: ScarceResourceCopyImportJs.importScarceResource() + + // the following properties are assigned on component completion. + property bool arePropertiesEqual + property var scarceResourceAssignedCopyOne; + property var scarceResourceAssignedCopyTwo; + Component.onCompleted: { + scarceResourceAssignedCopyOne = ScarceResourceCopyImportJs.importScarceResource(); + arePropertiesEqual = (scarceResourceAssignedCopyOne != scarceResourceImportedCopy); // they're not the same object. + ScarceResourceCopyImportJs.destroyScarceResource(); // makes the MOST RECENT resource invalid (ie, assignedCopyOne). + scarceResourceAssignedCopyTwo = ScarceResourceCopyImportJs.importScarceResource(); + } +}
\ No newline at end of file diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImportFail.var.js b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImportFail.var.js new file mode 100644 index 0000000000..ba52b323f0 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImportFail.var.js @@ -0,0 +1,19 @@ +.import Qt.test 1.0 as JsQtTest + +// In this case, the "retn" variable will be evaluated during import. +// Since the importScarceResource() function depends on this variable, +// because we DO NOT call "retn.preserve()", the scarce resource will +// be released after the import completes but prior to evaluation of +// any binding which calls "importScarceResource()". +// Thus, "importScarceResource()" will return a released (invalid) +// scarce resource. + +var component = Qt.createComponent("scarceResourceCopy.var.qml"); +var scarceResourceElement = component.createObject(null); +var scarceResourceProvider = scarceResourceElement.a; +var retn = scarceResourceProvider.scarceResource; + +function importScarceResource() { + return retn; // should return a released (invalid) scarce resource +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImportFail.var.qml b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImportFail.var.qml new file mode 100644 index 0000000000..a1a3c1d66f --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImportFail.var.qml @@ -0,0 +1,7 @@ +import QtQuick 2.0 +import Qt.test 1.0 +import "scarceResourceCopyImportFail.var.js" as ScarceResourceCopyImportFailJs + +QtObject { + property var scarceResourceCopy: ScarceResourceCopyImportFailJs.importScarceResource() +}
\ No newline at end of file diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImportFail.variant.js b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImportFail.variant.js new file mode 100644 index 0000000000..b59b5b1fa9 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImportFail.variant.js @@ -0,0 +1,19 @@ +.import Qt.test 1.0 as JsQtTest + +// In this case, the "retn" variable will be evaluated during import. +// Since the importScarceResource() function depends on this variable, +// because we DO NOT call "retn.preserve()", the scarce resource will +// be released after the import completes but prior to evaluation of +// any binding which calls "importScarceResource()". +// Thus, "importScarceResource()" will return a released (invalid) +// scarce resource. + +var component = Qt.createComponent("scarceResourceCopy.variant.qml"); +var scarceResourceElement = component.createObject(null); +var scarceResourceProvider = scarceResourceElement.a; +var retn = scarceResourceProvider.scarceResource; + +function importScarceResource() { + return retn; // should return a released (invalid) scarce resource +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImportFail.variant.qml b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImportFail.variant.qml new file mode 100644 index 0000000000..8f6dcd6603 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImportFail.variant.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 +import Qt.test 1.0 +import "scarceResourceCopyImportFail.variant.js" as ScarceResourceCopyImportFailJs + +QtObject { + property variant scarceResourceCopy: ScarceResourceCopyImportFailJs.importScarceResource() +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImportNoBinding.var.js b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImportNoBinding.var.js new file mode 100644 index 0000000000..130199f78a --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImportNoBinding.var.js @@ -0,0 +1,15 @@ +.import Qt.test 1.0 as JsQtTest + +// In this case, the "retn" variable will be evaluated during import. +// Since the importScarceResource() function depends on this variable, +// because we DO NOT call "retn.preserve()", the scarce resource will +// be released after the import completes but prior to evaluation of +// any binding which calls "importScarceResource()". +// Thus, "importScarceResource()" will return a released (invalid) +// scarce resource. + +var component = Qt.createComponent("scarceResourceCopyNoBinding.var.qml"); +var scarceResourceElement = component.createObject(null); +var scarceResourceProvider = scarceResourceElement.a; +var retn = scarceResourceProvider.scarceResource; + diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImportNoBinding.var.qml b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImportNoBinding.var.qml new file mode 100644 index 0000000000..5284b40cc8 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImportNoBinding.var.qml @@ -0,0 +1,12 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +// the following js import doesn't manually preserve or destroy any resources +import "scarceResourceCopyImportNoBinding.var.js" as ScarceResourceCopyImportNoBindingJs + +QtObject { + // in this case, there is an import but no binding evaluated. + // nonetheless, any resources which are not preserved, should + // be automatically released by the engine. +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImportNoBinding.variant.js b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImportNoBinding.variant.js new file mode 100644 index 0000000000..14a36a19ea --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImportNoBinding.variant.js @@ -0,0 +1,15 @@ +.import Qt.test 1.0 as JsQtTest + +// In this case, the "retn" variable will be evaluated during import. +// Since the importScarceResource() function depends on this variable, +// because we DO NOT call "retn.preserve()", the scarce resource will +// be released after the import completes but prior to evaluation of +// any binding which calls "importScarceResource()". +// Thus, "importScarceResource()" will return a released (invalid) +// scarce resource. + +var component = Qt.createComponent("scarceResourceCopyNoBinding.variant.qml"); +var scarceResourceElement = component.createObject(null); +var scarceResourceProvider = scarceResourceElement.a; +var retn = scarceResourceProvider.scarceResource; + diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImportNoBinding.variant.qml b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImportNoBinding.variant.qml new file mode 100644 index 0000000000..826cbe49fc --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyImportNoBinding.variant.qml @@ -0,0 +1,12 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +// the following js import doesn't manually preserve or destroy any resources +import "scarceResourceCopyImportNoBinding.variant.js" as ScarceResourceCopyImportNoBindingJs + +QtObject { + // in this case, there is an import but no binding evaluated. + // nonetheless, any resources which are not preserved, should + // be automatically released by the engine. +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyNoBinding.var.qml b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyNoBinding.var.qml new file mode 100644 index 0000000000..4adef39980 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyNoBinding.var.qml @@ -0,0 +1,14 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +QtObject { + // this component doesn't bind any property to a scarce + // resource from the scarce resource provider, + // so the binding evaluation resource cleanup + // codepath shouldn't be activated; so if the resources + // are released, it will be due to the import evaluation + // resource cleanup codepath being activated correctly. + property MyScarceResourceObject a; + a: MyScarceResourceObject { id: scarceResourceProvider } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyNoBinding.variant.qml b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyNoBinding.variant.qml new file mode 100644 index 0000000000..4adef39980 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceCopyNoBinding.variant.qml @@ -0,0 +1,14 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +QtObject { + // this component doesn't bind any property to a scarce + // resource from the scarce resource provider, + // so the binding evaluation resource cleanup + // codepath shouldn't be activated; so if the resources + // are released, it will be due to the import evaluation + // resource cleanup codepath being activated correctly. + property MyScarceResourceObject a; + a: MyScarceResourceObject { id: scarceResourceProvider } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceDestroyedCopy.var.qml b/tests/auto/qml/qqmlecmascript/data/scarceResourceDestroyedCopy.var.qml new file mode 100644 index 0000000000..500f5d5bd7 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceDestroyedCopy.var.qml @@ -0,0 +1,13 @@ +import QtQuick 2.0 +import Qt.test 1.0 +import "scarceResourceTest.var.js" as ScarceResourceProviderJs + +// In this case, following the evaluation of the binding, +// the scarceResourceTest value should be an invalid variant, +// since the scarce resource will have been released. + +QtObject { + property MyScarceResourceObject a; + a: MyScarceResourceObject { id: scarceResourceProvider } + property var scarceResourceCopy: ScarceResourceProviderJs.importReleasedScarceResource(scarceResourceProvider); +}
\ No newline at end of file diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceDestroyedCopy.variant.qml b/tests/auto/qml/qqmlecmascript/data/scarceResourceDestroyedCopy.variant.qml new file mode 100644 index 0000000000..7a3b845247 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceDestroyedCopy.variant.qml @@ -0,0 +1,14 @@ +import QtQuick 2.0 +import Qt.test 1.0 +import "scarceResourceTest.variant.js" as ScarceResourceProviderJs + +// In this case, following the evaluation of the binding, +// the scarceResourceTest value should be an invalid variant, +// since the scarce resource will have been released. + +QtObject { + property MyScarceResourceObject a; + a: MyScarceResourceObject { id: scarceResourceProvider } + property variant scarceResourceCopy: ScarceResourceProviderJs.importReleasedScarceResource(scarceResourceProvider); +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceFunction.var.qml b/tests/auto/qml/qqmlecmascript/data/scarceResourceFunction.var.qml new file mode 100644 index 0000000000..23e4c8d15e --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceFunction.var.qml @@ -0,0 +1,23 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +// Here we import a scarce resource directly. +// The copy is only assigned when retrieveScarceResource() +// is called, and so should be detached prior to that. +// The copy should be released when releaseScarceResource() +// is called, and so should be detached after that. + +QtObject { + id: root + property MyScarceResourceObject a: MyScarceResourceObject { id: scarceResourceProvider } + property var scarceResourceCopy; + + function retrieveScarceResource() { + root.scarceResourceCopy = scarceResourceProvider.scarceResource; + } + + function releaseScarceResource() { + root.scarceResourceCopy = null; + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceFunction.variant.qml b/tests/auto/qml/qqmlecmascript/data/scarceResourceFunction.variant.qml new file mode 100644 index 0000000000..fe3707b5d3 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceFunction.variant.qml @@ -0,0 +1,23 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +// Here we import a scarce resource directly. +// The copy is only assigned when retrieveScarceResource() +// is called, and so should be detached prior to that. +// The copy should be released when releaseScarceResource() +// is called, and so should be detached after that. + +QtObject { + id: root + property MyScarceResourceObject a: MyScarceResourceObject { id: scarceResourceProvider } + property variant scarceResourceCopy; + + function retrieveScarceResource() { + root.scarceResourceCopy = scarceResourceProvider.scarceResource; + } + + function releaseScarceResource() { + root.scarceResourceCopy = null; + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceFunctionFail.var.qml b/tests/auto/qml/qqmlecmascript/data/scarceResourceFunctionFail.var.qml new file mode 100644 index 0000000000..9b4b1e6fd9 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceFunctionFail.var.qml @@ -0,0 +1,23 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +// In this example, a common syntax error will only be "caught" +// when the function is called via: +// QQmlVMEMetaObject::metaCall->invokeMetaMethod() +// We would like to ensure that a useful error message is printed. + +QtObject { + id: root + property MyScarceResourceObject a: MyScarceResourceObject { id: scarceResourceProvider } + property var scarceResourceCopy; + property string srp_name: a.toString(); + + function retrieveScarceResource() { + root.scarceResourceCopy = scarceResourceProvider.scarceResource(); // common syntax error, should throw exception + } + + function releaseScarceResource() { + root.scarceResourceCopy = null; + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceFunctionFail.variant.qml b/tests/auto/qml/qqmlecmascript/data/scarceResourceFunctionFail.variant.qml new file mode 100644 index 0000000000..57673de3f3 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceFunctionFail.variant.qml @@ -0,0 +1,23 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +// In this example, a common syntax error will only be "caught" +// when the function is called via: +// QQmlVMEMetaObject::metaCall->invokeMetaMethod() +// We would like to ensure that a useful error message is printed. + +QtObject { + id: root + property MyScarceResourceObject a: MyScarceResourceObject { id: scarceResourceProvider } + property variant scarceResourceCopy; + property string srp_name: a.toString(); + + function retrieveScarceResource() { + root.scarceResourceCopy = scarceResourceProvider.scarceResource(); // common syntax error, should throw exception + } + + function releaseScarceResource() { + root.scarceResourceCopy = null; + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceMultipleDifferentNoBinding.var.js b/tests/auto/qml/qqmlecmascript/data/scarceResourceMultipleDifferentNoBinding.var.js new file mode 100644 index 0000000000..217f693456 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceMultipleDifferentNoBinding.var.js @@ -0,0 +1,14 @@ +.import Qt.test 1.0 as JsQtTest + +function importScarceResource() { + var component = Qt.createComponent("scarceResourceCopy.var.qml"); + var scarceResourceElement = component.createObject(null); + var scarceResourceProvider = scarceResourceElement.a; + var retn = scarceResourceProvider.scarceResource; + retn.preserve(); + return retn; +} + +function releaseScarceResource(resource) { + resource.destroy(); +}
\ No newline at end of file diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceMultipleDifferentNoBinding.var.qml b/tests/auto/qml/qqmlecmascript/data/scarceResourceMultipleDifferentNoBinding.var.qml new file mode 100644 index 0000000000..205131661f --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceMultipleDifferentNoBinding.var.qml @@ -0,0 +1,14 @@ +import QtQuick 2.0 +import Qt.test 1.0 +import "scarceResourceMultipleDifferentNoBinding.var.js" as ScarceResourcesMultipleDifferentNoBinding + +QtObject { + property var resourceOne + property var resourceTwo + + Component.onCompleted: { + resourceOne = ScarceResourcesMultipleDifferentNoBinding.importScarceResource(); + resourceTwo = ScarceResourcesMultipleDifferentNoBinding.importScarceResource(); + ScarceResourcesMultipleDifferentNoBinding.releaseScarceResource(resourceTwo); + } +}
\ No newline at end of file diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceMultipleSameNoBinding.var.js b/tests/auto/qml/qqmlecmascript/data/scarceResourceMultipleSameNoBinding.var.js new file mode 100644 index 0000000000..5b2494c8e6 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceMultipleSameNoBinding.var.js @@ -0,0 +1,15 @@ +.import Qt.test 1.0 as JsQtTest + +var component = Qt.createComponent("scarceResourceCopy.var.qml"); +var scarceResourceElement = component.createObject(null); +var scarceResourceProvider = scarceResourceElement.a; +var retn = scarceResourceProvider.scarceResource; +retn.preserve(); + +function importScarceResource() { + return retn; +} + +function releaseScarceResource(resource) { + resource.destroy(); +}
\ No newline at end of file diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceMultipleSameNoBinding.var.qml b/tests/auto/qml/qqmlecmascript/data/scarceResourceMultipleSameNoBinding.var.qml new file mode 100644 index 0000000000..e7f6d7868f --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceMultipleSameNoBinding.var.qml @@ -0,0 +1,14 @@ +import QtQuick 2.0 +import Qt.test 1.0 +import "scarceResourceMultipleSameNoBinding.var.js" as ScarceResourcesMultipleSameNoBinding + +QtObject { + property var resourceOne + property var resourceTwo + + Component.onCompleted: { + resourceOne = ScarceResourcesMultipleSameNoBinding.importScarceResource(); + resourceTwo = ScarceResourcesMultipleSameNoBinding.importScarceResource(); + ScarceResourcesMultipleSameNoBinding.releaseScarceResource(resourceTwo); + } +}
\ No newline at end of file diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceMultipleSameWithBinding.var.qml b/tests/auto/qml/qqmlecmascript/data/scarceResourceMultipleSameWithBinding.var.qml new file mode 100644 index 0000000000..34cb97f39c --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceMultipleSameWithBinding.var.qml @@ -0,0 +1,12 @@ +import QtQuick 2.0 +import Qt.test 1.0 +import "scarceResourceMultipleDifferentNoBinding.var.js" as ScarceResourcesMultipleDifferentNoBinding + +QtObject { + property var resourceOne: ScarceResourcesMultipleDifferentNoBinding.importScarceResource() + property var resourceTwo: resourceOne + + Component.onCompleted: { + ScarceResourcesMultipleDifferentNoBinding.releaseScarceResource(resourceTwo); + } +}
\ No newline at end of file diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceObjectGc.var.qml b/tests/auto/qml/qqmlecmascript/data/scarceResourceObjectGc.var.qml new file mode 100644 index 0000000000..7ec98e6619 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceObjectGc.var.qml @@ -0,0 +1,30 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: testScarce + + property var varProperty + + property var canary: 4 + + // constructs an Item which contains a scarce resource. + function constructScarceObject() { + var retn = 1; + var component = Qt.createComponent("ScarceResourceVarComponent.qml"); + if (component.status == Component.Ready) { + retn = component.createObject(null); // has JavaScript ownership + } + return retn; + } + + function assignVarProperty() { + varProperty = constructScarceObject(); + gc(); + } + + function deassignVarProperty() { + varProperty = 2; // causes the original object to be garbage collected. + gc(); // image should be detached; ep->sr should be empty! + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceSignal.var.qml b/tests/auto/qml/qqmlecmascript/data/scarceResourceSignal.var.qml new file mode 100644 index 0000000000..0b30e88fa8 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceSignal.var.qml @@ -0,0 +1,29 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +QtObject { + id: root + + property MyScarceResourceObject a; + a: MyScarceResourceObject { id: scarceResourceProvider } + + property ScarceResourceSignalComponentVar b; + b: ScarceResourceSignalComponentVar { + objectName: "srsc" + + onTestSignal: { + // this signal will be invoked manually in the test. + // the scarce resource should be released automatically after evaluation + // and since we don't keep a copy of it, the pixmap will be detached. + width = (scarceResourceProvider.scarceResource,10) + } + + onTestSignal2: { + // this signal will be invoked manually in the test. + // the scarce resource should be released automatically after evaluation + // but since we assign it to a property, the pixmap won't be detached. + scarceResourceCopy = scarceResourceProvider.scarceResource + } + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceSignal.variant.qml b/tests/auto/qml/qqmlecmascript/data/scarceResourceSignal.variant.qml new file mode 100644 index 0000000000..1011c7e240 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceSignal.variant.qml @@ -0,0 +1,29 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +QtObject { + id: root + + property MyScarceResourceObject a; + a: MyScarceResourceObject { id: scarceResourceProvider } + + property ScarceResourceSignalComponentVariant b; + b: ScarceResourceSignalComponentVariant { + objectName: "srsc" + + onTestSignal: { + // this signal will be invoked manually in the test. + // the scarce resource should be released automatically after evaluation + // and since we don't keep a copy of it, the pixmap will be detached. + width = (scarceResourceProvider.scarceResource,10) + } + + onTestSignal2: { + // this signal will be invoked manually in the test. + // the scarce resource should be released automatically after evaluation + // but since we assign it to a property, the pixmap won't be detached. + scarceResourceCopy = scarceResourceProvider.scarceResource + } + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceTest.var.js b/tests/auto/qml/qqmlecmascript/data/scarceResourceTest.var.js new file mode 100644 index 0000000000..c904eb3564 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceTest.var.js @@ -0,0 +1,48 @@ +.import Qt.test 1.0 as JsQtTest + +function importScarceResource(scarceResourceProvider) { + // the scarce resource should be automatically released + // after the binding is evaluated if preserve is not + // called. + return scarceResourceProvider.scarceResource; +} + +function importPreservedScarceResource(scarceResourceProvider) { + // the scarce resource is manually preserved + // during the evaluation of the binding. + // it should not be released. + var scarceResource = scarceResourceProvider.scarceResource; + scarceResource.preserve(); + return scarceResource; +} + +function importReleasedScarceResource(scarceResourceProvider) { + // release the scarce resource during the + // evaluation of the binding. The returned + // variant will therefore be invalid. + var scarceResource = scarceResourceProvider.scarceResource; + scarceResource.destroy(); + return scarceResource; +} + +function importPreservedScarceResourceFromMultiple(scarceResourceProvider) { + // some scarce resources are manually preserved, + // some of them are manually destroyed, + // and some are automatically managed. + // We return a preserved resource + var sr1 = scarceResourceProvider.scarceResource; // preserved/destroyed. + sr1.preserve(); + var sr2 = scarceResourceProvider.scarceResource; // preserved/destroyed + sr2.preserve(); + var sr3 = scarceResourceProvider.scarceResource; // automatic. + var sr4 = scarceResourceProvider.scarceResource; // automatic and returned. + var sr5 = scarceResourceProvider.scarceResource; // destroyed + sr5.destroy(); + sr2.destroy(); + var sr6 = scarceResourceProvider.scarceResource; // destroyed + var sr7 = scarceResourceProvider.scarceResource; // automatic + sr1.destroy(); + sr6.destroy(); + return sr4; +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceTest.var.qml b/tests/auto/qml/qqmlecmascript/data/scarceResourceTest.var.qml new file mode 100644 index 0000000000..1d4e67055e --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceTest.var.qml @@ -0,0 +1,14 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +// Here we import a scarce resource directly, and use it in a binding. +// It is not preserved or released manually, so it should be +// automatically released once evaluation of the binding +// is complete. + +QtObject { + property MyScarceResourceObject a; + a: MyScarceResourceObject { id: scarceResourceProvider } + property int scarceResourceTest: scarceResourceProvider.scarceResource,100 // return 100, but include the scarceResource in the binding to be evaluated. +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceTest.variant.js b/tests/auto/qml/qqmlecmascript/data/scarceResourceTest.variant.js new file mode 100644 index 0000000000..c904eb3564 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceTest.variant.js @@ -0,0 +1,48 @@ +.import Qt.test 1.0 as JsQtTest + +function importScarceResource(scarceResourceProvider) { + // the scarce resource should be automatically released + // after the binding is evaluated if preserve is not + // called. + return scarceResourceProvider.scarceResource; +} + +function importPreservedScarceResource(scarceResourceProvider) { + // the scarce resource is manually preserved + // during the evaluation of the binding. + // it should not be released. + var scarceResource = scarceResourceProvider.scarceResource; + scarceResource.preserve(); + return scarceResource; +} + +function importReleasedScarceResource(scarceResourceProvider) { + // release the scarce resource during the + // evaluation of the binding. The returned + // variant will therefore be invalid. + var scarceResource = scarceResourceProvider.scarceResource; + scarceResource.destroy(); + return scarceResource; +} + +function importPreservedScarceResourceFromMultiple(scarceResourceProvider) { + // some scarce resources are manually preserved, + // some of them are manually destroyed, + // and some are automatically managed. + // We return a preserved resource + var sr1 = scarceResourceProvider.scarceResource; // preserved/destroyed. + sr1.preserve(); + var sr2 = scarceResourceProvider.scarceResource; // preserved/destroyed + sr2.preserve(); + var sr3 = scarceResourceProvider.scarceResource; // automatic. + var sr4 = scarceResourceProvider.scarceResource; // automatic and returned. + var sr5 = scarceResourceProvider.scarceResource; // destroyed + sr5.destroy(); + sr2.destroy(); + var sr6 = scarceResourceProvider.scarceResource; // destroyed + var sr7 = scarceResourceProvider.scarceResource; // automatic + sr1.destroy(); + sr6.destroy(); + return sr4; +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceTest.variant.qml b/tests/auto/qml/qqmlecmascript/data/scarceResourceTest.variant.qml new file mode 100644 index 0000000000..1d4e67055e --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceTest.variant.qml @@ -0,0 +1,14 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +// Here we import a scarce resource directly, and use it in a binding. +// It is not preserved or released manually, so it should be +// automatically released once evaluation of the binding +// is complete. + +QtObject { + property MyScarceResourceObject a; + a: MyScarceResourceObject { id: scarceResourceProvider } + property int scarceResourceTest: scarceResourceProvider.scarceResource,100 // return 100, but include the scarceResource in the binding to be evaluated. +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceTestMultiple.var.qml b/tests/auto/qml/qqmlecmascript/data/scarceResourceTestMultiple.var.qml new file mode 100644 index 0000000000..5e6c2d97f9 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceTestMultiple.var.qml @@ -0,0 +1,16 @@ +import QtQuick 2.0 +import Qt.test 1.0 +import "scarceResourceTest.var.js" as ScarceResourceProviderJs + +// In this case, multiple scarce resource are explicity preserved +// and then explicitly destroyed, while others are automatically +// managed. Since none are manually preserved without subsequently +// being destroyed, after the evaluation of the binding the +// scarce resource should be detached. + +QtObject { + property MyScarceResourceObject a; + a: MyScarceResourceObject { id: scarceResourceProvider } + property int scarceResourceTest: ScarceResourceProviderJs.importPreservedScarceResourceFromMultiple(scarceResourceProvider), 100 +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceTestMultiple.variant.qml b/tests/auto/qml/qqmlecmascript/data/scarceResourceTestMultiple.variant.qml new file mode 100644 index 0000000000..2970bcb26c --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceTestMultiple.variant.qml @@ -0,0 +1,15 @@ +import QtQuick 2.0 +import Qt.test 1.0 +import "scarceResourceTest.variant.js" as ScarceResourceProviderJs + +// In this case, multiple scarce resource are explicity preserved +// and then explicitly destroyed, while others are automatically +// managed. Since none are manually preserved without subsequently +// being destroyed, after the evaluation of the binding the +// scarce resource should be detached. + +QtObject { + property MyScarceResourceObject a; + a: MyScarceResourceObject { id: scarceResourceProvider } + property int scarceResourceTest: ScarceResourceProviderJs.importPreservedScarceResourceFromMultiple(scarceResourceProvider), 100 +} diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceTestPreserve.var.qml b/tests/auto/qml/qqmlecmascript/data/scarceResourceTestPreserve.var.qml new file mode 100644 index 0000000000..9e9495c0fa --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceTestPreserve.var.qml @@ -0,0 +1,15 @@ +import QtQuick 2.0 +import Qt.test 1.0 +import "scarceResourceTest.var.js" as ScarceResourceProviderJs + +// In this case, the scarce resource is explicity preserved. +// It should not be automatically released after the evaluation +// of the binding is complete, but instead will be kept in +// memory until the JS garbage collector runs. + +QtObject { + property MyScarceResourceObject a; + a: MyScarceResourceObject { id: scarceResourceProvider } + property int scarceResourceTest: ScarceResourceProviderJs.importPreservedScarceResource(scarceResourceProvider),100 // return 100, but the resource should be preserved. +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scarceResourceTestPreserve.variant.qml b/tests/auto/qml/qqmlecmascript/data/scarceResourceTestPreserve.variant.qml new file mode 100644 index 0000000000..022067beca --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scarceResourceTestPreserve.variant.qml @@ -0,0 +1,15 @@ +import QtQuick 2.0 +import Qt.test 1.0 +import "scarceResourceTest.variant.js" as ScarceResourceProviderJs + +// In this case, the scarce resource is explicity preserved. +// It should not be automatically released after the evaluation +// of the binding is complete, but instead will be kept in +// memory until the JS garbage collector runs. + +QtObject { + property MyScarceResourceObject a; + a: MyScarceResourceObject { id: scarceResourceProvider } + property int scarceResourceTest: ScarceResourceProviderJs.importPreservedScarceResource(scarceResourceProvider),100 // return 100, but the resource should be preserved. +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scope.2.qml b/tests/auto/qml/qqmlecmascript/data/scope.2.qml new file mode 100644 index 0000000000..fe1c4c7931 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scope.2.qml @@ -0,0 +1,40 @@ +import QtQuick 2.0 + +Item { + property int a: 0 + property int b: 14 + + function b() { return 11; } + function c() { return 33; } + + QtObject { + id: a + property int value: 19 + } + + QtObject { + id: c + property int value: 24 + } + + QtObject { + id: nested + property int a: 1 + property int test: a.value + property int test2: b + property int test3: c.value + } + + + // id takes precedence over local, and root properties + property int test1: a.value + property alias test2: nested.test + + // properties takes precedence over local, and root methods + property int test3: b + property alias test4: nested.test2 + + // id takes precedence over methods + property int test5: c.value + property alias test6: nested.test3 +} diff --git a/tests/auto/qml/qqmlecmascript/data/scope.3.qml b/tests/auto/qml/qqmlecmascript/data/scope.3.qml new file mode 100644 index 0000000000..9add81809c --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scope.3.qml @@ -0,0 +1,13 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +MyQmlObject { + id: root + + property int foo: 12 + + property bool test1: foo == 12 + property bool test2: console != 11 + property bool test3: root.console == 11 +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scope.4.qml b/tests/auto/qml/qqmlecmascript/data/scope.4.qml new file mode 100644 index 0000000000..d65b6e7c7c --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scope.4.qml @@ -0,0 +1,12 @@ +import Qt.test 1.0 + +MyQmlObject { + id: a + property int b: 9 + + property int test + property string test2 + + // Should resolve to signal arguments, not to other elements in the file + onArgumentSignal: { test = a; test2 = b; } +} diff --git a/tests/auto/qml/qqmlecmascript/data/scope.5.qml b/tests/auto/qml/qqmlecmascript/data/scope.5.qml new file mode 100644 index 0000000000..6dbcbe2a40 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scope.5.qml @@ -0,0 +1,27 @@ +import QtQuick 2.0 + +Item { + property bool test1: false; + property bool test2: false; + + property int a: 10 + + Item { + id: nested + property int a: 11 + + function mynestedfunction() { + return a; + } + } + + function myouterfunction() { + return a; + } + + Component.onCompleted: { + test1 = (myouterfunction() == 10); + test2 = (nested.mynestedfunction() == 11); + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scope.6.qml b/tests/auto/qml/qqmlecmascript/data/scope.6.qml new file mode 100644 index 0000000000..5897b533d7 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scope.6.qml @@ -0,0 +1,10 @@ +import QtQuick 2.0 + +Item { + id: me + property bool test: nested.runtest(me); + + Scope6Nested { + id: nested + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/scope.qml b/tests/auto/qml/qqmlecmascript/data/scope.qml new file mode 100644 index 0000000000..a00352b684 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scope.qml @@ -0,0 +1,44 @@ +import QtQuick 2.0 + +Item { + id: root + + property int a: 1 + property int binding: a + property string binding2: a + "Test" + property int binding3: myFunction() + property int binding4: nestedObject.myNestedFunction() + + function myFunction() { + return a; + } + + Item { + id: nestedObject + + function myNestedFunction() { + return a; + } + + property int a: 2 + property int binding: a + property string binding2: a + "Test" + property int binding3: myFunction() + property int binding4: myNestedFunction() + } + + ScopeObject { + id: compObject + } + + property alias test1: root.binding + property alias test2: nestedObject.binding + property alias test3: root.binding2 + property alias test4: nestedObject.binding2 + property alias test5: root.binding3 + property alias test6: nestedObject.binding3 + property alias test7: root.binding4 + property alias test8: nestedObject.binding4 + property alias test9: compObject.binding + property alias test10: compObject.binding2 +} diff --git a/tests/auto/qml/qqmlecmascript/data/scriptConnect.1.js b/tests/auto/qml/qqmlecmascript/data/scriptConnect.1.js new file mode 100644 index 0000000000..54284fea47 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scriptConnect.1.js @@ -0,0 +1,4 @@ +function testFunction() { + test = true; +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scriptConnect.1.qml b/tests/auto/qml/qqmlecmascript/data/scriptConnect.1.qml new file mode 100644 index 0000000000..ace473756e --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scriptConnect.1.qml @@ -0,0 +1,10 @@ +import Qt.test 1.0 +import QtQuick 2.0 +import "scriptConnect.1.js" as Script +MyQmlObject { + property bool test: false + + id: root + + Component.onCompleted: root.argumentSignal.connect(Script.testFunction); +} diff --git a/tests/auto/qml/qqmlecmascript/data/scriptConnect.2.js b/tests/auto/qml/qqmlecmascript/data/scriptConnect.2.js new file mode 100644 index 0000000000..595c778aa7 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scriptConnect.2.js @@ -0,0 +1,5 @@ +function testFunction() { + if (this.b == 12) + test = true; +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scriptConnect.2.qml b/tests/auto/qml/qqmlecmascript/data/scriptConnect.2.qml new file mode 100644 index 0000000000..cdf2d6ad98 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scriptConnect.2.qml @@ -0,0 +1,16 @@ +import Qt.test 1.0 +import QtQuick 2.0 +import "scriptConnect.2.js" as Script + +MyQmlObject { + property bool test: false + + id: root + + Component.onCompleted: { + var a = new Object; + a.b = 12; + root.argumentSignal.connect(a, Script.testFunction); + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scriptConnect.3.qml b/tests/auto/qml/qqmlecmascript/data/scriptConnect.3.qml new file mode 100644 index 0000000000..b0e40565c0 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scriptConnect.3.qml @@ -0,0 +1,15 @@ +import Qt.test 1.0 +import QtQuick 2.0 + +MyQmlObject { + property bool test: false + + id: root + + function testFunction() { + test = true; + } + + Component.onCompleted: root.argumentSignal.connect(testFunction); +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scriptConnect.4.qml b/tests/auto/qml/qqmlecmascript/data/scriptConnect.4.qml new file mode 100644 index 0000000000..ef5331c94a --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scriptConnect.4.qml @@ -0,0 +1,12 @@ +import Qt.test 1.0 +import QtQuick 2.0 + +MyQmlObject { + property bool test: false + + id: root + + Component.onCompleted: root.argumentSignal.connect(methodNoArgs); +} + + diff --git a/tests/auto/qml/qqmlecmascript/data/scriptConnect.5.qml b/tests/auto/qml/qqmlecmascript/data/scriptConnect.5.qml new file mode 100644 index 0000000000..8dcacbcbb7 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scriptConnect.5.qml @@ -0,0 +1,11 @@ +import Qt.test 1.0 +import QtQuick 2.0 + +MyQmlObject { + property bool test: false + + id: root + + Component.onCompleted: root.argumentSignal.connect(root, methodNoArgs); +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scriptConnect.6.js b/tests/auto/qml/qqmlecmascript/data/scriptConnect.6.js new file mode 100644 index 0000000000..71bdd088a2 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scriptConnect.6.js @@ -0,0 +1,3 @@ +function testFunction() { + test++; +} diff --git a/tests/auto/qml/qqmlecmascript/data/scriptConnect.6.qml b/tests/auto/qml/qqmlecmascript/data/scriptConnect.6.qml new file mode 100644 index 0000000000..06b6f0fa62 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scriptConnect.6.qml @@ -0,0 +1,15 @@ +import Qt.test 1.0 +import QtQuick 2.0 +import "scriptConnect.6.js" as Script + +MyQmlObject { + property int test: 0 + + id: root + + Component.onCompleted: { + root.argumentSignal.connect(Script.testFunction); + root.argumentSignal.connect(Script.testFunction); + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scriptDisconnect.1.js b/tests/auto/qml/qqmlecmascript/data/scriptDisconnect.1.js new file mode 100644 index 0000000000..407426fcd1 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scriptDisconnect.1.js @@ -0,0 +1,6 @@ +function testFunction() { + test++; +} + +function otherFunction() { +} diff --git a/tests/auto/qml/qqmlecmascript/data/scriptDisconnect.1.qml b/tests/auto/qml/qqmlecmascript/data/scriptDisconnect.1.qml new file mode 100644 index 0000000000..e546ee44d8 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scriptDisconnect.1.qml @@ -0,0 +1,13 @@ +import Qt.test 1.0 +import QtQuick 2.0 +import "scriptDisconnect.1.js" as Script + +MyQmlObject { + property int test: 0 + + id: root + + Component.onCompleted: root.argumentSignal.connect(Script.testFunction); + + onBasicSignal: root.argumentSignal.disconnect(Script.testFunction); +} diff --git a/tests/auto/qml/qqmlecmascript/data/scriptDisconnect.2.qml b/tests/auto/qml/qqmlecmascript/data/scriptDisconnect.2.qml new file mode 100644 index 0000000000..e70cd8b900 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scriptDisconnect.2.qml @@ -0,0 +1,14 @@ +import Qt.test 1.0 +import QtQuick 2.0 +import "scriptDisconnect.1.js" as Script + +MyQmlObject { + property int test: 0 + + id: root + + Component.onCompleted: root.argumentSignal.connect(root, Script.testFunction); + + onBasicSignal: root.argumentSignal.disconnect(root, Script.testFunction); +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scriptDisconnect.3.qml b/tests/auto/qml/qqmlecmascript/data/scriptDisconnect.3.qml new file mode 100644 index 0000000000..6f47776ea5 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scriptDisconnect.3.qml @@ -0,0 +1,14 @@ +import Qt.test 1.0 +import QtQuick 2.0 +import "scriptDisconnect.1.js" as Script + +MyQmlObject { + property int test: 0 + + id: root + + Component.onCompleted: root.argumentSignal.connect(root, Script.testFunction); + + onBasicSignal: root.argumentSignal.disconnect(Script.testFunction); +} + diff --git a/tests/auto/qml/qqmlecmascript/data/scriptDisconnect.4.qml b/tests/auto/qml/qqmlecmascript/data/scriptDisconnect.4.qml new file mode 100644 index 0000000000..b3887545fb --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scriptDisconnect.4.qml @@ -0,0 +1,13 @@ +import Qt.test 1.0 +import QtQuick 2.0 +import "scriptDisconnect.1.js" as Script + +MyQmlObject { + property int test: 0 + + id: root + + Component.onCompleted: root.argumentSignal.connect(Script.testFunction); + + onBasicSignal: root.argumentSignal.disconnect(Script.otherFunction); +} diff --git a/tests/auto/qml/qqmlecmascript/data/scriptErrors.js b/tests/auto/qml/qqmlecmascript/data/scriptErrors.js new file mode 100644 index 0000000000..d22f623edb --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scriptErrors.js @@ -0,0 +1,4 @@ +// Comment +a = 10 + +function getValue() { a = 10; return 0; } diff --git a/tests/auto/qml/qqmlecmascript/data/scriptErrors.qml b/tests/auto/qml/qqmlecmascript/data/scriptErrors.qml new file mode 100644 index 0000000000..4998f63929 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/scriptErrors.qml @@ -0,0 +1,18 @@ +import Qt.test 1.0 +import "scriptErrors.js" as Script + +MyQmlObject { + property int t: a.value + property int w: Script.getValue(); + property int d: undefined + ? 0 // multi-line binding + : 1 + property int x: undefined + property int y: (a.value, undefinedObject) + + onBasicSignal: { console.log(a.value); } + id: myObj + onAnotherBasicSignal: myObj.trueProperty = false; + onThirdBasicSignal: myObj.fakeProperty = ""; +} + diff --git a/tests/auto/qml/qqmlecmascript/data/selfDeletingBinding.2.qml b/tests/auto/qml/qqmlecmascript/data/selfDeletingBinding.2.qml new file mode 100644 index 0000000000..58cf8051f0 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/selfDeletingBinding.2.qml @@ -0,0 +1,17 @@ +import Qt.test 1.0 + +MyQmlContainer { + property bool triggerDelete: false + + children: [ + MyQmlObject { + // Will trigger deletion on binding assignment + deleteOnSet: Math.max(0, 1) + }, + + MyQmlObject { + // Will trigger deletion on binding assignment, but after component creation + deleteOnSet: if (triggerDelete) 1; else 0; + } + ] +} diff --git a/tests/auto/qml/qqmlecmascript/data/selfDeletingBinding.qml b/tests/auto/qml/qqmlecmascript/data/selfDeletingBinding.qml new file mode 100644 index 0000000000..074851a67b --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/selfDeletingBinding.qml @@ -0,0 +1,18 @@ +import Qt.test 1.0 + +MyQmlContainer { + property bool triggerDelete: false + + children: [ + MyQmlObject { + // Will trigger deletion during binding evaluation + stringProperty: {deleteMe(), "Hello"} + }, + + MyQmlObject { + // Will trigger deletion during binding evaluation, but after component creation + stringProperty: if (triggerDelete) { deleteMe(), "Hello" } else { "World" } + } + + ] +} diff --git a/tests/auto/qml/qqmlecmascript/data/sequenceConversion.array.qml b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.array.qml new file mode 100644 index 0000000000..52abda1e55 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.array.qml @@ -0,0 +1,193 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: root + objectName: "root" + + MySequenceConversionObject { + id: msco + objectName: "msco" + } + + property bool success: false + + property variant intList + property variant qrealList + property variant boolList + property variant stringList + + function indexedAccess() { + intList = msco.intListProperty; + var jsIntList = msco.intListProperty; + qrealList = msco.qrealListProperty; + var jsQrealList = msco.qrealListProperty; + boolList = msco.boolListProperty; + var jsBoolList = msco.boolListProperty; + stringList = msco.stringListProperty; + var jsStringList = msco.stringListProperty; + + // Three cases: direct property modification, variant copy modification, js var reference modification. + // Only the first and third should "write back" to the original QObject Q_PROPERTY; the second one + // should have no effect whatsoever to maintain "property variant" semantics (see e.g., valuetype). + success = true; + + msco.intListProperty[1] = 33; + if (msco.intListProperty[1] != 33) success = false; // ensure write back + intList[1] = 44; + if (intList[1] == 44) success = false; // ensure no effect + jsIntList[1] = 55; + if (jsIntList[1] != 55 + || jsIntList[1] != msco.intListProperty[1]) success = false; // ensure write back + + msco.qrealListProperty[1] = 33.3; + if (msco.qrealListProperty[1] != 33.3) success = false; // ensure write back + qrealList[1] = 44.4; + if (qrealList[1] == 44.4) success = false; // ensure no effect + jsQrealList[1] = 55.5; + if (jsQrealList[1] != 55.5 + || jsQrealList[1] != msco.qrealListProperty[1]) success = false; // ensure write back + + msco.boolListProperty[1] = true; + if (msco.boolListProperty[1] != true) success = false; // ensure write back + boolList[1] = true; + if (boolList[1] != false) success = false; // ensure no effect + jsBoolList[1] = false; + if (jsBoolList[1] != false + || jsBoolList[1] != msco.boolListProperty[1]) success = false; // ensure write back + + msco.stringListProperty[1] = "changed"; + if (msco.stringListProperty[1] != "changed") success = false; // ensure write back + stringList[1] = "changed"; + if (stringList[1] != "second") success = false; // ensure no effect + jsStringList[1] = "different"; + if (jsStringList[1] != "different" + || jsStringList[1] != msco.stringListProperty[1]) success = false; // ensure write back + } + + function arrayOperations() { + success = true; + var expected = 0; + var expectedStr = ""; + + // ecma262r3 defines array as implementing Length and Put. Test put here. + msco.intListProperty.asdf = 5; // shouldn't work, only indexes are valid names. + if (msco.intListProperty.asdf == 5) success = false; + msco.intListProperty[3] = 38; // should work. + if (msco.intListProperty[3] != 38) success = false; + msco.intListProperty[199] = 200; // should work, and should set length to 200. + if (msco.intListProperty[199] != 200) success = false; + if (msco.intListProperty.length != 200) success = false; + + // test indexed deleter + msco.intListProperty = [ 1, 2, 3, 4, 5 ]; + delete msco.intListProperty[-1]; + expected = [ 1, 2, 3, 4, 5 ]; + if (msco.intListProperty.toString() != expected.toString()) success = false; + delete msco.intListProperty[0]; + expected = [ 0, 2, 3, 4, 5 ]; + if (msco.intListProperty.toString() != expected.toString()) success = false; + delete msco.intListProperty[2]; + expected = [ 0, 2, 0, 4, 5 ]; + if (msco.intListProperty.toString() != expected.toString()) success = false; + delete msco.intListProperty[7]; + expected = [ 0, 2, 0, 4, 5 ]; + if (msco.intListProperty.toString() != expected.toString()) success = false; + + // other operations are defined on the array prototype; see if they work. + + // splice + msco.intListProperty = [ 0, 1, 2, 3, 4, 5, 6, 7 ]; + msco.intListProperty.splice(1,3, 33, 44, 55, 66); + expected = [ 0, 33, 44, 55, 66, 4, 5, 6, 7 ]; + if (msco.intListProperty.toString() != expected.toString()) success = false; + msco.intListProperty = [ 0, 1, 2, 3, 4, 5, 6, 7 ]; + msco.intListProperty.splice(1, 3); + expected = [ 0, 4, 5, 6, 7 ]; + if (msco.intListProperty.toString() != expected.toString()) success = false; + + msco.qrealListProperty = [ 0.1, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1 ]; + msco.qrealListProperty.splice(1,3, 33.33, 44.44, 55.55, 66.66); + expected = [ 0.1, 33.33, 44.44, 55.55, 66.66, 4.1, 5.1, 6.1, 7.1 ]; + if (msco.qrealListProperty.toString() != expected.toString()) success = false; + msco.qrealListProperty = [ 0.1, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1 ]; + msco.qrealListProperty.splice(1,3); + expected = [ 0.1, 4.1, 5.1, 6.1, 7.1 ]; + if (msco.qrealListProperty.toString() != expected.toString()) success = false; + + msco.boolListProperty = [ false, true, true, false, false, true, false, true ]; + msco.boolListProperty.splice(1,3, false, true, false, false); + expected = [ false, false, true, false, false, false, true, false, true ]; + if (msco.boolListProperty.toString() != expected.toString()) success = false; + msco.boolListProperty = [ false, true, true, false, false, true, false, true ]; + msco.boolListProperty.splice(1,3); + expected = [ false, false, true, false, true ]; + if (msco.boolListProperty.toString() != expected.toString()) success = false; + + msco.stringListProperty = [ "one", "two", "three", "four", "five", "six", "seven", "eight" ]; + msco.stringListProperty.splice(1,3, "nine", "ten", "eleven", "twelve"); + expected = [ "one", "nine", "ten", "eleven", "twelve", "five", "six", "seven", "eight" ]; + if (msco.stringListProperty.toString() != expected.toString()) success = false; + msco.stringListProperty = [ "one", "two", "three", "four", "five", "six", "seven", "eight" ]; + msco.stringListProperty.splice(0,3); + expected = [ "four", "five", "six", "seven", "eight" ]; + if (msco.stringListProperty.toString() != expected.toString()) success = false; + + // pop + msco.intListProperty = [ 0, 1, 2, 3, 4, 5, 6, 7 ]; + var poppedVal = msco.intListProperty.pop(); + expected = [ 0, 1, 2, 3, 4, 5, 6 ]; + if (msco.intListProperty.toString() != expected.toString()) success = false; + expected = 7; + if (poppedVal != expected) success = false; + } + + property variant variantList: [ 1, 2, 3, 4, 5 ]; + property variant variantList2: [ 1, 2, 3, 4, 5 ]; + function testEqualitySemantics() { + // ensure equality semantics match JS array equality semantics + success = true; + + msco.intListProperty = [ 1, 2, 3, 4, 5 ]; + msco.intListProperty2 = [ 1, 2, 3, 4, 5 ]; + var jsIntList = [ 1, 2, 3, 4, 5 ]; + var jsIntList2 = [ 1, 2, 3, 4, 5 ]; + + if (jsIntList != jsIntList) success = false; + if (jsIntList == jsIntList2) success = false; + if (jsIntList == msco.intListProperty) success = false; + if (jsIntList == variantList) success = false; + + if (msco.intListProperty != msco.intListProperty) success = false; + if (msco.intListProperty == msco.intListProperty2) success = false; + if (msco.intListProperty == jsIntList) success = false; + if (msco.intListProperty == variantList) success = false; + + if (variantList == variantList) return false; + if (variantList == variantList2) return false; + if (variantList == msco.intListProperty) return false; + if (variantList == jsIntList) return false; + + if ((jsIntList == jsIntList2) != (jsIntList == msco.intListProperty)) success = false; + if ((jsIntList == jsIntList2) != (msco.intListProperty == msco.intListProperty2)) success = false; + if ((jsIntList == jsIntList) != (msco.intListProperty == msco.intListProperty)) success = false; + if ((jsIntList == variantList) != (msco.intListProperty == variantList)) success = false; + if ((variantList == jsIntList) != (variantList == msco.intListProperty)) success = false; + if ((msco.intListProperty == variantList) != (variantList == msco.intListProperty)) success = false; + } + + property bool referenceDeletion: false + function testReferenceDeletion() { + referenceDeletion = true; + var testObj = msco.generateTestObject(); + testObj.intListProperty = [1, 2, 3, 4, 5]; + var testSequence = testObj.intListProperty; + var prevString = testSequence.toString(); + var prevValueOf = testSequence.valueOf(); + var prevLength = testSequence.length; + msco.deleteTestObject(testObj); // delete referenced object. + if (testSequence.toString() == prevString) referenceDeletion = false; + if (testSequence.valueOf() == prevValueOf) referenceDeletion = false; + if (testSequence.length == prevLength) referenceDeletion = false; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/sequenceConversion.bindings.error.qml b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.bindings.error.qml new file mode 100644 index 0000000000..9c87dd293e --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.bindings.error.qml @@ -0,0 +1,19 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: root + objectName: "root" + + MySequenceConversionObject { + id: msco + objectName: "msco" + intListProperty: [ 1, 2, 3, 6, 7 ] + } + + MySequenceConversionObject { + id: mscoTwo + objectName: "mscoTwo" + boolListProperty: msco.intListProperty + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/sequenceConversion.bindings.qml b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.bindings.qml new file mode 100644 index 0000000000..8d83e9f9f5 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.bindings.qml @@ -0,0 +1,28 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: root + objectName: "root" + + MySequenceConversionObject { + id: msco + objectName: "msco" + intListProperty: [ 1, 2, 3, 6, 7 ] + } + + MySequenceConversionObject { + id: mscoTwo + objectName: "mscoTwo" + intListProperty: msco.intListProperty + } + + property variant boundSequence: msco.intListProperty + property int boundElement: msco.intListProperty[3] + property variant boundSequenceTwo: mscoTwo.intListProperty + + Component.onCompleted: { + msco.intListProperty[3] = 12; + mscoTwo.intListProperty[4] = 14; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/sequenceConversion.copy.qml b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.copy.qml new file mode 100644 index 0000000000..f6614dad0c --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.copy.qml @@ -0,0 +1,160 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: root + objectName: "root" + + MySequenceConversionObject { + id: msco + objectName: "msco" + } + + property bool success: true + + property variant intList + property variant qrealList + property variant boolList + property variant stringList + property variant urlList + property variant qstringList + + // this test ensures that the "copy resource" codepaths work + function testCopySequences() { + success = true; + + // create "copy resource" sequences + var jsIntList = msco.generateIntSequence(); + var jsQrealList = msco.generateQrealSequence(); + var jsBoolList = msco.generateBoolSequence(); + var jsStringList = msco.generateStringSequence(); + var jsUrlList = msco.generateUrlSequence(); + var jsQStringList = msco.generateQStringSequence(); + + if (jsIntList.toString() != [1, 2, 3].toString()) + success = false; + if (jsQrealList.toString() != [1.1, 2.2, 3.3].toString()) + success = false; + if (jsBoolList.toString() != [true, false, true].toString()) + success = false; + if (jsStringList.toString() != ["one", "two", "three"].toString()) + success = false; + if (jsUrlList.toString() != ["http://www.example1.com", "http://www.example2.com", "http://www.example3.com"].toString()) + success = false; + if (jsQStringList.toString() != ["one", "two", "three"].toString()) + success = false; + + // copy the sequence; should result in a new copy + intList = jsIntList; + qrealList = jsQrealList; + boolList = jsBoolList; + stringList = jsStringList; + urlList = jsUrlList; + qstringList = jsQStringList; + + // these operations shouldn't modify either variables - because + // we don't handle writing to the intermediate variant at list[index] + // for variant properties. + intList[1] = 8; + qrealList[1] = 8.8; + boolList[1] = true; + stringList[1] = "eight"; + urlList[1] = "http://www.example8.com"; + qstringList[1] = "eight"; + + if (jsIntList[1] == 8) + success = false; + if (jsQrealList[1] == 8.8) + success = false; + if (jsBoolList[1] == true) + success = false; + if (jsStringList[1] == "eight") + success = false; + if (jsUrlList[1] == "http://www.example8.com") + success = false; + if (jsQStringList[1] == "eight") + success = false; + + // assign a "copy resource" sequence to a QObject Q_PROPERTY + msco.intListProperty = intList; + msco.qrealListProperty = qrealList; + msco.boolListProperty = boolList; + msco.stringListProperty = stringList; + msco.urlListProperty = urlList; + msco.qstringListProperty = qstringList; + + if (msco.intListProperty.toString() != [1, 2, 3].toString()) + success = false; + if (msco.qrealListProperty.toString() != [1.1, 2.2, 3.3].toString()) + success = false; + if (msco.boolListProperty.toString() != [true, false, true].toString()) + success = false; + if (msco.stringListProperty.toString() != ["one", "two", "three"].toString()) + success = false; + if (msco.urlListProperty.toString() != ["http://www.example1.com", "http://www.example2.com", "http://www.example3.com"].toString()) + success = false; + if (msco.qstringListProperty.toString() != ["one", "two", "three"].toString()) + success = false; + + // now modify the QObject Q_PROPERTY (reference resource) sequences - shouldn't modify the copy resource sequences. + msco.intListProperty[2] = 9; + msco.qrealListProperty[2] = 9.9; + msco.boolListProperty[2] = false; + msco.stringListProperty[2] = "nine"; + msco.urlListProperty[2] = "http://www.example9.com"; + msco.qstringListProperty[2] = "nine"; + + if (intList[2] == 9) + success = false; + if (qrealList[2] == 9.9) + success = false; + if (boolList[2] == false) + success = false; + if (stringList[2] == "nine") + success = false; + if (urlList[2] == "http://www.example9.com") + success = false; + if (qstringList[2] == "nine") + success = false; + } + + property int intVal + property real qrealVal + property bool boolVal + property string stringVal + + // this test ensures that indexed access works for copy resource sequences. + function readSequenceCopyElements() { + success = true; + + var jsIntList = msco.generateIntSequence(); + var jsQrealList = msco.generateQrealSequence(); + var jsBoolList = msco.generateBoolSequence(); + var jsStringList = msco.generateStringSequence(); + + intVal = jsIntList[1]; + qrealVal = jsQrealList[1]; + boolVal = jsBoolList[1]; + stringVal = jsStringList[1]; + + if (intVal != 2) + success = false; + if (qrealVal != 2.2) + success = false; + if (boolVal != false) + success = false; + if (stringVal != "two") + success = false; + } + + // this test ensures that equality works for copy resource sequences. + function testEqualitySemantics() { + success = true; + + var jsIntList = msco.generateIntSequence(); + var jsIntList2 = msco.generateIntSequence(); + + if (jsIntList == jsIntList2) success = false; + if (jsIntList != jsIntList) success = false; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/sequenceConversion.indexes.qml b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.indexes.qml new file mode 100644 index 0000000000..23f1e90417 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.indexes.qml @@ -0,0 +1,89 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: root + objectName: "root" + + MySequenceConversionObject { + id: msco + objectName: "msco" + } + + property bool success: false + + function verifyExpected(array, idx) { + for (var i = 0; i < idx; ++i) { + if (array[i] != i) { + return false; + } + } + return true; + } + + function indexedAccess() { + success = true; + + msco.intListProperty = [ 0, 1, 2, 3, 4 ]; + var expectedLength = msco.intListProperty.length; + var maxIndex = msco.maxIndex; + var tooBigIndex = msco.tooBigIndex; + var negativeIndex = msco.negativeIndex; + + // shouldn't be able to set the length > maxIndex. + msco.intListProperty.length = tooBigIndex; + if (msco.intListProperty.length != expectedLength) + success = false; + if (!verifyExpected(msco.intListProperty, 4)) + success = false; + + // shouldn't be able to set any index > maxIndex. + msco.intListProperty[tooBigIndex] = 12; + if (msco.intListProperty.length != expectedLength) + success = false; + if (!verifyExpected(msco.intListProperty, 4)) + success = false; + + // shouldn't be able to access any index > maxIndex. + var valueAtTBI = msco.intListProperty[tooBigIndex]; + if (valueAtTBI != undefined) + success = false; + if (!verifyExpected(msco.intListProperty, 4)) + success = false; + + // shouldn't be able to set the length to < 0 + msco.intListProperty.length = negativeIndex; + if (msco.intListProperty.length != expectedLength) + success = false; // shouldn't have changed. + if (!verifyExpected(msco.intListProperty, 4)) + success = false; + + // shouldn't be able to set any index < 0. + msco.intListProperty[negativeIndex] = 12; + if (msco.intListProperty.length != expectedLength) + success = false; + if (!verifyExpected(msco.intListProperty, 4)) + success = false; + + // shouldn't be able to access any index < 0. + var valueAtNI = msco.intListProperty[negativeIndex]; + if (valueAtNI != undefined) + success = false; + if (!verifyExpected(msco.intListProperty, 4)) + success = false; + + // NOTE: while these two operations are technically + // fine, we expect std::bad_alloc exceptions here + // which we handle in the sequence wrapper. + msco.intListProperty.length = maxIndex; + if (msco.intListProperty.length != expectedLength) + success = false; + if (!verifyExpected(msco.intListProperty, 4)) + success = false; + msco.intListProperty[maxIndex] = 15; + if (msco.intListProperty.length != expectedLength) + success = false; + if (!verifyExpected(msco.intListProperty, 4)) + success = false; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/sequenceConversion.read.error.qml b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.read.error.qml new file mode 100644 index 0000000000..12a76d7e7d --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.read.error.qml @@ -0,0 +1,21 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: root + objectName: "root" + + MySequenceConversionObject { + id: msco + objectName: "msco" + } + + property int pointListLength: 0 + property variant pointList + + function performTest() { + // we have NOT registered QList<QPoint> as a type + pointListLength = msco.pointListProperty.length; + pointList = msco.pointListProperty; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/sequenceConversion.read.qml b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.read.qml new file mode 100644 index 0000000000..4a8a4a17b2 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.read.qml @@ -0,0 +1,105 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: root + objectName: "root" + + MySequenceConversionObject { + id: msco + objectName: "msco" + } + + property int intListLength: 0 + property variant intList + property int qrealListLength: 0 + property variant qrealList + property int boolListLength: 0 + property variant boolList + property int stringListLength: 0 + property variant stringList + property int urlListLength: 0 + property variant urlList + property int qstringListLength: 0 + property variant qstringList + + function readSequences() { + intListLength = msco.intListProperty.length; + intList = msco.intListProperty; + qrealListLength = msco.qrealListProperty.length; + qrealList = msco.qrealListProperty; + boolListLength = msco.boolListProperty.length; + boolList = msco.boolListProperty; + stringListLength = msco.stringListProperty.length; + stringList = msco.stringListProperty; + urlListLength = msco.urlListProperty.length; + urlList = msco.urlListProperty; + qstringListLength = msco.qstringListProperty.length; + qstringList = msco.qstringListProperty; + } + + property int intVal + property real qrealVal + property bool boolVal + property string stringVal + property url urlVal + property string qstringVal + + function readSequenceElements() { + intVal = msco.intListProperty[1]; + qrealVal = msco.qrealListProperty[1]; + boolVal = msco.boolListProperty[1]; + stringVal = msco.stringListProperty[1]; + urlVal = msco.urlListProperty[1]; + qstringVal = msco.qstringListProperty[1]; + } + + property bool enumerationMatches + function enumerateSequenceElements() { + var jsIntList = [1, 2, 3, 4, 5]; + msco.intListProperty = [1, 2, 3, 4, 5]; + + var jsIntListProps = [] + var seqIntListProps = [] + + enumerationMatches = true; + for (var i in jsIntList) { + jsIntListProps.push(i); + if (jsIntList[i] != msco.intListProperty[i]) { + enumerationMatches = false; + } + } + for (var j in msco.intListProperty) { + seqIntListProps.push(j); + if (jsIntList[j] != msco.intListProperty[j]) { + enumerationMatches = false; + } + } + + if (jsIntListProps.length != seqIntListProps.length) { + enumerationMatches = false; + } + + var emptyList = []; + msco.stringListProperty = [] + if (emptyList.toString() != msco.stringListProperty.toString()) { + enumerationMatches = false; + } + if (emptyList.valueOf() != msco.stringListProperty.valueOf()) { + enumerationMatches = false; + } + } + + property bool referenceDeletion: false + function testReferenceDeletion() { + referenceDeletion = true; + var testObj = msco.generateTestObject(); + testObj.intListProperty = [1, 2, 3, 4, 5]; + var testSequence = testObj.intListProperty; + if (testSequence[4] != 5) + referenceDeletion = false; + msco.deleteTestObject(testObj); // delete referenced object. + if (testSequence[4] == 5) + referenceDeletion = false; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/sequenceConversion.threads.qml b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.threads.qml new file mode 100644 index 0000000000..aefad89ca4 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.threads.qml @@ -0,0 +1,74 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: root + objectName: "root" + + MySequenceConversionObject { + id: msco + objectName: "msco" + } + + property bool success: false + property bool finished: false + + function testIntSequence() { + msco.intListProperty = [ 0, 1, 2, 3, 4, 5, 6, 7 ]; + worker.sendSequence(msco.intListProperty); + } + + function testQrealSequence() { + msco.qrealListProperty = [ 0.1, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1 ]; + worker.sendSequence(msco.qrealListProperty); + } + + function testBoolSequence() { + msco.boolListProperty = [ false, true, true, false, false, true, false, true ]; + worker.sendSequence(msco.boolListProperty); + } + + function testStringSequence() { + msco.stringListProperty = [ "one", "two", "three", "four" ]; + worker.sendSequence(msco.stringListProperty); + } + + function testQStringSequence() { + msco.qstringListProperty = [ "one", "two", "three", "four" ]; + worker.sendSequence(msco.qstringListProperty); + } + + function testUrlSequence() { + msco.urlListProperty = [ "www.example1.com", "www.example2.com", "www.example3.com", "www.example4.com" ]; + worker.sendSequence(msco.urlListProperty); + } + + function testVariantSequence() { + msco.variantListProperty = [ "one", true, 3, "four" ]; + worker.sendSequence(msco.variantListProperty); + } + + WorkerScript { + id: worker + source: "threadScript.js" + + property variant expected + property variant response + + function sendSequence(seq) { + root.success = false; + root.finished = false; + worker.expected = seq; + worker.sendMessage(seq); + } + + onMessage: { + worker.response = messageObject; + if (worker.response.toString() == worker.expected.toString()) + root.success = true; + else + root.success = false; + root.finished = true; + } + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/sequenceConversion.write.error.qml b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.write.error.qml new file mode 100644 index 0000000000..75beafd1ee --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.write.error.qml @@ -0,0 +1,18 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: root + objectName: "root" + + MySequenceConversionObject { + id: msco + objectName: "msco" + } + + function performTest() { + // we have NOT registered QList<QPoint> as a type + var pointList = [ Qt.point(7,7), Qt.point(8,8), Qt.point(9,9) ]; + msco.pointListProperty = pointList; // error. + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/sequenceConversion.write.qml b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.write.qml new file mode 100644 index 0000000000..812de043b7 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.write.qml @@ -0,0 +1,109 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + id: root + objectName: "root" + + MySequenceConversionObject { + id: msco + objectName: "msco" + } + + property bool success + + function writeSequences() { + success = true; + + var intList = [ 9, 8, 7, 6 ]; + msco.intListProperty = intList; + var qrealList = [ 9.9, 8.8, 7.7, 6.6 ]; + msco.qrealListProperty = qrealList; + var boolList = [ false, false, false, true ]; + msco.boolListProperty = boolList; + var stringList = [ "nine", "eight", "seven", "six" ] + msco.stringListProperty = stringList; + var urlList = [ "http://www.example9.com", "http://www.example8.com", "http://www.example7.com", "http://www.example6.com" ] + msco.urlListProperty = urlList; + var qstringList = [ "nine", "eight", "seven", "six" ] + msco.qstringListProperty = qstringList; + + if (msco.intListProperty[0] != 9 || msco.intListProperty[1] != 8 || msco.intListProperty[2] != 7 || msco.intListProperty[3] != 6) + success = false; + if (msco.qrealListProperty[0] != 9.9 || msco.qrealListProperty[1] != 8.8 || msco.qrealListProperty[2] != 7.7 || msco.qrealListProperty[3] != 6.6) + success = false; + if (msco.boolListProperty[0] != false || msco.boolListProperty[1] != false || msco.boolListProperty[2] != false || msco.boolListProperty[3] != true) + success = false; + if (msco.stringListProperty[0] != "nine" || msco.stringListProperty[1] != "eight" || msco.stringListProperty[2] != "seven" || msco.stringListProperty[3] != "six") + success = false; + if (msco.urlListProperty[0] != "http://www.example9.com" || msco.urlListProperty[1] != "http://www.example8.com" || msco.urlListProperty[2] != "http://www.example7.com" || msco.urlListProperty[3] != "http://www.example6.com") + success = false; + if (msco.qstringListProperty[0] != "nine" || msco.qstringListProperty[1] != "eight" || msco.qstringListProperty[2] != "seven" || msco.qstringListProperty[3] != "six") + success = false; + } + + function writeSequenceElements() { + // set up initial conditions. + writeSequences(); + success = true; + + // element set. + msco.intListProperty[3] = 2; + msco.qrealListProperty[3] = 2.2; + msco.boolListProperty[3] = false; + msco.stringListProperty[3] = "changed"; + msco.urlListProperty[3] = "http://www.examplechanged.com"; + msco.qstringListProperty[3] = "changed"; + + if (msco.intListProperty[0] != 9 || msco.intListProperty[1] != 8 || msco.intListProperty[2] != 7 || msco.intListProperty[3] != 2) + success = false; + if (msco.qrealListProperty[0] != 9.9 || msco.qrealListProperty[1] != 8.8 || msco.qrealListProperty[2] != 7.7 || msco.qrealListProperty[3] != 2.2) + success = false; + if (msco.boolListProperty[0] != false || msco.boolListProperty[1] != false || msco.boolListProperty[2] != false || msco.boolListProperty[3] != false) + success = false; + if (msco.stringListProperty[0] != "nine" || msco.stringListProperty[1] != "eight" || msco.stringListProperty[2] != "seven" || msco.stringListProperty[3] != "changed") + success = false; + if (msco.urlListProperty[0] != "http://www.example9.com" || msco.urlListProperty[1] != "http://www.example8.com" || msco.urlListProperty[2] != "http://www.example7.com" || msco.urlListProperty[3] != "http://www.examplechanged.com") + success = false; + if (msco.qstringListProperty[0] != "nine" || msco.qstringListProperty[1] != "eight" || msco.qstringListProperty[2] != "seven" || msco.qstringListProperty[3] != "changed") + success = false; + } + + function writeOtherElements() { + success = true; + var jsIntList = [1, 2, 3, 4, 5]; + msco.intListProperty = [1, 2, 3, 4, 5]; + + jsIntList[8] = 8; + msco.intListProperty[8] = 8; + if (jsIntList[8] != msco.intListProperty[8]) + success = false; + if (jsIntList.length != msco.intListProperty.length) + success = false; + + // NOTE: we can't exactly match the spec here -- we fill the sequence with a default (rather than empty) value + if (msco.intListProperty[5] != 0 || msco.intListProperty[6] != 0 || msco.intListProperty[7] != 0) + success = false; + + // should have no effect + var currLength = jsIntList.length; + jsIntList.someThing = 9; + msco.intListProperty.someThing = 9; + if (msco.intListProperty.length != currLength) + success = false; + } + + property bool referenceDeletion: false + function testReferenceDeletion() { + referenceDeletion = true; + var testObj = msco.generateTestObject(); + testObj.intListProperty = [1, 2, 3, 4, 5]; + var testSequence = testObj.intListProperty; + if (testSequence[4] != 5) + referenceDeletion = false; + msco.deleteTestObject(testObj); // delete referenced object. + testSequence[4] = 5; // shouldn't work, since referenced object no longer exists. + if (testSequence[4] == 5) + referenceDeletion = false; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/sharedAttachedObject.qml b/tests/auto/qml/qqmlecmascript/data/sharedAttachedObject.qml new file mode 100644 index 0000000000..b967f0984c --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/sharedAttachedObject.qml @@ -0,0 +1,16 @@ +import Qt.test 1.0 +import QtQuick 2.0 + +MyQmlObject { + id: root + property bool test1: false + property bool test2: false + + MyQmlObject.value2: 7 + + Component.onCompleted: { + test1 = root.MyQmlObject.value2 == 7; + test2 = root.MyQmlObjectAlias.value2 == 7; + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/shutdownErrors.qml b/tests/auto/qml/qqmlecmascript/data/shutdownErrors.qml new file mode 100644 index 0000000000..b30aa8b4cd --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/shutdownErrors.qml @@ -0,0 +1,13 @@ +import QtQuick 2.0 + +Item { + property int test: myObject.object.a + + Item { + id: myObject + property QtObject object; + object: QtObject { + property int a: 10 + } + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/signalAssignment.1.qml b/tests/auto/qml/qqmlecmascript/data/signalAssignment.1.qml new file mode 100644 index 0000000000..fbd09142f7 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/signalAssignment.1.qml @@ -0,0 +1,5 @@ +import Qt.test 1.0 + +MyQmlObject { + onBasicSignal: setString('pass') +} diff --git a/tests/auto/qml/qqmlecmascript/data/signalAssignment.2.qml b/tests/auto/qml/qqmlecmascript/data/signalAssignment.2.qml new file mode 100644 index 0000000000..6467c42bb9 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/signalAssignment.2.qml @@ -0,0 +1,5 @@ +import Qt.test 1.0 + +MyQmlObject { + onArgumentSignal: setString('pass ' + a + ' ' + b + ' ' + c + ' ' + d + ' ' + e) +} diff --git a/tests/auto/qml/qqmlecmascript/data/signalHandlers.qml b/tests/auto/qml/qqmlecmascript/data/signalHandlers.qml new file mode 100644 index 0000000000..975be1b2ad --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/signalHandlers.qml @@ -0,0 +1,60 @@ +import Qt.test 1.0 +import QtQuick 2.0 + +QtObject { + id: root + + property int count: 0 + signal testSignal + onTestSignal: count++ + + property int funcCount: 0 + function testFunction() { + funcCount++; + } + + //should increment count + function testSignalCall() { + testSignal() + } + + //should NOT increment count, and should throw an exception + property string errorString + function testSignalHandlerCall() { + try { + onTestSignal() + } catch (error) { + errorString = error.toString(); + } + } + + //should increment funcCount once + function testSignalConnection() { + testSignal.connect(testFunction) + testSignal(); + testSignal.disconnect(testFunction) + testSignal(); + } + + //should increment funcCount once + function testSignalHandlerConnection() { + onTestSignal.connect(testFunction) + testSignal(); + onTestSignal.disconnect(testFunction) + testSignal(); + } + + //should be defined + property bool definedResult: false + function testSignalDefined() { + if (testSignal !== undefined) + definedResult = true; + } + + //should be defined + property bool definedHandlerResult: false + function testSignalHandlerDefined() { + if (onTestSignal !== undefined) + definedHandlerResult = true; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/signalParameterTypes.qml b/tests/auto/qml/qqmlecmascript/data/signalParameterTypes.qml new file mode 100644 index 0000000000..4fc2dab943 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/signalParameterTypes.qml @@ -0,0 +1,18 @@ +import Qt.test 1.0 + +MyQmlObject +{ + id: root + property int intProperty + property real realProperty + property color colorProperty + property variant variantProperty + property int enumProperty + property int qtEnumProperty + + signal mySignal(int a, real b, color c, variant d, int e, int f) + + onMySignal: { intProperty = a; realProperty = b; colorProperty = c; variantProperty = d; enumProperty = e; qtEnumProperty = f; } + + onBasicSignal: root.mySignal(10, 19.2, Qt.rgba(1, 1, 0, 1), Qt.rgba(1, 0, 1, 1), MyQmlObject.EnumValue3, Qt.LeftButton) +} diff --git a/tests/auto/qml/qqmlecmascript/data/signalTriggeredBindings.qml b/tests/auto/qml/qqmlecmascript/data/signalTriggeredBindings.qml new file mode 100644 index 0000000000..d98d7e9c81 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/signalTriggeredBindings.qml @@ -0,0 +1,20 @@ +import Qt.test 1.0 +import QtQuick 2.0 + +MyQmlObject { + property real base: 50 + property alias test1: myObject.test1 + property alias test2: myObject.test2 + + objectProperty: QtObject { + id: myObject + property real test1: base + property real test2: Math.max(0, base) + } + + // Signal with no args + onBasicSignal: base = 200 + // Signal with args + onArgumentSignal: base = 400 +} + diff --git a/tests/auto/qml/qqmlecmascript/data/signalWithJSValueInVariant.qml b/tests/auto/qml/qqmlecmascript/data/signalWithJSValueInVariant.qml new file mode 100644 index 0000000000..a6f1aa381a --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/signalWithJSValueInVariant.qml @@ -0,0 +1,12 @@ +import Qt.test 1.0 + +MyQmlObject { + property string expression + property string compare + property bool pass: false + onSignalWithVariant: + { + var expected = eval(expression); + pass = eval(compare)(arg, expected); + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/signalWithQJSValue.qml b/tests/auto/qml/qqmlecmascript/data/signalWithQJSValue.qml new file mode 100644 index 0000000000..36f481d533 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/signalWithQJSValue.qml @@ -0,0 +1,14 @@ +import Qt.test 1.0 + +MyQmlObject { + property string expression + property string compare + property bool pass: false + + onSignalWithQJSValue: + { + qjsvalueMethod(arg); + var expected = eval(expression); + pass = eval(compare)(arg, expected); + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/signalWithUnknownTypes.qml b/tests/auto/qml/qqmlecmascript/data/signalWithUnknownTypes.qml new file mode 100644 index 0000000000..49293edfb3 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/signalWithUnknownTypes.qml @@ -0,0 +1,5 @@ +import Qt.test 1.0 + +MyQmlObject { + onSignalWithUnknownType: variantMethod(arg); +} diff --git a/tests/auto/qml/qqmlecmascript/data/strictlyEquals.qml b/tests/auto/qml/qqmlecmascript/data/strictlyEquals.qml new file mode 100644 index 0000000000..e709e3a8bd --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/strictlyEquals.qml @@ -0,0 +1,17 @@ +import QtQuick 2.0 + +QtObject { + property bool test1: (a === true) + property bool test2: !(a === false) + property bool test3: (b === 11.2) + property bool test4: !(b === 9) + property bool test5: (c === 9) + property bool test6: !(c === 13) + property bool test7: (d === "Hello world") + property bool test8: !(d === "Hi") + + property bool a: true + property real b: 11.2 + property int c: 9 + property string d: "Hello world" +} diff --git a/tests/auto/qml/qqmlecmascript/data/stringArg.qml b/tests/auto/qml/qqmlecmascript/data/stringArg.qml new file mode 100644 index 0000000000..7019af9da5 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/stringArg.qml @@ -0,0 +1,49 @@ +import QtQuick 2.0 + +Item { + id: root + property bool returnValue: false + + property string first + property string second + property string third + property string fourth + property string fifth + property string sixth + property string seventh + property string eighth + property string ninth + + function success() { + var a = "Value is %1"; + for (var ii = 0; ii < 10; ++ii) { + first = a.arg("string"); + second = a.arg(1); + third = a.arg(true); + fourth = a.arg(3.345); + fifth = a.arg(undefined); + sixth = a.arg(null); + seventh = a.arg({"test":5}); + eighth = a.arg({"test":5, "again":6}); + } + + if (first != "Value is string") returnValue = false; + if (second != "Value is 1") returnValue = false; + if (third != "Value is true") returnValue = false; + if (fourth != "Value is 3.345") returnValue = false; + if (fifth != "Value is undefined") returnValue = false; + if (sixth != "Value is null") returnValue = false; + if (seventh != "Value is [Object object]") returnValue = false; + if (eighth != "Value is [Object object]") returnValue = false; + returnValue = true; + } + + function failure() { + returnValue = true; + var a = "Value is %1"; + for (var ii = 0; ii < 10; ++ii) { + ninth = a.arg(1,2,3,4); + } + returnValue = false; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/switchStatement.1.qml b/tests/auto/qml/qqmlecmascript/data/switchStatement.1.qml new file mode 100644 index 0000000000..3c7870839d --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/switchStatement.1.qml @@ -0,0 +1,33 @@ +import Qt.test 1.0 + +MyQmlObject { + value: { + var value = 0 + switch (stringProperty) { + case "A": + value = value + 1 + value = value + 1 + /* should fall through */ + case "S": + value = value + 1 + value = value + 1 + value = value + 1 + break; + case "D": { // with curly braces + value = value + 1 + value = value + 1 + value = value + 1 + break; + } + case "F": { + value = value + 1 + value = value + 1 + value = value + 1 + } + /* should fall through */ + default: + value = value + 1 + } + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/switchStatement.2.qml b/tests/auto/qml/qqmlecmascript/data/switchStatement.2.qml new file mode 100644 index 0000000000..928d36be1f --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/switchStatement.2.qml @@ -0,0 +1,33 @@ +import Qt.test 1.0 + +MyQmlObject { + value: { + var value = 0 + switch (stringProperty) { + case "A": + value = value + 1 + value = value + 1 + /* should fall through */ + case "S": + value = value + 1 + value = value + 1 + value = value + 1 + break; + default: + value = value + 1 + case "D": { // with curly braces + value = value + 1 + value = value + 1 + value = value + 1 + break; + } + case "F": { + value = value + 1 + value = value + 1 + value = value + 1 + } + /* should fall through */ + } + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/switchStatement.3.qml b/tests/auto/qml/qqmlecmascript/data/switchStatement.3.qml new file mode 100644 index 0000000000..5b05d88767 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/switchStatement.3.qml @@ -0,0 +1,33 @@ +import Qt.test 1.0 + +MyQmlObject { + value: { + var value = 0 + switch (stringProperty) { + default: + value = value + 1 + case "A": + value = value + 1 + value = value + 1 + /* should fall through */ + case "S": + value = value + 1 + value = value + 1 + value = value + 1 + break; + case "D": { // with curly braces + value = value + 1 + value = value + 1 + value = value + 1 + break; + } + case "F": { + value = value + 1 + value = value + 1 + value = value + 1 + } + /* should fall through */ + } + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/switchStatement.4.qml b/tests/auto/qml/qqmlecmascript/data/switchStatement.4.qml new file mode 100644 index 0000000000..43ba199a04 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/switchStatement.4.qml @@ -0,0 +1,31 @@ +import Qt.test 1.0 + +MyQmlObject { + value: { + var value = 0 + switch (stringProperty) { + case "A": + value = value + 1 + value = value + 1 + /* should fall through */ + case "S": + value = value + 1 + value = value + 1 + value = value + 1 + break; + case "D": { // with curly braces + value = value + 1 + value = value + 1 + value = value + 1 + break; + } + case "F": { + value = value + 1 + value = value + 1 + value = value + 1 + } + /* should fall through */ + } + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/switchStatement.5.qml b/tests/auto/qml/qqmlecmascript/data/switchStatement.5.qml new file mode 100644 index 0000000000..e0fc62e392 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/switchStatement.5.qml @@ -0,0 +1,12 @@ +import Qt.test 1.0 + +MyQmlObject { + value: { + var value = 0 + switch (stringProperty) { + default: + value = value + 1 + } + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/switchStatement.6.qml b/tests/auto/qml/qqmlecmascript/data/switchStatement.6.qml new file mode 100644 index 0000000000..6fb71eb345 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/switchStatement.6.qml @@ -0,0 +1,13 @@ +import Qt.test 1.0 + +MyQmlObject { + function one(kind) { return 123 } + function two(kind) { return 321 } + + value: switch (stringProperty) { + case "A": case "S": one(stringProperty); break; + case "D": case "F": two(stringProperty); break; + default: 0 + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/threadScript.js b/tests/auto/qml/qqmlecmascript/data/threadScript.js new file mode 100644 index 0000000000..9f94de1bc1 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/threadScript.js @@ -0,0 +1,4 @@ +WorkerScript.onMessage = function(msg) { + WorkerScript.sendMessage(msg); +} + diff --git a/tests/auto/qml/qqmlecmascript/data/transientErrors.2.qml b/tests/auto/qml/qqmlecmascript/data/transientErrors.2.qml new file mode 100644 index 0000000000..c44acf4fd1 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/transientErrors.2.qml @@ -0,0 +1,14 @@ +import QtQuick 2.0 + +QtObject { + id: root + + property variant a: 10 + property int x: 10 + property int test: a.x + + Component.onCompleted: { + a = 11; + a = root; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/transientErrors.qml b/tests/auto/qml/qqmlecmascript/data/transientErrors.qml new file mode 100644 index 0000000000..451bb51996 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/transientErrors.qml @@ -0,0 +1,10 @@ +import QtQuick 2.0 + +QtObject { + property variant obj: nested + + property variant obj2 + obj2: NestedTypeTransientErrors { + id: nested + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/tryStatement.1.qml b/tests/auto/qml/qqmlecmascript/data/tryStatement.1.qml new file mode 100644 index 0000000000..71cc67a941 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/tryStatement.1.qml @@ -0,0 +1,13 @@ +import Qt.test 1.0 + +MyQmlObject { + property int defaultValue: 123 + + function go() { + undefinedObject.method() // this call will throw an exception + return 321 + } + + value: try { go() } catch(e) { defaultValue } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/tryStatement.2.qml b/tests/auto/qml/qqmlecmascript/data/tryStatement.2.qml new file mode 100644 index 0000000000..e7fca0bff7 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/tryStatement.2.qml @@ -0,0 +1,11 @@ +import Qt.test 1.0 + +MyQmlObject { + property int defaultValue: 123 + + function go() { + return 321 + } + + value: try { go() } catch(e) { defaultValue } +} diff --git a/tests/auto/qml/qqmlecmascript/data/tryStatement.3.qml b/tests/auto/qml/qqmlecmascript/data/tryStatement.3.qml new file mode 100644 index 0000000000..04b39f73d5 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/tryStatement.3.qml @@ -0,0 +1,13 @@ +import Qt.test 1.0 + +MyQmlObject { + property int defaultValue: 123 + + function go() { + undefinedObject.method() // this call will throw an exception + return 321 + } + + value: try { var p = go() } catch(e) { var p = defaultValue } finally { p == 123 } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/tryStatement.4.qml b/tests/auto/qml/qqmlecmascript/data/tryStatement.4.qml new file mode 100644 index 0000000000..231aaf0683 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/tryStatement.4.qml @@ -0,0 +1,12 @@ +import Qt.test 1.0 + +MyQmlObject { + property int defaultValue: 123 + + function go() { + return 321 + } + + value: try { var p = go() } catch(e) { var p = defaultValue } finally { p == 321 } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/typeOf.js b/tests/auto/qml/qqmlecmascript/data/typeOf.js new file mode 100644 index 0000000000..16a34234c0 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/typeOf.js @@ -0,0 +1,25 @@ +var test1 = typeof a + +var b = {} +var test2 = typeof b + +var c = 5 +var test3 = typeof c + +var d = "hello world" +var test4 = typeof d + +var e = function() {} +var test5 = typeof e + +var f = null +var test6 = typeof f + +var g = undefined +var test7 = typeof g + +var h = true +var test8 = typeof h + +var i = [] +var test9 = typeof i diff --git a/tests/auto/qml/qqmlecmascript/data/typeOf.qml b/tests/auto/qml/qqmlecmascript/data/typeOf.qml new file mode 100644 index 0000000000..28f7debed5 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/typeOf.qml @@ -0,0 +1,26 @@ +import QtQuick 2.0 +import "typeOf.js" as TypeOf + +QtObject { + property string test1 + property string test2 + property string test3 + property string test4 + property string test5 + property string test6 + property string test7 + property string test8 + property string test9 + + Component.onCompleted: { + test1 = TypeOf.test1 + test2 = TypeOf.test2 + test3 = TypeOf.test3 + test4 = TypeOf.test4 + test5 = TypeOf.test5 + test6 = TypeOf.test6 + test7 = TypeOf.test7 + test8 = TypeOf.test8 + test9 = TypeOf.test9 + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/unaryExpression.qml b/tests/auto/qml/qqmlecmascript/data/unaryExpression.qml new file mode 100644 index 0000000000..0d40bec710 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/unaryExpression.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 + +Item { + + Component.onCompleted: +++bar + + property int bar: 0 +} diff --git a/tests/auto/qml/qqmlecmascript/data/undefinedResetsProperty.2.qml b/tests/auto/qml/qqmlecmascript/data/undefinedResetsProperty.2.qml new file mode 100644 index 0000000000..e73d38e2ce --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/undefinedResetsProperty.2.qml @@ -0,0 +1,10 @@ +import Qt.test 1.0 + +MyQmlObject { + resettableProperty: 19 + + function doReset() { + resettableProperty = undefined; + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/undefinedResetsProperty.qml b/tests/auto/qml/qqmlecmascript/data/undefinedResetsProperty.qml new file mode 100644 index 0000000000..eceff60aa1 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/undefinedResetsProperty.qml @@ -0,0 +1,7 @@ +import Qt.test 1.0 + +MyQmlObject { + property bool setUndefined: false + + resettableProperty: setUndefined?undefined:92 +} diff --git a/tests/auto/qml/qqmlecmascript/data/urlListProperty.qml b/tests/auto/qml/qqmlecmascript/data/urlListProperty.qml new file mode 100644 index 0000000000..eeb0815f09 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/urlListProperty.qml @@ -0,0 +1,41 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + // single url assignment to url list property + MySequenceConversionObject { + id: msco1 + objectName: "msco1" + } + + // single url binding to url list property + MySequenceConversionObject { + id: msco2 + objectName: "msco2" + urlListProperty: "http://qt-project.org/?get%3cDATA%3e"; + } + + // multiple url assignment to url list property + MySequenceConversionObject { + id: msco3 + objectName: "msco3" + } + + // multiple url binding to url list property + MySequenceConversionObject { + id: msco4 + objectName: "msco4" + urlListProperty: [ + "http://qt-project.org/?get%3cDATA%3e", + "http://qt-project.org/?get%3cDATA%3e" + ]; + } + + Component.onCompleted: { + msco1.urlListProperty = "http://qt-project.org/?get%3cDATA%3e"; + msco3.urlListProperty = [ + "http://qt-project.org/?get%3cDATA%3e", + "http://qt-project.org/?get%3cDATA%3e" + ]; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/urlProperty.1.qml b/tests/auto/qml/qqmlecmascript/data/urlProperty.1.qml new file mode 100644 index 0000000000..451cb03206 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/urlProperty.1.qml @@ -0,0 +1,10 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +MyQmlObject { + property bool result + urlProperty: stringProperty + "/index.html" + intProperty: if (urlProperty) 123; else 321 + value: urlProperty == stringProperty + "/index.html" + result: urlProperty == urlProperty +} diff --git a/tests/auto/qml/qqmlecmascript/data/urlProperty.2.qml b/tests/auto/qml/qqmlecmascript/data/urlProperty.2.qml new file mode 100644 index 0000000000..0e8bdaec96 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/urlProperty.2.qml @@ -0,0 +1,10 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +MyQmlObject { + property bool result + stringProperty: "http://example.org" + urlProperty: stringProperty + "/?get%3cDATA%3e" + value: urlProperty == stringProperty + "/?get%3cDATA%3e" + result: urlProperty == urlProperty +} diff --git a/tests/auto/qml/qqmlecmascript/data/v8bindingException.qml b/tests/auto/qml/qqmlecmascript/data/v8bindingException.qml new file mode 100644 index 0000000000..ff203e23e3 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/v8bindingException.qml @@ -0,0 +1,21 @@ +import QtQuick 2.0 + +// This test uses a multi-line string which has \r-terminated +// string fragments. The expression rewriter deliberately doesn't +// handle \r-terminated string fragments (see QTBUG-24064) and thus +// this test ensures that we don't crash when we encounter a +// non-compilable binding such as this one. + +Item { + id: root + + Component { + id: comp + Text { + property var value: "," + text: 'multi
line ' + value + 'str
ings' + } + } + + Component.onCompleted: comp.createObject(root, { "value": undefined }) +} diff --git a/tests/auto/qml/qqmlecmascript/data/v8functionException.qml b/tests/auto/qml/qqmlecmascript/data/v8functionException.qml new file mode 100644 index 0000000000..51df1c65d8 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/v8functionException.qml @@ -0,0 +1,15 @@ +import QtQuick 2.0 + +// This test uses a multi-line string which has \r-terminated +// string fragments. The expression rewriter deliberately doesn't +// handle \r-terminated string fragments (see QTBUG-24064) and thus +// this test ensures that we don't crash when the client attempts +// to invoke a non-compiled dynamic slot. + +Item { + id: root + + function dynamicSlot() { + var someString = "Hello,
this is a
multiline string"; + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/valueTypeFunctions.qml b/tests/auto/qml/qqmlecmascript/data/valueTypeFunctions.qml new file mode 100644 index 0000000000..33b4a68c40 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/valueTypeFunctions.qml @@ -0,0 +1,6 @@ +import Qt.test 1.0 + +MyTypeObject { + rectProperty: Qt.rect(0,0,100,100) + rectFProperty: Qt.rect(0,0.5,100,99.5) +} diff --git a/tests/auto/qml/qqmlecmascript/data/variantsAssignedUndefined.qml b/tests/auto/qml/qqmlecmascript/data/variantsAssignedUndefined.qml new file mode 100644 index 0000000000..6aa8480365 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/variantsAssignedUndefined.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 + +QtObject { + property bool runTest: false + onRunTestChanged: test1 = undefined + + property variant test1: 10 + property variant test2: (runTest == false)?11:undefined +} diff --git a/tests/auto/qml/qqmlecmascript/data/withStatement.1.qml b/tests/auto/qml/qqmlecmascript/data/withStatement.1.qml new file mode 100644 index 0000000000..28f0c08451 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/withStatement.1.qml @@ -0,0 +1,14 @@ +import Qt.test 1.0 + +MyQmlObject { + property var other: MyQmlObject { + intProperty: 123 + + function go() { + return intProperty; + } + } + + value: with(other) go() +} + diff --git a/tests/auto/qml/qqmlecmascript/data/writeAttachedProperty.qml b/tests/auto/qml/qqmlecmascript/data/writeAttachedProperty.qml new file mode 100644 index 0000000000..3854b069a0 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/writeAttachedProperty.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +QtObject { + function writeValue2() { MyQmlObject.value2 = 9 } +} diff --git a/tests/auto/qml/qqmlecmascript/data/writeRemovesBinding.qml b/tests/auto/qml/qqmlecmascript/data/writeRemovesBinding.qml new file mode 100644 index 0000000000..a1ba5df071 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/writeRemovesBinding.qml @@ -0,0 +1,46 @@ +import QtQuick 2.0 + +QtObject { + id: root + + property bool test: false + + property real data: 9 + property real binding: data + + property alias aliasProperty: root.aliasBinding + property real aliasBinding: data + + Component.onCompleted: { + // Non-aliased properties + if (binding != 9) return; + + data = 11; + if (binding != 11) return; + + binding = 6; + if (binding != 6) return; + + data = 3; + if (binding != 6) return; + + + // Writing through an aliased property + if (aliasProperty != 3) return; + if (aliasBinding != 3) return; + + data = 4; + if (aliasProperty != 4) return; + if (aliasBinding != 4) return; + + aliasProperty = 19; + if (aliasProperty != 19) return; + if (aliasBinding != 19) return; + + data = 5; + if (aliasProperty != 19) return; + if (aliasBinding != 19) return; + + test = true; + } +} diff --git a/tests/auto/qml/qqmlecmascript/qqmlecmascript.pro b/tests/auto/qml/qqmlecmascript/qqmlecmascript.pro new file mode 100644 index 0000000000..b07e4393a9 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/qqmlecmascript.pro @@ -0,0 +1,22 @@ +CONFIG += testcase +TARGET = tst_qqmlecmascript +macx:CONFIG -= app_bundle + +SOURCES += tst_qqmlecmascript.cpp \ + testtypes.cpp \ + ../../shared/testhttpserver.cpp +HEADERS += testtypes.h \ + ../../shared/testhttpserver.h +INCLUDEPATH += ../../shared + +include (../../shared/util.pri) + +# QMAKE_CXXFLAGS = -fprofile-arcs -ftest-coverage +# LIBS += -lgcov + +testDataFiles.files = data +testDataFiles.path = . +DEPLOYMENT += testDataFiles + +CONFIG += parallel_test +QT += core-private gui-private v8-private qml-private network widgets testlib diff --git a/tests/auto/qml/qqmlecmascript/testtypes.cpp b/tests/auto/qml/qqmlecmascript/testtypes.cpp new file mode 100644 index 0000000000..78119cb776 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/testtypes.cpp @@ -0,0 +1,210 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "testtypes.h" +#include <QWidget> +#include <QPlainTextEdit> +#include <QQmlEngine> +#include <QJSEngine> + +class BaseExtensionObject : public QObject +{ + Q_OBJECT + Q_PROPERTY(int baseExtendedProperty READ extendedProperty WRITE setExtendedProperty NOTIFY extendedPropertyChanged) +public: + BaseExtensionObject(QObject *parent) : QObject(parent), m_value(0) {} + + int extendedProperty() const { return m_value; } + void setExtendedProperty(int v) { m_value = v; emit extendedPropertyChanged(); } + +signals: + void extendedPropertyChanged(); +private: + int m_value; +}; + +class ExtensionObject : public QObject +{ + Q_OBJECT + Q_PROPERTY(int extendedProperty READ extendedProperty WRITE setExtendedProperty NOTIFY extendedPropertyChanged) +public: + ExtensionObject(QObject *parent) : QObject(parent), m_value(0) {} + + int extendedProperty() const { return m_value; } + void setExtendedProperty(int v) { m_value = v; emit extendedPropertyChanged(); } + +signals: + void extendedPropertyChanged(); +private: + int m_value; +}; + +class DefaultPropertyExtensionObject : public QObject +{ + Q_OBJECT + Q_CLASSINFO("DefaultProperty", "firstProperty") +public: + DefaultPropertyExtensionObject(QObject *parent) : QObject(parent) {} +}; + +class QWidgetDeclarativeUI : public QObject +{ + Q_OBJECT + + Q_PROPERTY(int width READ width WRITE setWidth NOTIFY widthChanged) + +signals: + void widthChanged(); + +public: + QWidgetDeclarativeUI(QObject *other) : QObject(other) { } + +public: + int width() const { return 0; } + void setWidth(int) { } +}; + +void MyQmlObject::v8function(QQmlV8Function *args) +{ + const char *error = "Exception thrown from within QObject slot"; + v8::ThrowException(v8::Exception::Error(v8::String::New(error))); +} + +static QJSValue script_api(QQmlEngine *engine, QJSEngine *scriptEngine) +{ + Q_UNUSED(engine) + + static int testProperty = 13; + QJSValue v = scriptEngine->newObject(); + v.setProperty("scriptTestProperty", testProperty++); + return v; +} + +static QJSValue readonly_script_api(QQmlEngine *engine, QJSEngine *scriptEngine) +{ + Q_UNUSED(engine) + + static int testProperty = 42; + QJSValue v = scriptEngine->newObject(); + v.setProperty("scriptTestProperty", testProperty++); + + // now freeze it so that it's read-only + QJSValue freezeFunction = scriptEngine->evaluate("(function(obj) { return Object.freeze(obj); })"); + v = freezeFunction.call(QJSValueList() << v); + + return v; +} + +static QObject *qobject_api(QQmlEngine *engine, QJSEngine *scriptEngine) +{ + Q_UNUSED(engine) + Q_UNUSED(scriptEngine) + + testQObjectApi *o = new testQObjectApi(); + o->setQObjectTestProperty(20); + o->setQObjectTestWritableProperty(50); + return o; +} + +static QObject *qobject_api_engine_parent(QQmlEngine *engine, QJSEngine *scriptEngine) +{ + Q_UNUSED(scriptEngine) + + static int testProperty = 26; + testQObjectApi *o = new testQObjectApi(engine); + o->setQObjectTestProperty(testProperty++); + return o; +} + +void registerTypes() +{ + qmlRegisterType<MyQmlObject>("Qt.test", 1,0, "MyQmlObjectAlias"); + qmlRegisterType<MyQmlObject>("Qt.test", 1,0, "MyQmlObject"); + qmlRegisterType<MyDeferredObject>("Qt.test", 1,0, "MyDeferredObject"); + qmlRegisterType<MyQmlContainer>("Qt.test", 1,0, "MyQmlContainer"); + qmlRegisterExtendedType<MyBaseExtendedObject, BaseExtensionObject>("Qt.test", 1,0, "MyBaseExtendedObject"); + qmlRegisterExtendedType<MyExtendedObject, ExtensionObject>("Qt.test", 1,0, "MyExtendedObject"); + qmlRegisterType<MyTypeObject>("Qt.test", 1,0, "MyTypeObject"); + qmlRegisterType<MyDerivedObject>("Qt.test", 1,0, "MyDerivedObject"); + qmlRegisterType<NumberAssignment>("Qt.test", 1,0, "NumberAssignment"); + qmlRegisterExtendedType<DefaultPropertyExtendedObject, DefaultPropertyExtensionObject>("Qt.test", 1,0, "DefaultPropertyExtendedObject"); + qmlRegisterType<OverrideDefaultPropertyObject>("Qt.test", 1,0, "OverrideDefaultPropertyObject"); + qmlRegisterType<MyRevisionedClass>("Qt.test",1,0,"MyRevisionedClass"); + qmlRegisterType<MyDeleteObject>("Qt.test", 1,0, "MyDeleteObject"); + qmlRegisterType<MyRevisionedClass,1>("Qt.test",1,1,"MyRevisionedClass"); + + // test scarce resource property binding post-evaluation optimisation + // and for testing memory usage in property var circular reference test + qmlRegisterType<ScarceResourceObject>("Qt.test", 1,0, "MyScarceResourceObject"); + + // Register the uncreatable base class + qmlRegisterRevision<MyRevisionedBaseClassRegistered,1>("Qt.test",1,1); + // MyRevisionedSubclass 1.0 uses MyRevisionedClass revision 0 + qmlRegisterType<MyRevisionedSubclass>("Qt.test",1,0,"MyRevisionedSubclass"); + // MyRevisionedSubclass 1.1 uses MyRevisionedClass revision 1 + qmlRegisterType<MyRevisionedSubclass,1>("Qt.test",1,1,"MyRevisionedSubclass"); + + qmlRegisterExtendedType<QWidget,QWidgetDeclarativeUI>("Qt.test",1,0,"QWidget"); + qmlRegisterType<QPlainTextEdit>("Qt.test",1,0,"QPlainTextEdit"); + + qRegisterMetaType<MyQmlObject::MyType>("MyQmlObject::MyType"); + + qmlRegisterModuleApi("Qt.test",1,0,script_api); // register (script) module API for an existing uri which contains elements + qmlRegisterModuleApi("Qt.test",1,0,qobject_api); // register (qobject) for an existing uri for which another module API was previously regd. Should replace! + qmlRegisterModuleApi("Qt.test.scriptApi",1,0,script_api); // register (script) module API for a uri which doesn't contain elements + qmlRegisterModuleApi("Qt.test.scriptApi",2,0,readonly_script_api); // register (script) module API for a uri which doesn't contain elements - will be made read-only + qmlRegisterModuleApi("Qt.test.qobjectApi",1,0,qobject_api); // register (qobject) module API for a uri which doesn't contain elements + qmlRegisterModuleApi("Qt.test.qobjectApi",1,3,qobject_api); // register (qobject) module API for a uri which doesn't contain elements, minor version set + qmlRegisterModuleApi("Qt.test.qobjectApi",2,0,qobject_api); // register (qobject) module API for a uri which doesn't contain elements, major version set + qmlRegisterModuleApi("Qt.test.qobjectApiParented",1,0,qobject_api_engine_parent); // register (parented qobject) module API for a uri which doesn't contain elements + + qRegisterMetaType<MyQmlObject::MyType>("MyEnum2"); + qRegisterMetaType<Qt::MouseButtons>("Qt::MouseButtons"); + + qmlRegisterType<CircularReferenceObject>("Qt.test", 1, 0, "CircularReferenceObject"); + qmlRegisterType<CircularReferenceHandle>("Qt.test", 1, 0, "CircularReferenceHandle"); + + qmlRegisterType<MyDynamicCreationDestructionObject>("Qt.test", 1, 0, "MyDynamicCreationDestructionObject"); + qmlRegisterType<WriteCounter>("Qt.test", 1, 0, "WriteCounter"); + + qmlRegisterType<MySequenceConversionObject>("Qt.test", 1, 0, "MySequenceConversionObject"); +} + +#include "testtypes.moc" diff --git a/tests/auto/qml/qqmlecmascript/testtypes.h b/tests/auto/qml/qqmlecmascript/testtypes.h new file mode 100644 index 0000000000..154e6f019c --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/testtypes.h @@ -0,0 +1,1311 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef TESTTYPES_H +#define TESTTYPES_H + +#include <QtCore/qobject.h> +#include <QtQml/qqml.h> +#include <QtQml/qqmlexpression.h> +#include <QtCore/qpoint.h> +#include <QtCore/qsize.h> +#include <QtQml/qqmllist.h> +#include <QtCore/qrect.h> +#include <QtGui/qmatrix.h> +#include <QtGui/qcolor.h> +#include <QtGui/qvector3d.h> +#include <QtGui/QPixmap> +#include <QtCore/qdatetime.h> +#include <QtQml/qjsvalue.h> +#include <QtQml/qqmlscriptstring.h> +#include <QtQml/qqmlcomponent.h> + +#include <private/qqmlengine_p.h> +#include <private/qv8engine_p.h> + +class MyQmlAttachedObject : public QObject +{ + Q_OBJECT + Q_PROPERTY(int value READ value CONSTANT) + Q_PROPERTY(int value2 READ value2 WRITE setValue2 NOTIFY value2Changed) +public: + MyQmlAttachedObject(QObject *parent) : QObject(parent), m_value2(0) {} + + int value() const { return 19; } + int value2() const { return m_value2; } + void setValue2(int v) { if (m_value2 == v) return; m_value2 = v; emit value2Changed(); } + + void emitMySignal() { emit mySignal(); } + +signals: + void value2Changed(); + void mySignal(); + +private: + int m_value2; +}; + +class MyQmlObject : public QObject +{ + Q_OBJECT + Q_ENUMS(MyEnum) + Q_ENUMS(MyEnum2) + Q_PROPERTY(int deleteOnSet READ deleteOnSet WRITE setDeleteOnSet) + Q_PROPERTY(bool trueProperty READ trueProperty CONSTANT) + Q_PROPERTY(bool falseProperty READ falseProperty CONSTANT) + Q_PROPERTY(int value READ value WRITE setValue) + Q_PROPERTY(int console READ console CONSTANT) + Q_PROPERTY(QString stringProperty READ stringProperty WRITE setStringProperty NOTIFY stringChanged) + Q_PROPERTY(QUrl urlProperty READ urlProperty WRITE setUrlProperty NOTIFY urlChanged) + Q_PROPERTY(QObject *objectProperty READ objectProperty WRITE setObjectProperty NOTIFY objectChanged) + Q_PROPERTY(QQmlListProperty<QObject> objectListProperty READ objectListProperty CONSTANT) + Q_PROPERTY(int resettableProperty READ resettableProperty WRITE setResettableProperty RESET resetProperty) + Q_PROPERTY(QRegExp regExp READ regExp WRITE setRegExp) + Q_PROPERTY(int nonscriptable READ nonscriptable WRITE setNonscriptable SCRIPTABLE false) + Q_PROPERTY(int intProperty READ intProperty WRITE setIntProperty NOTIFY intChanged) + +public: + MyQmlObject(): myinvokableObject(0), m_methodCalled(false), m_methodIntCalled(false), m_object(0), m_value(0), m_resetProperty(13), m_intProperty(0) {} + + enum MyEnum { EnumValue1 = 0, EnumValue2 = 1 }; + enum MyEnum2 { EnumValue3 = 2, EnumValue4 = 3 }; + + bool trueProperty() const { return true; } + bool falseProperty() const { return false; } + + QString stringProperty() const { return m_string; } + void setStringProperty(const QString &s) + { + if (s == m_string) + return; + m_string = s; + emit stringChanged(); + } + + QUrl urlProperty() const { return m_url; } + void setUrlProperty(const QUrl &url) + { + if (url == m_url) + return; + m_url = url; + emit urlChanged(); + } + + QObject *objectProperty() const { return m_object; } + void setObjectProperty(QObject *obj) { + if (obj == m_object) + return; + m_object = obj; + emit objectChanged(); + } + + QQmlListProperty<QObject> objectListProperty() { return QQmlListProperty<QObject>(this, m_objectQList); } + + bool methodCalled() const { return m_methodCalled; } + bool methodIntCalled() const { return m_methodIntCalled; } + + QString string() const { return m_string; } + + static MyQmlAttachedObject *qmlAttachedProperties(QObject *o) { + return new MyQmlAttachedObject(o); + } + + int deleteOnSet() const { return 1; } + void setDeleteOnSet(int v) { if(v) delete this; } + + int value() const { return m_value; } + void setValue(int v) { m_value = v; } + + int resettableProperty() const { return m_resetProperty; } + void setResettableProperty(int v) { m_resetProperty = v; } + void resetProperty() { m_resetProperty = 13; } + + QRegExp regExp() { return m_regExp; } + void setRegExp(const QRegExp ®Exp) { m_regExp = regExp; } + + int console() const { return 11; } + + int nonscriptable() const { return 0; } + void setNonscriptable(int) {} + + MyQmlObject *myinvokableObject; + Q_INVOKABLE MyQmlObject *returnme() { return this; } + + struct MyType { + int value; + }; + QVariant variant() const { return m_variant; } + QJSValue qjsvalue() const { return m_qjsvalue; } + + int intProperty() const { return m_intProperty; } + void setIntProperty(int i) { m_intProperty = i; emit intChanged(); } + +signals: + void basicSignal(); + void argumentSignal(int a, QString b, qreal c, MyEnum2 d, Qt::MouseButtons e); + void stringChanged(); + void urlChanged(); + void objectChanged(); + void anotherBasicSignal(); + void thirdBasicSignal(); + void signalWithUnknownType(const MyQmlObject::MyType &arg); + void signalWithVariant(const QVariant &arg); + void signalWithQJSValue(const QJSValue &arg); + void intChanged(); + +public slots: + void deleteMe() { delete this; } + void methodNoArgs() { m_methodCalled = true; } + void method(int a) { if(a == 163) m_methodIntCalled = true; } + void setString(const QString &s) { m_string = s; } + void myinvokable(MyQmlObject *o) { myinvokableObject = o; } + void variantMethod(const QVariant &v) { m_variant = v; } + void qjsvalueMethod(const QJSValue &v) { m_qjsvalue = v; } + void v8function(QQmlV8Function*); + +private: + friend class tst_qqmlecmascript; + bool m_methodCalled; + bool m_methodIntCalled; + + QObject *m_object; + QString m_string; + QUrl m_url; + QList<QObject *> m_objectQList; + int m_value; + int m_resetProperty; + QRegExp m_regExp; + QVariant m_variant; + QJSValue m_qjsvalue; + int m_intProperty; +}; + +QML_DECLARE_TYPEINFO(MyQmlObject, QML_HAS_ATTACHED_PROPERTIES) + +class MyQmlContainer : public QObject +{ + Q_OBJECT + Q_PROPERTY(QQmlListProperty<MyQmlObject> children READ children CONSTANT) +public: + MyQmlContainer() {} + + QQmlListProperty<MyQmlObject> children() { return QQmlListProperty<MyQmlObject>(this, m_children); } + +private: + QList<MyQmlObject*> m_children; +}; + + +class MyExpression : public QQmlExpression +{ + Q_OBJECT +public: + MyExpression(QQmlContext *ctxt, const QString &expr) + : QQmlExpression(ctxt, 0, expr), changed(false) + { + QObject::connect(this, SIGNAL(valueChanged()), this, SLOT(expressionValueChanged())); + setNotifyOnValueChanged(true); + } + + bool changed; + +public slots: + void expressionValueChanged() { + changed = true; + } +}; + + +class MyDefaultObject1 : public QObject +{ + Q_OBJECT + Q_PROPERTY(int horseLegs READ horseLegs CONSTANT) + Q_PROPERTY(int antLegs READ antLegs CONSTANT) + Q_PROPERTY(int emuLegs READ emuLegs CONSTANT) +public: + int horseLegs() const { return 4; } + int antLegs() const { return 6; } + int emuLegs() const { return 2; } +}; + +class MyDefaultObject3 : public QObject +{ + Q_OBJECT + Q_PROPERTY(int antLegs READ antLegs CONSTANT) + Q_PROPERTY(int humanLegs READ humanLegs CONSTANT) +public: + int antLegs() const { return 7; } // Mutant + int humanLegs() const { return 2; } + int millipedeLegs() const { return 1000; } +}; + +class MyDeferredObject : public QObject +{ + Q_OBJECT + Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged) + Q_PROPERTY(QObject *objectProperty READ objectProperty WRITE setObjectProperty) + Q_PROPERTY(QObject *objectProperty2 READ objectProperty2 WRITE setObjectProperty2) + Q_CLASSINFO("DeferredPropertyNames", "value,objectProperty,objectProperty2") + +public: + MyDeferredObject() : m_value(0), m_object(0), m_object2(0) {} + + int value() const { return m_value; } + void setValue(int v) { m_value = v; emit valueChanged(); } + + QObject *objectProperty() const { return m_object; } + void setObjectProperty(QObject *obj) { m_object = obj; } + + QObject *objectProperty2() const { return m_object2; } + void setObjectProperty2(QObject *obj) { m_object2 = obj; } + +signals: + void valueChanged(); + +private: + int m_value; + QObject *m_object; + QObject *m_object2; +}; + +class MyBaseExtendedObject : public QObject +{ +Q_OBJECT +Q_PROPERTY(int baseProperty READ baseProperty WRITE setBaseProperty) +public: + MyBaseExtendedObject() : m_value(0) {} + + int baseProperty() const { return m_value; } + void setBaseProperty(int v) { m_value = v; } + +private: + int m_value; +}; + +class MyExtendedObject : public MyBaseExtendedObject +{ +Q_OBJECT +Q_PROPERTY(int coreProperty READ coreProperty WRITE setCoreProperty) +public: + MyExtendedObject() : m_value(0) {} + + int coreProperty() const { return m_value; } + void setCoreProperty(int v) { m_value = v; } + +private: + int m_value; +}; + +class MyTypeObject : public QObject +{ + Q_OBJECT + Q_ENUMS(MyEnum) + Q_FLAGS(MyFlags) + + Q_PROPERTY(QString id READ id WRITE setId) + Q_PROPERTY(QObject *objectProperty READ objectProperty WRITE setObjectProperty) + Q_PROPERTY(QQmlComponent *componentProperty READ componentProperty WRITE setComponentProperty) + Q_PROPERTY(MyFlags flagProperty READ flagProperty WRITE setFlagProperty) + Q_PROPERTY(MyEnum enumProperty READ enumProperty WRITE setEnumProperty) + Q_PROPERTY(QString stringProperty READ stringProperty WRITE setStringProperty) + Q_PROPERTY(uint uintProperty READ uintProperty WRITE setUintProperty) + Q_PROPERTY(int intProperty READ intProperty WRITE setIntProperty) + Q_PROPERTY(qreal realProperty READ realProperty WRITE setRealProperty) + Q_PROPERTY(double doubleProperty READ doubleProperty WRITE setDoubleProperty) + Q_PROPERTY(float floatProperty READ floatProperty WRITE setFloatProperty) + Q_PROPERTY(QColor colorProperty READ colorProperty WRITE setColorProperty) + Q_PROPERTY(QDate dateProperty READ dateProperty WRITE setDateProperty) + Q_PROPERTY(QTime timeProperty READ timeProperty WRITE setTimeProperty) + Q_PROPERTY(QDateTime dateTimeProperty READ dateTimeProperty WRITE setDateTimeProperty) + Q_PROPERTY(QPoint pointProperty READ pointProperty WRITE setPointProperty) + Q_PROPERTY(QPointF pointFProperty READ pointFProperty WRITE setPointFProperty) + Q_PROPERTY(QSize sizeProperty READ sizeProperty WRITE setSizeProperty) + Q_PROPERTY(QSizeF sizeFProperty READ sizeFProperty WRITE setSizeFProperty) + Q_PROPERTY(QRect rectProperty READ rectProperty WRITE setRectProperty NOTIFY rectPropertyChanged) + Q_PROPERTY(QRect rectProperty2 READ rectProperty2 WRITE setRectProperty2) + Q_PROPERTY(QRectF rectFProperty READ rectFProperty WRITE setRectFProperty) + Q_PROPERTY(bool boolProperty READ boolProperty WRITE setBoolProperty) + Q_PROPERTY(QVariant variantProperty READ variantProperty WRITE setVariantProperty) + Q_PROPERTY(QVector3D vectorProperty READ vectorProperty WRITE setVectorProperty) + Q_PROPERTY(QUrl urlProperty READ urlProperty WRITE setUrlProperty) + + Q_PROPERTY(QQmlScriptString scriptProperty READ scriptProperty WRITE setScriptProperty) + +public: + MyTypeObject() + : objectPropertyValue(0), componentPropertyValue(0) {} + + QString idValue; + QString id() const { + return idValue; + } + void setId(const QString &v) { + idValue = v; + } + + QObject *objectPropertyValue; + QObject *objectProperty() const { + return objectPropertyValue; + } + void setObjectProperty(QObject *v) { + objectPropertyValue = v; + } + + QQmlComponent *componentPropertyValue; + QQmlComponent *componentProperty() const { + return componentPropertyValue; + } + void setComponentProperty(QQmlComponent *v) { + componentPropertyValue = v; + } + + enum MyFlag { FlagVal1 = 0x01, FlagVal2 = 0x02, FlagVal3 = 0x04 }; + Q_DECLARE_FLAGS(MyFlags, MyFlag) + MyFlags flagPropertyValue; + MyFlags flagProperty() const { + return flagPropertyValue; + } + void setFlagProperty(MyFlags v) { + flagPropertyValue = v; + } + + enum MyEnum { EnumVal1, EnumVal2 }; + MyEnum enumPropertyValue; + MyEnum enumProperty() const { + return enumPropertyValue; + } + void setEnumProperty(MyEnum v) { + enumPropertyValue = v; + } + + QString stringPropertyValue; + QString stringProperty() const { + return stringPropertyValue; + } + void setStringProperty(const QString &v) { + stringPropertyValue = v; + } + + uint uintPropertyValue; + uint uintProperty() const { + return uintPropertyValue; + } + void setUintProperty(const uint &v) { + uintPropertyValue = v; + } + + int intPropertyValue; + int intProperty() const { + return intPropertyValue; + } + void setIntProperty(const int &v) { + intPropertyValue = v; + } + + qreal realPropertyValue; + qreal realProperty() const { + return realPropertyValue; + } + void setRealProperty(const qreal &v) { + realPropertyValue = v; + } + + double doublePropertyValue; + double doubleProperty() const { + return doublePropertyValue; + } + void setDoubleProperty(const double &v) { + doublePropertyValue = v; + } + + float floatPropertyValue; + float floatProperty() const { + return floatPropertyValue; + } + void setFloatProperty(const float &v) { + floatPropertyValue = v; + } + + QColor colorPropertyValue; + QColor colorProperty() const { + return colorPropertyValue; + } + void setColorProperty(const QColor &v) { + colorPropertyValue = v; + } + + QDate datePropertyValue; + QDate dateProperty() const { + return datePropertyValue; + } + void setDateProperty(const QDate &v) { + datePropertyValue = v; + } + + QTime timePropertyValue; + QTime timeProperty() const { + return timePropertyValue; + } + void setTimeProperty(const QTime &v) { + timePropertyValue = v; + } + + QDateTime dateTimePropertyValue; + QDateTime dateTimeProperty() const { + return dateTimePropertyValue; + } + void setDateTimeProperty(const QDateTime &v) { + dateTimePropertyValue = v; + } + + QPoint pointPropertyValue; + QPoint pointProperty() const { + return pointPropertyValue; + } + void setPointProperty(const QPoint &v) { + pointPropertyValue = v; + } + + QPointF pointFPropertyValue; + QPointF pointFProperty() const { + return pointFPropertyValue; + } + void setPointFProperty(const QPointF &v) { + pointFPropertyValue = v; + } + + QSize sizePropertyValue; + QSize sizeProperty() const { + return sizePropertyValue; + } + void setSizeProperty(const QSize &v) { + sizePropertyValue = v; + } + + QSizeF sizeFPropertyValue; + QSizeF sizeFProperty() const { + return sizeFPropertyValue; + } + void setSizeFProperty(const QSizeF &v) { + sizeFPropertyValue = v; + } + + QRect rectPropertyValue; + QRect rectProperty() const { + return rectPropertyValue; + } + void setRectProperty(const QRect &v) { + rectPropertyValue = v; + emit rectPropertyChanged(); + } + + QRect rectPropertyValue2; + QRect rectProperty2() const { + return rectPropertyValue2; + } + void setRectProperty2(const QRect &v) { + rectPropertyValue2 = v; + } + + QRectF rectFPropertyValue; + QRectF rectFProperty() const { + return rectFPropertyValue; + } + void setRectFProperty(const QRectF &v) { + rectFPropertyValue = v; + } + + bool boolPropertyValue; + bool boolProperty() const { + return boolPropertyValue; + } + void setBoolProperty(const bool &v) { + boolPropertyValue = v; + } + + QVariant variantPropertyValue; + QVariant variantProperty() const { + return variantPropertyValue; + } + void setVariantProperty(const QVariant &v) { + variantPropertyValue = v; + } + + QVector3D vectorPropertyValue; + QVector3D vectorProperty() const { + return vectorPropertyValue; + } + void setVectorProperty(const QVector3D &v) { + vectorPropertyValue = v; + } + + QUrl urlPropertyValue; + QUrl urlProperty() const { + return urlPropertyValue; + } + void setUrlProperty(const QUrl &v) { + urlPropertyValue = v; + } + + QQmlScriptString scriptPropertyValue; + QQmlScriptString scriptProperty() const { + return scriptPropertyValue; + } + void setScriptProperty(const QQmlScriptString &v) { + scriptPropertyValue = v; + } + + void doAction() { emit action(); } +signals: + void action(); + void rectPropertyChanged(); +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(MyTypeObject::MyFlags) + +class MyDerivedObject : public MyTypeObject +{ + Q_OBJECT +public: + Q_INVOKABLE bool intProperty() const { + return true; + } +}; + +Q_DECLARE_METATYPE(QJSValue); +class MyInvokableBaseObject : public QObject +{ + Q_OBJECT +public: + inline ~MyInvokableBaseObject() = 0; + + Q_INVOKABLE inline void method_inherited(int a); + Q_INVOKABLE inline void method_overload(); +}; + +class MyInvokableObject : public MyInvokableBaseObject +{ + Q_OBJECT + Q_ENUMS(TestEnum) +public: + enum TestEnum { EnumValue1, EnumValue2 }; + MyInvokableObject() { reset(); } + + int invoked() const { return m_invoked; } + bool error() const { return m_invokedError; } + const QVariantList &actuals() const { return m_actuals; } + void reset() { m_invoked = -1; m_invokedError = false; m_actuals.clear(); } + + Q_INVOKABLE QPointF method_get_QPointF() { return QPointF(99.3, -10.2); } + Q_INVOKABLE QPoint method_get_QPoint() { return QPoint(9, 12); } + + Q_INVOKABLE void method_NoArgs() { invoke(0); } + Q_INVOKABLE int method_NoArgs_int() { invoke(1); return 6; } + Q_INVOKABLE qreal method_NoArgs_real() { invoke(2); return 19.75; } + Q_INVOKABLE QPointF method_NoArgs_QPointF() { invoke(3); return QPointF(123, 4.5); } + Q_INVOKABLE QObject *method_NoArgs_QObject() { invoke(4); return this; } + Q_INVOKABLE MyInvokableObject *method_NoArgs_unknown() { invoke(5); return this; } + Q_INVOKABLE QJSValue method_NoArgs_QScriptValue() { invoke(6); return QJSValue("Hello world"); } + Q_INVOKABLE QVariant method_NoArgs_QVariant() { invoke(7); return QVariant("QML rocks"); } + + Q_INVOKABLE void method_int(int a) { invoke(8); m_actuals << a; } + Q_INVOKABLE void method_intint(int a, int b) { invoke(9); m_actuals << a << b; } + Q_INVOKABLE void method_real(qreal a) { invoke(10); m_actuals << a; } + Q_INVOKABLE void method_QString(QString a) { invoke(11); m_actuals << a; } + Q_INVOKABLE void method_QPointF(QPointF a) { invoke(12); m_actuals << a; } + Q_INVOKABLE void method_QObject(QObject *a) { invoke(13); m_actuals << qVariantFromValue(a); } + Q_INVOKABLE void method_QScriptValue(QJSValue a) { invoke(14); m_actuals << qVariantFromValue(a); } + Q_INVOKABLE void method_intQScriptValue(int a, QJSValue b) { invoke(15); m_actuals << a << qVariantFromValue(b); } + + Q_INVOKABLE void method_overload(int a) { invoke(16); m_actuals << a; } + Q_INVOKABLE void method_overload(int a, int b) { invoke(17); m_actuals << a << b; } + Q_INVOKABLE void method_overload(QString a) { invoke(18); m_actuals << a; } + + Q_INVOKABLE void method_with_enum(TestEnum e) { invoke(19); m_actuals << (int)e; } + + Q_INVOKABLE int method_default(int a, int b = 19) { invoke(20); m_actuals << a << b; return b; } + + Q_INVOKABLE void method_QVariant(QVariant a, QVariant b = QVariant()) { invoke(21); m_actuals << a << b; } + +private: + friend class MyInvokableBaseObject; + void invoke(int idx) { if (m_invoked != -1) m_invokedError = true; m_invoked = idx;} + int m_invoked; + bool m_invokedError; + QVariantList m_actuals; +}; + +MyInvokableBaseObject::~MyInvokableBaseObject() {} + +void MyInvokableBaseObject::method_inherited(int a) +{ + static_cast<MyInvokableObject *>(this)->invoke(-3); + static_cast<MyInvokableObject *>(this)->m_actuals << a; +} + +// This is a hidden overload of the MyInvokableObject::method_overload() method +void MyInvokableBaseObject::method_overload() +{ + static_cast<MyInvokableObject *>(this)->invoke(-2); +} + +class NumberAssignment : public QObject +{ + Q_OBJECT +public: + Q_PROPERTY(qreal test1 READ test1 WRITE setTest1) + qreal _test1; + qreal test1() const { return _test1; } + void setTest1(qreal v) { _test1 = v; } + + Q_PROPERTY(qreal test2 READ test2 WRITE setTest2) + qreal _test2; + qreal test2() const { return _test2; } + void setTest2(qreal v) { _test2 = v; } + + Q_PROPERTY(qreal test3 READ test3 WRITE setTest3) + qreal _test3; + qreal test3() const { return _test3; } + void setTest3(qreal v) { _test3 = v; } + + Q_PROPERTY(qreal test4 READ test4 WRITE setTest4) + qreal _test4; + qreal test4() const { return _test4; } + void setTest4(qreal v) { _test4 = v; } + + Q_PROPERTY(int test5 READ test5 WRITE setTest5) + int _test5; + int test5() const { return _test5; } + void setTest5(int v) { _test5 = v; } + + Q_PROPERTY(int test6 READ test6 WRITE setTest6) + int _test6; + int test6() const { return _test6; } + void setTest6(int v) { _test6 = v; } + + Q_PROPERTY(int test7 READ test7 WRITE setTest7) + int _test7; + int test7() const { return _test7; } + void setTest7(int v) { _test7 = v; } + + Q_PROPERTY(int test8 READ test8 WRITE setTest8) + int _test8; + int test8() const { return _test8; } + void setTest8(int v) { _test8 = v; } + + Q_PROPERTY(unsigned int test9 READ test9 WRITE setTest9) + unsigned int _test9; + unsigned int test9() const { return _test9; } + void setTest9(unsigned int v) { _test9 = v; } + + Q_PROPERTY(unsigned int test10 READ test10 WRITE setTest10) + unsigned int _test10; + unsigned int test10() const { return _test10; } + void setTest10(unsigned int v) { _test10 = v; } + + Q_PROPERTY(unsigned int test11 READ test11 WRITE setTest11) + unsigned int _test11; + unsigned int test11() const { return _test11; } + void setTest11(unsigned int v) { _test11 = v; } + + Q_PROPERTY(unsigned int test12 READ test12 WRITE setTest12) + unsigned int _test12; + unsigned int test12() const { return _test12; } + void setTest12(unsigned int v) { _test12 = v; } +}; + +class DefaultPropertyExtendedObject : public QObject +{ + Q_OBJECT + Q_PROPERTY(QObject *firstProperty READ firstProperty WRITE setFirstProperty) + Q_PROPERTY(QObject *secondProperty READ secondProperty WRITE setSecondProperty) +public: + DefaultPropertyExtendedObject(QObject *parent = 0) : QObject(parent), m_firstProperty(0), m_secondProperty(0) {} + + QObject *firstProperty() const { return m_firstProperty; } + QObject *secondProperty() const { return m_secondProperty; } + void setFirstProperty(QObject *property) { m_firstProperty = property; } + void setSecondProperty(QObject *property) { m_secondProperty = property; } +private: + QObject* m_firstProperty; + QObject* m_secondProperty; +}; + +class OverrideDefaultPropertyObject : public DefaultPropertyExtendedObject +{ + Q_OBJECT + Q_CLASSINFO("DefaultProperty", "secondProperty") +public: + OverrideDefaultPropertyObject() {} +}; + +class MyRevisionedBaseClassRegistered : public QObject +{ +Q_OBJECT + Q_PROPERTY(qreal propA READ propA WRITE setPropA NOTIFY propAChanged) + Q_PROPERTY(qreal propB READ propB WRITE setPropB NOTIFY propBChanged REVISION 1) + +public: + MyRevisionedBaseClassRegistered() : m_pa(1), m_pb(2) {} + + qreal propA() const { return m_pa; } + void setPropA(qreal p) { + if (p != m_pa) { + m_pa = p; + emit propAChanged(); + } + } + qreal propB() const { return m_pb; } + void setPropB(qreal p) { + if (p != m_pb) { + m_pb = p; + emit propBChanged(); + } + } + + Q_INVOKABLE void methodA() { } + Q_INVOKABLE Q_REVISION(1) void methodB() { } + +signals: + void propAChanged(); + void propBChanged(); + + void signalA(); + Q_REVISION(1) void signalB(); + +protected: + qreal m_pa; + qreal m_pb; +}; + +class MyRevisionedBaseClassUnregistered : public MyRevisionedBaseClassRegistered +{ +Q_OBJECT + Q_PROPERTY(qreal propC READ propC WRITE setPropC NOTIFY propCChanged) + Q_PROPERTY(qreal propD READ propD WRITE setPropD NOTIFY propDChanged REVISION 1) + +public: + MyRevisionedBaseClassUnregistered() : m_pc(1), m_pd(2) {} + + qreal propC() const { return m_pc; } + void setPropC(qreal p) { + if (p != m_pc) { + m_pc = p; + emit propCChanged(); + } + } + qreal propD() const { return m_pd; } + void setPropD(qreal p) { + if (p != m_pd) { + m_pd = p; + emit propDChanged(); + } + } + + Q_INVOKABLE void methodC() { } + Q_INVOKABLE Q_REVISION(1) void methodD() { } + +signals: + void propCChanged(); + void propDChanged(); + + void signalC(); + Q_REVISION(1) void signalD(); + +protected: + qreal m_pc; + qreal m_pd; +}; + +class MyRevisionedClass : public MyRevisionedBaseClassUnregistered +{ + Q_OBJECT + Q_PROPERTY(qreal prop1 READ prop1 WRITE setProp1 NOTIFY prop1Changed) + Q_PROPERTY(qreal prop2 READ prop2 WRITE setProp2 NOTIFY prop2Changed REVISION 1) + +public: + MyRevisionedClass() {} + + qreal prop1() const { return m_p1; } + void setProp1(qreal p) { + if (p != m_p1) { + m_p1 = p; + emit prop1Changed(); + } + } + qreal prop2() const { return m_p2; } + void setProp2(qreal p) { + if (p != m_p2) { + m_p2 = p; + emit prop2Changed(); + } + } + + Q_INVOKABLE void method1() { } + Q_INVOKABLE Q_REVISION(1) void method2() { } + +signals: + void prop1Changed(); + void prop2Changed(); + + void signal1(); + Q_REVISION(1) void signal2(); + +protected: + qreal m_p1; + qreal m_p2; +}; + +class MyRevisionedSubclass : public MyRevisionedClass +{ + Q_OBJECT + Q_PROPERTY(qreal prop3 READ prop3 WRITE setProp3 NOTIFY prop3Changed) + Q_PROPERTY(qreal prop4 READ prop4 WRITE setProp4 NOTIFY prop4Changed REVISION 1) + +public: + MyRevisionedSubclass() : m_p3(3), m_p4(4) {} + + qreal prop3() const { return m_p3; } + void setProp3(qreal p) { + if (p != m_p3) { + m_p3 = p; + emit prop3Changed(); + } + } + qreal prop4() const { return m_p4; } + void setProp4(qreal p) { + if (p != m_p4) { + m_p4 = p; + emit prop4Changed(); + } + } + + Q_INVOKABLE void method3() { } + Q_INVOKABLE Q_REVISION(1) void method4() { } + +signals: + void prop3Changed(); + void prop4Changed(); + + void signal3(); + Q_REVISION(1) void signal4(); + +protected: + qreal m_p3; + qreal m_p4; +}; + +QML_DECLARE_TYPE(MyRevisionedBaseClassRegistered) +QML_DECLARE_TYPE(MyRevisionedBaseClassUnregistered) +QML_DECLARE_TYPE(MyRevisionedClass) +QML_DECLARE_TYPE(MyRevisionedSubclass) +Q_DECLARE_METATYPE(MyQmlObject::MyType) + + +class ScarceResourceObject : public QObject +{ + Q_OBJECT + Q_PROPERTY(QPixmap scarceResource READ scarceResource WRITE setScarceResource NOTIFY scarceResourceChanged) +public: + ScarceResourceObject(QObject *parent = 0) : QObject(parent), m_value(100, 100) { m_value.fill(Qt::blue); } + ~ScarceResourceObject() {} + + QPixmap scarceResource() const { return m_value; } + void setScarceResource(QPixmap v) { m_value = v; emit scarceResourceChanged(); } + + bool scarceResourceIsDetached() const { return m_value.isDetached(); } + + // this particular one returns a new one each time + // this means that every Scarce Resource Copy will + // consume resources (so that we can track disposal + // of v8 handles with circular references). + Q_INVOKABLE QPixmap newScarceResource() const + { + QPixmap retn(800, 600); + retn.fill(QColor(100, 110, 120, 45)); + return retn; + } + +signals: + void scarceResourceChanged(); + +private: + QPixmap m_value; +}; +QML_DECLARE_TYPE(ScarceResourceObject) + +class testQObjectApi : public QObject +{ + Q_OBJECT + Q_ENUMS(MyEnum) + Q_PROPERTY (int qobjectTestProperty READ qobjectTestProperty NOTIFY qobjectTestPropertyChanged) + Q_PROPERTY (int qobjectTestWritableProperty READ qobjectTestWritableProperty WRITE setQObjectTestWritableProperty NOTIFY qobjectTestWritablePropertyChanged) + +public: + testQObjectApi(QObject* parent = 0) + : QObject(parent), m_testProperty(0), m_testWritableProperty(0), m_methodCallCount(0) + { + } + + ~testQObjectApi() {} + + enum MyEnum { EnumValue1 = 25, EnumValue2 = 42 }; + Q_INVOKABLE int qobjectEnumTestMethod(MyEnum val) { return (static_cast<int>(val) + 5); } + Q_INVOKABLE int qobjectTestMethod(int increment = 1) { m_methodCallCount += increment; return m_methodCallCount; } + + int qobjectTestProperty() const { return m_testProperty; } + void setQObjectTestProperty(int tp) { m_testProperty = tp; emit qobjectTestPropertyChanged(tp); } + + int qobjectTestWritableProperty() const { return m_testWritableProperty; } + void setQObjectTestWritableProperty(int tp) { m_testWritableProperty = tp; emit qobjectTestWritablePropertyChanged(tp); } + +signals: + void qobjectTestPropertyChanged(int testProperty); + void qobjectTestWritablePropertyChanged(int testWritableProperty); + +private: + int m_testProperty; + int m_testWritableProperty; + int m_methodCallCount; +}; + +class CircularReferenceObject : public QObject, + public QV8GCCallback::Node +{ + Q_OBJECT + +public: + CircularReferenceObject(QObject *parent = 0) + : QObject(parent), QV8GCCallback::Node(callback), m_referenced(0), m_dtorCount(0) + { + QV8GCCallback::addGcCallbackNode(this); + } + + ~CircularReferenceObject() + { + if (m_dtorCount) *m_dtorCount = *m_dtorCount + 1; + } + + Q_INVOKABLE void setDtorCount(int *dtorCount) + { + m_dtorCount = dtorCount; + } + + Q_INVOKABLE CircularReferenceObject *generate(QObject *parent = 0) + { + CircularReferenceObject *retn = new CircularReferenceObject(parent); + retn->m_dtorCount = m_dtorCount; + retn->m_engine = m_engine; + return retn; + } + + Q_INVOKABLE void addReference(QObject *other) + { + m_referenced = other; + } + + static void callback(QV8GCCallback::Node *n) + { + CircularReferenceObject *cro = static_cast<CircularReferenceObject*>(n); + if (cro->m_referenced) { + cro->m_engine->addRelationshipForGC(cro, cro->m_referenced); + } + } + + void setEngine(QQmlEngine* declarativeEngine) + { + m_engine = QQmlEnginePrivate::get(declarativeEngine)->v8engine(); + } + +private: + QObject *m_referenced; + int *m_dtorCount; + QV8Engine* m_engine; +}; +Q_DECLARE_METATYPE(CircularReferenceObject*) + +class CircularReferenceHandle : public QObject, + public QV8GCCallback::Node +{ + Q_OBJECT + +public: + CircularReferenceHandle(QObject *parent = 0) + : QObject(parent), QV8GCCallback::Node(gccallback), m_dtorCount(0), m_engine(0) + { + QV8GCCallback::addGcCallbackNode(this); + } + + ~CircularReferenceHandle() + { + if (m_dtorCount) *m_dtorCount = *m_dtorCount + 1; + } + + Q_INVOKABLE void setDtorCount(int *dtorCount) + { + m_dtorCount = dtorCount; + } + + Q_INVOKABLE CircularReferenceHandle *generate(QObject *parent = 0) + { + CircularReferenceHandle *retn = new CircularReferenceHandle(parent); + retn->m_dtorCount = m_dtorCount; + retn->m_engine = m_engine; + return retn; + } + + Q_INVOKABLE void addReference(v8::Persistent<v8::Value> handle) + { + m_referenced = qPersistentNew(handle); + m_referenced.MakeWeak(static_cast<void*>(this), wrcallback); + } + + static void wrcallback(v8::Persistent<v8::Value> handle, void *params) + { + CircularReferenceHandle *crh = static_cast<CircularReferenceHandle*>(params); + qPersistentDispose(handle); + crh->m_referenced.Clear(); + } + + static void gccallback(QV8GCCallback::Node *n) + { + CircularReferenceHandle *crh = static_cast<CircularReferenceHandle*>(n); + crh->m_engine->addRelationshipForGC(crh, crh->m_referenced); + } + + void setEngine(QQmlEngine* declarativeEngine) + { + m_engine = QQmlEnginePrivate::get(declarativeEngine)->v8engine(); + } + +private: + v8::Persistent<v8::Value> m_referenced; + int *m_dtorCount; + QV8Engine* m_engine; +}; +Q_DECLARE_METATYPE(CircularReferenceHandle*) + +class MyDynamicCreationDestructionObject : public QObject +{ + Q_OBJECT + Q_PROPERTY (int intProperty READ intProperty WRITE setIntProperty NOTIFY intPropertyChanged) + +public: + MyDynamicCreationDestructionObject(QObject *parent = 0) : QObject(parent), m_intProperty(0), m_dtorCount(0) + { + } + + ~MyDynamicCreationDestructionObject() + { + if (m_dtorCount) { + (*m_dtorCount)++; + } + } + + int intProperty() const { return m_intProperty; } + void setIntProperty(int val) { m_intProperty = val; emit intPropertyChanged(); } + + Q_INVOKABLE MyDynamicCreationDestructionObject *createNew() + { + // no parent == ownership transfers to JS; same dtor counter. + MyDynamicCreationDestructionObject *retn = new MyDynamicCreationDestructionObject; + retn->setDtorCount(m_dtorCount); + return retn; + } + + void setDtorCount(int *dtorCount) + { + m_dtorCount = dtorCount; + } + +signals: + void intPropertyChanged(); + +private: + int m_intProperty; + int *m_dtorCount; +}; + +class WriteCounter : public QObject +{ + Q_OBJECT + Q_PROPERTY(int value READ value WRITE setValue); +public: + WriteCounter() : m_value(0), m_count(0) {} + + int value() const { return m_value; } + void setValue(int v) { m_value = v; ++m_count; } + + int count() const { return m_count; } + +private: + int m_value; + int m_count; +}; + +class MySequenceConversionObject : public QObject +{ + Q_OBJECT + + Q_PROPERTY (QList<int> intListProperty READ intListProperty WRITE setIntListProperty NOTIFY intListPropertyChanged) + Q_PROPERTY (QList<int> intListProperty2 READ intListProperty2 WRITE setIntListProperty2 NOTIFY intListProperty2Changed) + Q_PROPERTY (QList<qreal> qrealListProperty READ qrealListProperty WRITE setQrealListProperty NOTIFY qrealListPropertyChanged) + Q_PROPERTY (QList<bool> boolListProperty READ boolListProperty WRITE setBoolListProperty NOTIFY boolListPropertyChanged) + Q_PROPERTY (QList<QString> stringListProperty READ stringListProperty WRITE setStringListProperty NOTIFY stringListPropertyChanged) + Q_PROPERTY (QList<QUrl> urlListProperty READ urlListProperty WRITE setUrlListProperty NOTIFY urlListPropertyChanged) + Q_PROPERTY (QStringList qstringListProperty READ qstringListProperty WRITE setQStringListProperty NOTIFY qstringListPropertyChanged) + + Q_PROPERTY (QList<QPoint> pointListProperty READ pointListProperty WRITE setPointListProperty NOTIFY pointListPropertyChanged) + Q_PROPERTY (QList<QVariant> variantListProperty READ variantListProperty WRITE setVariantListProperty NOTIFY variantListPropertyChanged) + + Q_PROPERTY (qint32 maxIndex READ maxIndex CONSTANT) + Q_PROPERTY (quint32 tooBigIndex READ tooBigIndex CONSTANT) + Q_PROPERTY (qint32 negativeIndex READ negativeIndex CONSTANT) + +public: + MySequenceConversionObject() + { + m_intList << 1 << 2 << 3 << 4; + m_intList2 << 1 << 2 << 3 << 4; + m_qrealList << 1.1 << 2.2 << 3.3 << 4.4; + m_boolList << true << false << true << false; + m_stringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth"); + m_urlList << QUrl("http://www.example1.com") << QUrl("http://www.example2.com") << QUrl("http://www.example3.com"); + m_qstringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth"); + + m_pointList << QPoint(1, 2) << QPoint(3, 4) << QPoint(5, 6); + m_variantList << QVariant(QLatin1String("one")) << QVariant(true) << QVariant(3); + } + + ~MySequenceConversionObject() {} + + qint32 maxIndex() const + { + return INT_MAX; + } + quint32 tooBigIndex() const + { + quint32 retn = 7; + retn += INT_MAX; + return retn; + } + qint32 negativeIndex() const + { + return -5; + } + + QList<int> intListProperty() const { return m_intList; } + void setIntListProperty(const QList<int> &list) { m_intList = list; emit intListPropertyChanged(); } + QList<int> intListProperty2() const { return m_intList2; } + void setIntListProperty2(const QList<int> &list) { m_intList2 = list; emit intListProperty2Changed(); } + QList<qreal> qrealListProperty() const { return m_qrealList; } + void setQrealListProperty(const QList<qreal> &list) { m_qrealList = list; emit qrealListPropertyChanged(); } + QList<bool> boolListProperty() const { return m_boolList; } + void setBoolListProperty(const QList<bool> &list) { m_boolList = list; emit boolListPropertyChanged(); } + QList<QString> stringListProperty() const { return m_stringList; } + void setStringListProperty(const QList<QString> &list) { m_stringList = list; emit stringListPropertyChanged(); } + QList<QUrl> urlListProperty() const { return m_urlList; } + void setUrlListProperty(const QList<QUrl> &list) { m_urlList = list; emit urlListPropertyChanged(); } + QStringList qstringListProperty() const { return m_qstringList; } + void setQStringListProperty(const QStringList &list) { m_qstringList = list; emit qstringListPropertyChanged(); } + QList<QPoint> pointListProperty() const { return m_pointList; } + void setPointListProperty(const QList<QPoint> &list) { m_pointList = list; emit pointListPropertyChanged(); } + QList<QVariant> variantListProperty() const { return m_variantList; } + void setVariantListProperty(const QList<QVariant> &list) { m_variantList = list; emit variantListPropertyChanged(); } + + // now for "copy resource" sequences: + Q_INVOKABLE QList<int> generateIntSequence() const { QList<int> retn; retn << 1 << 2 << 3; return retn; } + Q_INVOKABLE QList<qreal> generateQrealSequence() const { QList<qreal> retn; retn << 1.1 << 2.2 << 3.3; return retn; } + Q_INVOKABLE QList<bool> generateBoolSequence() const { QList<bool> retn; retn << true << false << true; return retn; } + Q_INVOKABLE QList<QString> generateStringSequence() const { QList<QString> retn; retn << "one" << "two" << "three"; return retn; } + Q_INVOKABLE QList<QUrl> generateUrlSequence() const { QList<QUrl> retn; retn << QUrl("http://www.example1.com") << QUrl("http://www.example2.com") << QUrl("http://www.example3.com"); return retn; } + Q_INVOKABLE QStringList generateQStringSequence() const { QStringList retn; retn << "one" << "two" << "three"; return retn; } + + // "reference resource" underlying qobject deletion test: + Q_INVOKABLE MySequenceConversionObject *generateTestObject() const { return new MySequenceConversionObject; } + Q_INVOKABLE void deleteTestObject(QObject *object) const { delete object; } + +signals: + void intListPropertyChanged(); + void intListProperty2Changed(); + void qrealListPropertyChanged(); + void boolListPropertyChanged(); + void stringListPropertyChanged(); + void urlListPropertyChanged(); + void qstringListPropertyChanged(); + void pointListPropertyChanged(); + void variantListPropertyChanged(); + +private: + QList<int> m_intList; + QList<int> m_intList2; + QList<qreal> m_qrealList; + QList<bool> m_boolList; + QList<QString> m_stringList; + QList<QUrl> m_urlList; + QStringList m_qstringList; + + QList<QPoint> m_pointList; // not a supported sequence type + QList<QVariant> m_variantList; // not a supported sequence type, but QVariantList support is hardcoded. +}; + +class MyDeleteObject : public QObject +{ + Q_OBJECT + Q_PROPERTY(QObject *nestedObject READ nestedObject NOTIFY nestedObjectChanged); + Q_PROPERTY(int deleteNestedObject READ deleteNestedObject NOTIFY deleteNestedObjectChanged); + +public: + MyDeleteObject() : m_nestedObject(new MyQmlObject) {} + + QObject *nestedObject() const { return m_nestedObject; } + int deleteNestedObject() { delete m_nestedObject; m_nestedObject = 0; return 1; } + +signals: + void nestedObjectChanged(); + void deleteNestedObjectChanged(); + +private: + MyQmlObject *m_nestedObject; +}; + +void registerTypes(); + +#endif // TESTTYPES_H + diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp new file mode 100644 index 0000000000..af219c8826 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -0,0 +1,6062 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <QtTest/QtTest> +#include <QtQml/qqmlcomponent.h> +#include <QtQml/qqmlengine.h> +#include <QtQml/qqmlexpression.h> +#include <QtQml/qqmlcontext.h> +#include <QtCore/qfileinfo.h> +#include <QtCore/qdebug.h> +#include <QtQml/private/qqmlguard_p.h> +#include <QtCore/qdir.h> +#include <QtCore/qnumeric.h> +#include <private/qqmlengine_p.h> +#include <private/qqmlvmemetaobject_p.h> +#include <private/qv4compiler_p.h> +#include "testtypes.h" +#include "testhttpserver.h" +#include "../../shared/util.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 +*/ + +class tst_qqmlecmascript : public QQmlDataTest +{ + Q_OBJECT +public: + tst_qqmlecmascript() {} + +private slots: + void initTestCase(); + void assignBasicTypes(); + void idShortcutInvalidates(); + void boolPropertiesEvaluateAsBool(); + void methods(); + void signalAssignment(); + void bindingLoop(); + void basicExpressions(); + void basicExpressions_data(); + void arrayExpressions(); + void contextPropertiesTriggerReeval(); + void objectPropertiesTriggerReeval(); + void deferredProperties(); + void deferredPropertiesErrors(); + void extensionObjects(); + void overrideExtensionProperties(); + void attachedProperties(); + void enums(); + void valueTypeFunctions(); + void constantsOverrideBindings(); + void outerBindingOverridesInnerBinding(); + void aliasPropertyAndBinding(); + void aliasPropertyReset(); + void nonExistentAttachedObject(); + void scope(); + void importScope(); + void signalParameterTypes(); + void objectsCompareAsEqual(); + void dynamicCreation_data(); + void dynamicCreation(); + void dynamicDestruction(); + void objectToString(); + void objectHasOwnProperty(); + void selfDeletingBinding(); + void extendedObjectPropertyLookup(); + void extendedObjectPropertyLookup2(); + void scriptErrors(); + void functionErrors(); + void propertyAssignmentErrors(); + void signalTriggeredBindings(); + void listProperties(); + void exceptionClearsOnReeval(); + void exceptionSlotProducesWarning(); + void exceptionBindingProducesWarning(); + void compileInvalidBinding(); + void transientErrors(); + void shutdownErrors(); + void compositePropertyType(); + void jsObject(); + void undefinedResetsProperty(); + void listToVariant(); + void listAssignment(); + void multiEngineObject(); + void deletedObject(); + void attachedPropertyScope(); + void scriptConnect(); + void scriptDisconnect(); + void ownership(); + void cppOwnershipReturnValue(); + void ownershipCustomReturnValue(); + void qlistqobjectMethods(); + void strictlyEquals(); + void compiled(); + void numberAssignment(); + void propertySplicing(); + void signalWithUnknownTypes(); + void signalWithJSValueInVariant_data(); + void signalWithJSValueInVariant(); + void signalWithJSValueInVariant_twoEngines_data(); + void signalWithJSValueInVariant_twoEngines(); + void signalWithQJSValue_data(); + void signalWithQJSValue(); + void moduleApi_data(); + void moduleApi(); + void importScripts_data(); + void importScripts(); + void scarceResources(); + void scarceResources_data(); + void scarceResources_other(); + void propertyChangeSlots(); + void propertyVar_data(); + void propertyVar(); + void propertyVarCpp(); + void propertyVarOwnership(); + void propertyVarImplicitOwnership(); + void propertyVarReparent(); + void propertyVarReparentNullContext(); + void propertyVarCircular(); + void propertyVarCircular2(); + void propertyVarInheritance(); + void propertyVarInheritance2(); + void elementAssign(); + void objectPassThroughSignals(); + void objectConversion(); + void booleanConversion(); + void handleReferenceManagement(); + void stringArg(); + void readonlyDeclaration(); + void sequenceConversionRead(); + void sequenceConversionWrite(); + void sequenceConversionArray(); + void sequenceConversionIndexes(); + void sequenceConversionThreads(); + void sequenceConversionBindings(); + void sequenceConversionCopy(); + void assignSequenceTypes(); + void qtbug_22464(); + void qtbug_21580(); + + void bug1(); + void bug2(); + void dynamicCreationCrash(); + void dynamicCreationOwnership(); + void regExpBug(); + void nullObjectBinding(); + void deletedEngine(); + void libraryScriptAssert(); + void variantsAssignedUndefined(); + void qtbug_9792(); + void qtcreatorbug_1289(); + void noSpuriousWarningsAtShutdown(); + void canAssignNullToQObject(); + void functionAssignment_fromBinding(); + void functionAssignment_fromJS(); + void functionAssignment_fromJS_data(); + void functionAssignmentfromJS_invalid(); + void eval(); + void function(); + void functionException(); + void qtbug_10696(); + void qtbug_11606(); + void qtbug_11600(); + void qtbug_21864(); + void qobjectConnectionListExceptionHandling(); + void nonscriptable(); + void deleteLater(); + void in(); + void typeOf(); + void sharedAttachedObject(); + void objectName(); + void writeRemovesBinding(); + void aliasBindingsAssignCorrectly(); + void aliasBindingsOverrideTarget(); + void aliasWritesOverrideBindings(); + void aliasToCompositeElement(); + void realToInt(); + void urlProperty(); + void urlPropertyWithEncoding(); + void urlListPropertyWithEncoding(); + void dynamicString(); + void include(); + void signalHandlers(); + void doubleEvaluate(); + void forInLoop(); + void nonNotifyable(); + void deleteWhileBindingRunning(); + void callQtInvokables(); + void invokableObjectArg(); + void invokableObjectRet(); + void qtbug_20344(); + void qtbug_22679(); + void qtbug_22843_data(); + void qtbug_22843(); + void rewriteMultiLineStrings(); + void revisionErrors(); + void revision(); + void invokableWithQObjectDerived(); + + void automaticSemicolon(); + void unaryExpression(); + void switchStatement(); + void withStatement(); + void tryStatement(); + +private: + static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter); + QQmlEngine engine; +}; + +void tst_qqmlecmascript::initTestCase() +{ + QQmlDataTest::initTestCase(); + registerTypes(); +} + +void tst_qqmlecmascript::assignBasicTypes() +{ + { + QQmlComponent component(&engine, testFileUrl("assignBasicTypes.qml")); + MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3); + QCOMPARE(object->enumProperty(), MyTypeObject::EnumVal2); + QCOMPARE(object->stringProperty(), QString("Hello World!")); + QCOMPARE(object->uintProperty(), uint(10)); + QCOMPARE(object->intProperty(), -19); + QCOMPARE((float)object->realProperty(), float(23.2)); + QCOMPARE((float)object->doubleProperty(), float(-19.75)); + QCOMPARE((float)object->floatProperty(), float(8.5)); + QCOMPARE(object->colorProperty(), QColor("red")); + QCOMPARE(object->dateProperty(), QDate(1982, 11, 25)); + QCOMPARE(object->timeProperty(), QTime(11, 11, 32)); + QCOMPARE(object->dateTimeProperty(), QDateTime(QDate(2009, 5, 12), QTime(13, 22, 1))); + QCOMPARE(object->pointProperty(), QPoint(99,13)); + QCOMPARE(object->pointFProperty(), QPointF(-10.1, 12.3)); + QCOMPARE(object->sizeProperty(), QSize(99, 13)); + QCOMPARE(object->sizeFProperty(), QSizeF(0.1, 0.2)); + QCOMPARE(object->rectProperty(), QRect(9, 7, 100, 200)); + QCOMPARE(object->rectFProperty(), QRectF(1000.1, -10.9, 400, 90.99)); + QCOMPARE(object->boolProperty(), true); + QCOMPARE(object->variantProperty(), QVariant("Hello World!")); + QCOMPARE(object->vectorProperty(), QVector3D(10, 1, 2.2)); + QCOMPARE(object->urlProperty(), component.url().resolved(QUrl("main.qml"))); + delete object; + } + { + QQmlComponent component(&engine, testFileUrl("assignBasicTypes.2.qml")); + MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3); + QCOMPARE(object->enumProperty(), MyTypeObject::EnumVal2); + QCOMPARE(object->stringProperty(), QString("Hello World!")); + QCOMPARE(object->uintProperty(), uint(10)); + QCOMPARE(object->intProperty(), -19); + QCOMPARE((float)object->realProperty(), float(23.2)); + QCOMPARE((float)object->doubleProperty(), float(-19.75)); + QCOMPARE((float)object->floatProperty(), float(8.5)); + QCOMPARE(object->colorProperty(), QColor("red")); + QCOMPARE(object->dateProperty(), QDate(1982, 11, 25)); + QCOMPARE(object->timeProperty(), QTime(11, 11, 32)); + QCOMPARE(object->dateTimeProperty(), QDateTime(QDate(2009, 5, 12), QTime(13, 22, 1))); + QCOMPARE(object->pointProperty(), QPoint(99,13)); + QCOMPARE(object->pointFProperty(), QPointF(-10.1, 12.3)); + QCOMPARE(object->sizeProperty(), QSize(99, 13)); + QCOMPARE(object->sizeFProperty(), QSizeF(0.1, 0.2)); + QCOMPARE(object->rectProperty(), QRect(9, 7, 100, 200)); + QCOMPARE(object->rectFProperty(), QRectF(1000.1, -10.9, 400, 90.99)); + QCOMPARE(object->boolProperty(), true); + QCOMPARE(object->variantProperty(), QVariant("Hello World!")); + QCOMPARE(object->vectorProperty(), QVector3D(10, 1, 2.2)); + QCOMPARE(object->urlProperty(), component.url().resolved(QUrl("main.qml"))); + delete object; + } +} + +void tst_qqmlecmascript::idShortcutInvalidates() +{ + { + QQmlComponent component(&engine, testFileUrl("idShortcutInvalidates.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + QVERIFY(object->objectProperty() != 0); + delete object->objectProperty(); + QVERIFY(object->objectProperty() == 0); + delete object; + } + + { + QQmlComponent component(&engine, testFileUrl("idShortcutInvalidates.1.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + QVERIFY(object->objectProperty() != 0); + delete object->objectProperty(); + QVERIFY(object->objectProperty() == 0); + delete object; + } +} + +void tst_qqmlecmascript::boolPropertiesEvaluateAsBool() +{ + { + QQmlComponent component(&engine, testFileUrl("boolPropertiesEvaluateAsBool.1.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->stringProperty(), QLatin1String("pass")); + delete object; + } + { + QQmlComponent component(&engine, testFileUrl("boolPropertiesEvaluateAsBool.2.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->stringProperty(), QLatin1String("pass")); + delete object; + } +} + +void tst_qqmlecmascript::signalAssignment() +{ + { + QQmlComponent component(&engine, testFileUrl("signalAssignment.1.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->string(), QString()); + emit object->basicSignal(); + QCOMPARE(object->string(), QString("pass")); + delete object; + } + + { + QQmlComponent component(&engine, testFileUrl("signalAssignment.2.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->string(), QString()); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->string(), QString("pass 19 Hello world! 10.25 3 2")); + delete object; + } +} + +void tst_qqmlecmascript::methods() +{ + { + QQmlComponent component(&engine, testFileUrl("methods.1.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->methodCalled(), false); + QCOMPARE(object->methodIntCalled(), false); + emit object->basicSignal(); + QCOMPARE(object->methodCalled(), true); + QCOMPARE(object->methodIntCalled(), false); + delete object; + } + + { + QQmlComponent component(&engine, testFileUrl("methods.2.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->methodCalled(), false); + QCOMPARE(object->methodIntCalled(), false); + emit object->basicSignal(); + QCOMPARE(object->methodCalled(), false); + QCOMPARE(object->methodIntCalled(), true); + delete object; + } + + { + QQmlComponent component(&engine, testFileUrl("methods.3.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QCOMPARE(object->property("test").toInt(), 19); + delete object; + } + + { + QQmlComponent component(&engine, testFileUrl("methods.4.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QCOMPARE(object->property("test").toInt(), 19); + QCOMPARE(object->property("test2").toInt(), 17); + QCOMPARE(object->property("test3").toInt(), 16); + delete object; + } + + { + QQmlComponent component(&engine, testFileUrl("methods.5.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QCOMPARE(object->property("test").toInt(), 9); + delete object; + } +} + +void tst_qqmlecmascript::bindingLoop() +{ + QQmlComponent component(&engine, testFileUrl("bindingLoop.qml")); + QString warning = component.url().toString() + ":5:9: QML MyQmlObject: Binding loop detected for property \"stringProperty\""; + QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); + QObject *object = component.create(); + QVERIFY(object != 0); + delete object; +} + +void tst_qqmlecmascript::basicExpressions_data() +{ + QTest::addColumn<QString>("expression"); + QTest::addColumn<QVariant>("result"); + QTest::addColumn<bool>("nest"); + + QTest::newRow("Syntax error (self test)") << "{console.log({'a':1'}.a)}" << QVariant() << false; + 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_qqmlecmascript::basicExpressions() +{ + QFETCH(QString, expression); + QFETCH(QVariant, result); + QFETCH(bool, nest); + + MyQmlObject object1; + MyQmlObject object2; + MyQmlObject object3; + MyDefaultObject1 default1; + MyDefaultObject3 default3; + object1.setStringProperty("Object1"); + object2.setStringProperty("Object2"); + object3.setStringProperty("Object3"); + + QQmlContext context(engine.rootContext()); + QQmlContext nestedContext(&context); + + context.setContextObject(&default1); + context.setContextProperty("a", QVariant(1944)); + context.setContextProperty("b", QVariant("Milk")); + context.setContextProperty("object", &object1); + context.setContextProperty("objectOverride", &object2); + nestedContext.setContextObject(&default3); + nestedContext.setContextProperty("b", QVariant("Cow")); + nestedContext.setContextProperty("objectOverride", &object3); + nestedContext.setContextProperty("millipedeLegs", QVariant(100)); + + MyExpression expr(nest?&nestedContext:&context, expression); + QCOMPARE(expr.evaluate(), result); +} + +void tst_qqmlecmascript::arrayExpressions() +{ + QObject obj1; + QObject obj2; + QObject obj3; + + QQmlContext 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.evaluate(); + QCOMPARE(result.userType(), qMetaTypeId<QList<QObject *> >()); + QList<QObject *> list = qvariant_cast<QList<QObject *> >(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_qqmlecmascript::contextPropertiesTriggerReeval() +{ + QQmlContext 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.evaluate(), QVariant(2)); + + context.setContextProperty("testProp", QVariant(2)); + QCOMPARE(expr.changed, true); + QCOMPARE(expr.evaluate(), QVariant(3)); + } + + { + MyExpression expr(&context, "testProp + testProp + testProp"); + QCOMPARE(expr.changed, false); + QCOMPARE(expr.evaluate(), QVariant(6)); + + context.setContextProperty("testProp", QVariant(4)); + QCOMPARE(expr.changed, true); + QCOMPARE(expr.evaluate(), QVariant(12)); + } + + { + MyExpression expr(&context, "testObj.stringProperty"); + QCOMPARE(expr.changed, false); + QCOMPARE(expr.evaluate(), QVariant("Hello")); + + context.setContextProperty("testObj", &object2); + QCOMPARE(expr.changed, true); + QCOMPARE(expr.evaluate(), QVariant("World")); + } + + { + MyExpression expr(&context, "testObj.stringProperty /**/"); + QCOMPARE(expr.changed, false); + QCOMPARE(expr.evaluate(), QVariant("World")); + + context.setContextProperty("testObj", &object1); + QCOMPARE(expr.changed, true); + QCOMPARE(expr.evaluate(), QVariant("Hello")); + } + + { + MyExpression expr(&context, "testObj2"); + QCOMPARE(expr.changed, false); + QCOMPARE(expr.evaluate(), QVariant::fromValue((QObject *)object3)); + } + + delete object3; +} + +void tst_qqmlecmascript::objectPropertiesTriggerReeval() +{ + QQmlContext 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.evaluate(), QVariant("Hello")); + + object1.setStringProperty(QLatin1String("World")); + QCOMPARE(expr.changed, true); + QCOMPARE(expr.evaluate(), QVariant("World")); + } + + { + MyExpression expr(&context, "testObj.objectProperty.stringProperty"); + QCOMPARE(expr.changed, false); + QCOMPARE(expr.evaluate(), QVariant()); + + object1.setObjectProperty(&object2); + QCOMPARE(expr.changed, true); + expr.changed = false; + QCOMPARE(expr.evaluate(), QVariant("Dog")); + + object1.setObjectProperty(&object3); + QCOMPARE(expr.changed, true); + expr.changed = false; + QCOMPARE(expr.evaluate(), QVariant("Cat")); + + object1.setObjectProperty(0); + QCOMPARE(expr.changed, true); + expr.changed = false; + QCOMPARE(expr.evaluate(), QVariant()); + + object1.setObjectProperty(&object3); + QCOMPARE(expr.changed, true); + expr.changed = false; + QCOMPARE(expr.evaluate(), QVariant("Cat")); + + object3.setStringProperty("Donkey"); + QCOMPARE(expr.changed, true); + expr.changed = false; + QCOMPARE(expr.evaluate(), QVariant("Donkey")); + } +} + +void tst_qqmlecmascript::deferredProperties() +{ + QQmlComponent component(&engine, testFileUrl("deferredProperties.qml")); + MyDeferredObject *object = + qobject_cast<MyDeferredObject *>(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<MyQmlObject *>(object->objectProperty()); + QVERIFY(qmlObject != 0); + QCOMPARE(qmlObject->value(), 10); + object->setValue(19); + QCOMPARE(qmlObject->value(), 19); + + delete object; +} + +// Check errors on deferred properties are correctly emitted +void tst_qqmlecmascript::deferredPropertiesErrors() +{ + QQmlComponent component(&engine, testFileUrl("deferredPropertiesErrors.qml")); + MyDeferredObject *object = + qobject_cast<MyDeferredObject *>(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->value(), 0); + QVERIFY(object->objectProperty() == 0); + QVERIFY(object->objectProperty2() == 0); + + QString warning = component.url().toString() + ":6: Unable to assign [undefined] to QObject*"; + QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); + + qmlExecuteDeferred(object); + + delete object; +} + +void tst_qqmlecmascript::extensionObjects() +{ + QQmlComponent component(&engine, testFileUrl("extensionObjects.qml")); + MyExtendedObject *object = + qobject_cast<MyExtendedObject *>(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); + + MyExtendedObject *nested = qobject_cast<MyExtendedObject*>(qvariant_cast<QObject *>(object->property("nested"))); + QVERIFY(nested); + QCOMPARE(nested->baseProperty(), 13); + QCOMPARE(nested->coreProperty(), 9); + nested->setProperty("extendedProperty", QVariant(11)); + nested->setProperty("baseExtendedProperty", QVariant(92)); + QCOMPARE(nested->coreProperty(), 11); + QCOMPARE(nested->baseProperty(), 92); + + delete object; +} + +void tst_qqmlecmascript::overrideExtensionProperties() +{ + QQmlComponent component(&engine, testFileUrl("extensionObjectsPropertyOverride.qml")); + OverrideDefaultPropertyObject *object = + qobject_cast<OverrideDefaultPropertyObject *>(component.create()); + QVERIFY(object != 0); + QVERIFY(object->secondProperty() != 0); + QVERIFY(object->firstProperty() == 0); + + delete object; +} + +void tst_qqmlecmascript::attachedProperties() +{ + { + QQmlComponent component(&engine, testFileUrl("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); + delete object; + } + + { + QQmlComponent component(&engine, testFileUrl("attachedProperty.2.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QCOMPARE(object->property("a").toInt(), 26); + QCOMPARE(object->property("b").toInt(), 26); + QCOMPARE(object->property("c").toInt(), 26); + QCOMPARE(object->property("d").toInt(), 26); + + delete object; + } + + { + QQmlComponent component(&engine, testFileUrl("writeAttachedProperty.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QMetaObject::invokeMethod(object, "writeValue2"); + + MyQmlAttachedObject *attached = + qobject_cast<MyQmlAttachedObject *>(qmlAttachedPropertiesObject<MyQmlObject>(object)); + QVERIFY(attached != 0); + + QCOMPARE(attached->value2(), 9); + delete object; + } +} + +void tst_qqmlecmascript::enums() +{ + // Existent enums + { + QQmlComponent component(&engine, testFileUrl("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); + + delete object; + } + // Non-existent enums + { + QQmlComponent component(&engine, testFileUrl("enums.2.qml")); + + QString warning1 = component.url().toString() + ":5: Unable to assign [undefined] to int"; + QString warning2 = component.url().toString() + ":6: Unable to assign [undefined] to int"; + QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1)); + QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); + + QObject *object = component.create(); + QVERIFY(object != 0); + QCOMPARE(object->property("a").toInt(), 0); + QCOMPARE(object->property("b").toInt(), 0); + + delete object; + } +} + +void tst_qqmlecmascript::valueTypeFunctions() +{ + QQmlComponent component(&engine, testFileUrl("valueTypeFunctions.qml")); + MyTypeObject *obj = qobject_cast<MyTypeObject*>(component.create()); + QVERIFY(obj != 0); + QCOMPARE(obj->rectProperty(), QRect(0,0,100,100)); + QCOMPARE(obj->rectFProperty(), QRectF(0,0.5,100,99.5)); + + delete obj; +} + +/* +Tests that writing a constant to a property with a binding on it disables the +binding. +*/ +void tst_qqmlecmascript::constantsOverrideBindings() +{ + // From ECMAScript + { + QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.1.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(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); + + delete object; + } + + // During construction + { + QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.2.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(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); + + delete object; + } + +#if 0 + // From C++ + { + QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.3.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(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); + + delete object; + } +#endif + + // Using an alias + { + QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.4.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->property("c1").toInt(), 0); + QCOMPARE(object->property("c3").toInt(), 10); + object->setProperty("c1", QVariant(9)); + QCOMPARE(object->property("c1").toInt(), 9); + QCOMPARE(object->property("c3").toInt(), 10); + + delete object; + } +} + +/* +Tests that assigning a binding to a property that already has a binding causes +the original binding to be disabled. +*/ +void tst_qqmlecmascript::outerBindingOverridesInnerBinding() +{ + QQmlComponent component(&engine, + testFileUrl("outerBindingOverridesInnerBinding.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(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); + + delete object; +} + +/* +Access a non-existent attached object. + +Tests for a regression where this used to crash. +*/ +void tst_qqmlecmascript::nonExistentAttachedObject() +{ + QQmlComponent component(&engine, testFileUrl("nonExistentAttachedObject.qml")); + + QString warning = component.url().toString() + ":4: Unable to assign [undefined] to QString"; + QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); + + QObject *object = component.create(); + QVERIFY(object != 0); + + delete object; +} + +void tst_qqmlecmascript::scope() +{ + { + QQmlComponent component(&engine, testFileUrl("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); + + delete object; + } + + { + QQmlComponent component(&engine, testFileUrl("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(), 14); + QCOMPARE(object->property("test4").toInt(), 14); + QCOMPARE(object->property("test5").toInt(), 24); + QCOMPARE(object->property("test6").toInt(), 24); + + delete object; + } + + { + QQmlComponent component(&engine, testFileUrl("scope.3.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); + + delete object; + } + + // Signal argument scope + { + QQmlComponent component(&engine, testFileUrl("scope.4.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toInt(), 0); + QCOMPARE(object->property("test2").toString(), QString()); + + emit object->argumentSignal(13, "Argument Scope", 9, MyQmlObject::EnumValue4, Qt::RightButton); + + QCOMPARE(object->property("test").toInt(), 13); + QCOMPARE(object->property("test2").toString(), QString("Argument Scope")); + + delete object; + } + + { + QQmlComponent component(&engine, testFileUrl("scope.5.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test1").toBool(), true); + QCOMPARE(object->property("test2").toBool(), true); + + delete object; + } + + { + QQmlComponent component(&engine, testFileUrl("scope.6.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), true); + + delete object; + } +} + +// In 4.7, non-library javascript files that had no imports shared the imports of their +// importing context +void tst_qqmlecmascript::importScope() +{ + QQmlComponent component(&engine, testFileUrl("importScope.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test").toInt(), 240); + + delete o; +} + +/* +Tests that "any" type passes through a synthesized signal parameter. This +is essentially a test of QQmlMetaType::copy() +*/ +void tst_qqmlecmascript::signalParameterTypes() +{ + QQmlComponent component(&engine, testFileUrl("signalParameterTypes.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(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>() == QColor(255, 255, 0, 255)); + QVERIFY(object->property("variantProperty") == QVariant::fromValue(QColor(255, 0, 255, 255))); + QVERIFY(object->property("enumProperty") == MyQmlObject::EnumValue3); + QVERIFY(object->property("qtEnumProperty") == Qt::LeftButton); + + delete object; +} + +/* +Test that two JS objects for the same QObject compare as equal. +*/ +void tst_qqmlecmascript::objectsCompareAsEqual() +{ + QQmlComponent component(&engine, testFileUrl("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); + + delete object; +} + +/* +Confirm bindings and alias properties can coexist. + +Tests for a regression where the binding would not reevaluate. +*/ +void tst_qqmlecmascript::aliasPropertyAndBinding() +{ + QQmlComponent component(&engine, testFileUrl("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); + + delete object; +} + +/* +Ensure that we can write undefined value to an alias property, +and that the aliased property is reset correctly if possible. +*/ +void tst_qqmlecmascript::aliasPropertyReset() +{ + QObject *object = 0; + + // test that a manual write (of undefined) to a resettable aliased property succeeds + QQmlComponent c1(&engine, testFileUrl("aliasreset/aliasPropertyReset.1.qml")); + object = c1.create(); + QVERIFY(object != 0); + QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0); + QCOMPARE(object->property("aliasIsUndefined"), QVariant(false)); + QMetaObject::invokeMethod(object, "resetAliased"); + QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0); + QCOMPARE(object->property("aliasIsUndefined"), QVariant(true)); + delete object; + + // test that a manual write (of undefined) to a resettable alias property succeeds + QQmlComponent c2(&engine, testFileUrl("aliasreset/aliasPropertyReset.2.qml")); + object = c2.create(); + QVERIFY(object != 0); + QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0); + QCOMPARE(object->property("loaderSourceComponentIsUndefined"), QVariant(false)); + QMetaObject::invokeMethod(object, "resetAlias"); + QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0); + QCOMPARE(object->property("loaderSourceComponentIsUndefined"), QVariant(true)); + delete object; + + // test that an alias to a bound property works correctly + QQmlComponent c3(&engine, testFileUrl("aliasreset/aliasPropertyReset.3.qml")); + object = c3.create(); + QVERIFY(object != 0); + QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0); + QCOMPARE(object->property("loaderOneSourceComponentIsUndefined"), QVariant(false)); + QCOMPARE(object->property("loaderTwoSourceComponentIsUndefined"), QVariant(false)); + QMetaObject::invokeMethod(object, "resetAlias"); + QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0); + QCOMPARE(object->property("loaderOneSourceComponentIsUndefined"), QVariant(true)); + QCOMPARE(object->property("loaderTwoSourceComponentIsUndefined"), QVariant(false)); + delete object; + + // test that a manual write (of undefined) to a resettable alias property + // whose aliased property's object has been deleted, does not crash. + QQmlComponent c4(&engine, testFileUrl("aliasreset/aliasPropertyReset.4.qml")); + object = c4.create(); + QVERIFY(object != 0); + QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0); + QObject *loader = object->findChild<QObject*>("loader"); + QVERIFY(loader != 0); + delete loader; + QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0); // deletion should have caused value unset. + QMetaObject::invokeMethod(object, "resetAlias"); // shouldn't crash. + QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0); + QMetaObject::invokeMethod(object, "setAlias"); // shouldn't crash, and shouldn't change value (since it's no longer referencing anything). + QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0); + delete object; + + // test that binding an alias property to an undefined value works correctly + QQmlComponent c5(&engine, testFileUrl("aliasreset/aliasPropertyReset.5.qml")); + object = c5.create(); + QVERIFY(object != 0); + QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0); // bound to undefined value. + delete object; + + // test that a manual write (of undefined) to a non-resettable property fails properly + QUrl url = testFileUrl("aliasreset/aliasPropertyReset.error.1.qml"); + QString warning1 = url.toString() + QLatin1String(":15: Error: Cannot assign [undefined] to int"); + QQmlComponent e1(&engine, url); + object = e1.create(); + QVERIFY(object != 0); + QCOMPARE(object->property("intAlias").value<int>(), 12); + QCOMPARE(object->property("aliasedIntIsUndefined"), QVariant(false)); + QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData()); + QMetaObject::invokeMethod(object, "resetAlias"); + QCOMPARE(object->property("intAlias").value<int>(), 12); + QCOMPARE(object->property("aliasedIntIsUndefined"), QVariant(false)); + delete object; +} + +void tst_qqmlecmascript::dynamicCreation_data() +{ + QTest::addColumn<QString>("method"); + QTest::addColumn<QString>("createdName"); + + QTest::newRow("One") << "createOne" << "objectOne"; + QTest::newRow("Two") << "createTwo" << "objectTwo"; + QTest::newRow("Three") << "createThree" << "objectThree"; +} + +/* +Test using createQmlObject to dynamically generate an item +Also using createComponent is tested. +*/ +void tst_qqmlecmascript::dynamicCreation() +{ + QFETCH(QString, method); + QFETCH(QString, createdName); + + QQmlComponent component(&engine, testFileUrl("dynamicCreation.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); + QVERIFY(object != 0); + + QMetaObject::invokeMethod(object, method.toUtf8()); + QObject *created = object->objectProperty(); + QVERIFY(created); + QCOMPARE(created->objectName(), createdName); + + delete object; +} + +/* + Tests the destroy function +*/ +void tst_qqmlecmascript::dynamicDestruction() +{ + { + QQmlComponent component(&engine, testFileUrl("dynamicDeletion.qml")); + QQmlGuard<MyQmlObject> object = qobject_cast<MyQmlObject*>(component.create()); + QVERIFY(object != 0); + QQmlGuard<QObject> createdQmlObject = 0; + + QMetaObject::invokeMethod(object, "create"); + createdQmlObject = object->objectProperty(); + QVERIFY(createdQmlObject); + QCOMPARE(createdQmlObject->objectName(), QString("emptyObject")); + + QMetaObject::invokeMethod(object, "killOther"); + QVERIFY(createdQmlObject); + + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + QVERIFY(createdQmlObject); + for (int ii = 0; createdQmlObject && ii < 50; ++ii) { // After 5 seconds we should give up + if (createdQmlObject) { + QTest::qWait(100); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + } + } + QVERIFY(!createdQmlObject); + + QQmlEngine::setObjectOwnership(object, QQmlEngine::JavaScriptOwnership); + QMetaObject::invokeMethod(object, "killMe"); + QVERIFY(object); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + QVERIFY(!object); + } + + { + QQmlComponent component(&engine, testFileUrl("dynamicDeletion.2.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0); + + QMetaObject::invokeMethod(o, "create"); + + QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) != 0); + + QMetaObject::invokeMethod(o, "destroy"); + + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + + QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) == 0); + + delete o; + } +} + +/* + tests that id.toString() works +*/ +void tst_qqmlecmascript::objectToString() +{ + QQmlComponent component(&engine, testFileUrl("qmlToString.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); + QVERIFY(object != 0); + QMetaObject::invokeMethod(object, "testToString"); + QVERIFY(object->stringProperty().startsWith("MyQmlObject_QML_")); + QVERIFY(object->stringProperty().endsWith(", \"objName\")")); + + delete object; +} + +/* + tests that id.hasOwnProperty() works +*/ +void tst_qqmlecmascript::objectHasOwnProperty() +{ + QUrl url = testFileUrl("qmlHasOwnProperty.qml"); + QString warning1 = url.toString() + ":59: TypeError: Cannot call method 'hasOwnProperty' of undefined"; + QString warning2 = url.toString() + ":64: TypeError: Cannot call method 'hasOwnProperty' of undefined"; + QString warning3 = url.toString() + ":69: TypeError: Cannot call method 'hasOwnProperty' of undefined"; + + QQmlComponent component(&engine, url); + QObject *object = component.create(); + QVERIFY(object != 0); + + // test QObjects in QML + QMetaObject::invokeMethod(object, "testHasOwnPropertySuccess"); + QVERIFY(object->property("result").value<bool>() == true); + QMetaObject::invokeMethod(object, "testHasOwnPropertyFailure"); + QVERIFY(object->property("result").value<bool>() == false); + + // now test other types in QML + QObject *child = object->findChild<QObject*>("typeObj"); + QVERIFY(child != 0); + QMetaObject::invokeMethod(child, "testHasOwnPropertySuccess"); + QCOMPARE(child->property("valueTypeHasOwnProperty").toBool(), true); + QCOMPARE(child->property("valueTypeHasOwnProperty2").toBool(), true); + QCOMPARE(child->property("variantTypeHasOwnProperty").toBool(), true); + QCOMPARE(child->property("stringTypeHasOwnProperty").toBool(), true); + QCOMPARE(child->property("listTypeHasOwnProperty").toBool(), true); + QCOMPARE(child->property("emptyListTypeHasOwnProperty").toBool(), true); + QCOMPARE(child->property("enumTypeHasOwnProperty").toBool(), true); + QCOMPARE(child->property("typenameHasOwnProperty").toBool(), true); + QCOMPARE(child->property("typenameHasOwnProperty2").toBool(), true); + QCOMPARE(child->property("moduleApiTypeHasOwnProperty").toBool(), true); + QCOMPARE(child->property("moduleApiPropertyTypeHasOwnProperty").toBool(), true); + + QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData()); + QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureOne"); + QCOMPARE(child->property("enumNonValueHasOwnProperty").toBool(), false); + QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData()); + QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureTwo"); + QCOMPARE(child->property("moduleApiNonPropertyHasOwnProperty").toBool(), false); + QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData()); + QMetaObject::invokeMethod(child, "testHasOwnPropertyFailureThree"); + QCOMPARE(child->property("listAtInvalidHasOwnProperty").toBool(), false); + + delete object; +} + +/* +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_qqmlecmascript::selfDeletingBinding() +{ + { + QQmlComponent component(&engine, testFileUrl("selfDeletingBinding.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + object->setProperty("triggerDelete", true); + delete object; + } + + { + QQmlComponent component(&engine, testFileUrl("selfDeletingBinding.2.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + object->setProperty("triggerDelete", true); + delete object; + } +} + +/* +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_qqmlecmascript::extendedObjectPropertyLookup() +{ + QQmlComponent component(&engine, testFileUrl("extendedObjectPropertyLookup.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + delete object; +} + +/* +Test that extended object properties can be accessed correctly. +*/ +void tst_qqmlecmascript::extendedObjectPropertyLookup2() +{ + QQmlComponent component(&engine, testFileUrl("extendedObjectPropertyLookup2.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QVariant returnValue; + QVERIFY(QMetaObject::invokeMethod(object, "getValue", Q_RETURN_ARG(QVariant, returnValue))); + QCOMPARE(returnValue.toInt(), 42); + + delete object; +} +/* +Test file/lineNumbers for binding/Script errors. +*/ +void tst_qqmlecmascript::scriptErrors() +{ + QQmlComponent component(&engine, testFileUrl("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 + ":5: ReferenceError: Can't find variable: a"; + QString warning3 = url.left(url.length() - 3) + "js:4: Error: Invalid write to global property \"a\""; + QString warning4 = url + ":13: ReferenceError: Can't find variable: a"; + QString warning5 = url + ":11: ReferenceError: Can't find variable: a"; + QString warning6 = url + ":10: Unable to assign [undefined] to int"; + QString warning7 = url + ":15: Error: Cannot assign to read-only property \"trueProperty\""; + QString warning8 = url + ":16: Error: Cannot assign to non-existent property \"fakeProperty\""; + + QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData()); + QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData()); + QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData()); + QTest::ignoreMessage(QtWarningMsg, warning5.toLatin1().constData()); + QTest::ignoreMessage(QtWarningMsg, warning6.toLatin1().constData()); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QTest::ignoreMessage(QtWarningMsg, warning4.toLatin1().constData()); + emit object->basicSignal(); + + QTest::ignoreMessage(QtWarningMsg, warning7.toLatin1().constData()); + emit object->anotherBasicSignal(); + + QTest::ignoreMessage(QtWarningMsg, warning8.toLatin1().constData()); + emit object->thirdBasicSignal(); + + delete object; +} + +/* +Test file/lineNumbers for inline functions. +*/ +void tst_qqmlecmascript::functionErrors() +{ + QQmlComponent component(&engine, testFileUrl("functionErrors.qml")); + QString url = component.url().toString(); + + QString warning = url + ":5: Error: Invalid write to global property \"a\""; + + QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); + + QObject *object = component.create(); + QVERIFY(object != 0); + delete object; + + // test that if an exception occurs while invoking js function from cpp, it is reported as expected. + QQmlComponent componentTwo(&engine, testFileUrl("scarceResourceFunctionFail.var.qml")); + url = componentTwo.url().toString(); + object = componentTwo.create(); + QVERIFY(object != 0); + + QString srpname = object->property("srp_name").toString(); + + warning = url + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srpname + + QLatin1String(" is not a function"); + QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); // we expect a meaningful warning to be printed. + QMetaObject::invokeMethod(object, "retrieveScarceResource"); + delete object; +} + +/* +Test various errors that can occur when assigning a property from script +*/ +void tst_qqmlecmascript::propertyAssignmentErrors() +{ + QQmlComponent component(&engine, testFileUrl("propertyAssignmentErrors.qml")); + + QString url = component.url().toString(); + + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test1").toBool(), true); + QCOMPARE(object->property("test2").toBool(), true); + + delete object; +} + +/* +Test bindings still work when the reeval is triggered from within +a signal script. +*/ +void tst_qqmlecmascript::signalTriggeredBindings() +{ + QQmlComponent component(&engine, testFileUrl("signalTriggeredBindings.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject*>(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, MyQmlObject::EnumValue4, Qt::RightButton); + + QCOMPARE(object->property("base").toReal(), 400.); + QCOMPARE(object->property("test1").toReal(), 400.); + QCOMPARE(object->property("test2").toReal(), 400.); + + delete object; +} + +/* +Test that list properties can be iterated from ECMAScript +*/ +void tst_qqmlecmascript::listProperties() +{ + QQmlComponent component(&engine, testFileUrl("listProperties.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->property("test1").toInt(), 21); + QCOMPARE(object->property("test2").toInt(), 2); + QCOMPARE(object->property("test3").toBool(), true); + QCOMPARE(object->property("test4").toBool(), true); + + delete object; +} + +void tst_qqmlecmascript::exceptionClearsOnReeval() +{ + QQmlComponent component(&engine, testFileUrl("exceptionClearsOnReeval.qml")); + QString url = component.url().toString(); + + QString warning = url + ":4: TypeError: Cannot read property 'objectProperty' of null"; + + QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); + MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), false); + + MyQmlObject object2; + MyQmlObject object3; + object2.setObjectProperty(&object3); + object->setObjectProperty(&object2); + + QCOMPARE(object->property("test").toBool(), true); + + delete object; +} + +void tst_qqmlecmascript::exceptionSlotProducesWarning() +{ + QQmlComponent component(&engine, testFileUrl("exceptionProducesWarning.qml")); + QString url = component.url().toString(); + + QString warning = component.url().toString() + ":6: Error: JS exception"; + + QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); + MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); + QVERIFY(object != 0); + delete object; +} + +void tst_qqmlecmascript::exceptionBindingProducesWarning() +{ + QQmlComponent component(&engine, testFileUrl("exceptionProducesWarning2.qml")); + QString url = component.url().toString(); + + QString warning = component.url().toString() + ":5: Error: JS exception"; + + QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); + MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); + QVERIFY(object != 0); + delete object; +} + +void tst_qqmlecmascript::compileInvalidBinding() +{ + // QTBUG-23387: ensure that invalid bindings don't cause a crash. + QQmlComponent component(&engine, testFileUrl("v8bindingException.qml")); + QString warning = component.url().toString() + ":16: SyntaxError: Unexpected token ILLEGAL"; + QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); + QObject *object = component.create(); + QVERIFY(object != 0); + delete object; +} + +static int transientErrorsMsgCount = 0; +static void transientErrorsMsgHandler(QtMsgType, const char *) +{ + ++transientErrorsMsgCount; +} + +// Check that transient binding errors are not displayed +void tst_qqmlecmascript::transientErrors() +{ + { + QQmlComponent component(&engine, testFileUrl("transientErrors.qml")); + + transientErrorsMsgCount = 0; + QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler); + + QObject *object = component.create(); + QVERIFY(object != 0); + + qInstallMsgHandler(old); + + QCOMPARE(transientErrorsMsgCount, 0); + + delete object; + } + + // One binding erroring multiple times, but then resolving + { + QQmlComponent component(&engine, testFileUrl("transientErrors.2.qml")); + + transientErrorsMsgCount = 0; + QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler); + + QObject *object = component.create(); + QVERIFY(object != 0); + + qInstallMsgHandler(old); + + QCOMPARE(transientErrorsMsgCount, 0); + + delete object; + } +} + +// Check that errors during shutdown are minimized +void tst_qqmlecmascript::shutdownErrors() +{ + QQmlComponent component(&engine, testFileUrl("shutdownErrors.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + transientErrorsMsgCount = 0; + QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler); + + delete object; + + qInstallMsgHandler(old); + QCOMPARE(transientErrorsMsgCount, 0); +} + +void tst_qqmlecmascript::compositePropertyType() +{ + QQmlComponent component(&engine, testFileUrl("compositePropertyType.qml")); + + QTest::ignoreMessage(QtDebugMsg, "hello world"); + QObject *object = qobject_cast<QObject *>(component.create()); + delete object; +} + +// QTBUG-5759 +void tst_qqmlecmascript::jsObject() +{ + QQmlComponent component(&engine, testFileUrl("jsObject.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toInt(), 92); + + delete object; +} + +void tst_qqmlecmascript::undefinedResetsProperty() +{ + { + QQmlComponent component(&engine, testFileUrl("undefinedResetsProperty.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("resettableProperty").toInt(), 92); + + object->setProperty("setUndefined", true); + + QCOMPARE(object->property("resettableProperty").toInt(), 13); + + object->setProperty("setUndefined", false); + + QCOMPARE(object->property("resettableProperty").toInt(), 92); + + delete object; + } + { + QQmlComponent component(&engine, testFileUrl("undefinedResetsProperty.2.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("resettableProperty").toInt(), 19); + + QMetaObject::invokeMethod(object, "doReset"); + + QCOMPARE(object->property("resettableProperty").toInt(), 13); + + delete object; + } +} + +// Aliases to variant properties should work +void tst_qqmlecmascript::qtbug_22464() +{ + QQmlComponent component(&engine, testFileUrl("qtbug_22464.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), true); + + delete object; +} + +void tst_qqmlecmascript::qtbug_21580() +{ + QQmlComponent component(&engine, testFileUrl("qtbug_21580.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), true); + + delete object; +} + +// QTBUG-6781 +void tst_qqmlecmascript::bug1() +{ + QQmlComponent component(&engine, testFileUrl("bug.1.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toInt(), 14); + + object->setProperty("a", 11); + + QCOMPARE(object->property("test").toInt(), 3); + + object->setProperty("b", true); + + QCOMPARE(object->property("test").toInt(), 9); + + delete object; +} + +void tst_qqmlecmascript::bug2() +{ + QQmlComponent component(&engine); + component.setData("import Qt.test 1.0;\nQPlainTextEdit { width: 100 }", QUrl()); + + QObject *object = component.create(); + QVERIFY(object != 0); + + delete object; +} + +// Don't crash in createObject when the component has errors. +void tst_qqmlecmascript::dynamicCreationCrash() +{ + QQmlComponent component(&engine, testFileUrl("dynamicCreation.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); + QVERIFY(object != 0); + + QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); + QMetaObject::invokeMethod(object, "dontCrash"); + QObject *created = object->objectProperty(); + QVERIFY(created == 0); + + delete object; +} + +// ownership transferred to JS, ensure that GC runs the dtor +void tst_qqmlecmascript::dynamicCreationOwnership() +{ + int dtorCount = 0; + int expectedDtorCount = 1; // start at 1 since we expect mdcdo to dtor too. + + // allow the engine to go out of scope too. + { + QQmlEngine dcoEngine; + QQmlComponent component(&dcoEngine, testFileUrl("dynamicCreationOwnership.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + MyDynamicCreationDestructionObject *mdcdo = object->findChild<MyDynamicCreationDestructionObject*>("mdcdo"); + QVERIFY(mdcdo != 0); + mdcdo->setDtorCount(&dtorCount); + + for (int i = 1; i < 105; ++i, ++expectedDtorCount) { + QMetaObject::invokeMethod(object, "dynamicallyCreateJsOwnedObject"); + if (i % 90 == 0) { + // we do this once manually, but it should be done automatically + // when the engine goes out of scope (since it should gc in dtor) + QMetaObject::invokeMethod(object, "performGc"); + } + if (i % 10 == 0) { + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + } + } + + delete object; + } + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + QCOMPARE(dtorCount, expectedDtorCount); +} + +void tst_qqmlecmascript::regExpBug() +{ + //QTBUG-9367 + { + QQmlComponent component(&engine, testFileUrl("regExp.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->regExp().pattern(), QLatin1String("[a-zA-z]")); + delete object; + } + + //QTBUG-23068 + { + QString err = QString(QLatin1String("%1:6 Invalid property assignment: regular expression expected; use /pattern/ syntax\n")).arg(testFileUrl("regExp.2.qml").toString()); + QQmlComponent component(&engine, testFileUrl("regExp.2.qml")); + QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); + MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); + QVERIFY(!object); + QCOMPARE(component.errorString(), err); + } +} + +static inline bool evaluate_error(QV8Engine *engine, v8::Handle<v8::Object> o, const char *source) +{ + QString functionSource = QLatin1String("(function(object) { return ") + + QLatin1String(source) + QLatin1String(" })"); + v8::TryCatch tc; + v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource)); + if (tc.HasCaught()) + return false; + v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run()); + if (function.IsEmpty()) + return false; + v8::Handle<v8::Value> args[] = { o }; + function->Call(engine->global(), 1, args); + return tc.HasCaught(); +} + +static inline bool evaluate_value(QV8Engine *engine, v8::Handle<v8::Object> o, + const char *source, v8::Handle<v8::Value> result) +{ + QString functionSource = QLatin1String("(function(object) { return ") + + QLatin1String(source) + QLatin1String(" })"); + v8::TryCatch tc; + v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource)); + if (tc.HasCaught()) + return false; + v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run()); + if (function.IsEmpty()) + return false; + v8::Handle<v8::Value> args[] = { o }; + + v8::Handle<v8::Value> value = function->Call(engine->global(), 1, args); + + if (tc.HasCaught()) + return false; + + return value->StrictEquals(result); +} + +static inline v8::Handle<v8::Value> evaluate(QV8Engine *engine, v8::Handle<v8::Object> o, + const char *source) +{ + QString functionSource = QLatin1String("(function(object) { return ") + + QLatin1String(source) + QLatin1String(" })"); + v8::TryCatch tc; + v8::Local<v8::Script> program = v8::Script::Compile(engine->toString(functionSource)); + if (tc.HasCaught()) + return v8::Handle<v8::Value>(); + v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(program->Run()); + if (function.IsEmpty()) + return v8::Handle<v8::Value>(); + v8::Handle<v8::Value> args[] = { o }; + + v8::Handle<v8::Value> value = function->Call(engine->global(), 1, args); + + if (tc.HasCaught()) + return v8::Handle<v8::Value>(); + return value; +} + +#define EVALUATE_ERROR(source) evaluate_error(engine, object, source) +#define EVALUATE_VALUE(source, result) evaluate_value(engine, object, source, result) +#define EVALUATE(source) evaluate(engine, object, source) + +void tst_qqmlecmascript::callQtInvokables() +{ + MyInvokableObject o; + + QQmlEngine qmlengine; + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&qmlengine); + + QV8Engine *engine = ep->v8engine(); + + v8::HandleScope handle_scope; + v8::Context::Scope scope(engine->context()); + + v8::Local<v8::Object> object = engine->newQObject(&o)->ToObject(); + + // Non-existent methods + o.reset(); + QVERIFY(EVALUATE_ERROR("object.method_nonexistent()")); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), -1); + QCOMPARE(o.actuals().count(), 0); + + o.reset(); + QVERIFY(EVALUATE_ERROR("object.method_nonexistent(10, 11)")); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), -1); + QCOMPARE(o.actuals().count(), 0); + + // Insufficient arguments + o.reset(); + QVERIFY(EVALUATE_ERROR("object.method_int()")); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), -1); + QCOMPARE(o.actuals().count(), 0); + + o.reset(); + QVERIFY(EVALUATE_ERROR("object.method_intint(10)")); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), -1); + QCOMPARE(o.actuals().count(), 0); + + // Excessive arguments + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_int(10, 11)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 8); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(10)); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_intint(10, 11, 12)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 9); + QCOMPARE(o.actuals().count(), 2); + QCOMPARE(o.actuals().at(0), QVariant(10)); + QCOMPARE(o.actuals().at(1), QVariant(11)); + + // Test return types + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_NoArgs()", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 0); + QCOMPARE(o.actuals().count(), 0); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_NoArgs_int()", v8::Integer::New(6))); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 1); + QCOMPARE(o.actuals().count(), 0); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_NoArgs_real()", v8::Number::New(19.75))); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 2); + QCOMPARE(o.actuals().count(), 0); + + o.reset(); + { + v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QPointF()"); + QVERIFY(!ret.IsEmpty()); + QCOMPARE(engine->toVariant(ret, -1), QVariant(QPointF(123, 4.5))); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 3); + QCOMPARE(o.actuals().count(), 0); + } + + o.reset(); + { + v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QObject()"); + QCOMPARE(engine->toQObject(ret), (QObject *)&o); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 4); + QCOMPARE(o.actuals().count(), 0); + } + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_NoArgs_unknown()", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 5); + QCOMPARE(o.actuals().count(), 0); + + o.reset(); + { + v8::Handle<v8::Value> ret = EVALUATE("object.method_NoArgs_QScriptValue()"); + QVERIFY(ret->IsString()); + QCOMPARE(engine->toString(ret), QString("Hello world")); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 6); + QCOMPARE(o.actuals().count(), 0); + } + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_NoArgs_QVariant()", engine->toString("QML rocks"))); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 7); + QCOMPARE(o.actuals().count(), 0); + + // Test arg types + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_int(94)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 8); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(94)); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_int(\"94\")", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 8); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(94)); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_int(\"not a number\")", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 8); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(0)); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_int(null)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 8); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(0)); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_int(undefined)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 8); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(0)); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_int(object)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 8); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(0)); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_intint(122, 9)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 9); + QCOMPARE(o.actuals().count(), 2); + QCOMPARE(o.actuals().at(0), QVariant(122)); + QCOMPARE(o.actuals().at(1), QVariant(9)); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_real(94.3)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 10); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(94.3)); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_real(\"94.3\")", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 10); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(94.3)); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_real(\"not a number\")", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 10); + QCOMPARE(o.actuals().count(), 1); + QVERIFY(qIsNaN(o.actuals().at(0).toDouble())); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_real(null)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 10); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(0)); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_real(undefined)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 10); + QCOMPARE(o.actuals().count(), 1); + QVERIFY(qIsNaN(o.actuals().at(0).toDouble())); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_real(object)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 10); + QCOMPARE(o.actuals().count(), 1); + QVERIFY(qIsNaN(o.actuals().at(0).toDouble())); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_QString(\"Hello world\")", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 11); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant("Hello world")); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_QString(19)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 11); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant("19")); + + o.reset(); + { + QString expected = "MyInvokableObject(0x" + QString::number((quintptr)&o, 16) + ")"; + QVERIFY(EVALUATE_VALUE("object.method_QString(object)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 11); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(expected)); + } + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_QString(null)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 11); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(QString())); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_QString(undefined)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 11); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(QString())); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_QPointF(0)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 12); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(QPointF())); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_QPointF(null)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 12); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(QPointF())); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_QPointF(undefined)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 12); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(QPointF())); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_QPointF(object)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 12); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(QPointF())); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPointF())", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 12); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(QPointF(99.3, -10.2))); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPoint())", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 12); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(QPointF(9, 12))); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_QObject(0)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 13); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0)); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_QObject(\"Hello world\")", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 13); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0)); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_QObject(null)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 13); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0)); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_QObject(undefined)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 13); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)0)); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_QObject(object)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 13); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)&o)); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(null)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 14); + QCOMPARE(o.actuals().count(), 1); + QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isNull()); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(undefined)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 14); + QCOMPARE(o.actuals().count(), 1); + QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isUndefined()); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(19)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 14); + QCOMPARE(o.actuals().count(), 1); + QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).strictlyEquals(QJSValue(19))); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_QScriptValue([19, 20])", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 14); + QCOMPARE(o.actuals().count(), 1); + QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(0)).isArray()); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(4, null)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 15); + QCOMPARE(o.actuals().count(), 2); + QCOMPARE(o.actuals().at(0), QVariant(4)); + QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isNull()); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(8, undefined)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 15); + QCOMPARE(o.actuals().count(), 2); + QCOMPARE(o.actuals().at(0), QVariant(8)); + QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isUndefined()); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(3, 19)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 15); + QCOMPARE(o.actuals().count(), 2); + QCOMPARE(o.actuals().at(0), QVariant(3)); + QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).strictlyEquals(QJSValue(19))); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(44, [19, 20])", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 15); + QCOMPARE(o.actuals().count(), 2); + QCOMPARE(o.actuals().at(0), QVariant(44)); + QVERIFY(qvariant_cast<QJSValue>(o.actuals().at(1)).isArray()); + + o.reset(); + QVERIFY(EVALUATE_ERROR("object.method_overload()")); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), -1); + QCOMPARE(o.actuals().count(), 0); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_overload(10)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 16); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(10)); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_overload(10, 11)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 17); + QCOMPARE(o.actuals().count(), 2); + QCOMPARE(o.actuals().at(0), QVariant(10)); + QCOMPARE(o.actuals().at(1), QVariant(11)); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_overload(\"Hello\")", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 18); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(QString("Hello"))); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_with_enum(9)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 19); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(9)); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_default(10)", v8::Integer::New(19))); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 20); + QCOMPARE(o.actuals().count(), 2); + QCOMPARE(o.actuals().at(0), QVariant(10)); + QCOMPARE(o.actuals().at(1), QVariant(19)); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_default(10, 13)", v8::Integer::New(13))); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 20); + QCOMPARE(o.actuals().count(), 2); + QCOMPARE(o.actuals().at(0), QVariant(10)); + QCOMPARE(o.actuals().at(1), QVariant(13)); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_inherited(9)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), -3); + QCOMPARE(o.actuals().count(), 1); + QCOMPARE(o.actuals().at(0), QVariant(9)); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_QVariant(9)", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 21); + QCOMPARE(o.actuals().count(), 2); + QCOMPARE(o.actuals().at(0), QVariant(9)); + QCOMPARE(o.actuals().at(1), QVariant()); + + o.reset(); + QVERIFY(EVALUATE_VALUE("object.method_QVariant(\"Hello\", \"World\")", v8::Undefined())); + QCOMPARE(o.error(), false); + QCOMPARE(o.invoked(), 21); + QCOMPARE(o.actuals().count(), 2); + QCOMPARE(o.actuals().at(0), QVariant(QString("Hello"))); + QCOMPARE(o.actuals().at(1), QVariant(QString("World"))); +} + +// QTBUG-13047 (check that you can pass registered object types as args) +void tst_qqmlecmascript::invokableObjectArg() +{ + QQmlComponent component(&engine, testFileUrl("invokableObjectArg.qml")); + + QObject *o = component.create(); + QVERIFY(o); + MyQmlObject *qmlobject = qobject_cast<MyQmlObject *>(o); + QVERIFY(qmlobject); + QCOMPARE(qmlobject->myinvokableObject, qmlobject); + + delete o; +} + +// QTBUG-13047 (check that you can return registered object types from methods) +void tst_qqmlecmascript::invokableObjectRet() +{ + QQmlComponent component(&engine, testFileUrl("invokableObjectRet.qml")); + + QObject *o = component.create(); + QVERIFY(o); + QCOMPARE(o->property("test").toBool(), true); + delete o; +} + +// QTBUG-5675 +void tst_qqmlecmascript::listToVariant() +{ + QQmlComponent component(&engine, testFileUrl("listToVariant.qml")); + + MyQmlContainer container; + + QQmlContext context(engine.rootContext()); + context.setContextObject(&container); + + QObject *object = component.create(&context); + QVERIFY(object != 0); + + QVariant v = object->property("test"); + QCOMPARE(v.userType(), qMetaTypeId<QQmlListReference>()); + QVERIFY(qvariant_cast<QQmlListReference>(v).object() == &container); + + delete object; +} + +// QTBUG-16316 +Q_DECLARE_METATYPE(QQmlListProperty<MyQmlObject>) +void tst_qqmlecmascript::listAssignment() +{ + QQmlComponent component(&engine, testFileUrl("listAssignment.qml")); + QObject *obj = component.create(); + QCOMPARE(obj->property("list1length").toInt(), 2); + QQmlListProperty<MyQmlObject> list1 = obj->property("list1").value<QQmlListProperty<MyQmlObject> >(); + QQmlListProperty<MyQmlObject> list2 = obj->property("list2").value<QQmlListProperty<MyQmlObject> >(); + QCOMPARE(list1.count(&list1), list2.count(&list2)); + QCOMPARE(list1.at(&list1, 0), list2.at(&list2, 0)); + QCOMPARE(list1.at(&list1, 1), list2.at(&list2, 1)); + delete obj; +} + +// QTBUG-7957 +void tst_qqmlecmascript::multiEngineObject() +{ + MyQmlObject obj; + obj.setStringProperty("Howdy planet"); + + QQmlEngine e1; + e1.rootContext()->setContextProperty("thing", &obj); + QQmlComponent c1(&e1, testFileUrl("multiEngineObject.qml")); + + QQmlEngine e2; + e2.rootContext()->setContextProperty("thing", &obj); + QQmlComponent c2(&e2, testFileUrl("multiEngineObject.qml")); + + QObject *o1 = c1.create(); + QObject *o2 = c2.create(); + + QCOMPARE(o1->property("test").toString(), QString("Howdy planet")); + QCOMPARE(o2->property("test").toString(), QString("Howdy planet")); + + delete o2; + delete o1; +} + +// Test that references to QObjects are cleanup when the object is destroyed +void tst_qqmlecmascript::deletedObject() +{ + QQmlComponent component(&engine, testFileUrl("deletedObject.qml")); + + QObject *object = component.create(); + + QCOMPARE(object->property("test1").toBool(), true); + QCOMPARE(object->property("test2").toBool(), true); + QCOMPARE(object->property("test3").toBool(), true); + QCOMPARE(object->property("test4").toBool(), true); + + delete object; +} + +void tst_qqmlecmascript::attachedPropertyScope() +{ + QQmlComponent component(&engine, testFileUrl("attachedPropertyScope.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + + MyQmlAttachedObject *attached = + qobject_cast<MyQmlAttachedObject *>(qmlAttachedPropertiesObject<MyQmlObject>(object)); + QVERIFY(attached != 0); + + QCOMPARE(object->property("value2").toInt(), 0); + + attached->emitMySignal(); + + QCOMPARE(object->property("value2").toInt(), 9); + + delete object; +} + +void tst_qqmlecmascript::scriptConnect() +{ + { + QQmlComponent component(&engine, testFileUrl("scriptConnect.1.qml")); + + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), false); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->property("test").toBool(), true); + + delete object; + } + + { + QQmlComponent component(&engine, testFileUrl("scriptConnect.2.qml")); + + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), false); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->property("test").toBool(), true); + + delete object; + } + + { + QQmlComponent component(&engine, testFileUrl("scriptConnect.3.qml")); + + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), false); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->property("test").toBool(), true); + + delete object; + } + + { + QQmlComponent component(&engine, testFileUrl("scriptConnect.4.qml")); + + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->methodCalled(), false); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->methodCalled(), true); + + delete object; + } + + { + QQmlComponent component(&engine, testFileUrl("scriptConnect.5.qml")); + + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->methodCalled(), false); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->methodCalled(), true); + + delete object; + } + + { + QQmlComponent component(&engine, testFileUrl("scriptConnect.6.qml")); + + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toInt(), 0); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->property("test").toInt(), 2); + + delete object; + } +} + +void tst_qqmlecmascript::scriptDisconnect() +{ + { + QQmlComponent component(&engine, testFileUrl("scriptDisconnect.1.qml")); + + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toInt(), 0); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->property("test").toInt(), 1); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->property("test").toInt(), 2); + emit object->basicSignal(); + QCOMPARE(object->property("test").toInt(), 2); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->property("test").toInt(), 2); + + delete object; + } + + { + QQmlComponent component(&engine, testFileUrl("scriptDisconnect.2.qml")); + + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toInt(), 0); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->property("test").toInt(), 1); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->property("test").toInt(), 2); + emit object->basicSignal(); + QCOMPARE(object->property("test").toInt(), 2); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->property("test").toInt(), 2); + + delete object; + } + + { + QQmlComponent component(&engine, testFileUrl("scriptDisconnect.3.qml")); + + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toInt(), 0); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->property("test").toInt(), 1); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->property("test").toInt(), 2); + emit object->basicSignal(); + QCOMPARE(object->property("test").toInt(), 2); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->property("test").toInt(), 3); + + delete object; + } + { + QQmlComponent component(&engine, testFileUrl("scriptDisconnect.4.qml")); + + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toInt(), 0); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->property("test").toInt(), 1); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->property("test").toInt(), 2); + emit object->basicSignal(); + QCOMPARE(object->property("test").toInt(), 2); + emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton); + QCOMPARE(object->property("test").toInt(), 3); + + delete object; + } +} + +class OwnershipObject : public QObject +{ + Q_OBJECT +public: + OwnershipObject() { object = new QObject; } + + QPointer<QObject> object; + +public slots: + QObject *getObject() { return object; } +}; + +void tst_qqmlecmascript::ownership() +{ + OwnershipObject own; + QQmlContext *context = new QQmlContext(engine.rootContext()); + context->setContextObject(&own); + + { + QQmlComponent component(&engine, testFileUrl("ownership.qml")); + + QVERIFY(own.object != 0); + + QObject *object = component.create(context); + + engine.collectGarbage(); + + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + + QVERIFY(own.object == 0); + + delete object; + } + + own.object = new QObject(&own); + + { + QQmlComponent component(&engine, testFileUrl("ownership.qml")); + + QVERIFY(own.object != 0); + + QObject *object = component.create(context); + + engine.collectGarbage(); + + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + + QVERIFY(own.object != 0); + + delete object; + } + + delete context; +} + +class CppOwnershipReturnValue : public QObject +{ + Q_OBJECT +public: + CppOwnershipReturnValue() : value(0) {} + ~CppOwnershipReturnValue() { delete value; } + + Q_INVOKABLE QObject *create() { + value = new QObject; + QQmlEngine::setObjectOwnership(value, QQmlEngine::CppOwnership); + return value; + } + + Q_INVOKABLE MyQmlObject *createQmlObject() { + MyQmlObject *rv = new MyQmlObject; + value = rv; + return rv; + } + + QPointer<QObject> value; +}; + +// QTBUG-15695. +// Test setObjectOwnership(CppOwnership) works even when there is no QQmlData +void tst_qqmlecmascript::cppOwnershipReturnValue() +{ + CppOwnershipReturnValue source; + + { + QQmlEngine engine; + engine.rootContext()->setContextProperty("source", &source); + + QVERIFY(source.value == 0); + + QQmlComponent component(&engine); + component.setData("import QtQuick 2.0\nQtObject {\nComponent.onCompleted: { var a = source.create(); }\n}\n", QUrl()); + + QObject *object = component.create(); + + QVERIFY(object != 0); + QVERIFY(source.value != 0); + + delete object; + } + + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + + QVERIFY(source.value != 0); +} + +// QTBUG-15697 +void tst_qqmlecmascript::ownershipCustomReturnValue() +{ + CppOwnershipReturnValue source; + + { + QQmlEngine engine; + engine.rootContext()->setContextProperty("source", &source); + + QVERIFY(source.value == 0); + + QQmlComponent component(&engine); + component.setData("import QtQuick 2.0\nQtObject {\nComponent.onCompleted: { var a = source.createQmlObject(); }\n}\n", QUrl()); + + QObject *object = component.create(); + + QVERIFY(object != 0); + QVERIFY(source.value != 0); + + delete object; + } + + engine.collectGarbage(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + + QVERIFY(source.value == 0); +} + +class QListQObjectMethodsObject : public QObject +{ + Q_OBJECT +public: + QListQObjectMethodsObject() { + m_objects.append(new MyQmlObject()); + m_objects.append(new MyQmlObject()); + } + + ~QListQObjectMethodsObject() { + qDeleteAll(m_objects); + } + +public slots: + QList<QObject *> getObjects() { return m_objects; } + +private: + QList<QObject *> m_objects; +}; + +// Tests that returning a QList<QObject*> from a method works +void tst_qqmlecmascript::qlistqobjectMethods() +{ + QListQObjectMethodsObject obj; + QQmlContext *context = new QQmlContext(engine.rootContext()); + context->setContextObject(&obj); + + QQmlComponent component(&engine, testFileUrl("qlistqobjectMethods.qml")); + + QObject *object = component.create(context); + + QCOMPARE(object->property("test").toInt(), 2); + QCOMPARE(object->property("test2").toBool(), true); + + delete object; + delete context; +} + +// QTBUG-9205 +void tst_qqmlecmascript::strictlyEquals() +{ + QQmlComponent component(&engine, testFileUrl("strictlyEquals.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); + QCOMPARE(object->property("test6").toBool(), true); + QCOMPARE(object->property("test7").toBool(), true); + QCOMPARE(object->property("test8").toBool(), true); + + delete object; +} + +void tst_qqmlecmascript::compiled() +{ + QQmlComponent component(&engine, testFileUrl("compiled.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test1").toReal(), qreal(15.7)); + QCOMPARE(object->property("test2").toReal(), qreal(-6.7)); + QCOMPARE(object->property("test3").toBool(), true); + QCOMPARE(object->property("test4").toBool(), false); + QCOMPARE(object->property("test5").toBool(), false); + QCOMPARE(object->property("test6").toBool(), true); + + QCOMPARE(object->property("test7").toInt(), 185); + QCOMPARE(object->property("test8").toInt(), 167); + QCOMPARE(object->property("test9").toBool(), true); + QCOMPARE(object->property("test10").toBool(), false); + QCOMPARE(object->property("test11").toBool(), false); + QCOMPARE(object->property("test12").toBool(), true); + + QCOMPARE(object->property("test13").toString(), QLatin1String("HelloWorld")); + QCOMPARE(object->property("test14").toString(), QLatin1String("Hello World")); + QCOMPARE(object->property("test15").toBool(), false); + QCOMPARE(object->property("test16").toBool(), true); + + QCOMPARE(object->property("test17").toInt(), 5); + QCOMPARE(object->property("test18").toReal(), qreal(176)); + QCOMPARE(object->property("test19").toInt(), 7); + QCOMPARE(object->property("test20").toReal(), qreal(6.7)); + QCOMPARE(object->property("test21").toString(), QLatin1String("6.7")); + QCOMPARE(object->property("test22").toString(), QLatin1String("!")); + QCOMPARE(object->property("test23").toBool(), true); + QCOMPARE(qvariant_cast<QColor>(object->property("test24")), QColor(0x11,0x22,0x33)); + QCOMPARE(qvariant_cast<QColor>(object->property("test25")), QColor(0x11,0x22,0x33,0xAA)); + + delete object; +} + +// Test that numbers assigned in bindings as strings work consistently +void tst_qqmlecmascript::numberAssignment() +{ + QQmlComponent component(&engine, testFileUrl("numberAssignment.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test1"), QVariant((qreal)6.7)); + QCOMPARE(object->property("test2"), QVariant((qreal)6.7)); + QCOMPARE(object->property("test2"), QVariant((qreal)6.7)); + QCOMPARE(object->property("test3"), QVariant((qreal)6)); + QCOMPARE(object->property("test4"), QVariant((qreal)6)); + + QCOMPARE(object->property("test5"), QVariant((int)7)); + QCOMPARE(object->property("test6"), QVariant((int)7)); + QCOMPARE(object->property("test7"), QVariant((int)6)); + QCOMPARE(object->property("test8"), QVariant((int)6)); + + QCOMPARE(object->property("test9"), QVariant((unsigned int)7)); + QCOMPARE(object->property("test10"), QVariant((unsigned int)7)); + QCOMPARE(object->property("test11"), QVariant((unsigned int)6)); + QCOMPARE(object->property("test12"), QVariant((unsigned int)6)); + + delete object; +} + +void tst_qqmlecmascript::propertySplicing() +{ + QQmlComponent component(&engine, testFileUrl("propertySplicing.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), true); + + delete object; +} + +// QTBUG-16683 +void tst_qqmlecmascript::signalWithUnknownTypes() +{ + QQmlComponent component(&engine, testFileUrl("signalWithUnknownTypes.qml")); + + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + MyQmlObject::MyType type; + type.value = 0x8971123; + emit object->signalWithUnknownType(type); + + MyQmlObject::MyType result = qvariant_cast<MyQmlObject::MyType>(object->variant()); + + QCOMPARE(result.value, type.value); + + + delete object; +} + +void tst_qqmlecmascript::signalWithJSValueInVariant_data() +{ + QTest::addColumn<QString>("expression"); + QTest::addColumn<QString>("compare"); + + QString compareStrict("(function(a, b) { return a === b; })"); + QTest::newRow("true") << "true" << compareStrict; + QTest::newRow("undefined") << "undefined" << compareStrict; + QTest::newRow("null") << "null" << compareStrict; + QTest::newRow("123") << "123" << compareStrict; + QTest::newRow("'ciao'") << "'ciao'" << compareStrict; + + QString comparePropertiesStrict( + "(function(a, b) {" + " if (typeof b != 'object')" + " return a === b;" + " var props = Object.getOwnPropertyNames(b);" + " for (var i = 0; i < props.length; ++i) {" + " var p = props[i];" + " return arguments.callee(a[p], b[p]);" + " }" + "})"); + QTest::newRow("{ foo: 'bar' }") << "({ foo: 'bar' })" << comparePropertiesStrict; + QTest::newRow("[10,20,30]") << "[10,20,30]" << comparePropertiesStrict; +} + +void tst_qqmlecmascript::signalWithJSValueInVariant() +{ + QFETCH(QString, expression); + QFETCH(QString, compare); + + QQmlComponent component(&engine, testFileUrl("signalWithJSValueInVariant.qml")); + QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create())); + QVERIFY(object != 0); + + QJSValue value = engine.evaluate(expression); + QVERIFY(!engine.hasUncaughtException()); + object->setProperty("expression", expression); + object->setProperty("compare", compare); + object->setProperty("pass", false); + + emit object->signalWithVariant(QVariant::fromValue(value)); + QVERIFY(object->property("pass").toBool()); +} + +void tst_qqmlecmascript::signalWithJSValueInVariant_twoEngines_data() +{ + signalWithJSValueInVariant_data(); +} + +void tst_qqmlecmascript::signalWithJSValueInVariant_twoEngines() +{ + QFETCH(QString, expression); + QFETCH(QString, compare); + + QQmlComponent component(&engine, testFileUrl("signalWithJSValueInVariant.qml")); + QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create())); + QVERIFY(object != 0); + + QJSEngine engine2; + QJSValue value = engine2.evaluate(expression); + QVERIFY(!engine2.hasUncaughtException()); + object->setProperty("expression", expression); + object->setProperty("compare", compare); + object->setProperty("pass", false); + + QTest::ignoreMessage(QtWarningMsg, "JSValue can't be rassigned to an another engine."); + emit object->signalWithVariant(QVariant::fromValue(value)); + QVERIFY(!object->property("pass").toBool()); +} + +void tst_qqmlecmascript::signalWithQJSValue_data() +{ + signalWithJSValueInVariant_data(); +} + +void tst_qqmlecmascript::signalWithQJSValue() +{ + QFETCH(QString, expression); + QFETCH(QString, compare); + + QQmlComponent component(&engine, testFileUrl("signalWithQJSValue.qml")); + QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(component.create())); + QVERIFY(object != 0); + + QJSValue value = engine.evaluate(expression); + QVERIFY(!engine.hasUncaughtException()); + object->setProperty("expression", expression); + object->setProperty("compare", compare); + object->setProperty("pass", false); + + emit object->signalWithQJSValue(value); + + QVERIFY(object->property("pass").toBool()); + QVERIFY(object->qjsvalue().strictlyEquals(value)); +} + +void tst_qqmlecmascript::moduleApi_data() +{ + QTest::addColumn<QUrl>("testfile"); + QTest::addColumn<QString>("errorMessage"); + QTest::addColumn<QStringList>("warningMessages"); + QTest::addColumn<QStringList>("readProperties"); + QTest::addColumn<QVariantList>("readExpectedValues"); + QTest::addColumn<QStringList>("writeProperties"); + QTest::addColumn<QVariantList>("writeValues"); + QTest::addColumn<QStringList>("readBackProperties"); + QTest::addColumn<QVariantList>("readBackExpectedValues"); + + QTest::newRow("qobject, register + read + method") + << testFileUrl("moduleapi/qobjectModuleApi.qml") + << QString() + << QStringList() + << (QStringList() << "existingUriTest" << "qobjectTest" << "qobjectMethodTest" + << "qobjectMinorVersionTest" << "qobjectMajorVersionTest" << "qobjectParentedTest") + << (QVariantList() << 20 << 20 << 1 << 20 << 20 << 26) + << QStringList() + << QVariantList() + << QStringList() + << QVariantList(); + + QTest::newRow("script, register + read") + << testFileUrl("moduleapi/scriptModuleApi.qml") + << QString() + << QStringList() + << (QStringList() << "scriptTest") + << (QVariantList() << 13) + << QStringList() + << QVariantList() + << QStringList() + << QVariantList(); + + QTest::newRow("qobject, caching + read") + << testFileUrl("moduleapi/qobjectModuleApiCaching.qml") + << QString() + << QStringList() + << (QStringList() << "existingUriTest" << "qobjectParentedTest") + << (QVariantList() << 20 << 26) // 26, shouldn't have incremented to 27. + << QStringList() + << QVariantList() + << QStringList() + << QVariantList(); + + QTest::newRow("script, caching + read") + << testFileUrl("moduleapi/scriptModuleApiCaching.qml") + << QString() + << QStringList() + << (QStringList() << "scriptTest") + << (QVariantList() << 13) // 13, shouldn't have incremented to 14. + << QStringList() + << QVariantList() + << QStringList() + << QVariantList(); + + QTest::newRow("qobject, writing + readonly constraints") + << testFileUrl("moduleapi/qobjectModuleApiWriting.qml") + << QString() + << (QStringList() << QString(QLatin1String("file://") + testFileUrl("moduleapi/qobjectModuleApiWriting.qml").toLocalFile() + QLatin1String(":14: Error: Cannot assign to read-only property \"qobjectTestProperty\""))) + << (QStringList() << "readOnlyProperty" << "writableProperty") + << (QVariantList() << 20 << 50) + << (QStringList() << "firstProperty" << "writableProperty") + << (QVariantList() << 30 << 30) + << (QStringList() << "readOnlyProperty" << "writableProperty") + << (QVariantList() << 20 << 30); + + QTest::newRow("script, writing + readonly constraints") + << testFileUrl("moduleapi/scriptModuleApiWriting.qml") + << QString() + << (QStringList() << QString(QLatin1String("file://") + testFileUrl("moduleapi/scriptModuleApiWriting.qml").toLocalFile() + QLatin1String(":21: Error: Cannot assign to read-only property \"scriptTestProperty\""))) + << (QStringList() << "readBack" << "unchanged") + << (QVariantList() << 13 << 42) + << (QStringList() << "firstProperty" << "secondProperty") + << (QVariantList() << 30 << 30) + << (QStringList() << "readBack" << "unchanged") + << (QVariantList() << 30 << 42); + + QTest::newRow("qobject module API enum values in JS") + << testFileUrl("moduleapi/qobjectModuleApiEnums.qml") + << QString() + << QStringList() + << (QStringList() << "enumValue" << "enumMethod") + << (QVariantList() << 42 << 30) + << QStringList() + << QVariantList() + << QStringList() + << QVariantList(); + + QTest::newRow("qobject, invalid major version fail") + << testFileUrl("moduleapi/moduleApiMajorVersionFail.qml") + << QString("QQmlComponent: Component is not ready") + << QStringList() + << QStringList() + << QVariantList() + << QStringList() + << QVariantList() + << QStringList() + << QVariantList(); + + QTest::newRow("qobject, invalid minor version fail") + << testFileUrl("moduleapi/moduleApiMinorVersionFail.qml") + << QString("QQmlComponent: Component is not ready") + << QStringList() + << QStringList() + << QVariantList() + << QStringList() + << QVariantList() + << QStringList() + << QVariantList(); +} + +void tst_qqmlecmascript::moduleApi() +{ + QFETCH(QUrl, testfile); + QFETCH(QString, errorMessage); + QFETCH(QStringList, warningMessages); + QFETCH(QStringList, readProperties); + QFETCH(QVariantList, readExpectedValues); + QFETCH(QStringList, writeProperties); + QFETCH(QVariantList, writeValues); + QFETCH(QStringList, readBackProperties); + QFETCH(QVariantList, readBackExpectedValues); + + QQmlComponent component(&engine, testfile); + + if (!errorMessage.isEmpty()) + QTest::ignoreMessage(QtWarningMsg, errorMessage.toAscii().constData()); + + if (warningMessages.size()) + foreach (const QString &warning, warningMessages) + QTest::ignoreMessage(QtWarningMsg, warning.toAscii().constData()); + + QObject *object = component.create(); + if (!errorMessage.isEmpty()) { + QVERIFY(object == 0); + } else { + QVERIFY(object != 0); + for (int i = 0; i < readProperties.size(); ++i) + QCOMPARE(object->property(readProperties.at(i).toAscii().constData()), readExpectedValues.at(i)); + for (int i = 0; i < writeProperties.size(); ++i) + QVERIFY(object->setProperty(writeProperties.at(i).toAscii().constData(), writeValues.at(i))); + for (int i = 0; i < readBackProperties.size(); ++i) + QCOMPARE(object->property(readBackProperties.at(i).toAscii().constData()), readBackExpectedValues.at(i)); + delete object; + } +} + +void tst_qqmlecmascript::importScripts_data() +{ + QTest::addColumn<QUrl>("testfile"); + QTest::addColumn<QString>("errorMessage"); + QTest::addColumn<QStringList>("warningMessages"); + QTest::addColumn<QStringList>("propertyNames"); + QTest::addColumn<QVariantList>("propertyValues"); + + QTest::newRow("basic functionality") + << testFileUrl("jsimport/testImport.qml") + << QString() + << QStringList() + << (QStringList() << QLatin1String("importedScriptStringValue") + << QLatin1String("importedScriptFunctionValue") + << QLatin1String("importedModuleAttachedPropertyValue") + << QLatin1String("importedModuleEnumValue")) + << (QVariantList() << QVariant(QLatin1String("Hello, World!")) + << QVariant(20) + << QVariant(19) + << QVariant(2)); + + QTest::newRow("import scoping") + << testFileUrl("jsimport/testImportScoping.qml") + << QString() + << QStringList() + << (QStringList() << QLatin1String("componentError")) + << (QVariantList() << QVariant(5)); + + QTest::newRow("parent scope shouldn't be inherited by import with imports") + << testFileUrl("jsimportfail/failOne.qml") + << QString() + << (QStringList() << QString(QLatin1String("file://") + testFileUrl("jsimportfail/failOne.qml").toLocalFile() + QLatin1String(":6: TypeError: Cannot call method 'greetingString' of undefined"))) + << (QStringList() << QLatin1String("importScriptFunctionValue")) + << (QVariantList() << QVariant(QString())); + + QTest::newRow("javascript imports in an import should be private to the import scope") + << testFileUrl("jsimportfail/failTwo.qml") + << QString() + << (QStringList() << QString(QLatin1String("file://") + testFileUrl("jsimportfail/failTwo.qml").toLocalFile() + QLatin1String(":6: ReferenceError: Can't find variable: ImportOneJs"))) + << (QStringList() << QLatin1String("importScriptFunctionValue")) + << (QVariantList() << QVariant(QString())); + + QTest::newRow("module imports in an import should be private to the import scope") + << testFileUrl("jsimportfail/failThree.qml") + << QString() + << (QStringList() << QString(QLatin1String("file://") + testFileUrl("jsimportfail/failThree.qml").toLocalFile() + QLatin1String(":7: TypeError: Cannot read property 'JsQtTest' of undefined"))) + << (QStringList() << QLatin1String("importedModuleAttachedPropertyValue")) + << (QVariantList() << QVariant(false)); + + QTest::newRow("typenames in an import should be private to the import scope") + << testFileUrl("jsimportfail/failFour.qml") + << QString() + << (QStringList() << QString(QLatin1String("file://") + testFileUrl("jsimportfail/failFour.qml").toLocalFile() + QLatin1String(":6: ReferenceError: Can't find variable: JsQtTest"))) + << (QStringList() << QLatin1String("importedModuleEnumValue")) + << (QVariantList() << QVariant(0)); + + QTest::newRow("import with imports has it's own activation scope") + << testFileUrl("jsimportfail/failFive.qml") + << QString() + << (QStringList() << QString(QLatin1String("file://") + testFileUrl("jsimportfail/importWithImports.js").toLocalFile() + QLatin1String(":8: ReferenceError: Can't find variable: Component"))) + << (QStringList() << QLatin1String("componentError")) + << (QVariantList() << QVariant(0)); + + QTest::newRow("import pragma library script") + << testFileUrl("jsimport/testImportPragmaLibrary.qml") + << QString() + << QStringList() + << (QStringList() << QLatin1String("testValue")) + << (QVariantList() << QVariant(31)); + + QTest::newRow("pragma library imports shouldn't inherit parent imports or scope") + << testFileUrl("jsimportfail/testImportPragmaLibrary.qml") + << QString() + << (QStringList() << QString(QLatin1String("file://") + testFileUrl("jsimportfail/importPragmaLibrary.js").toLocalFile() + QLatin1String(":6: ReferenceError: Can't find variable: Component"))) + << (QStringList() << QLatin1String("testValue")) + << (QVariantList() << QVariant(0)); + + QTest::newRow("import pragma library script which has an import") + << testFileUrl("jsimport/testImportPragmaLibraryWithImports.qml") + << QString() + << QStringList() + << (QStringList() << QLatin1String("testValue")) + << (QVariantList() << QVariant(55)); + + QTest::newRow("import pragma library script which has a pragma library import") + << testFileUrl("jsimport/testImportPragmaLibraryWithPragmaLibraryImports.qml") + << QString() + << QStringList() + << (QStringList() << QLatin1String("testValue")) + << (QVariantList() << QVariant(18)); +} + +void tst_qqmlecmascript::importScripts() +{ + QFETCH(QUrl, testfile); + QFETCH(QString, errorMessage); + QFETCH(QStringList, warningMessages); + QFETCH(QStringList, propertyNames); + QFETCH(QVariantList, propertyValues); + + QQmlComponent component(&engine, testfile); + + if (!errorMessage.isEmpty()) + QTest::ignoreMessage(QtWarningMsg, errorMessage.toAscii().constData()); + + if (warningMessages.size()) + foreach (const QString &warning, warningMessages) + QTest::ignoreMessage(QtWarningMsg, warning.toAscii().constData()); + + QObject *object = component.create(); + if (!errorMessage.isEmpty()) { + QVERIFY(object == 0); + } else { + QVERIFY(object != 0); + for (int i = 0; i < propertyNames.size(); ++i) + QCOMPARE(object->property(propertyNames.at(i).toAscii().constData()), propertyValues.at(i)); + delete object; + } +} + +void tst_qqmlecmascript::scarceResources_other() +{ + /* These tests require knowledge of state, since we test values after + performing signal or function invocation. */ + + QPixmap origPixmap(100, 100); + origPixmap.fill(Qt::blue); + QString srp_name, expectedWarning; + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&engine); + ScarceResourceObject *eo = 0; + QObject *srsc = 0; + QObject *object = 0; + + /* property var semantics */ + + // test that scarce resources are handled properly in signal invocation + QQmlComponent varComponentTen(&engine, testFileUrl("scarceResourceSignal.var.qml")); + object = varComponentTen.create(); + srsc = object->findChild<QObject*>("srsc"); + QVERIFY(srsc); + QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet. + QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5. + eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>()); + QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage. + QMetaObject::invokeMethod(srsc, "testSignal"); + QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated + QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10. + eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>()); + QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage. + QMetaObject::invokeMethod(srsc, "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap. + QVERIFY(srsc->property("scarceResourceCopy").isValid()); + QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap); + eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>()); + QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now. + QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point. + delete object; + + // test that scarce resources are handled properly from js functions in qml files + QQmlComponent varComponentEleven(&engine, testFileUrl("scarceResourceFunction.var.qml")); + object = varComponentEleven.create(); + QVERIFY(object != 0); + QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid + eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>()); + QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage. + QMetaObject::invokeMethod(object, "retrieveScarceResource"); + QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid. + QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap); + eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>()); + QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage. + QMetaObject::invokeMethod(object, "releaseScarceResource"); + QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid + eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>()); + QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage. + QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point. + delete object; + + // test that if an exception occurs while invoking js function from cpp, that the resources are released. + QQmlComponent varComponentTwelve(&engine, testFileUrl("scarceResourceFunctionFail.var.qml")); + object = varComponentTwelve.create(); + QVERIFY(object != 0); + QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid + eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>()); + QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage. + srp_name = object->property("srp_name").toString(); + expectedWarning = varComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function"); + QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed. + QMetaObject::invokeMethod(object, "retrieveScarceResource"); + QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred. + eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>()); + QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage. + QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point. + delete object; + + // test that if an Item which has JS ownership but has a scarce resource property is garbage collected, + // that the scarce resource is removed from the engine's list of scarce resources to clean up. + QQmlComponent varComponentThirteen(&engine, testFileUrl("scarceResourceObjectGc.var.qml")); + object = varComponentThirteen.create(); + QVERIFY(object != 0); + QVERIFY(!object->property("varProperty").isValid()); // not assigned yet + QMetaObject::invokeMethod(object, "assignVarProperty"); + QVERIFY(ep->scarceResources.isEmpty()); // the scarce resource is a VME property. + QMetaObject::invokeMethod(object, "deassignVarProperty"); + QVERIFY(ep->scarceResources.isEmpty()); // should still be empty; the resource should have been released on gc. + delete object; + + /* property variant semantics */ + + // test that scarce resources are handled properly in signal invocation + QQmlComponent variantComponentTen(&engine, testFileUrl("scarceResourceSignal.variant.qml")); + object = variantComponentTen.create(); + QVERIFY(object != 0); + srsc = object->findChild<QObject*>("srsc"); + QVERIFY(srsc); + QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet. + QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5. + eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>()); + QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage. + QMetaObject::invokeMethod(srsc, "testSignal"); + QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated + QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10. + eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>()); + QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage. + QMetaObject::invokeMethod(srsc, "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap. + QVERIFY(srsc->property("scarceResourceCopy").isValid()); + QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap); + eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>()); + QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now. + QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point. + delete object; + + // test that scarce resources are handled properly from js functions in qml files + QQmlComponent variantComponentEleven(&engine, testFileUrl("scarceResourceFunction.variant.qml")); + object = variantComponentEleven.create(); + QVERIFY(object != 0); + QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid + eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>()); + QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage. + QMetaObject::invokeMethod(object, "retrieveScarceResource"); + QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid. + QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap); + eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>()); + QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage. + QMetaObject::invokeMethod(object, "releaseScarceResource"); + QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid + eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>()); + QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage. + QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point. + delete object; + + // test that if an exception occurs while invoking js function from cpp, that the resources are released. + QQmlComponent variantComponentTwelve(&engine, testFileUrl("scarceResourceFunctionFail.variant.qml")); + object = variantComponentTwelve.create(); + QVERIFY(object != 0); + QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid + eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>()); + QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage. + srp_name = object->property("srp_name").toString(); + expectedWarning = variantComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function"); + QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed. + QMetaObject::invokeMethod(object, "retrieveScarceResource"); + QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred. + eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>()); + QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage. + QVERIFY(ep->scarceResources.isEmpty()); // should have been released by this point. + delete object; +} + +void tst_qqmlecmascript::scarceResources_data() +{ + QTest::addColumn<QUrl>("qmlFile"); + QTest::addColumn<bool>("readDetachStatus"); + QTest::addColumn<bool>("expectedDetachStatus"); + QTest::addColumn<QStringList>("propertyNames"); + QTest::addColumn<QVariantList>("expectedValidity"); + QTest::addColumn<QVariantList>("expectedValues"); + QTest::addColumn<QStringList>("expectedErrors"); + + QPixmap origPixmap(100, 100); + origPixmap.fill(Qt::blue); + + /* property var semantics */ + + // in the following three cases, the instance created from the component + // has a property which is a copy of the scarce resource; hence, the + // resource should NOT be detached prior to deletion of the object instance, + // unless the resource is destroyed explicitly. + QTest::newRow("var: import scarce resource copy directly") + << testFileUrl("scarceResourceCopy.var.qml") + << true + << false // won't be detached, because assigned to property and not explicitly released + << (QStringList() << QLatin1String("scarceResourceCopy")) + << (QList<QVariant>() << true) + << (QList<QVariant>() << origPixmap) + << QStringList(); + + QTest::newRow("var: import scarce resource copy from JS") + << testFileUrl("scarceResourceCopyFromJs.var.qml") + << true + << false // won't be detached, because assigned to property and not explicitly released + << (QStringList() << QLatin1String("scarceResourceCopy")) + << (QList<QVariant>() << true) + << (QList<QVariant>() << origPixmap) + << QStringList(); + + QTest::newRow("var: import released scarce resource copy from JS") + << testFileUrl("scarceResourceDestroyedCopy.var.qml") + << true + << true // explicitly released, so it will be detached + << (QStringList() << QLatin1String("scarceResourceCopy")) + << (QList<QVariant>() << false) + << (QList<QVariant>() << QVariant()) + << QStringList(); + + // in the following three cases, no other copy should exist in memory, + // and so it should be detached (unless explicitly preserved). + QTest::newRow("var: import auto-release SR from JS in binding side-effect") + << testFileUrl("scarceResourceTest.var.qml") + << true + << true // auto released, so it will be detached + << (QStringList() << QLatin1String("scarceResourceTest")) + << (QList<QVariant>() << true) + << (QList<QVariant>() << QVariant(100)) + << QStringList(); + QTest::newRow("var: import explicit-preserve SR from JS in binding side-effect") + << testFileUrl("scarceResourceTestPreserve.var.qml") + << true + << false // won't be detached because we explicitly preserve it + << (QStringList() << QLatin1String("scarceResourceTest")) + << (QList<QVariant>() << true) + << (QList<QVariant>() << QVariant(100)) + << QStringList(); + QTest::newRow("var: import explicit-preserve SR from JS in binding side-effect") + << testFileUrl("scarceResourceTestMultiple.var.qml") + << true + << true // will be detached because all resources were released manually or automatically. + << (QStringList() << QLatin1String("scarceResourceTest")) + << (QList<QVariant>() << true) + << (QList<QVariant>() << QVariant(100)) + << QStringList(); + + // In the following three cases, test that scarce resources are handled + // correctly for imports. + QTest::newRow("var: import with no binding") + << testFileUrl("scarceResourceCopyImportNoBinding.var.qml") + << false // cannot check detach status. + << false + << QStringList() + << QList<QVariant>() + << QList<QVariant>() + << QStringList(); + QTest::newRow("var: import with binding without explicit preserve") + << testFileUrl("scarceResourceCopyImportNoBinding.var.qml") + << false + << false + << (QStringList() << QLatin1String("scarceResourceCopy")) + << (QList<QVariant>() << false) // will have been released prior to evaluation of binding. + << (QList<QVariant>() << QVariant()) + << QStringList(); + QTest::newRow("var: import with explicit release after binding evaluation") + << testFileUrl("scarceResourceCopyImport.var.qml") + << false + << false + << (QStringList() << QLatin1String("scarceResourceImportedCopy") << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo") << QLatin1String("arePropertiesEqual")) + << (QList<QVariant>() << false << false << false << true) // since property var = JS object reference, by releasing the provider's resource, all handles are invalidated. + << (QList<QVariant>() << QVariant() << QVariant() << QVariant() << QVariant(true)) + << QStringList(); + QTest::newRow("var: import with different js objects") + << testFileUrl("scarceResourceCopyImportDifferent.var.qml") + << false + << false + << (QStringList() << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo") << QLatin1String("arePropertiesEqual")) + << (QList<QVariant>() << false << true << true) // invalidating one shouldn't invalidate the other, because they're not references to the same JS object. + << (QList<QVariant>() << QVariant() << QVariant(origPixmap) << QVariant(false)) + << QStringList(); + QTest::newRow("var: import with different js objects and explicit release") + << testFileUrl("scarceResourceMultipleDifferentNoBinding.var.qml") + << false + << false + << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo")) + << (QList<QVariant>() << true << false) // invalidating one shouldn't invalidate the other, because they're not references to the same JS object. + << (QList<QVariant>() << QVariant(origPixmap) << QVariant()) + << QStringList(); + QTest::newRow("var: import with same js objects and explicit release") + << testFileUrl("scarceResourceMultipleSameNoBinding.var.qml") + << false + << false + << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo")) + << (QList<QVariant>() << false << false) // invalidating one should invalidate the other, because they're references to the same JS object. + << (QList<QVariant>() << QVariant() << QVariant()) + << QStringList(); + QTest::newRow("var: binding with same js objects and explicit release") + << testFileUrl("scarceResourceMultipleSameWithBinding.var.qml") + << false + << false + << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo")) + << (QList<QVariant>() << false << false) // invalidating one should invalidate the other, because they're references to the same JS object. + << (QList<QVariant>() << QVariant() << QVariant()) + << QStringList(); + + + /* property variant semantics */ + + // in the following three cases, the instance created from the component + // has a property which is a copy of the scarce resource; hence, the + // resource should NOT be detached prior to deletion of the object instance, + // unless the resource is destroyed explicitly. + QTest::newRow("variant: import scarce resource copy directly") + << testFileUrl("scarceResourceCopy.variant.qml") + << true + << false // won't be detached, because assigned to property and not explicitly released + << (QStringList() << QLatin1String("scarceResourceCopy")) + << (QList<QVariant>() << true) + << (QList<QVariant>() << origPixmap) + << QStringList(); + + QTest::newRow("variant: import scarce resource copy from JS") + << testFileUrl("scarceResourceCopyFromJs.variant.qml") + << true + << false // won't be detached, because assigned to property and not explicitly released + << (QStringList() << QLatin1String("scarceResourceCopy")) + << (QList<QVariant>() << true) + << (QList<QVariant>() << origPixmap) + << QStringList(); + + QTest::newRow("variant: import released scarce resource copy from JS") + << testFileUrl("scarceResourceDestroyedCopy.variant.qml") + << true + << true // explicitly released, so it will be detached + << (QStringList() << QLatin1String("scarceResourceCopy")) + << (QList<QVariant>() << false) + << (QList<QVariant>() << QVariant()) + << QStringList(); + + // in the following three cases, no other copy should exist in memory, + // and so it should be detached (unless explicitly preserved). + QTest::newRow("variant: import auto-release SR from JS in binding side-effect") + << testFileUrl("scarceResourceTest.variant.qml") + << true + << true // auto released, so it will be detached + << (QStringList() << QLatin1String("scarceResourceTest")) + << (QList<QVariant>() << true) + << (QList<QVariant>() << QVariant(100)) + << QStringList(); + QTest::newRow("variant: import explicit-preserve SR from JS in binding side-effect") + << testFileUrl("scarceResourceTestPreserve.variant.qml") + << true + << false // won't be detached because we explicitly preserve it + << (QStringList() << QLatin1String("scarceResourceTest")) + << (QList<QVariant>() << true) + << (QList<QVariant>() << QVariant(100)) + << QStringList(); + QTest::newRow("variant: import multiple scarce resources") + << testFileUrl("scarceResourceTestMultiple.variant.qml") + << true + << true // will be detached because all resources were released manually or automatically. + << (QStringList() << QLatin1String("scarceResourceTest")) + << (QList<QVariant>() << true) + << (QList<QVariant>() << QVariant(100)) + << QStringList(); + + // In the following three cases, test that scarce resources are handled + // correctly for imports. + QTest::newRow("variant: import with no binding") + << testFileUrl("scarceResourceCopyImportNoBinding.variant.qml") + << false // cannot check detach status. + << false + << QStringList() + << QList<QVariant>() + << QList<QVariant>() + << QStringList(); + QTest::newRow("variant: import with binding without explicit preserve") + << testFileUrl("scarceResourceCopyImportNoBinding.variant.qml") + << false + << false + << (QStringList() << QLatin1String("scarceResourceCopy")) + << (QList<QVariant>() << false) // will have been released prior to evaluation of binding. + << (QList<QVariant>() << QVariant()) + << QStringList(); + QTest::newRow("variant: import with explicit release after binding evaluation") + << testFileUrl("scarceResourceCopyImport.variant.qml") + << false + << false + << (QStringList() << QLatin1String("scarceResourceImportedCopy") << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo")) + << (QList<QVariant>() << true << true << false) // since property variant = variant copy, releasing the provider's resource does not invalidate previously assigned copies. + << (QList<QVariant>() << origPixmap << origPixmap << QVariant()) + << QStringList(); +} + +void tst_qqmlecmascript::scarceResources() +{ + QFETCH(QUrl, qmlFile); + QFETCH(bool, readDetachStatus); + QFETCH(bool, expectedDetachStatus); + QFETCH(QStringList, propertyNames); + QFETCH(QVariantList, expectedValidity); + QFETCH(QVariantList, expectedValues); + QFETCH(QStringList, expectedErrors); + + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&engine); + ScarceResourceObject *eo = 0; + QObject *object = 0; + + QQmlComponent c(&engine, qmlFile); + object = c.create(); + QVERIFY(object != 0); + for (int i = 0; i < propertyNames.size(); ++i) { + QString prop = propertyNames.at(i); + bool validity = expectedValidity.at(i).toBool(); + QVariant value = expectedValues.at(i); + + QCOMPARE(object->property(prop.toLatin1().constData()).isValid(), validity); + if (value.type() == QVariant::Int) { + QCOMPARE(object->property(prop.toLatin1().constData()).toInt(), value.toInt()); + } else if (value.type() == QVariant::Pixmap) { + QCOMPARE(object->property(prop.toLatin1().constData()).value<QPixmap>(), value.value<QPixmap>()); + } + } + + if (readDetachStatus) { + eo = qobject_cast<ScarceResourceObject*>(QQmlProperty::read(object, "a").value<QObject*>()); + QCOMPARE(eo->scarceResourceIsDetached(), expectedDetachStatus); + } + + QVERIFY(ep->scarceResources.isEmpty()); + delete object; +} + +void tst_qqmlecmascript::propertyChangeSlots() +{ + // ensure that allowable property names are allowed and onPropertyNameChanged slots are generated correctly. + QQmlComponent component(&engine, testFileUrl("changeslots/propertyChangeSlots.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + delete object; + + // ensure that invalid property names fail properly. + QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); + QQmlComponent e1(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.1.qml")); + QString expectedErrorString = e1.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_nameWithUnderscoreChanged\""); + QCOMPARE(e1.errors().at(0).toString(), expectedErrorString); + object = e1.create(); + QVERIFY(object == 0); + delete object; + + QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); + QQmlComponent e2(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.2.qml")); + expectedErrorString = e2.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on____nameWithUnderscoresChanged\""); + QCOMPARE(e2.errors().at(0).toString(), expectedErrorString); + object = e2.create(); + QVERIFY(object == 0); + delete object; + + QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); + QQmlComponent e3(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.3.qml")); + expectedErrorString = e3.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on$NameWithDollarsignChanged\""); + QCOMPARE(e3.errors().at(0).toString(), expectedErrorString); + object = e3.create(); + QVERIFY(object == 0); + delete object; + + QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); + QQmlComponent e4(&engine, testFileUrl("changeslots/propertyChangeSlotErrors.4.qml")); + expectedErrorString = e4.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_6NameWithUnderscoreNumberChanged\""); + QCOMPARE(e4.errors().at(0).toString(), expectedErrorString); + object = e4.create(); + QVERIFY(object == 0); + delete object; +} + +void tst_qqmlecmascript::propertyVar_data() +{ + QTest::addColumn<QUrl>("qmlFile"); + + // valid + QTest::newRow("non-bindable object subproperty changed") << testFileUrl("propertyVar.1.qml"); + QTest::newRow("non-bindable object changed") << testFileUrl("propertyVar.2.qml"); + QTest::newRow("primitive changed") << testFileUrl("propertyVar.3.qml"); + QTest::newRow("javascript array modification") << testFileUrl("propertyVar.4.qml"); + QTest::newRow("javascript map modification") << testFileUrl("propertyVar.5.qml"); + QTest::newRow("javascript array assignment") << testFileUrl("propertyVar.6.qml"); + QTest::newRow("javascript map assignment") << testFileUrl("propertyVar.7.qml"); + QTest::newRow("literal property assignment") << testFileUrl("propertyVar.8.qml"); + QTest::newRow("qobject property assignment") << testFileUrl("propertyVar.9.qml"); + QTest::newRow("base class var property assignment") << testFileUrl("propertyVar.10.qml"); +} + +void tst_qqmlecmascript::propertyVar() +{ + QFETCH(QUrl, qmlFile); + + QQmlComponent component(&engine, qmlFile); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), true); + + delete object; +} + +// Tests that we can write QVariant values to var properties from C++ +void tst_qqmlecmascript::propertyVarCpp() +{ + QObject *object = 0; + + // ensure that writing to and reading from a var property from cpp works as required. + // Literal values stored in var properties can be read and written as QVariants + // of a specific type, whereas object values are read as QVariantMaps. + QQmlComponent component(&engine, testFileUrl("propertyVarCpp.qml")); + object = component.create(); + QVERIFY(object != 0); + // assign int to property var that currently has int assigned + QVERIFY(object->setProperty("varProperty", QVariant::fromValue(10))); + QCOMPARE(object->property("varBound"), QVariant(15)); + QCOMPARE(object->property("intBound"), QVariant(15)); + QCOMPARE(object->property("varProperty").userType(), (int)QVariant::Int); + QCOMPARE(object->property("varBound").userType(), (int)QVariant::Int); + // assign string to property var that current has bool assigned + QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::Bool); + QVERIFY(object->setProperty("varProperty2", QVariant(QLatin1String("randomString")))); + QCOMPARE(object->property("varProperty2"), QVariant(QLatin1String("randomString"))); + QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::String); + // now enforce behaviour when accessing JavaScript objects from cpp. + QCOMPARE(object->property("jsobject").userType(), (int)QVariant::Map); + delete object; +} + +static void gc(QQmlEngine &engine) +{ + engine.collectGarbage(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); +} + +void tst_qqmlecmascript::propertyVarOwnership() +{ + // Referenced JS objects are not collected + { + QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QCOMPARE(object->property("test").toBool(), false); + QMetaObject::invokeMethod(object, "runTest"); + QCOMPARE(object->property("test").toBool(), true); + delete object; + } + // Referenced JS objects are not collected + { + QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.2.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QCOMPARE(object->property("test").toBool(), false); + QMetaObject::invokeMethod(object, "runTest"); + QCOMPARE(object->property("test").toBool(), true); + delete object; + } + // Qt objects are not collected until they've been dereferenced + { + QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.3.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test2").toBool(), false); + QCOMPARE(object->property("test2").toBool(), false); + + QMetaObject::invokeMethod(object, "runTest"); + QCOMPARE(object->property("test1").toBool(), true); + + QPointer<QObject> referencedObject = object->property("object").value<QObject*>(); + QVERIFY(!referencedObject.isNull()); + gc(engine); + QVERIFY(!referencedObject.isNull()); + + QMetaObject::invokeMethod(object, "runTest2"); + QCOMPARE(object->property("test2").toBool(), true); + gc(engine); + QVERIFY(referencedObject.isNull()); + + delete object; + } + // Self reference does not prevent Qt object collection + { + QQmlComponent component(&engine, testFileUrl("propertyVarOwnership.4.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), true); + + QPointer<QObject> referencedObject = object->property("object").value<QObject*>(); + QVERIFY(!referencedObject.isNull()); + gc(engine); + QVERIFY(!referencedObject.isNull()); + + QMetaObject::invokeMethod(object, "runTest"); + gc(engine); + QVERIFY(referencedObject.isNull()); + + delete object; + } +} + +void tst_qqmlecmascript::propertyVarImplicitOwnership() +{ + // The childObject has a reference to a different QObject. We want to ensure + // that the different item will not be cleaned up until required. IE, the childObject + // has implicit ownership of the constructed QObject. + QQmlComponent component(&engine, testFileUrl("propertyVarImplicitOwnership.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QMetaObject::invokeMethod(object, "assignCircular"); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + QObject *rootObject = object->property("vp").value<QObject*>(); + QVERIFY(rootObject != 0); + QObject *childObject = rootObject->findChild<QObject*>("text"); + QVERIFY(childObject != 0); + QCOMPARE(rootObject->property("rectCanary").toInt(), 5); + QCOMPARE(childObject->property("textCanary").toInt(), 10); + QMetaObject::invokeMethod(childObject, "constructQObject"); // creates a reference to a constructed QObject. + QWeakPointer<QObject> qobjectGuard(childObject->property("vp").value<QObject*>()); // get the pointer prior to processing deleteLater events. + QVERIFY(!qobjectGuard.isNull()); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + QVERIFY(!qobjectGuard.isNull()); + QMetaObject::invokeMethod(object, "deassignCircular"); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + QVERIFY(qobjectGuard.isNull()); // should have been collected now. + delete object; +} + +void tst_qqmlecmascript::propertyVarReparent() +{ + // ensure that nothing breaks if we re-parent objects + QQmlComponent component(&engine, testFileUrl("propertyVar.reparent.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QMetaObject::invokeMethod(object, "assignVarProp"); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + QObject *rect = object->property("vp").value<QObject*>(); + QObject *text = rect->findChild<QObject*>("textOne"); + QObject *text2 = rect->findChild<QObject*>("textTwo"); + QWeakPointer<QObject> rectGuard(rect); + QWeakPointer<QObject> textGuard(text); + QWeakPointer<QObject> text2Guard(text2); + QVERIFY(!rectGuard.isNull()); + QVERIFY(!textGuard.isNull()); + QVERIFY(!text2Guard.isNull()); + QCOMPARE(text->property("textCanary").toInt(), 11); + QCOMPARE(text2->property("textCanary").toInt(), 12); + // now construct an image which we will reparent. + QMetaObject::invokeMethod(text2, "constructQObject"); + QObject *image = text2->property("vp").value<QObject*>(); + QWeakPointer<QObject> imageGuard(image); + QVERIFY(!imageGuard.isNull()); + QCOMPARE(image->property("imageCanary").toInt(), 13); + // now reparent the "Image" object (currently, it has JS ownership) + image->setParent(text); // shouldn't be collected after deassignVp now, since has a parent. + QMetaObject::invokeMethod(text2, "deassignVp"); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + QCOMPARE(text->property("textCanary").toInt(), 11); + QCOMPARE(text2->property("textCanary").toInt(), 22); + QVERIFY(!imageGuard.isNull()); // should still be alive. + QCOMPARE(image->property("imageCanary").toInt(), 13); // still able to access var properties + QMetaObject::invokeMethod(object, "deassignVarProp"); // now deassign the root-object's vp, causing gc of rect+text+text2 + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + QVERIFY(imageGuard.isNull()); // should now have been deleted, due to parent being deleted. + delete object; +} + +void tst_qqmlecmascript::propertyVarReparentNullContext() +{ + // sometimes reparenting can cause problems + // (eg, if the ctxt is collected, varproperties are no longer available) + // this test ensures that no crash occurs in that situation. + QQmlComponent component(&engine, testFileUrl("propertyVar.reparent.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QMetaObject::invokeMethod(object, "assignVarProp"); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + QObject *rect = object->property("vp").value<QObject*>(); + QObject *text = rect->findChild<QObject*>("textOne"); + QObject *text2 = rect->findChild<QObject*>("textTwo"); + QWeakPointer<QObject> rectGuard(rect); + QWeakPointer<QObject> textGuard(text); + QWeakPointer<QObject> text2Guard(text2); + QVERIFY(!rectGuard.isNull()); + QVERIFY(!textGuard.isNull()); + QVERIFY(!text2Guard.isNull()); + QCOMPARE(text->property("textCanary").toInt(), 11); + QCOMPARE(text2->property("textCanary").toInt(), 12); + // now construct an image which we will reparent. + QMetaObject::invokeMethod(text2, "constructQObject"); + QObject *image = text2->property("vp").value<QObject*>(); + QWeakPointer<QObject> imageGuard(image); + QVERIFY(!imageGuard.isNull()); + QCOMPARE(image->property("imageCanary").toInt(), 13); + // now reparent the "Image" object (currently, it has JS ownership) + image->setParent(object); // reparented to base object. after deassignVarProp, the ctxt will be invalid. + QMetaObject::invokeMethod(object, "deassignVarProp"); // now deassign the root-object's vp, causing gc of rect+text+text2 + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + QVERIFY(!imageGuard.isNull()); // should still be alive. + QVERIFY(!image->property("imageCanary").isValid()); // but varProperties won't be available (null context). + delete object; + QVERIFY(imageGuard.isNull()); // should now be dead. +} + +void tst_qqmlecmascript::propertyVarCircular() +{ + // enforce behaviour regarding circular references - ensure qdvmemo deletion. + QQmlComponent component(&engine, testFileUrl("propertyVar.circular.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QMetaObject::invokeMethod(object, "assignCircular"); // cause assignment and gc + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + QCOMPARE(object->property("canaryInt"), QVariant(5)); + QVariant canaryResourceVariant = object->property("canaryResource"); + QVERIFY(canaryResourceVariant.isValid()); + QPixmap canaryResourcePixmap = canaryResourceVariant.value<QPixmap>(); + canaryResourceVariant = QVariant(); // invalidate it to remove one copy of the pixmap from memory. + QMetaObject::invokeMethod(object, "deassignCanaryResource"); // remove one copy of the pixmap from memory + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + QVERIFY(!canaryResourcePixmap.isDetached()); // two copies extant - this and the propertyVar.vp.vp.vp.vp.memoryHog. + QMetaObject::invokeMethod(object, "deassignCircular"); // cause deassignment and gc + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + QCOMPARE(object->property("canaryInt"), QVariant(2)); + QCOMPARE(object->property("canaryResource"), QVariant(1)); + QVERIFY(canaryResourcePixmap.isDetached()); // now detached, since orig copy was member of qdvmemo which was deleted. + delete object; +} + +void tst_qqmlecmascript::propertyVarCircular2() +{ + // track deletion of JS-owned parent item with Cpp-owned child + // where the child has a var property referencing its parent. + QQmlComponent component(&engine, testFileUrl("propertyVar.circular.2.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QMetaObject::invokeMethod(object, "assignCircular"); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + QObject *rootObject = object->property("vp").value<QObject*>(); + QVERIFY(rootObject != 0); + QObject *childObject = rootObject->findChild<QObject*>("text"); + QVERIFY(childObject != 0); + QWeakPointer<QObject> rootObjectTracker(rootObject); + QVERIFY(!rootObjectTracker.isNull()); + QWeakPointer<QObject> childObjectTracker(childObject); + QVERIFY(!childObjectTracker.isNull()); + gc(engine); + QCOMPARE(rootObject->property("rectCanary").toInt(), 5); + QCOMPARE(childObject->property("textCanary").toInt(), 10); + QMetaObject::invokeMethod(object, "deassignCircular"); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + QVERIFY(rootObjectTracker.isNull()); // should have been collected + QVERIFY(childObjectTracker.isNull()); // should have been collected + delete object; +} + +void tst_qqmlecmascript::propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter) +{ + *(int*)(parameter) += 1; + qPersistentDispose(object); +} + +void tst_qqmlecmascript::propertyVarInheritance() +{ + int propertyVarWeakRefCallbackCount = 0; + + // enforce behaviour regarding element inheritance - ensure handle disposal. + // The particular component under test here has a chain of references. + QQmlComponent component(&engine, testFileUrl("propertyVar.inherit.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QMetaObject::invokeMethod(object, "assignCircular"); // cause assignment and gc + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + // we want to be able to track when the varProperties array of the last metaobject is disposed + QObject *cco5 = object->property("varProperty").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>(); + QObject *ico5 = object->property("varProperty").value<QObject*>()->property("inheritanceVarProperty").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>(); + QQmlVMEMetaObject *icovmemo = ((QQmlVMEMetaObject *)(ico5->metaObject())); + QQmlVMEMetaObject *ccovmemo = ((QQmlVMEMetaObject *)(cco5->metaObject())); + v8::Persistent<v8::Value> icoCanaryHandle; + v8::Persistent<v8::Value> ccoCanaryHandle; + { + v8::HandleScope hs; + // XXX NOTE: this is very implementation dependent. QDVMEMO->vmeProperty() is the only + // public function which can return us a handle to something in the varProperties array. + icoCanaryHandle = qPersistentNew(icovmemo->vmeProperty(ico5->metaObject()->indexOfProperty("circ"))); + ccoCanaryHandle = qPersistentNew(ccovmemo->vmeProperty(cco5->metaObject()->indexOfProperty("circ"))); + // we make them weak and invoke the gc, but we should not hit the weak-callback yet + // as the varproperties array of each vmemo still references the resource. + icoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback); + ccoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback); + gc(engine); + QVERIFY(propertyVarWeakRefCallbackCount == 0); + } + // now we deassign the var prop, which should trigger collection of item subtrees. + QMetaObject::invokeMethod(object, "deassignCircular"); // cause deassignment and gc + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + // ensure that there are only weak handles to the underlying varProperties array remaining. + gc(engine); + QCOMPARE(propertyVarWeakRefCallbackCount, 2); // should have been called for both, since all refs should be weak. + delete object; + // since there are no parent vmemo's to keep implicit references alive, and the only handles + // to what remains are weak, all varProperties arrays must have been collected. +} + +void tst_qqmlecmascript::propertyVarInheritance2() +{ + int propertyVarWeakRefCallbackCount = 0; + + // The particular component under test here does NOT have a chain of references; the + // only link between rootObject and childObject is that rootObject is the parent of childObject. + QQmlComponent component(&engine, testFileUrl("propertyVar.circular.2.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QMetaObject::invokeMethod(object, "assignCircular"); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + QObject *rootObject = object->property("vp").value<QObject*>(); + QVERIFY(rootObject != 0); + QObject *childObject = rootObject->findChild<QObject*>("text"); + QVERIFY(childObject != 0); + QCOMPARE(rootObject->property("rectCanary").toInt(), 5); + QCOMPARE(childObject->property("textCanary").toInt(), 10); + v8::Persistent<v8::Value> childObjectVarArrayValueHandle; + { + v8::HandleScope hs; + propertyVarWeakRefCallbackCount = 0; // reset callback count. + childObjectVarArrayValueHandle = qPersistentNew(((QQmlVMEMetaObject *)(childObject->metaObject()))->vmeProperty(childObject->metaObject()->indexOfProperty("vp"))); + childObjectVarArrayValueHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback); + gc(engine); + QVERIFY(propertyVarWeakRefCallbackCount == 0); // should not have been collected yet. + QCOMPARE(childObject->property("vp").value<QObject*>(), rootObject); + QCOMPARE(childObject->property("textCanary").toInt(), 10); + } + QMetaObject::invokeMethod(object, "deassignCircular"); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper. + QCoreApplication::processEvents(); + QVERIFY(propertyVarWeakRefCallbackCount == 1); // should have been collected now. + delete object; +} + +// Ensure that QObject type conversion works on binding assignment +void tst_qqmlecmascript::elementAssign() +{ + QQmlComponent component(&engine, testFileUrl("elementAssign.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), true); + + delete object; +} + +// QTBUG-12457 +void tst_qqmlecmascript::objectPassThroughSignals() +{ + QQmlComponent component(&engine, testFileUrl("objectsPassThroughSignals.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), true); + + delete object; +} + +// QTBUG-21626 +void tst_qqmlecmascript::objectConversion() +{ + QQmlComponent component(&engine, testFileUrl("objectConversion.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + QVariant retn; + QMetaObject::invokeMethod(object, "circularObject", Q_RETURN_ARG(QVariant, retn)); + QCOMPARE(retn.value<QVariantMap>().value("test"), QVariant(100)); + + delete object; +} + + +// QTBUG-20242 +void tst_qqmlecmascript::booleanConversion() +{ + QQmlComponent component(&engine, testFileUrl("booleanConversion.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test_true1").toBool(), true); + QCOMPARE(object->property("test_true2").toBool(), true); + QCOMPARE(object->property("test_true3").toBool(), true); + QCOMPARE(object->property("test_true4").toBool(), true); + QCOMPARE(object->property("test_true5").toBool(), true); + + QCOMPARE(object->property("test_false1").toBool(), false); + QCOMPARE(object->property("test_false2").toBool(), false); + QCOMPARE(object->property("test_false3").toBool(), false); + + delete object; +} + +void tst_qqmlecmascript::handleReferenceManagement() +{ + + int dtorCount = 0; + { + // Linear QObject reference + QQmlEngine hrmEngine; + QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.object.1.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro"); + cro->setEngine(&hrmEngine); + cro->setDtorCount(&dtorCount); + QMetaObject::invokeMethod(object, "createReference"); + gc(engine); + QCOMPARE(dtorCount, 0); // second has JS ownership, kept alive by first's reference + delete object; + hrmEngine.collectGarbage(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + QCOMPARE(dtorCount, 3); + } + + dtorCount = 0; + { + // Circular QObject reference + QQmlEngine hrmEngine; + QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.object.2.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro"); + cro->setEngine(&hrmEngine); + cro->setDtorCount(&dtorCount); + QMetaObject::invokeMethod(object, "circularReference"); + gc(engine); + QCOMPARE(dtorCount, 2); // both should be cleaned up, since circular references shouldn't keep alive. + delete object; + hrmEngine.collectGarbage(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + QCOMPARE(dtorCount, 3); + } + + dtorCount = 0; + { + // Linear handle reference + QQmlEngine hrmEngine; + QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.handle.1.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh"); + QVERIFY(crh != 0); + crh->setEngine(&hrmEngine); + crh->setDtorCount(&dtorCount); + QMetaObject::invokeMethod(object, "createReference"); + CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>(); + CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>(); + QVERIFY(first != 0); + QVERIFY(second != 0); + first->addReference(QQmlData::get(second)->v8object); // create reference + // now we have to reparent second and make second owned by JS. + second->setParent(0); + QQmlEngine::setObjectOwnership(second, QQmlEngine::JavaScriptOwnership); + gc(engine); + QCOMPARE(dtorCount, 0); // due to reference from first to second, second shouldn't be collected. + delete object; + hrmEngine.collectGarbage(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + QCOMPARE(dtorCount, 3); + } + + dtorCount = 0; + { + // Circular handle reference + QQmlEngine hrmEngine; + QQmlComponent component(&hrmEngine, testFileUrl("handleReferenceManagement.handle.2.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh"); + QVERIFY(crh != 0); + crh->setEngine(&hrmEngine); + crh->setDtorCount(&dtorCount); + QMetaObject::invokeMethod(object, "circularReference"); + CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>(); + CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>(); + QVERIFY(first != 0); + QVERIFY(second != 0); + first->addReference(QQmlData::get(second)->v8object); // create circular reference + second->addReference(QQmlData::get(first)->v8object); // note: must be weak. + // now we have to reparent and change ownership. + first->setParent(0); + second->setParent(0); + QQmlEngine::setObjectOwnership(first, QQmlEngine::JavaScriptOwnership); + QQmlEngine::setObjectOwnership(second, QQmlEngine::JavaScriptOwnership); + gc(engine); + QCOMPARE(dtorCount, 2); // despite circular references, both will be collected. + delete object; + hrmEngine.collectGarbage(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + QCOMPARE(dtorCount, 3); + } + + dtorCount = 0; + { + // multiple engine interaction - linear reference + QQmlEngine hrmEngine1; + QQmlEngine hrmEngine2; + QQmlComponent component1(&hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml")); + QQmlComponent component2(&hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml")); + QObject *object1 = component1.create(); + QObject *object2 = component2.create(); + QVERIFY(object1 != 0); + QVERIFY(object2 != 0); + CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh"); + CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh"); + QVERIFY(crh1 != 0); + QVERIFY(crh2 != 0); + crh1->setEngine(&hrmEngine1); + crh2->setEngine(&hrmEngine2); + crh1->setDtorCount(&dtorCount); + crh2->setDtorCount(&dtorCount); + QMetaObject::invokeMethod(object1, "createReference"); + QMetaObject::invokeMethod(object2, "createReference"); + CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>(); + CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>(); + CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>(); + CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>(); + QVERIFY(first1 != 0); + QVERIFY(second1 != 0); + QVERIFY(first2 != 0); + QVERIFY(second2 != 0); + first1->addReference(QQmlData::get(second2)->v8object); // create reference across engines + // now we have to reparent second2 and make second2 owned by JS. + second2->setParent(0); + QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership); + gc(engine); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + QCOMPARE(dtorCount, 0); // due to reference from first1 to second2, second2 shouldn't be collected. + delete object1; + delete object2; + hrmEngine1.collectGarbage(); + hrmEngine2.collectGarbage(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + QCOMPARE(dtorCount, 6); + } + + dtorCount = 0; + { + // multiple engine interaction - circular reference + QQmlEngine hrmEngine1; + QQmlEngine hrmEngine2; + QQmlComponent component1(&hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml")); + QQmlComponent component2(&hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml")); + QObject *object1 = component1.create(); + QObject *object2 = component2.create(); + QVERIFY(object1 != 0); + QVERIFY(object2 != 0); + CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh"); + CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh"); + QVERIFY(crh1 != 0); + QVERIFY(crh2 != 0); + crh1->setEngine(&hrmEngine1); + crh2->setEngine(&hrmEngine2); + crh1->setDtorCount(&dtorCount); + crh2->setDtorCount(&dtorCount); + QMetaObject::invokeMethod(object1, "createReference"); + QMetaObject::invokeMethod(object2, "createReference"); + CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>(); + CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>(); + CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>(); + CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>(); + QVERIFY(first1 != 0); + QVERIFY(second1 != 0); + QVERIFY(first2 != 0); + QVERIFY(second2 != 0); + first1->addReference(QQmlData::get(second1)->v8object); // create linear reference within engine1 + second1->addReference(QQmlData::get(second2)->v8object); // create linear reference across engines + second2->addReference(QQmlData::get(first2)->v8object); // create linear reference within engine2 + first2->addReference(QQmlData::get(first1)->v8object); // close the loop - circular ref across engines + // now we have to reparent and change ownership to JS. + first1->setParent(0); + second1->setParent(0); + first2->setParent(0); + second2->setParent(0); + QQmlEngine::setObjectOwnership(first1, QQmlEngine::JavaScriptOwnership); + QQmlEngine::setObjectOwnership(second1, QQmlEngine::JavaScriptOwnership); + QQmlEngine::setObjectOwnership(first2, QQmlEngine::JavaScriptOwnership); + QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership); + gc(engine); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + QCOMPARE(dtorCount, 4); // circular references shouldn't keep them alive. + delete object1; + delete object2; + hrmEngine1.collectGarbage(); + hrmEngine2.collectGarbage(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + QCOMPARE(dtorCount, 6); + } + + dtorCount = 0; + { + // multiple engine interaction - linear reference with engine deletion + QQmlEngine *hrmEngine1 = new QQmlEngine; + QQmlEngine *hrmEngine2 = new QQmlEngine; + QQmlComponent component1(hrmEngine1, testFileUrl("handleReferenceManagement.handle.1.qml")); + QQmlComponent component2(hrmEngine2, testFileUrl("handleReferenceManagement.handle.1.qml")); + QObject *object1 = component1.create(); + QObject *object2 = component2.create(); + QVERIFY(object1 != 0); + QVERIFY(object2 != 0); + CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh"); + CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh"); + QVERIFY(crh1 != 0); + QVERIFY(crh2 != 0); + crh1->setEngine(hrmEngine1); + crh2->setEngine(hrmEngine2); + crh1->setDtorCount(&dtorCount); + crh2->setDtorCount(&dtorCount); + QMetaObject::invokeMethod(object1, "createReference"); + QMetaObject::invokeMethod(object2, "createReference"); + CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>(); + CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>(); + CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>(); + CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>(); + QVERIFY(first1 != 0); + QVERIFY(second1 != 0); + QVERIFY(first2 != 0); + QVERIFY(second2 != 0); + first1->addReference(QQmlData::get(second1)->v8object); // create linear reference within engine1 + second1->addReference(QQmlData::get(second2)->v8object); // create linear reference across engines + second2->addReference(QQmlData::get(first2)->v8object); // create linear reference within engine2 + // now we have to reparent and change ownership to JS. + first1->setParent(crh1); + second1->setParent(0); + first2->setParent(0); + second2->setParent(0); + QQmlEngine::setObjectOwnership(second1, QQmlEngine::JavaScriptOwnership); + QQmlEngine::setObjectOwnership(first2, QQmlEngine::JavaScriptOwnership); + QQmlEngine::setObjectOwnership(second2, QQmlEngine::JavaScriptOwnership); + gc(engine); + QCOMPARE(dtorCount, 0); + delete hrmEngine2; + gc(engine); + QCOMPARE(dtorCount, 0); + delete object1; + delete object2; + hrmEngine1->collectGarbage(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + QCOMPARE(dtorCount, 6); + delete hrmEngine1; + } +} + +void tst_qqmlecmascript::stringArg() +{ + QQmlComponent component(&engine, testFileUrl("stringArg.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QMetaObject::invokeMethod(object, "success"); + QVERIFY(object->property("returnValue").toBool()); + + QString w1 = testFileUrl("stringArg.qml").toString() + QLatin1String(":45: Error: String.arg(): Invalid arguments"); + QTest::ignoreMessage(QtWarningMsg, w1.toAscii().constData()); + QMetaObject::invokeMethod(object, "failure"); + QVERIFY(object->property("returnValue").toBool()); + + delete object; +} + +void tst_qqmlecmascript::readonlyDeclaration() +{ + QQmlComponent component(&engine, testFileUrl("readonlyDeclaration.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), true); + + delete object; +} + +Q_DECLARE_METATYPE(QList<int>) +Q_DECLARE_METATYPE(QList<qreal>) +Q_DECLARE_METATYPE(QList<bool>) +Q_DECLARE_METATYPE(QList<QString>) +Q_DECLARE_METATYPE(QList<QUrl>) +void tst_qqmlecmascript::sequenceConversionRead() +{ + { + QUrl qmlFile = testFileUrl("sequenceConversion.read.qml"); + QQmlComponent component(&engine, qmlFile); + QObject *object = component.create(); + QVERIFY(object != 0); + MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco"); + QVERIFY(seq != 0); + + QMetaObject::invokeMethod(object, "readSequences"); + QList<int> intList; intList << 1 << 2 << 3 << 4; + QCOMPARE(object->property("intListLength").toInt(), intList.length()); + QCOMPARE(object->property("intList").value<QList<int> >(), intList); + QList<qreal> qrealList; qrealList << 1.1 << 2.2 << 3.3 << 4.4; + QCOMPARE(object->property("qrealListLength").toInt(), qrealList.length()); + QCOMPARE(object->property("qrealList").value<QList<qreal> >(), qrealList); + QList<bool> boolList; boolList << true << false << true << false; + QCOMPARE(object->property("boolListLength").toInt(), boolList.length()); + QCOMPARE(object->property("boolList").value<QList<bool> >(), boolList); + QList<QString> stringList; stringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth"); + QCOMPARE(object->property("stringListLength").toInt(), stringList.length()); + QCOMPARE(object->property("stringList").value<QList<QString> >(), stringList); + QList<QUrl> urlList; urlList << QUrl("http://www.example1.com") << QUrl("http://www.example2.com") << QUrl("http://www.example3.com"); + QCOMPARE(object->property("urlListLength").toInt(), urlList.length()); + QCOMPARE(object->property("urlList").value<QList<QUrl> >(), urlList); + QStringList qstringList; qstringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth"); + QCOMPARE(object->property("qstringListLength").toInt(), qstringList.length()); + QCOMPARE(object->property("qstringList").value<QStringList>(), qstringList); + + QMetaObject::invokeMethod(object, "readSequenceElements"); + QCOMPARE(object->property("intVal").toInt(), 2); + QCOMPARE(object->property("qrealVal").toReal(), 2.2); + QCOMPARE(object->property("boolVal").toBool(), false); + QCOMPARE(object->property("stringVal").toString(), QString(QLatin1String("second"))); + QCOMPARE(object->property("urlVal").toUrl(), QUrl("http://www.example2.com")); + QCOMPARE(object->property("qstringVal").toString(), QString(QLatin1String("second"))); + + QMetaObject::invokeMethod(object, "enumerateSequenceElements"); + QCOMPARE(object->property("enumerationMatches").toBool(), true); + + intList.clear(); intList << 1 << 2 << 3 << 4 << 5; // set by the enumerateSequenceElements test. + QQmlProperty seqProp(seq, "intListProperty"); + QCOMPARE(seqProp.read().value<QList<int> >(), intList); + QQmlProperty seqProp2(seq, "intListProperty", &engine); + QCOMPARE(seqProp2.read().value<QList<int> >(), intList); + + QMetaObject::invokeMethod(object, "testReferenceDeletion"); + QCOMPARE(object->property("referenceDeletion").toBool(), true); + + delete object; + } + + { + QUrl qmlFile = testFileUrl("sequenceConversion.read.error.qml"); + QQmlComponent component(&engine, qmlFile); + QObject *object = component.create(); + QVERIFY(object != 0); + MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco"); + QVERIFY(seq != 0); + + // we haven't registered QList<QPoint> as a sequence type. + QString warningOne = QLatin1String("QMetaProperty::read: Unable to handle unregistered datatype 'QList<QPoint>' for property 'MySequenceConversionObject::pointListProperty'"); + QString warningTwo = qmlFile.toString() + QLatin1String(":18: TypeError: Cannot read property 'length' of undefined"); + QTest::ignoreMessage(QtWarningMsg, warningOne.toAscii().constData()); + QTest::ignoreMessage(QtWarningMsg, warningTwo.toAscii().constData()); + + QMetaObject::invokeMethod(object, "performTest"); + + // QList<QPoint> has not been registered as a sequence type. + QCOMPARE(object->property("pointListLength").toInt(), 0); + QVERIFY(!object->property("pointList").isValid()); + QTest::ignoreMessage(QtWarningMsg, "QMetaProperty::read: Unable to handle unregistered datatype 'QList<QPoint>' for property 'MySequenceConversionObject::pointListProperty'"); + QQmlProperty seqProp(seq, "pointListProperty", &engine); + QVERIFY(!seqProp.read().isValid()); // not a valid/known sequence type + + delete object; + } +} + +void tst_qqmlecmascript::sequenceConversionWrite() +{ + { + QUrl qmlFile = testFileUrl("sequenceConversion.write.qml"); + QQmlComponent component(&engine, qmlFile); + QObject *object = component.create(); + QVERIFY(object != 0); + MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco"); + QVERIFY(seq != 0); + + QMetaObject::invokeMethod(object, "writeSequences"); + QCOMPARE(object->property("success").toBool(), true); + + QMetaObject::invokeMethod(object, "writeSequenceElements"); + QCOMPARE(object->property("success").toBool(), true); + + QMetaObject::invokeMethod(object, "writeOtherElements"); + QCOMPARE(object->property("success").toBool(), true); + + QMetaObject::invokeMethod(object, "testReferenceDeletion"); + QCOMPARE(object->property("referenceDeletion").toBool(), true); + + delete object; + } + + { + QUrl qmlFile = testFileUrl("sequenceConversion.write.error.qml"); + QQmlComponent component(&engine, qmlFile); + QObject *object = component.create(); + QVERIFY(object != 0); + MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>("msco"); + QVERIFY(seq != 0); + + // we haven't registered QList<QPoint> as a sequence type, so writing shouldn't work. + QString warningOne = qmlFile.toString() + QLatin1String(":16: Error: Cannot assign QVariantList to void"); + QTest::ignoreMessage(QtWarningMsg, warningOne.toAscii().constData()); + + QMetaObject::invokeMethod(object, "performTest"); + + QList<QPoint> pointList; pointList << QPoint(1, 2) << QPoint(3, 4) << QPoint(5, 6); // original values, shouldn't have changed + QCOMPARE(seq->pointListProperty(), pointList); + + delete object; + } +} + +void tst_qqmlecmascript::sequenceConversionArray() +{ + // ensure that in JS the returned sequences act just like normal JS Arrays. + QUrl qmlFile = testFileUrl("sequenceConversion.array.qml"); + QQmlComponent component(&engine, qmlFile); + QObject *object = component.create(); + QVERIFY(object != 0); + QMetaObject::invokeMethod(object, "indexedAccess"); + QVERIFY(object->property("success").toBool()); + QMetaObject::invokeMethod(object, "arrayOperations"); + QVERIFY(object->property("success").toBool()); + QMetaObject::invokeMethod(object, "testEqualitySemantics"); + QVERIFY(object->property("success").toBool()); + QMetaObject::invokeMethod(object, "testReferenceDeletion"); + QCOMPARE(object->property("referenceDeletion").toBool(), true); + delete object; +} + + +void tst_qqmlecmascript::sequenceConversionIndexes() +{ + // ensure that we gracefully fail if unsupported index values are specified. + // Qt container classes only support non-negative, signed integer index values. + QUrl qmlFile = testFileUrl("sequenceConversion.indexes.qml"); + QQmlComponent component(&engine, qmlFile); + QObject *object = component.create(); + QVERIFY(object != 0); + QString w1 = qmlFile.toString() + QLatin1String(":34: Index out of range during length set"); + QString w2 = qmlFile.toString() + QLatin1String(":41: Index out of range during indexed set"); + QString w3 = qmlFile.toString() + QLatin1String(":48: Index out of range during indexed get"); + QString w4 = qmlFile.toString() + QLatin1String(":78: std::bad_alloc during length set"); + QString w5 = qmlFile.toString() + QLatin1String(":83: std::bad_alloc during indexed set"); + QTest::ignoreMessage(QtWarningMsg, qPrintable(w1)); + QTest::ignoreMessage(QtWarningMsg, qPrintable(w2)); + QTest::ignoreMessage(QtWarningMsg, qPrintable(w3)); + QTest::ignoreMessage(QtWarningMsg, qPrintable(w4)); + QTest::ignoreMessage(QtWarningMsg, qPrintable(w5)); + QMetaObject::invokeMethod(object, "indexedAccess"); + QVERIFY(object->property("success").toBool()); + delete object; +} + +void tst_qqmlecmascript::sequenceConversionThreads() +{ + // ensure that sequence conversion operations work correctly in a worker thread + // and that serialisation between the main and worker thread succeeds. + QUrl qmlFile = testFileUrl("sequenceConversion.threads.qml"); + QQmlComponent component(&engine, qmlFile); + QObject *object = component.create(); + QVERIFY(object != 0); + + QMetaObject::invokeMethod(object, "testIntSequence"); + QTRY_VERIFY(object->property("finished").toBool()); + QVERIFY(object->property("success").toBool()); + + QMetaObject::invokeMethod(object, "testQrealSequence"); + QTRY_VERIFY(object->property("finished").toBool()); + QVERIFY(object->property("success").toBool()); + + QMetaObject::invokeMethod(object, "testBoolSequence"); + QTRY_VERIFY(object->property("finished").toBool()); + QVERIFY(object->property("success").toBool()); + + QMetaObject::invokeMethod(object, "testStringSequence"); + QTRY_VERIFY(object->property("finished").toBool()); + QVERIFY(object->property("success").toBool()); + + QMetaObject::invokeMethod(object, "testQStringSequence"); + QTRY_VERIFY(object->property("finished").toBool()); + QVERIFY(object->property("success").toBool()); + + QMetaObject::invokeMethod(object, "testUrlSequence"); + QTRY_VERIFY(object->property("finished").toBool()); + QVERIFY(object->property("success").toBool()); + + QMetaObject::invokeMethod(object, "testVariantSequence"); + QTRY_VERIFY(object->property("finished").toBool()); + QVERIFY(object->property("success").toBool()); + + delete object; +} + +void tst_qqmlecmascript::sequenceConversionBindings() +{ + { + QUrl qmlFile = testFileUrl("sequenceConversion.bindings.qml"); + QQmlComponent component(&engine, qmlFile); + QObject *object = component.create(); + QVERIFY(object != 0); + QList<int> intList; intList << 1 << 2 << 3 << 12 << 7; + QCOMPARE(object->property("boundSequence").value<QList<int> >(), intList); + QCOMPARE(object->property("boundElement").toInt(), intList.at(3)); + QList<int> intListTwo; intListTwo << 1 << 2 << 3 << 12 << 14; + QCOMPARE(object->property("boundSequenceTwo").value<QList<int> >(), intListTwo); + delete object; + } + + { + QUrl qmlFile = testFileUrl("sequenceConversion.bindings.error.qml"); + QString warning = QString(QLatin1String("%1:17: Unable to assign QList<int> to QList<bool>")).arg(qmlFile.toString()); + QTest::ignoreMessage(QtWarningMsg, warning.toAscii().constData()); + QQmlComponent component(&engine, qmlFile); + QObject *object = component.create(); + QVERIFY(object != 0); + delete object; + } +} + +void tst_qqmlecmascript::sequenceConversionCopy() +{ + QUrl qmlFile = testFileUrl("sequenceConversion.copy.qml"); + QQmlComponent component(&engine, qmlFile); + QObject *object = component.create(); + QVERIFY(object != 0); + QMetaObject::invokeMethod(object, "testCopySequences"); + QCOMPARE(object->property("success").toBool(), true); + QMetaObject::invokeMethod(object, "readSequenceCopyElements"); + QCOMPARE(object->property("success").toBool(), true); + QMetaObject::invokeMethod(object, "testEqualitySemantics"); + QCOMPARE(object->property("success").toBool(), true); + delete object; +} + +void tst_qqmlecmascript::assignSequenceTypes() +{ + // test binding array to sequence type property + { + QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.1.qml")); + MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2)); + QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2)); + QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true)); + QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com"))); + QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one") << QLatin1String("two"))); + QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two"))); + delete object; + } + + // test binding literal to sequence type property + { + QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.2.qml")); + MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->intListProperty(), (QList<int>() << 1)); + QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1)); + QCOMPARE(object->boolListProperty(), (QList<bool>() << false)); + QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com"))); + QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one"))); + QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two"))); + delete object; + } + + // test binding single value to sequence type property + { + QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.3.qml")); + MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->intListProperty(), (QList<int>() << 1)); + QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1)); + QCOMPARE(object->boolListProperty(), (QList<bool>() << false)); + QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")))); + delete object; + } + + // test assigning array to sequence type property in js function + { + QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.4.qml")); + MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2)); + QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2)); + QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true)); + QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com"))); + QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one") << QLatin1String("two"))); + QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two"))); + delete object; + } + + // test assigning literal to sequence type property in js function + { + QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.5.qml")); + MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->intListProperty(), (QList<int>() << 1)); + QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1)); + QCOMPARE(object->boolListProperty(), (QList<bool>() << false)); + QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com"))); + QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one"))); + QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two"))); + delete object; + } + + // test assigning single value to sequence type property in js function + { + QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.6.qml")); + MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->intListProperty(), (QList<int>() << 1)); + QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1)); + QCOMPARE(object->boolListProperty(), (QList<bool>() << false)); + QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")))); + delete object; + } + + // test QList<QUrl> literal assignment and binding assignment causes url resolution when required + { + QQmlComponent component(&engine, testFileUrl("assignSequenceTypes.7.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + MySequenceConversionObject *msco1 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco1")); + MySequenceConversionObject *msco2 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco2")); + MySequenceConversionObject *msco3 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco3")); + MySequenceConversionObject *msco4 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco4")); + MySequenceConversionObject *msco5 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco5")); + QVERIFY(msco1 != 0 && msco2 != 0 && msco3 != 0 && msco4 != 0 && msco5 != 0); + QCOMPARE(msco1->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")))); + QCOMPARE(msco2->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")))); + QCOMPARE(msco3->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html")))); + QCOMPARE(msco4->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html")))); + QCOMPARE(msco5->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html")))); + delete object; + } +} + +// Test that assigning a null object works +// Regressed with: df1788b4dbbb2826ae63f26bdf166342595343f4 +void tst_qqmlecmascript::nullObjectBinding() +{ + QQmlComponent component(&engine, testFileUrl("nullObjectBinding.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + + QVERIFY(object->property("test") == QVariant::fromValue((QObject *)0)); + + delete object; +} + +// Test that bindings don't evaluate once the engine has been destroyed +void tst_qqmlecmascript::deletedEngine() +{ + QQmlEngine *engine = new QQmlEngine; + QQmlComponent component(engine, testFileUrl("deletedEngine.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("a").toInt(), 39); + object->setProperty("b", QVariant(9)); + QCOMPARE(object->property("a").toInt(), 117); + + delete engine; + + QCOMPARE(object->property("a").toInt(), 117); + object->setProperty("b", QVariant(10)); + QCOMPARE(object->property("a").toInt(), 117); + + delete object; +} + +// Test the crashing part of QTBUG-9705 +void tst_qqmlecmascript::libraryScriptAssert() +{ + QQmlComponent component(&engine, testFileUrl("libraryScriptAssert.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + + delete object; +} + +void tst_qqmlecmascript::variantsAssignedUndefined() +{ + QQmlComponent component(&engine, testFileUrl("variantsAssignedUndefined.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test1").toInt(), 10); + QCOMPARE(object->property("test2").toInt(), 11); + + object->setProperty("runTest", true); + + QCOMPARE(object->property("test1"), QVariant()); + QCOMPARE(object->property("test2"), QVariant()); + + + delete object; +} + +void tst_qqmlecmascript::qtbug_9792() +{ + QQmlComponent component(&engine, testFileUrl("qtbug_9792.qml")); + + QQmlContext *context = new QQmlContext(engine.rootContext()); + + MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create(context)); + QVERIFY(object != 0); + + QTest::ignoreMessage(QtDebugMsg, "Hello world!"); + object->basicSignal(); + + delete context; + + transientErrorsMsgCount = 0; + QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler); + + object->basicSignal(); + + qInstallMsgHandler(old); + + QCOMPARE(transientErrorsMsgCount, 0); + + delete object; +} + +// Verifies that QQmlGuard<>s used in the vmemetaobject are cleaned correctly +void tst_qqmlecmascript::qtcreatorbug_1289() +{ + QQmlComponent component(&engine, testFileUrl("qtcreatorbug_1289.qml")); + + QObject *o = component.create(); + QVERIFY(o != 0); + + QObject *nested = qvariant_cast<QObject *>(o->property("object")); + QVERIFY(nested != 0); + + QVERIFY(qvariant_cast<QObject *>(nested->property("nestedObject")) == o); + + delete nested; + nested = qvariant_cast<QObject *>(o->property("object")); + QVERIFY(nested == 0); + + // If the bug is present, the next line will crash + delete o; +} + +// Test that we shut down without stupid warnings +void tst_qqmlecmascript::noSpuriousWarningsAtShutdown() +{ + { + QQmlComponent component(&engine, testFileUrl("noSpuriousWarningsAtShutdown.qml")); + + QObject *o = component.create(); + + transientErrorsMsgCount = 0; + QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler); + + delete o; + + qInstallMsgHandler(old); + + QCOMPARE(transientErrorsMsgCount, 0); + } + + + { + QQmlComponent component(&engine, testFileUrl("noSpuriousWarningsAtShutdown.2.qml")); + + QObject *o = component.create(); + + transientErrorsMsgCount = 0; + QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler); + + delete o; + + qInstallMsgHandler(old); + + QCOMPARE(transientErrorsMsgCount, 0); + } +} + +void tst_qqmlecmascript::canAssignNullToQObject() +{ + { + QQmlComponent component(&engine, testFileUrl("canAssignNullToQObject.1.qml")); + + MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(o != 0); + + QVERIFY(o->objectProperty() != 0); + + o->setProperty("runTest", true); + + QVERIFY(o->objectProperty() == 0); + + delete o; + } + + { + QQmlComponent component(&engine, testFileUrl("canAssignNullToQObject.2.qml")); + + MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(o != 0); + + QVERIFY(o->objectProperty() == 0); + + delete o; + } +} + +void tst_qqmlecmascript::functionAssignment_fromBinding() +{ + QQmlComponent component(&engine, testFileUrl("functionAssignment.1.qml")); + + QString url = component.url().toString(); + QString warning = url + ":4: Unable to assign a function to a property."; + QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); + + MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(o != 0); + + QVERIFY(!o->property("a").isValid()); + + delete o; +} + +void tst_qqmlecmascript::functionAssignment_fromJS() +{ + QFETCH(QString, triggerProperty); + + QQmlComponent component(&engine, testFileUrl("functionAssignment.2.qml")); + QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString())); + + MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(o != 0); + QVERIFY(!o->property("a").isValid()); + + o->setProperty("aNumber", QVariant(5)); + o->setProperty(triggerProperty.toUtf8().constData(), true); + QCOMPARE(o->property("a"), QVariant(50)); + + o->setProperty("aNumber", QVariant(10)); + QCOMPARE(o->property("a"), QVariant(100)); + + delete o; +} + +void tst_qqmlecmascript::functionAssignment_fromJS_data() +{ + QTest::addColumn<QString>("triggerProperty"); + + QTest::newRow("assign to property") << "assignToProperty"; + QTest::newRow("assign to property, from JS file") << "assignToPropertyFromJsFile"; + + QTest::newRow("assign to value type") << "assignToValueType"; + + QTest::newRow("use 'this'") << "assignWithThis"; + QTest::newRow("use 'this' from JS file") << "assignWithThisFromJsFile"; +} + +void tst_qqmlecmascript::functionAssignmentfromJS_invalid() +{ + QQmlComponent component(&engine, testFileUrl("functionAssignment.2.qml")); + QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString())); + + MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(o != 0); + QVERIFY(!o->property("a").isValid()); + + o->setProperty("assignFuncWithoutReturn", true); + QVERIFY(!o->property("a").isValid()); + + QString url = component.url().toString(); + QString warning = url + ":67: Unable to assign QString to int"; + QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); + o->setProperty("assignWrongType", true); + + warning = url + ":71: Unable to assign QString to int"; + QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); + o->setProperty("assignWrongTypeToValueType", true); + + delete o; +} + +void tst_qqmlecmascript::eval() +{ + QQmlComponent component(&engine, testFileUrl("eval.qml")); + + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test1").toBool(), true); + QCOMPARE(o->property("test2").toBool(), true); + QCOMPARE(o->property("test3").toBool(), true); + QCOMPARE(o->property("test4").toBool(), true); + QCOMPARE(o->property("test5").toBool(), true); + + delete o; +} + +void tst_qqmlecmascript::function() +{ + QQmlComponent component(&engine, testFileUrl("function.qml")); + + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test1").toBool(), true); + QCOMPARE(o->property("test2").toBool(), true); + QCOMPARE(o->property("test3").toBool(), true); + + delete o; +} + +void tst_qqmlecmascript::functionException() +{ + // QTBUG-24037 - shouldn't crash. + QString errstr = testFileUrl("v8functionException.qml").toString() + QLatin1String(":13: SyntaxError: Unexpected token ILLEGAL"); + QTest::ignoreMessage(QtWarningMsg, qPrintable(errstr)); + QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: Exception occurred during compilation of function: dynamicSlot()"); + QQmlComponent component(&engine, testFileUrl("v8functionException.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + QMetaObject::invokeMethod(o, "dynamicSlot"); + delete o; +} + +// Test the "Qt.include" method +void tst_qqmlecmascript::include() +{ + // Non-library relative include + { + QQmlComponent component(&engine, testFileUrl("include.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test0").toInt(), 99); + QCOMPARE(o->property("test1").toBool(), true); + QCOMPARE(o->property("test2").toBool(), true); + QCOMPARE(o->property("test2_1").toBool(), true); + QCOMPARE(o->property("test3").toBool(), true); + QCOMPARE(o->property("test3_1").toBool(), true); + + delete o; + } + + // Library relative include + { + QQmlComponent component(&engine, testFileUrl("include_shared.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test0").toInt(), 99); + QCOMPARE(o->property("test1").toBool(), true); + QCOMPARE(o->property("test2").toBool(), true); + QCOMPARE(o->property("test2_1").toBool(), true); + QCOMPARE(o->property("test3").toBool(), true); + QCOMPARE(o->property("test3_1").toBool(), true); + + delete o; + } + + // Callback + { + QQmlComponent component(&engine, testFileUrl("include_callback.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test1").toBool(), true); + QCOMPARE(o->property("test2").toBool(), true); + QCOMPARE(o->property("test3").toBool(), true); + QCOMPARE(o->property("test4").toBool(), true); + QCOMPARE(o->property("test5").toBool(), true); + QCOMPARE(o->property("test6").toBool(), true); + + delete o; + } + + // Including file with ".pragma library" + { + QQmlComponent component(&engine, testFileUrl("include_pragma.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + QCOMPARE(o->property("test1").toInt(), 100); + + delete o; + } + + // Remote - success + { + TestHTTPServer server(8111); + QVERIFY(server.isValid()); + server.serveDirectory(dataDirectory()); + + QQmlComponent component(&engine, testFileUrl("include_remote.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QTRY_VERIFY(o->property("done").toBool() == true); + QTRY_VERIFY(o->property("done2").toBool() == true); + + QCOMPARE(o->property("test1").toBool(), true); + QCOMPARE(o->property("test2").toBool(), true); + QCOMPARE(o->property("test3").toBool(), true); + QCOMPARE(o->property("test4").toBool(), true); + QCOMPARE(o->property("test5").toBool(), true); + + QCOMPARE(o->property("test6").toBool(), true); + QCOMPARE(o->property("test7").toBool(), true); + QCOMPARE(o->property("test8").toBool(), true); + QCOMPARE(o->property("test9").toBool(), true); + QCOMPARE(o->property("test10").toBool(), true); + + delete o; + } + + // Remote - error + { + TestHTTPServer server(8111); + QVERIFY(server.isValid()); + server.serveDirectory(dataDirectory()); + + QQmlComponent component(&engine, testFileUrl("include_remote_missing.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QTRY_VERIFY(o->property("done").toBool() == true); + + QCOMPARE(o->property("test1").toBool(), true); + QCOMPARE(o->property("test2").toBool(), true); + QCOMPARE(o->property("test3").toBool(), true); + + delete o; + } +} + +void tst_qqmlecmascript::signalHandlers() +{ + QQmlComponent component(&engine, testFileUrl("signalHandlers.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QVERIFY(o->property("count").toInt() == 0); + QMetaObject::invokeMethod(o, "testSignalCall"); + QCOMPARE(o->property("count").toInt(), 1); + + QMetaObject::invokeMethod(o, "testSignalHandlerCall"); + QCOMPARE(o->property("count").toInt(), 1); + QCOMPARE(o->property("errorString").toString(), QLatin1String("TypeError: Property 'onTestSignal' of object [object Object] is not a function")); + + QVERIFY(o->property("funcCount").toInt() == 0); + QMetaObject::invokeMethod(o, "testSignalConnection"); + QCOMPARE(o->property("funcCount").toInt(), 1); + + QMetaObject::invokeMethod(o, "testSignalHandlerConnection"); + QCOMPARE(o->property("funcCount").toInt(), 2); + + QMetaObject::invokeMethod(o, "testSignalDefined"); + QCOMPARE(o->property("definedResult").toBool(), true); + + QMetaObject::invokeMethod(o, "testSignalHandlerDefined"); + QCOMPARE(o->property("definedHandlerResult").toBool(), true); + + delete o; +} + +void tst_qqmlecmascript::qtbug_10696() +{ + QQmlComponent component(&engine, testFileUrl("qtbug_10696.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + delete o; +} + +void tst_qqmlecmascript::qtbug_11606() +{ + QQmlComponent component(&engine, testFileUrl("qtbug_11606.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + QCOMPARE(o->property("test").toBool(), true); + delete o; +} + +void tst_qqmlecmascript::qtbug_11600() +{ + QQmlComponent component(&engine, testFileUrl("qtbug_11600.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + QCOMPARE(o->property("test").toBool(), true); + delete o; +} + +void tst_qqmlecmascript::qtbug_21864() +{ + QQmlComponent component(&engine, testFileUrl("qtbug_21864.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + QCOMPARE(o->property("test").toBool(), true); + delete o; +} + +void tst_qqmlecmascript::rewriteMultiLineStrings() +{ + // QTBUG-23387 + QQmlComponent component(&engine, testFileUrl("rewriteMultiLineStrings.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + QTRY_COMPARE(o->property("test").toBool(), true); + delete o; +} + +void tst_qqmlecmascript::qobjectConnectionListExceptionHandling() +{ + // QTBUG-23375 + QQmlComponent component(&engine, testFileUrl("qobjectConnectionListExceptionHandling.qml")); + QString warning = component.url().toString() + QLatin1String(":13: TypeError: Cannot read property 'undefined' of undefined"); + QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); + QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); + QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); + QObject *o = component.create(); + QVERIFY(o != 0); + QCOMPARE(o->property("test").toBool(), true); + delete o; +} + +// Reading and writing non-scriptable properties should fail +void tst_qqmlecmascript::nonscriptable() +{ + QQmlComponent component(&engine, testFileUrl("nonscriptable.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + QCOMPARE(o->property("readOk").toBool(), true); + QCOMPARE(o->property("writeOk").toBool(), true); + delete o; +} + +// deleteLater() should not be callable from QML +void tst_qqmlecmascript::deleteLater() +{ + QQmlComponent component(&engine, testFileUrl("deleteLater.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + QCOMPARE(o->property("test").toBool(), true); + delete o; +} + +void tst_qqmlecmascript::in() +{ + QQmlComponent component(&engine, testFileUrl("in.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + QCOMPARE(o->property("test1").toBool(), true); + QCOMPARE(o->property("test2").toBool(), true); + delete o; +} + +void tst_qqmlecmascript::typeOf() +{ + QQmlComponent component(&engine, testFileUrl("typeOf.qml")); + + // These warnings should not happen once QTBUG-21864 is fixed + QString warning1 = component.url().toString() + QLatin1String(":16: Error: Cannot assign [undefined] to QString"); + QString warning2 = component.url().resolved(QUrl("typeOf.js")).toString() + QLatin1String(":1: ReferenceError: Can't find variable: a"); + + QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1)); + QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); + + QObject *o = component.create(); + QVERIFY(o != 0); + + QEXPECT_FAIL("", "QTBUG-21864", Abort); + QCOMPARE(o->property("test1").toString(), QLatin1String("undefined")); + QCOMPARE(o->property("test2").toString(), QLatin1String("object")); + QCOMPARE(o->property("test3").toString(), QLatin1String("number")); + QCOMPARE(o->property("test4").toString(), QLatin1String("string")); + QCOMPARE(o->property("test5").toString(), QLatin1String("function")); + QCOMPARE(o->property("test6").toString(), QLatin1String("object")); + QCOMPARE(o->property("test7").toString(), QLatin1String("undefined")); + QCOMPARE(o->property("test8").toString(), QLatin1String("boolean")); + QCOMPARE(o->property("test9").toString(), QLatin1String("object")); + + delete o; +} + +void tst_qqmlecmascript::sharedAttachedObject() +{ + QQmlComponent component(&engine, testFileUrl("sharedAttachedObject.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + QCOMPARE(o->property("test1").toBool(), true); + QCOMPARE(o->property("test2").toBool(), true); + delete o; +} + +// QTBUG-13999 +void tst_qqmlecmascript::objectName() +{ + QQmlComponent component(&engine, testFileUrl("objectName.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test1").toString(), QString("hello")); + QCOMPARE(o->property("test2").toString(), QString("ell")); + + o->setObjectName("world"); + + QCOMPARE(o->property("test1").toString(), QString("world")); + QCOMPARE(o->property("test2").toString(), QString("orl")); + + delete o; +} + +void tst_qqmlecmascript::writeRemovesBinding() +{ + QQmlComponent component(&engine, testFileUrl("writeRemovesBinding.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test").toBool(), true); + + delete o; +} + +// Test bindings assigned to alias properties actually assign to the alias' target +void tst_qqmlecmascript::aliasBindingsAssignCorrectly() +{ + QQmlComponent component(&engine, testFileUrl("aliasBindingsAssignCorrectly.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test").toBool(), true); + + delete o; +} + +// Test bindings assigned to alias properties override a binding on the target (QTBUG-13719) +void tst_qqmlecmascript::aliasBindingsOverrideTarget() +{ + { + QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test").toBool(), true); + + delete o; + } + + { + QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.2.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test").toBool(), true); + + delete o; + } + + { + QQmlComponent component(&engine, testFileUrl("aliasBindingsOverrideTarget.3.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test").toBool(), true); + + delete o; + } +} + +// Test that writes to alias properties override bindings on the alias target (QTBUG-13719) +void tst_qqmlecmascript::aliasWritesOverrideBindings() +{ + { + QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test").toBool(), true); + + delete o; + } + + { + QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.2.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test").toBool(), true); + + delete o; + } + + { + QQmlComponent component(&engine, testFileUrl("aliasWritesOverrideBindings.3.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test").toBool(), true); + + delete o; + } +} + +// Allow an alais to a composite element +// QTBUG-20200 +void tst_qqmlecmascript::aliasToCompositeElement() +{ + QQmlComponent component(&engine, testFileUrl("aliasToCompositeElement.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + + delete object; +} + +void tst_qqmlecmascript::qtbug_20344() +{ + QQmlComponent component(&engine, testFileUrl("qtbug_20344.qml")); + + QString warning = component.url().toString() + ":5: Error: Exception thrown from within QObject slot"; + QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); + + QObject *object = component.create(); + QVERIFY(object != 0); + + delete object; +} + +void tst_qqmlecmascript::revisionErrors() +{ + { + QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors.qml")); + QString url = component.url().toString(); + + QString warning1 = url + ":8: ReferenceError: Can't find variable: prop2"; + QString warning2 = url + ":11: ReferenceError: Can't find variable: prop2"; + QString warning3 = url + ":13: ReferenceError: Can't find variable: method2"; + + QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData()); + QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData()); + QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData()); + MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create()); + QVERIFY(object != 0); + delete object; + } + { + QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors2.qml")); + QString url = component.url().toString(); + + // MyRevisionedSubclass 1.0 uses MyRevisionedClass revision 0 + // method2, prop2 from MyRevisionedClass not available + // method4, prop4 from MyRevisionedSubclass not available + QString warning1 = url + ":8: ReferenceError: Can't find variable: prop2"; + QString warning2 = url + ":14: ReferenceError: Can't find variable: prop2"; + QString warning3 = url + ":10: ReferenceError: Can't find variable: prop4"; + QString warning4 = url + ":16: ReferenceError: Can't find variable: prop4"; + QString warning5 = url + ":20: ReferenceError: Can't find variable: method2"; + + QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData()); + QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData()); + QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData()); + QTest::ignoreMessage(QtWarningMsg, warning4.toLatin1().constData()); + QTest::ignoreMessage(QtWarningMsg, warning5.toLatin1().constData()); + MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create()); + QVERIFY(object != 0); + delete object; + } + { + QQmlComponent component(&engine, testFileUrl("metaobjectRevisionErrors3.qml")); + QString url = component.url().toString(); + + // MyRevisionedSubclass 1.1 uses MyRevisionedClass revision 1 + // All properties/methods available, except MyRevisionedBaseClassUnregistered rev 1 + QString warning1 = url + ":30: ReferenceError: Can't find variable: methodD"; + QString warning2 = url + ":10: ReferenceError: Can't find variable: propD"; + QString warning3 = url + ":20: ReferenceError: Can't find variable: propD"; + QTest::ignoreMessage(QtWarningMsg, warning1.toLatin1().constData()); + QTest::ignoreMessage(QtWarningMsg, warning2.toLatin1().constData()); + QTest::ignoreMessage(QtWarningMsg, warning3.toLatin1().constData()); + MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create()); + QVERIFY(object != 0); + delete object; + } +} + +void tst_qqmlecmascript::revision() +{ + { + QQmlComponent component(&engine, testFileUrl("metaobjectRevision.qml")); + QString url = component.url().toString(); + + MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create()); + QVERIFY(object != 0); + delete object; + } + { + QQmlComponent component(&engine, testFileUrl("metaobjectRevision2.qml")); + QString url = component.url().toString(); + + MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create()); + QVERIFY(object != 0); + delete object; + } + { + QQmlComponent component(&engine, testFileUrl("metaobjectRevision3.qml")); + QString url = component.url().toString(); + + MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(component.create()); + QVERIFY(object != 0); + delete object; + } + // Test that non-root classes can resolve revisioned methods + { + QQmlComponent component(&engine, testFileUrl("metaobjectRevision4.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + QCOMPARE(object->property("test").toReal(), 11.); + delete object; + } +} + +void tst_qqmlecmascript::realToInt() +{ + QQmlComponent component(&engine, testFileUrl("realToInt.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); + QVERIFY(object != 0); + + QMetaObject::invokeMethod(object, "test1"); + QCOMPARE(object->value(), int(4)); + QMetaObject::invokeMethod(object, "test2"); + QCOMPARE(object->value(), int(8)); +} + +void tst_qqmlecmascript::urlProperty() +{ + { + QQmlComponent component(&engine, testFileUrl("urlProperty.1.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); + QVERIFY(object != 0); + object->setStringProperty("http://qt-project.org"); + QCOMPARE(object->urlProperty(), QUrl("http://qt-project.org/index.html")); + QCOMPARE(object->intProperty(), 123); + QCOMPARE(object->value(), 1); + QCOMPARE(object->property("result").toBool(), true); + } +} + +void tst_qqmlecmascript::urlPropertyWithEncoding() +{ + { + QQmlComponent component(&engine, testFileUrl("urlProperty.2.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create()); + QVERIFY(object != 0); + object->setStringProperty("http://qt-project.org"); + QUrl encoded; + encoded.setEncodedUrl("http://qt-project.org/?get%3cDATA%3e", QUrl::TolerantMode); + QCOMPARE(object->urlProperty(), encoded); + QCOMPARE(object->value(), 0); // Interpreting URL as string yields canonicalised version + QCOMPARE(object->property("result").toBool(), true); + } +} + +void tst_qqmlecmascript::urlListPropertyWithEncoding() +{ + { + QQmlComponent component(&engine, testFileUrl("urlListProperty.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + MySequenceConversionObject *msco1 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco1")); + MySequenceConversionObject *msco2 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco2")); + MySequenceConversionObject *msco3 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco3")); + MySequenceConversionObject *msco4 = object->findChild<MySequenceConversionObject *>(QLatin1String("msco4")); + QVERIFY(msco1 != 0 && msco2 != 0 && msco3 != 0 && msco4 != 0); + QUrl encoded; + encoded.setEncodedUrl("http://qt-project.org/?get%3cDATA%3e", QUrl::TolerantMode); + QCOMPARE(msco1->urlListProperty(), (QList<QUrl>() << encoded)); + QCOMPARE(msco2->urlListProperty(), (QList<QUrl>() << encoded)); + QCOMPARE(msco3->urlListProperty(), (QList<QUrl>() << encoded << encoded)); + QCOMPARE(msco4->urlListProperty(), (QList<QUrl>() << encoded << encoded)); + delete object; + } +} + +void tst_qqmlecmascript::dynamicString() +{ + QQmlComponent component(&engine, testFileUrl("dynamicString.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QCOMPARE(object->property("stringProperty").toString(), + QString::fromLatin1("string:Hello World false:0 true:1 uint32:100 int32:-100 double:3.14159 date:2011-02-11 05::30:50!")); +} + +void tst_qqmlecmascript::automaticSemicolon() +{ + QQmlComponent component(&engine, testFileUrl("automaticSemicolon.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); +} + +void tst_qqmlecmascript::unaryExpression() +{ + QQmlComponent component(&engine, testFileUrl("unaryExpression.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); +} + +// Makes sure that a binding isn't double re-evaluated when it depends on the same variable twice +void tst_qqmlecmascript::doubleEvaluate() +{ + QQmlComponent component(&engine, testFileUrl("doubleEvaluate.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + WriteCounter *wc = qobject_cast<WriteCounter *>(object); + QVERIFY(wc != 0); + QCOMPARE(wc->count(), 1); + + wc->setProperty("x", 9); + + QCOMPARE(wc->count(), 2); + + delete object; +} + +static QStringList messages; +static void captureMsgHandler(QtMsgType, const char *msg) +{ + messages.append(QLatin1String(msg)); +} + +void tst_qqmlecmascript::nonNotifyable() +{ + QV4Compiler::enableV4(false); + QQmlComponent component(&engine, testFileUrl("nonNotifyable.qml")); + QV4Compiler::enableV4(true); + + QtMsgHandler old = qInstallMsgHandler(captureMsgHandler); + messages.clear(); + QObject *object = component.create(); + qInstallMsgHandler(old); + + QVERIFY(object != 0); + + QString expected1 = QLatin1String("QQmlExpression: Expression ") + + component.url().toString() + + QLatin1String(":5 depends on non-NOTIFYable properties:"); + QString expected2 = QLatin1String(" ") + + QLatin1String(object->metaObject()->className()) + + QLatin1String("::value"); + + QCOMPARE(messages.length(), 2); + QCOMPARE(messages.at(0), expected1); + QCOMPARE(messages.at(1), expected2); + + delete object; +} + +void tst_qqmlecmascript::forInLoop() +{ + QQmlComponent component(&engine, testFileUrl("forInLoop.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QMetaObject::invokeMethod(object, "listProperty"); + + QStringList r = object->property("listResult").toString().split("|", QString::SkipEmptyParts); + QCOMPARE(r.size(), 3); + QCOMPARE(r[0],QLatin1String("0=obj1")); + QCOMPARE(r[1],QLatin1String("1=obj2")); + QCOMPARE(r[2],QLatin1String("2=obj3")); + + //TODO: should test for in loop for other objects (such as QObjects) as well. + + delete object; +} + +// An object the binding depends on is deleted while the binding is still running +void tst_qqmlecmascript::deleteWhileBindingRunning() +{ + QQmlComponent component(&engine, testFileUrl("deleteWhileBindingRunning.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + delete object; +} + +void tst_qqmlecmascript::qtbug_22679() +{ + MyQmlObject object; + object.setStringProperty(QLatin1String("Please work correctly")); + engine.rootContext()->setContextProperty("contextProp", &object); + + QQmlComponent component(&engine, testFileUrl("qtbug_22679.qml")); + qRegisterMetaType<QList<QQmlError> >("QList<QQmlError>"); + QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QQmlError>))); + + QObject *o = component.create(); + QVERIFY(o != 0); + QCOMPARE(warningsSpy.count(), 0); + delete o; +} + +void tst_qqmlecmascript::qtbug_22843_data() +{ + QTest::addColumn<bool>("library"); + + QTest::newRow("without .pragma library") << false; + QTest::newRow("with .pragma library") << true; +} + +void tst_qqmlecmascript::qtbug_22843() +{ + QFETCH(bool, library); + + QString fileName("qtbug_22843"); + if (library) + fileName += QLatin1String(".library"); + fileName += QLatin1String(".qml"); + + QQmlComponent component(&engine, testFileUrl(fileName)); + QString url = component.url().toString(); + QString warning1 = url.left(url.length()-3) + QLatin1String("js:4: SyntaxError: Unexpected token )"); + QString warning2 = url + QLatin1String(":5: TypeError: Object [object Object] has no method 'func'"); + + qRegisterMetaType<QList<QQmlError> >("QList<QQmlError>"); + QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QQmlError>))); + for (int x = 0; x < 3; ++x) { + warningsSpy.clear(); + // For libraries, only the first import attempt should produce a + // SyntaxError warning; subsequent component creation should not + // attempt to reload the script. + bool expectSyntaxError = !library || (x == 0); + if (expectSyntaxError) + QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1)); + QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2)); + QObject *object = component.create(); + QVERIFY(object != 0); + QCOMPARE(warningsSpy.count(), 1 + (expectSyntaxError?1:0)); + delete object; + } +} + + +void tst_qqmlecmascript::switchStatement() +{ + { + QQmlComponent component(&engine, testFileUrl("switchStatement.1.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + // `object->value()' is the number of executed statements + + object->setStringProperty("A"); + QCOMPARE(object->value(), 5); + + object->setStringProperty("S"); + QCOMPARE(object->value(), 3); + + object->setStringProperty("D"); + QCOMPARE(object->value(), 3); + + object->setStringProperty("F"); + QCOMPARE(object->value(), 4); + + object->setStringProperty("something else"); + QCOMPARE(object->value(), 1); + } + + { + QQmlComponent component(&engine, testFileUrl("switchStatement.2.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + // `object->value()' is the number of executed statements + + object->setStringProperty("A"); + QCOMPARE(object->value(), 5); + + object->setStringProperty("S"); + QCOMPARE(object->value(), 3); + + object->setStringProperty("D"); + QCOMPARE(object->value(), 3); + + object->setStringProperty("F"); + QCOMPARE(object->value(), 3); + + object->setStringProperty("something else"); + QCOMPARE(object->value(), 4); + } + + { + QQmlComponent component(&engine, testFileUrl("switchStatement.3.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + // `object->value()' is the number of executed statements + + object->setStringProperty("A"); + QCOMPARE(object->value(), 5); + + object->setStringProperty("S"); + QCOMPARE(object->value(), 3); + + object->setStringProperty("D"); + QCOMPARE(object->value(), 3); + + object->setStringProperty("F"); + QCOMPARE(object->value(), 3); + + object->setStringProperty("something else"); + QCOMPARE(object->value(), 6); + } + + { + QQmlComponent component(&engine, testFileUrl("switchStatement.4.qml")); + + QString warning = component.url().toString() + ":4: Unable to assign [undefined] to int"; + QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); + + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + // `object->value()' is the number of executed statements + + object->setStringProperty("A"); + QCOMPARE(object->value(), 5); + + object->setStringProperty("S"); + QCOMPARE(object->value(), 3); + + object->setStringProperty("D"); + QCOMPARE(object->value(), 3); + + object->setStringProperty("F"); + QCOMPARE(object->value(), 3); + + QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); + + object->setStringProperty("something else"); + } + + { + QQmlComponent component(&engine, testFileUrl("switchStatement.5.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + // `object->value()' is the number of executed statements + + object->setStringProperty("A"); + QCOMPARE(object->value(), 1); + + object->setStringProperty("S"); + QCOMPARE(object->value(), 1); + + object->setStringProperty("D"); + QCOMPARE(object->value(), 1); + + object->setStringProperty("F"); + QCOMPARE(object->value(), 1); + + object->setStringProperty("something else"); + QCOMPARE(object->value(), 1); + } + + { + QQmlComponent component(&engine, testFileUrl("switchStatement.6.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + // `object->value()' is the number of executed statements + + object->setStringProperty("A"); + QCOMPARE(object->value(), 123); + + object->setStringProperty("S"); + QCOMPARE(object->value(), 123); + + object->setStringProperty("D"); + QCOMPARE(object->value(), 321); + + object->setStringProperty("F"); + QCOMPARE(object->value(), 321); + + object->setStringProperty("something else"); + QCOMPARE(object->value(), 0); + } +} + +void tst_qqmlecmascript::withStatement() +{ + { + QQmlComponent component(&engine, testFileUrl("withStatement.1.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->value(), 123); + } +} + +void tst_qqmlecmascript::tryStatement() +{ + { + QQmlComponent component(&engine, testFileUrl("tryStatement.1.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->value(), 123); + } + + { + QQmlComponent component(&engine, testFileUrl("tryStatement.2.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->value(), 321); + } + + { + QQmlComponent component(&engine, testFileUrl("tryStatement.3.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->value(), 1); + } + + { + QQmlComponent component(&engine, testFileUrl("tryStatement.4.qml")); + MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->value(), 1); + } +} + +class CppInvokableWithQObjectDerived : public QObject +{ + Q_OBJECT +public: + CppInvokableWithQObjectDerived() {} + ~CppInvokableWithQObjectDerived() {} + + Q_INVOKABLE MyQmlObject *createMyQmlObject(QString data) + { + MyQmlObject *obj = new MyQmlObject(); + obj->setStringProperty(data); + return obj; + } + + Q_INVOKABLE QString getStringProperty(MyQmlObject *obj) + { + return obj->stringProperty(); + } +}; + +void tst_qqmlecmascript::invokableWithQObjectDerived() +{ + CppInvokableWithQObjectDerived invokable; + + { + QQmlEngine engine; + engine.rootContext()->setContextProperty("invokable", &invokable); + + QQmlComponent component(&engine, testFileUrl("qobjectDerivedArgument.qml")); + + QObject *object = component.create(); + + QVERIFY(object != 0); + QVERIFY(object->property("result").value<bool>() == true); + + delete object; + } +} + +QTEST_MAIN(tst_qqmlecmascript) + +#include "tst_qqmlecmascript.moc" |