aboutsummaryrefslogtreecommitdiffstats
path: root/tests/auto/qml/qqmlecmascript
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/qml/qqmlecmascript')
-rw-r--r--tests/auto/qml/qqmlecmascript/CMakeLists.txt7
-rw-r--r--tests/auto/qml/qqmlecmascript/data/AssignListPropertyByIndexOnGadget.qml20
-rw-r--r--tests/auto/qml/qqmlecmascript/data/PropertyVarCircularComponent4.qml1
-rw-r--r--tests/auto/qml/qqmlecmascript/data/PropertyVarOwnershipComponent.qml1
-rw-r--r--tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlotErrors.3.qml12
-rw-r--r--tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlotErrors.4.qml12
-rw-r--r--tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlots.qml13
-rw-r--r--tests/auto/qml/qqmlecmascript/data/date.qml6
-rw-r--r--tests/auto/qml/qqmlecmascript/data/frozenQObject3.qml30
-rw-r--r--tests/auto/qml/qqmlecmascript/data/getThis.qml2
-rw-r--r--tests/auto/qml/qqmlecmascript/data/lookupsDoNotBypassProxy.qml29
-rw-r--r--tests/auto/qml/qqmlecmascript/data/methodCallOnDerivedSingleton.qml6
-rw-r--r--tests/auto/qml/qqmlecmascript/data/propertyVar.reparent.qml1
-rw-r--r--tests/auto/qml/qqmlecmascript/data/propertyVarImplicitOwnership.qml1
-rw-r--r--tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs3.qml12
-rw-r--r--tests/auto/qml/qqmlecmascript/data/qpropertyResetCorrectlyLinked.qml8
-rw-r--r--tests/auto/qml/qqmlecmascript/data/resetGadget.qml9
-rw-r--r--tests/auto/qml/qqmlecmascript/data/restoreObserverAfterReset.qml20
-rw-r--r--tests/auto/qml/qqmlecmascript/data/scriptConnect.8.qml21
-rw-r--r--tests/auto/qml/qqmlecmascript/data/scriptConnect.9.qml30
-rw-r--r--tests/auto/qml/qqmlecmascript/data/scriptConnect.deletion.qml36
-rw-r--r--tests/auto/qml/qqmlecmascript/data/scriptConnectSingleton.qml21
-rw-r--r--tests/auto/qml/qqmlecmascript/data/scriptDisconnect.5.qml19
-rw-r--r--tests/auto/qml/qqmlecmascript/data/singletonTest.qml2
-rw-r--r--tests/auto/qml/qqmlecmascript/data/singletonTest2.qml2
-rw-r--r--tests/auto/qml/qqmlecmascript/testtypes.cpp59
-rw-r--r--tests/auto/qml/qqmlecmascript/testtypes.h217
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp409
28 files changed, 888 insertions, 118 deletions
diff --git a/tests/auto/qml/qqmlecmascript/CMakeLists.txt b/tests/auto/qml/qqmlecmascript/CMakeLists.txt
index f4b4169c82..12cab47a36 100644
--- a/tests/auto/qml/qqmlecmascript/CMakeLists.txt
+++ b/tests/auto/qml/qqmlecmascript/CMakeLists.txt
@@ -7,6 +7,12 @@
## tst_qqmlecmascript Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qqmlecmascript LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
@@ -24,6 +30,7 @@ qt_internal_add_test(tst_qqmlecmascript
Qt::Network
Qt::QmlPrivate
Qt::QuickTestUtilsPrivate
+ Qt::QuickPrivate
TESTDATA ${test_data}
)
diff --git a/tests/auto/qml/qqmlecmascript/data/AssignListPropertyByIndexOnGadget.qml b/tests/auto/qml/qqmlecmascript/data/AssignListPropertyByIndexOnGadget.qml
new file mode 100644
index 0000000000..f8ee283f1e
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/AssignListPropertyByIndexOnGadget.qml
@@ -0,0 +1,20 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQml
+import Qt.test
+
+QtObject {
+ property listPropertyAssignment_Gadget gadget
+ property ListPropertyAssignment_Object object: ListPropertyAssignment_Object { }
+
+ Component.onCompleted: {
+ gadget.gadgetStringList = ["Element1", "Element2", "Element3"]
+ gadget.gadgetVariantList = [1, "foo", null, true]
+ object.qobjectStringList = ["Element1", "Element2", "Element3"]
+
+ gadget.gadgetStringList[0] = "Completely new Element"
+ gadget.gadgetVariantList[0] = "Completely new Element"
+ object.qobjectStringList[0] = "Completely new Element"
+ }
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/PropertyVarCircularComponent4.qml b/tests/auto/qml/qqmlecmascript/data/PropertyVarCircularComponent4.qml
index 9273a52f54..ff7fe4434c 100644
--- a/tests/auto/qml/qqmlecmascript/data/PropertyVarCircularComponent4.qml
+++ b/tests/auto/qml/qqmlecmascript/data/PropertyVarCircularComponent4.qml
@@ -22,7 +22,6 @@ Rectangle {
if (component.status == Component.Ready) {
text.vp = component.createObject(null); // has JavaScript ownership
}
- gc();
}
}
}
diff --git a/tests/auto/qml/qqmlecmascript/data/PropertyVarOwnershipComponent.qml b/tests/auto/qml/qqmlecmascript/data/PropertyVarOwnershipComponent.qml
index f4307081c5..4e8da872f5 100644
--- a/tests/auto/qml/qqmlecmascript/data/PropertyVarOwnershipComponent.qml
+++ b/tests/auto/qml/qqmlecmascript/data/PropertyVarOwnershipComponent.qml
@@ -25,7 +25,6 @@ Rectangle {
if (component.status == Component.Ready) {
textTwo.vp = component.createObject(null); // has JavaScript ownership
}
- gc();
}
function deassignVp() {
diff --git a/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlotErrors.3.qml b/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlotErrors.3.qml
deleted file mode 100644
index d611e0fe30..0000000000
--- a/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlotErrors.3.qml
+++ /dev/null
@@ -1,12 +0,0 @@
-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
deleted file mode 100644
index a6862517c6..0000000000
--- a/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlotErrors.4.qml
+++ /dev/null
@@ -1,12 +0,0 @@
-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
index f91fb71f1f..9a3141e15a 100644
--- a/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlots.qml
+++ b/tests/auto/qml/qqmlecmascript/data/changeslots/propertyChangeSlots.qml
@@ -6,6 +6,8 @@ Item {
property bool normalName: false
property bool _nameWithUnderscore: false
property bool ____nameWithUnderscores: false
+ property bool _6nameWithUnderscoreNumber: false
+ property bool $nameWithDollarsign: false
onNormalNameChanged: {
changeCount = changeCount + 1;
@@ -19,9 +21,20 @@ Item {
changeCount = changeCount + 3;
}
+ on$NameWithDollarsignChanged: {
+ changeCount = changeCount + 4;
+ }
+
+ on_6NameWithUnderscoreNumberChanged: {
+ changeCount = changeCount + 5;
+ }
+
Component.onCompleted: {
normalName = true;
_nameWithUnderscore = true;
____nameWithUnderscores = true;
+ $nameWithDollarsign = true;
+ _6nameWithUnderscoreNumber = true;
}
+
}
diff --git a/tests/auto/qml/qqmlecmascript/data/date.qml b/tests/auto/qml/qqmlecmascript/data/date.qml
index 8e190b1f8f..33644b604d 100644
--- a/tests/auto/qml/qqmlecmascript/data/date.qml
+++ b/tests/auto/qml/qqmlecmascript/data/date.qml
@@ -26,8 +26,12 @@ Item {
function check_value(date, tag, qdt) {
var result = true;
+ if (isNaN(date)) {
+ console.warn("Invalid Date");
+ return false;
+ }
if (date.getFullYear() != 2014) {
- console.warn("Wrong year (" + tag + "):", date.getFullYear(), "!= 2014")
+ console.warn("Wrong year (" + tag + "):", date.getFullYear(), "!= 2014");
result = false;
}
// July; JS's months are Jan 0 to 11 Dec, vs. Qt's 1 to 12.
diff --git a/tests/auto/qml/qqmlecmascript/data/frozenQObject3.qml b/tests/auto/qml/qqmlecmascript/data/frozenQObject3.qml
new file mode 100644
index 0000000000..51c321684e
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/frozenQObject3.qml
@@ -0,0 +1,30 @@
+import QtQml
+import test
+
+QtObject {
+ id: root
+
+ property FrozenObjects a: FrozenObjects { objectName: "a" }
+ property FrozenObjects b: FrozenObjects { objectName: "b" }
+
+ // Create wrappers and immediately discard them
+ objectName: a.getConst().objectName + "/" + b.getNonConst().objectName
+
+ // Create a non-const wrapper and retain it
+ property var objNonConst: a.getNonConst()
+
+ // Create a const wrapper and retain it
+ property var objConst: b.getConst()
+
+ property int gcs: 0
+
+ property Timer t: Timer {
+ interval: 1
+ running: true
+ repeat: true
+ onTriggered: {
+ gc();
+ ++root.gcs;
+ }
+ }
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/getThis.qml b/tests/auto/qml/qqmlecmascript/data/getThis.qml
index db9854e872..90c51caf4d 100644
--- a/tests/auto/qml/qqmlecmascript/data/getThis.qml
+++ b/tests/auto/qml/qqmlecmascript/data/getThis.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2019 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQml 2.12
diff --git a/tests/auto/qml/qqmlecmascript/data/lookupsDoNotBypassProxy.qml b/tests/auto/qml/qqmlecmascript/data/lookupsDoNotBypassProxy.qml
new file mode 100644
index 0000000000..321bd21ad8
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/lookupsDoNotBypassProxy.qml
@@ -0,0 +1,29 @@
+import QtQml
+
+QtObject {
+ function test_proxy() {
+ let base = {
+ id: 'baseid',
+ name: 'basename',
+ length: 42
+ };
+
+ let handler = {
+ get: function (ao, prop) {
+ return Reflect.get(ao, prop);
+ }
+ };
+
+ let r = new Proxy(base, handler);
+ let validCount = 0;
+ if (r.id === base.id)
+ ++validCount;
+ if (r.length === base.length)
+ ++validCount;
+ if (r.name === base.name)
+ ++validCount;
+ return validCount;
+ }
+
+ property int result: test_proxy()
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/methodCallOnDerivedSingleton.qml b/tests/auto/qml/qqmlecmascript/data/methodCallOnDerivedSingleton.qml
new file mode 100644
index 0000000000..9d2ee433fd
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/methodCallOnDerivedSingleton.qml
@@ -0,0 +1,6 @@
+import Qt.test
+import QtQml
+
+QtObject {
+ Component.onCompleted: SingletonInheritanceTest.trackPage("test", {x: 42})
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVar.reparent.qml b/tests/auto/qml/qqmlecmascript/data/propertyVar.reparent.qml
index 6f5094de27..5f017c4880 100644
--- a/tests/auto/qml/qqmlecmascript/data/propertyVar.reparent.qml
+++ b/tests/auto/qml/qqmlecmascript/data/propertyVar.reparent.qml
@@ -16,7 +16,6 @@ Item {
function assignVarProp() {
vp = constructGarbage();
- gc();
}
function deassignVarProp() {
diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVarImplicitOwnership.qml b/tests/auto/qml/qqmlecmascript/data/propertyVarImplicitOwnership.qml
index ecc4892334..144fcc8a93 100644
--- a/tests/auto/qml/qqmlecmascript/data/propertyVarImplicitOwnership.qml
+++ b/tests/auto/qml/qqmlecmascript/data/propertyVarImplicitOwnership.qml
@@ -16,7 +16,6 @@ Item {
function assignCircular() {
vp = constructGarbage();
- gc();
}
function deassignCircular() {
diff --git a/tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs3.qml b/tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs3.qml
new file mode 100644
index 0000000000..a50c8e0f46
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/qmlTypeWrapperArgs3.qml
@@ -0,0 +1,12 @@
+import QtQml
+import Qt.test.singletonWithEnum
+
+QtObject {
+ id: root
+ required property QtObject invokableObject
+
+ Component.onCompleted: {
+ root.invokableObject.method_typeWrapper(Component)
+ root.invokableObject.method_typeWrapper(SingletonWithEnum)
+ }
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/qpropertyResetCorrectlyLinked.qml b/tests/auto/qml/qqmlecmascript/data/qpropertyResetCorrectlyLinked.qml
new file mode 100644
index 0000000000..490fec2dc8
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/qpropertyResetCorrectlyLinked.qml
@@ -0,0 +1,8 @@
+import QtQuick
+
+Item {
+ property var val: undefined
+ property var observes: width
+ width: val
+ implicitWidth: 200
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/resetGadget.qml b/tests/auto/qml/qqmlecmascript/data/resetGadget.qml
new file mode 100644
index 0000000000..2bc196da34
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/resetGadget.qml
@@ -0,0 +1,9 @@
+import Qt.test
+
+ResettableGadgetHolder {
+ id: root
+ property bool trigger: false
+ onTriggerChanged: {
+ root.g.value = undefined
+ }
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/restoreObserverAfterReset.qml b/tests/auto/qml/qqmlecmascript/data/restoreObserverAfterReset.qml
new file mode 100644
index 0000000000..2933d9b4d5
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/restoreObserverAfterReset.qml
@@ -0,0 +1,20 @@
+import QtQuick
+
+Item {
+ height: undefined
+ implicitHeight: 30
+ property int steps: 0
+
+ Behavior on height {
+ NumberAnimation {
+ duration: 500
+ }
+ }
+
+ onHeightChanged: ++steps
+
+ Component.onCompleted: {
+ height = Qt.binding(() => implicitHeight);
+ implicitHeight = 60;
+ }
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/scriptConnect.8.qml b/tests/auto/qml/qqmlecmascript/data/scriptConnect.8.qml
new file mode 100644
index 0000000000..7d43aa6c05
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/scriptConnect.8.qml
@@ -0,0 +1,21 @@
+import Qt.test
+import QtQuick
+
+Item {
+ id: root
+ property int count: 0
+ signal someSignal
+
+ property Item item: Item {
+ id: contextItem
+ function test() {
+ count++;
+ }
+ }
+
+ function itemDestroy() {
+ contextItem.destroy()
+ }
+
+ Component.onCompleted: root.someSignal.connect(contextItem, contextItem.test);
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/scriptConnect.9.qml b/tests/auto/qml/qqmlecmascript/data/scriptConnect.9.qml
new file mode 100644
index 0000000000..1123edf3f7
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/scriptConnect.9.qml
@@ -0,0 +1,30 @@
+import Qt.test
+import QtQuick
+
+MyQmlObject {
+ id: root
+ property int a: 0
+
+ signal someSignal
+
+ function disconnectSignal() {
+ root.someSignal.disconnect(other.MyQmlObject, root.test)
+ }
+
+ function destroyObj() {
+ other.destroy()
+ }
+
+ function test() {
+ other.MyQmlObject.value2++
+ root.a = other.MyQmlObject.value2
+ }
+
+ property MyQmlObject obj
+ obj: MyQmlObject {
+ id: other
+ MyQmlObject.value2: 0
+ }
+
+ Component.onCompleted: root.someSignal.connect(other.MyQmlObject, root.test)
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/scriptConnect.deletion.qml b/tests/auto/qml/qqmlecmascript/data/scriptConnect.deletion.qml
new file mode 100644
index 0000000000..efbbc9fedc
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/scriptConnect.deletion.qml
@@ -0,0 +1,36 @@
+import Qt.test
+import QtQml
+
+QtObject {
+ id: root
+
+ property int a: 0
+ property int b: 0
+
+ signal someSignal
+
+ function destroyObj() {
+ obj.destroy()
+ }
+
+ function test() {
+ ++a
+ }
+
+ component DestructionReceiver: QtObject {
+ // Has its own context and therefore can receive Component.onDestruction
+ }
+
+ property QtObject obj: QtObject {
+ property QtObject inner: DestructionReceiver {
+ Component.onDestruction: {
+ // The outer obj is already queued for deletion.
+ // We don't want to see this signal delivered.
+ root.someSignal();
+ ++root.b
+ }
+ }
+ }
+
+ Component.onCompleted: someSignal.connect(obj, test)
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/scriptConnectSingleton.qml b/tests/auto/qml/qqmlecmascript/data/scriptConnectSingleton.qml
new file mode 100644
index 0000000000..f666945b33
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/scriptConnectSingleton.qml
@@ -0,0 +1,21 @@
+import QtQuick
+import Test
+
+Item {
+ id: root
+
+ property int a: 0
+ signal mySignal
+
+ function test() {
+ MyInheritedQmlObjectSingleton.value++
+ root.a = MyInheritedQmlObjectSingleton.value
+ }
+
+ function disconnectSingleton() {
+ root.mySignal.disconnect(MyInheritedQmlObjectSingleton, root.test)
+ }
+
+ Component.onCompleted: root.mySignal.connect(MyInheritedQmlObjectSingleton,
+ root.test)
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/scriptDisconnect.5.qml b/tests/auto/qml/qqmlecmascript/data/scriptDisconnect.5.qml
new file mode 100644
index 0000000000..9d24fa85ae
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/scriptDisconnect.5.qml
@@ -0,0 +1,19 @@
+import Qt.test
+import QtQuick
+
+Item {
+ id: root
+ property int count: 0
+ signal someSignal
+ signal disconnectSignal
+
+ property Item item: Item {
+ id: contextItem
+ function test() {
+ count++;
+ }
+ }
+
+ Component.onCompleted: root.someSignal.connect(contextItem, contextItem.test);
+ onDisconnectSignal: { root.someSignal.disconnect(contextItem, contextItem.test); }
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/singletonTest.qml b/tests/auto/qml/qqmlecmascript/data/singletonTest.qml
index b0e951b89a..f4bdcdbed3 100644
--- a/tests/auto/qml/qqmlecmascript/data/singletonTest.qml
+++ b/tests/auto/qml/qqmlecmascript/data/singletonTest.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2013 Canonical Limited and/or its subsidiary(-ies).
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import Test 1.0
diff --git a/tests/auto/qml/qqmlecmascript/data/singletonTest2.qml b/tests/auto/qml/qqmlecmascript/data/singletonTest2.qml
index 5a5bad5b8c..5d6af4d67c 100644
--- a/tests/auto/qml/qqmlecmascript/data/singletonTest2.qml
+++ b/tests/auto/qml/qqmlecmascript/data/singletonTest2.qml
@@ -1,5 +1,5 @@
// Copyright (C) 2013 Canonical Limited and/or its subsidiary(-ies).
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick 2.0
import Test 1.0
diff --git a/tests/auto/qml/qqmlecmascript/testtypes.cpp b/tests/auto/qml/qqmlecmascript/testtypes.cpp
index 40f5e5cf5c..5f7713392b 100644
--- a/tests/auto/qml/qqmlecmascript/testtypes.cpp
+++ b/tests/auto/qml/qqmlecmascript/testtypes.cpp
@@ -1,5 +1,6 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
#include "testtypes.h"
#ifndef QT_NO_WIDGETS
# include <QWidget>
@@ -8,6 +9,7 @@
#include <QQmlEngine>
#include <QJSEngine>
#include <QThread>
+#include <QtQuickTestUtils/private/qmlutils_p.h>
class BaseExtensionObject : public QObject
{
@@ -104,7 +106,7 @@ public:
void setWidth(int) { }
};
-void MyQmlObject::v8function(QQmlV4Function *function)
+void MyQmlObject::v8function(QQmlV4FunctionPtr function)
{
function->v4engine()->throwError(QStringLiteral("Exception thrown from within QObject slot"));
}
@@ -393,9 +395,7 @@ void QObjectContainer::children_append(QQmlListProperty<QObject> *prop, QObject
if (that->gcOnAppend) {
QQmlEngine *engine = qmlEngine(that);
- engine->collectGarbage();
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
- QCoreApplication::processEvents();
+ gc(*engine);
}
}
@@ -412,7 +412,7 @@ QObject *QObjectContainer::children_at(QQmlListProperty<QObject> *prop, qsizetyp
void QObjectContainer::children_clear(QQmlListProperty<QObject> *prop)
{
QObjectContainer *that = static_cast<QObjectContainer*>(prop->object);
- foreach (QObject *c, that->dataChildren)
+ for (QObject *c : std::as_const(that->dataChildren))
QObject::disconnect(c, SIGNAL(destroyed(QObject*)), that, SLOT(childDestroyed(QObject*)));
that->dataChildren.clear();
}
@@ -436,6 +436,44 @@ void ClassWithQProperty2::callback()
// Q_UNUSED(this->value.value()); // force evaluation
}
+ListPropertyAssignment_Gadget::ListPropertyAssignment_Gadget() { }
+
+QStringList ListPropertyAssignment_Gadget::gadgetStringList() const
+{
+ return m_gadgetStringList;
+}
+
+void ListPropertyAssignment_Gadget::setGadgetStringList(const QStringList &list)
+{
+ if (m_gadgetStringList == list)
+ return;
+ m_gadgetStringList = list;
+}
+
+QVariantList ListPropertyAssignment_Gadget::gadgetVariantList() const
+{
+ return m_gadgetVariantList;
+}
+
+void ListPropertyAssignment_Gadget::setGadgetVariantList(const QVariantList &list)
+{
+ if (m_gadgetVariantList == list)
+ return;
+ m_gadgetVariantList = list;
+}
+
+ListPropertyAssignment_Object::ListPropertyAssignment_Object(QObject *parent)
+ : QObject{ parent } { }
+
+void ListPropertyAssignment_Object::setQobjectStringList(const QStringList &newList)
+{
+ if (m_qobjectStringList == newList)
+ return;
+ m_qobjectStringList = newList;
+}
+
+bool MetaCallInterceptor::didGetObjectDestroyedCallback = false;
+
void registerTypes()
{
qmlRegisterType<MyQmlObject>("Qt.test", 1,0, "MyQmlObjectAlias");
@@ -542,6 +580,15 @@ void registerTypes()
qmlRegisterType<Receiver>("Qt.test", 1,0, "Receiver");
qmlRegisterType<Sender>("Qt.test", 1,0, "Sender");
qmlRegisterTypesAndRevisions<ReadOnlyBindable>("Qt.test", 1);
+ qmlRegisterTypesAndRevisions<ResettableGadgetHolder>("Qt.test", 1);
+
+ qmlRegisterTypesAndRevisions<ListPropertyAssignment_Gadget>("Qt.test", 1);
+ qmlRegisterTypesAndRevisions<ListPropertyAssignment_Object>("Qt.test", 1);
+
+ qmlRegisterTypesAndRevisions<SingletonRegistrationWrapper>("Qt.test", 1);
+
+ qmlRegisterExtendedType<TypeWithCustomMetaObject, TypeToTriggerProxyMetaObject>(
+ "Qt.test", 1,0, "TypeWithCustomMetaObject");
}
#include "testtypes.moc"
diff --git a/tests/auto/qml/qqmlecmascript/testtypes.h b/tests/auto/qml/qqmlecmascript/testtypes.h
index ff9dda36d1..cc20437fff 100644
--- a/tests/auto/qml/qqmlecmascript/testtypes.h
+++ b/tests/auto/qml/qqmlecmascript/testtypes.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TESTTYPES_H
#define TESTTYPES_H
@@ -31,6 +31,7 @@
#include <private/qqmlengine_p.h>
#include <private/qv4qobjectwrapper_p.h>
+#include <private/qqmlcomponentattached_p.h>
class MyQmlAttachedObject : public QObject
{
@@ -243,7 +244,7 @@ public slots:
void myinvokable(MyQmlObject *o) { myinvokableObject = o; }
void variantMethod(const QVariant &v) { m_variant = v; }
void qjsvalueMethod(const QJSValue &v) { m_qjsvalue = v; }
- void v8function(QQmlV4Function*);
+ void v8function(QQmlV4FunctionPtr);
void registeredFlagMethod(Qt::MouseButtons v) { m_buttons = v; }
QString slotWithReturnValue(const QString &arg) { return arg; }
int resetCount() { return m_resetCount; }
@@ -862,6 +863,17 @@ struct NonRegisteredType
struct CompletelyUnknown;
+class SingletonWithEnum : public QObject
+{
+ Q_OBJECT
+ Q_ENUMS(TestEnum)
+public:
+ enum TestEnum {
+ TestValue = 42,
+ TestValue_MinusOne = -1
+ };
+};
+
class MyInvokableObject : public MyInvokableBaseObject
{
Q_OBJECT
@@ -919,7 +931,7 @@ public:
Q_INVOKABLE void method_unknown(NonRegisteredType) { invoke(28); }
- Q_INVOKABLE void method_overload2(QQmlV4Function *v)
+ Q_INVOKABLE void method_overload2(QQmlV4FunctionPtr v)
{
invoke(31);
QV4::Scope scope(v->v4engine());
@@ -955,12 +967,43 @@ public:
invoke(40);
m_actuals << f;
}
+
Q_INVOKABLE void method_qobject(QObject *o)
{
invoke(41);
m_actuals << QVariant::fromValue(o);
}
+ Q_INVOKABLE QQmlComponent *someComponent() { return &m_someComponent; }
+ Q_INVOKABLE void method_component(QQmlComponent *c)
+ {
+ invoke(42);
+ m_actuals << QVariant::fromValue(c);
+ }
+
+ Q_INVOKABLE MyTypeObject *someTypeObject() { return &m_someTypeObject; }
+ Q_INVOKABLE void method_component(MyTypeObject *c)
+ {
+ invoke(43);
+ m_actuals << QVariant::fromValue(c);
+ }
+
+ Q_INVOKABLE void method_component(const QUrl &c)
+ {
+ invoke(44);
+ m_actuals << QVariant::fromValue(c);
+ }
+
+ Q_INVOKABLE void method_typeWrapper(QQmlComponentAttached *attached)
+ {
+ m_actuals << QVariant::fromValue(attached);
+ }
+
+ Q_INVOKABLE void method_typeWrapper(SingletonWithEnum *singleton)
+ {
+ m_actuals << QVariant::fromValue(singleton);
+ }
+
private:
friend class MyInvokableBaseObject;
void invoke(int idx) { if (m_invoked != -1) m_invokedError = true; m_invoked = idx;}
@@ -969,6 +1012,8 @@ private:
QVariantList m_actuals;
QFont m_someFont;
+ QQmlComponent m_someComponent;
+ MyTypeObject m_someTypeObject;
public:
Q_SIGNALS:
@@ -1807,17 +1852,6 @@ public:
QML_DECLARE_TYPEINFO(FallbackBindingsTypeObject, QML_HAS_ATTACHED_PROPERTIES)
QML_DECLARE_TYPEINFO(FallbackBindingsTypeDerived, QML_HAS_ATTACHED_PROPERTIES)
-class SingletonWithEnum : public QObject
-{
- Q_OBJECT
- Q_ENUMS(TestEnum)
-public:
- enum TestEnum {
- TestValue = 42,
- TestValue_MinusOne = -1
- };
-};
-
// Like QtObject, but with default property
class QObjectContainer : public QObject
{
@@ -2000,6 +2034,161 @@ public:
QBindable<int> bindableX() const { return &_xProp; }
};
+class ResettableGadget
+{
+ Q_GADGET
+ Q_PROPERTY(qreal value READ value WRITE setValue RESET resetValue)
+
+ qreal m_value = 0;
+
+public:
+ qreal value() const { return m_value; }
+ void setValue(qreal val) { m_value = val; }
+ void resetValue() { m_value = 42; }
+};
+
+class ResettableGadgetHolder : public QObject {
+ Q_OBJECT
+ QML_ELEMENT
+
+ Q_PROPERTY(ResettableGadget g READ g WRITE setG NOTIFY gChanged)
+ ResettableGadget m_g;
+
+signals:
+ void gChanged();
+
+public:
+ ResettableGadget g() const { return m_g; }
+ void setG(ResettableGadget newG)
+ {
+ if (m_g.value() == newG.value())
+ return;
+ m_g = newG;
+ Q_EMIT gChanged();
+ }
+};
+
+class ListPropertyAssignment_Gadget
+{
+ Q_GADGET
+ Q_PROPERTY(QStringList gadgetStringList READ gadgetStringList WRITE setGadgetStringList)
+ Q_PROPERTY(QVariantList gadgetVariantList READ gadgetVariantList WRITE setGadgetVariantList)
+ QML_VALUE_TYPE(listPropertyAssignment_Gadget)
+public:
+ ListPropertyAssignment_Gadget();
+ QStringList gadgetStringList() const;
+ void setGadgetStringList(const QStringList &list);
+
+ QVariantList gadgetVariantList() const;
+ void setGadgetVariantList(const QVariantList &list);
+
+private:
+ QStringList m_gadgetStringList;
+ QVariantList m_gadgetVariantList;
+};
+
+class ListPropertyAssignment_Object : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_PROPERTY(QStringList qobjectStringList READ qobjectStringList WRITE setQobjectStringList)
+public:
+ explicit ListPropertyAssignment_Object(QObject *parent = nullptr);
+
+ QStringList qobjectStringList() const { return m_qobjectStringList; }
+
+ void setQobjectStringList(const QStringList &newList);
+
+private:
+ QStringList m_qobjectStringList;
+};
+
+class SingletonBase : public QObject {
+ Q_OBJECT
+
+public:
+ Q_INVOKABLE virtual void trackPage(const QString&) {}
+ Q_INVOKABLE virtual void trackPage(const QString&, const QVariantMap&) {}
+
+ bool m_okay = false;
+};
+
+class SingletonImpl : public SingletonBase {
+ Q_OBJECT
+
+public:
+ Q_INVOKABLE virtual void trackPage(const QString&) override {}
+ Q_INVOKABLE virtual void trackPage(const QString&, const QVariantMap&) override
+ {
+ m_okay = true;
+ }
+};
+
+class SingletonRegistrationWrapper {
+ Q_GADGET
+ QML_FOREIGN(SingletonBase)
+ QML_NAMED_ELEMENT(SingletonInheritanceTest)
+ QML_SINGLETON
+
+public:
+ static SingletonBase* create(QQmlEngine*, QJSEngine*) {
+ return new SingletonImpl();
+ }
+
+private:
+ SingletonRegistrationWrapper() = default;
+};
+
+class MetaCallInterceptor : public QObject, public QDynamicMetaObjectData
+{
+ Q_OBJECT
+public:
+ MetaCallInterceptor()
+ {
+ didGetObjectDestroyedCallback = false;
+ }
+
+ void objectDestroyed(QObject *object) override
+ {
+ didGetObjectDestroyedCallback = true;
+
+ // Deletes this meta object
+ QDynamicMetaObjectData::objectDestroyed(object);
+ }
+
+ QMetaObject *toDynamicMetaObject(QObject *) override
+ {
+ return const_cast<QMetaObject *>(&MetaCallInterceptor::staticMetaObject);
+ }
+
+ int metaCall(QObject *o, QMetaObject::Call call, int idx, void **argv) override
+ {
+ return o->qt_metacall(call, idx, argv);
+ }
+
+ static bool didGetObjectDestroyedCallback;
+};
+
+struct TypeToTriggerProxyMetaObject
+{
+ Q_GADGET
+};
+
+class TypeWithCustomMetaObject : public QObject
+{
+ Q_OBJECT
+ QML_NAMED_ELEMENT(TypeWithCustomMetaObject)
+ QML_EXTENDED_NAMESPACE(TypeToTriggerProxyMetaObject)
+
+public:
+ TypeWithCustomMetaObject()
+ {
+ auto *p = QObjectPrivate::get(this);
+ Q_ASSERT(!p->metaObject);
+ p->metaObject = new MetaCallInterceptor;
+ }
+};
+
void registerTypes();
#endif // TESTTYPES_H
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index edb1e9ba80..2d124da279 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -1,6 +1,7 @@
// Copyright (C) 2017 Crimson AS <info@crimson.no>
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
#include <QtTest/QtTest>
#include <QtQml/qqmlcomponent.h>
#include <QtQml/qqmlengine.h>
@@ -27,6 +28,8 @@
#include <private/qqmlabstractbinding_p.h>
#include <private/qqmlvaluetypeproxybinding_p.h>
#include <QtCore/private/qproperty_p.h>
+#include <QtQuick/qquickwindow.h>
+#include <QtQuick/private/qquickitem_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/testhttpserver_p.h>
@@ -376,6 +379,8 @@ private slots:
void qpropertyBindingHandlesUndefinedCorrectly();
void qpropertyBindingHandlesUndefinedWithoutResetCorrectly_data();
void qpropertyBindingHandlesUndefinedWithoutResetCorrectly();
+ void qpropertyBindingRestoresObserverAfterReset();
+ void qpropertyBindingObserverCorrectlyLinkedAfterReset();
void hugeRegexpQuantifiers();
void singletonTypeWrapperLookup();
void getThisObject();
@@ -396,6 +401,7 @@ private slots:
void sequenceConversionMethod();
void proxyIteration();
void proxyHandlerTraps();
+ void lookupsDoNotBypassProxy();
void gcCrashRegressionTest();
void cmpInThrows();
void frozenQObject();
@@ -416,6 +422,13 @@ private slots:
void doNotCrashOnReadOnlyBindable();
+ void resetGadget();
+ void assignListPropertyByIndexOnGadget();
+
+ void methodCallOnDerivedSingleton();
+
+ void proxyMetaObject();
+
private:
// static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
static void verifyContextLifetime(const QQmlRefPointer<QQmlContextData> &ctxt);
@@ -432,14 +445,6 @@ private:
}
};
-static void gc(QQmlEngine &engine)
-{
- engine.collectGarbage();
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
- QCoreApplication::processEvents();
-}
-
-
tst_qqmlecmascript::tst_qqmlecmascript()
: QQmlDataTest(QT_QMLTEST_DATADIR)
{
@@ -3127,11 +3132,14 @@ void tst_qqmlecmascript::callQtInvokables()
QCOMPARE(o->invoked(), -1);
QCOMPARE(o->actuals().size(), 0);
- o->reset();
- QVERIFY(EVALUATE_ERROR("object.method_QPointF(object)"));
- QCOMPARE(o->error(), false);
- QCOMPARE(o->invoked(), -1);
- QCOMPARE(o->actuals().size(), 0);
+ // This fails if the QtQml module is loaded but works if it's not.
+ // If QtQml is loaded, QPointF is a structured value type that can be created from any object.
+ //
+ // o->reset();
+ // QVERIFY(EVALUATE_ERROR("object.method_QPointF(object)"));
+ // QCOMPARE(o->error(), false);
+ // QCOMPARE(o->invoked(), -1);
+ // QCOMPARE(o->actuals().size(), 0);
o->reset();
QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPointF())", QV4::Primitive::undefinedValue()));
@@ -3186,6 +3194,17 @@ void tst_qqmlecmascript::callQtInvokables()
QCOMPARE(o->invoked(), -1); // no function got called due to incompatible arguments
}
+ {
+ o->reset();
+ QQmlComponent comp(&qmlengine, testFileUrl("qmlTypeWrapperArgs3.qml"));
+ QScopedPointer<QObject> root {comp.createWithInitialProperties({{"invokableObject", QVariant::fromValue(o)}}) };
+ QVERIFY(root);
+ QCOMPARE(o->error(), false);
+ QCOMPARE(o->actuals().size(), 2);
+ QCOMPARE(o->actuals().at(0).metaType(), QMetaType::fromType<QQmlComponentAttached *>());
+ QCOMPARE(o->actuals().at(1).metaType(), QMetaType::fromType<SingletonWithEnum *>());
+ }
+
o->reset();
QVERIFY(EVALUATE_VALUE("object.method_QObject(undefined)", QV4::Primitive::undefinedValue()));
QCOMPARE(o->error(), false);
@@ -3517,6 +3536,27 @@ void tst_qqmlecmascript::callQtInvokables()
QCOMPARE(o->error(), false);
QCOMPARE(o->invoked(), -1);
QCOMPARE(o->actuals(), QVariantList());
+
+ o->reset();
+ QVERIFY(EVALUATE_VALUE("object.method_component(object.someComponent())",
+ QV4::Primitive::undefinedValue()));
+ QCOMPARE(o->error(), false);
+ QCOMPARE(o->invoked(), 42);
+ QCOMPARE(o->actuals(), QVariantList() << QVariant::fromValue(o->someComponent()));
+
+ o->reset();
+ QVERIFY(EVALUATE_VALUE("object.method_component(object.someTypeObject())",
+ QV4::Primitive::undefinedValue()));
+ QCOMPARE(o->error(), false);
+ QCOMPARE(o->invoked(), 43);
+ QCOMPARE(o->actuals(), QVariantList() << QVariant::fromValue(o->someTypeObject()));
+
+ o->reset();
+ QVERIFY(EVALUATE_VALUE("object.method_component('qrc:/somewhere/else')",
+ QV4::Primitive::undefinedValue()));
+ QCOMPARE(o->error(), false);
+ QCOMPARE(o->invoked(), 44);
+ QCOMPARE(o->actuals(), QVariantList() << QVariant::fromValue(QUrl("qrc:/somewhere/else")));
}
void tst_qqmlecmascript::resolveClashingProperties()
@@ -3815,6 +3855,82 @@ void tst_qqmlecmascript::scriptConnect()
QScopedPointer<QObject> root { component.create() };
QVERIFY2(root, qPrintable(component.errorString()));
}
+
+ {
+ QQmlComponent component(&engine, testFileUrl("scriptConnect.8.qml"));
+
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY2(obj, qPrintable(component.errorString()));
+ QVERIFY(obj.data() != nullptr);
+
+ QCOMPARE(obj.data()->property("count"), 0);
+
+ QMetaObject::invokeMethod(obj.data(), "someSignal");
+ QCOMPARE(obj.data()->property("count"), 1);
+
+ QMetaObject::invokeMethod(obj.data(), "itemDestroy");
+ QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
+ QCoreApplication::processEvents();
+
+ QMetaObject::invokeMethod(obj.data(), "someSignal");
+ QCOMPARE(obj.data()->property("count"), 1);
+ }
+
+ {
+ QQmlComponent component(&engine, testFileUrl("scriptConnect.9.qml"));
+
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY2(obj, qPrintable(component.errorString()));
+ QVERIFY(obj.data() != nullptr);
+
+ MyQmlObject *object = qobject_cast<MyQmlObject *>(obj.data());
+
+ QCOMPARE(object->property("a"), 0);
+
+ QMetaObject::invokeMethod(object, "someSignal");
+ QCOMPARE(object->property("a"), 1);
+
+ QMetaObject::invokeMethod(object, "destroyObj", Qt::DirectConnection);
+ QApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
+ QApplication::processEvents();
+
+ QMetaObject::invokeMethod(object, "someSignal");
+
+ QCOMPARE(object->property("a"), 1);
+ }
+
+ {
+ QQmlComponent component(&engine, testFileUrl("scriptConnectSingleton.qml"));
+
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY2(obj, qPrintable(component.errorString()));
+ QVERIFY(obj.data() != nullptr);
+
+ QMetaObject::invokeMethod(obj.data(), "mySignal", Qt::DirectConnection);
+ QCOMPARE(obj.data()->property("a").toInt(), 1);
+ engine.clearSingletons();
+ QMetaObject::invokeMethod(obj.data(), "mySignal", Qt::DirectConnection);
+ QCOMPARE(obj.data()->property("a").toInt(), 1);
+ }
+
+ {
+ QQmlComponent component(&engine, testFileUrl("scriptConnect.deletion.qml"));
+
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY2(obj, qPrintable(component.errorString()));
+ QVERIFY(!obj.isNull());
+
+ QCOMPARE(obj->property("a"), 0);
+
+ QMetaObject::invokeMethod(obj.data(), "someSignal");
+ QCOMPARE(obj->property("a"), 1);
+
+ QCOMPARE(obj->property("b"), 0);
+ QMetaObject::invokeMethod(obj.data(), "destroyObj", Qt::DirectConnection);
+
+ QTRY_COMPARE(obj->property("b"), 1);
+ QCOMPARE(obj->property("a"), 1);
+ }
}
void tst_qqmlecmascript::scriptDisconnect()
@@ -3895,6 +4011,60 @@ void tst_qqmlecmascript::scriptDisconnect()
emit object->argumentSignal(19, "Hello world!", 10.25, MyQmlObject::EnumValue4, Qt::RightButton);
QCOMPARE(object->property("test").toInt(), 3);
}
+
+ {
+ QQmlComponent component(&engine, testFileUrl("scriptDisconnect.5.qml"));
+
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY2(obj, qPrintable(component.errorString()));
+ QVERIFY(obj.data() != nullptr);
+
+ QCOMPARE(obj.data()->property("count"), 0);
+
+ QMetaObject::invokeMethod(obj.data(), "someSignal");
+ QCOMPARE(obj.data()->property("count"), 1);
+
+ QMetaObject::invokeMethod(obj.data(), "disconnectSignal");
+
+ QMetaObject::invokeMethod(obj.data(), "someSignal");
+ QCOMPARE(obj.data()->property("count"), 1);
+ }
+
+ {
+ QQmlComponent component(&engine, testFileUrl("scriptConnect.9.qml"));
+
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY2(obj, qPrintable(component.errorString()));
+ QVERIFY(obj.data() != nullptr);
+
+ MyQmlObject *object = qobject_cast<MyQmlObject *>(obj.data());
+
+ QCOMPARE(object->property("a"), 0);
+
+ QMetaObject::invokeMethod(object, "someSignal");
+ QCOMPARE(object->property("a"), 1);
+
+ QMetaObject::invokeMethod(object, "disconnectSignal", Qt::DirectConnection);
+
+ QMetaObject::invokeMethod(object, "someSignal");
+
+ QCOMPARE(object->property("a"), 1);
+ }
+
+ {
+ QQmlComponent component(&engine, testFileUrl("scriptConnectSingleton.qml"));
+
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY2(obj, qPrintable(component.errorString()));
+ QVERIFY(obj.data() != nullptr);
+
+ QMetaObject::invokeMethod(obj.data(), "mySignal", Qt::DirectConnection);
+ QCOMPARE(obj.data()->property("a").toInt(), 1);
+
+ QMetaObject::invokeMethod(obj.data(), "disconnectSingleton", Qt::DirectConnection);
+ QMetaObject::invokeMethod(obj.data(), "mySignal", Qt::DirectConnection);
+ QCOMPARE(obj.data()->property("a").toInt(), 1);
+ }
}
class OwnershipObject : public QObject
@@ -3923,10 +4093,7 @@ void tst_qqmlecmascript::ownership()
QScopedPointer<QObject> object(component.create(context.data()));
- engine.collectGarbage();
-
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
- QCoreApplication::processEvents();
+ gc(engine);
QVERIFY(own.object.isNull());
}
@@ -3940,10 +4107,7 @@ void tst_qqmlecmascript::ownership()
QScopedPointer<QObject> object(component.create(context.data()));
- engine.collectGarbage();
-
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
- QCoreApplication::processEvents();
+ gc(engine);
QVERIFY(own.object != nullptr);
}
@@ -4019,9 +4183,7 @@ void tst_qqmlecmascript::ownershipCustomReturnValue()
QVERIFY(source.value != nullptr);
}
- engine.collectGarbage();
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
- QCoreApplication::processEvents();
+ gc(engine);
QVERIFY(source.value.isNull());
}
@@ -4052,10 +4214,7 @@ void tst_qqmlecmascript::ownershipRootObject()
QScopedPointer<QObject> object(component.create(context.data()));
QVERIFY2(object, qPrintable(component.errorString()));
- engine.collectGarbage();
-
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
- QCoreApplication::processEvents();
+ gc(engine);
QVERIFY(own.object != nullptr);
}
@@ -4080,10 +4239,7 @@ void tst_qqmlecmascript::ownershipConsistency()
QScopedPointer<QObject> object(component.create(context.data()));
QVERIFY2(object, qPrintable(component.errorString()));
- engine.collectGarbage();
-
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
- QCoreApplication::processEvents();
+ gc(engine);
QVERIFY(own.object != nullptr);
}
@@ -4613,6 +4769,7 @@ void tst_qqmlecmascript::verifyContextLifetime(const QQmlRefPointer<QQmlContextD
}
ctxt->engine()->collectGarbage();
+ QTRY_VERIFY(gcDone(ctxt->engine()));
qml = scripts->get(i);
newContext = qml ? qml->getContext() : nullptr;
QCOMPARE(scriptContext.data(), newContext.data());
@@ -5257,6 +5414,7 @@ void tst_qqmlecmascript::propertyChangeSlots()
QQmlComponent component(&engine, testFileUrl("changeslots/propertyChangeSlots.qml"));
QScopedPointer<QObject> object(component.create());
QVERIFY2(object, qPrintable(component.errorString()));
+ QCOMPARE(object->property("changeCount"), 15);
// ensure that invalid property names fail properly.
QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
@@ -5272,20 +5430,6 @@ void tst_qqmlecmascript::propertyChangeSlots()
QCOMPARE(e2.errors().at(0).toString(), expectedErrorString);
object.reset(e2.create());
QVERIFY(!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.reset(e3.create());
- QVERIFY(!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.reset(e4.create());
- QVERIFY(!object);
}
void tst_qqmlecmascript::propertyVar_data()
@@ -5453,7 +5597,9 @@ void tst_qqmlecmascript::propertyVarOwnership()
QScopedPointer<QObject> object(component.create());
QVERIFY2(object, qPrintable(component.errorString()));
QMetaObject::invokeMethod(object.data(), "createComponent");
- engine.collectGarbage();
+ // This test only works if we don't deliver the pending delete later event
+ // that collectGarbage will post before calling runTest
+ gc(engine, GCFlags::DontSendPostedEvents);
QMetaObject::invokeMethod(object.data(), "runTest");
QCOMPARE(object->property("test").toBool(), true);
}
@@ -5469,8 +5615,7 @@ void tst_qqmlecmascript::propertyVarImplicitOwnership()
QScopedPointer<QObject> object(component.create());
QVERIFY2(object, qPrintable(component.errorString()));
QMetaObject::invokeMethod(object.data(), "assignCircular");
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
- QCoreApplication::processEvents();
+ gc(engine);
QObject *rootObject = object->property("vp").value<QObject*>();
QVERIFY(rootObject != nullptr);
QObject *childObject = rootObject->findChild<QObject*>("text");
@@ -5479,6 +5624,8 @@ void tst_qqmlecmascript::propertyVarImplicitOwnership()
QCOMPARE(childObject->property("textCanary").toInt(), 10);
// Creates a reference to a constructed QObject:
QMetaObject::invokeMethod(childObject, "constructQObject");
+ // Don't send delete later events yet, we do it manually later
+ gc(engine, GCFlags::DontSendPostedEvents);
QPointer<QObject> qobjectGuard(childObject->property("vp").value<QObject*>()); // get the pointer prior to processing deleteLater events.
QVERIFY(!qobjectGuard.isNull());
QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
@@ -5497,8 +5644,7 @@ void tst_qqmlecmascript::propertyVarReparent()
QScopedPointer<QObject> object(component.create());
QVERIFY2(object, qPrintable(component.errorString()));
QMetaObject::invokeMethod(object.data(), "assignVarProp");
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
- QCoreApplication::processEvents();
+ gc(engine);
QObject *rect = object->property("vp").value<QObject*>();
QObject *text = rect->findChild<QObject*>("textOne");
QObject *text2 = rect->findChild<QObject*>("textTwo");
@@ -5512,6 +5658,7 @@ void tst_qqmlecmascript::propertyVarReparent()
QCOMPARE(text2->property("textCanary").toInt(), 12);
// now construct an image which we will reparent.
QMetaObject::invokeMethod(text2, "constructQObject");
+ gc(engine, GCFlags::DontSendPostedEvents);
QObject *image = text2->property("vp").value<QObject*>();
QPointer<QObject> imageGuard(image);
QVERIFY(!imageGuard.isNull());
@@ -5539,8 +5686,7 @@ void tst_qqmlecmascript::propertyVarReparentNullContext()
QScopedPointer<QObject> object(component.create());
QVERIFY2(object, qPrintable(component.errorString()));
QMetaObject::invokeMethod(object.data(), "assignVarProp");
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
- QCoreApplication::processEvents();
+ gc(engine);
QObject *rect = object->property("vp").value<QObject*>();
QObject *text = rect->findChild<QObject*>("textOne");
QObject *text2 = rect->findChild<QObject*>("textTwo");
@@ -5554,6 +5700,7 @@ void tst_qqmlecmascript::propertyVarReparentNullContext()
QCOMPARE(text2->property("textCanary").toInt(), 12);
// now construct an image which we will reparent.
QMetaObject::invokeMethod(text2, "constructQObject");
+ gc(engine);
QObject *image = text2->property("vp").value<QObject*>();
QPointer<QObject> imageGuard(image);
QVERIFY(!imageGuard.isNull());
@@ -5780,9 +5927,7 @@ void tst_qqmlecmascript::handleReferenceManagement()
gc(hrmEngine);
QCOMPARE(dtorCount, 0); // second has JS ownership, kept alive by first's reference
object.reset();
- hrmEngine.collectGarbage();
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
- QCoreApplication::processEvents();
+ gc(hrmEngine);
QCOMPARE(dtorCount, 3);
}
@@ -5799,9 +5944,7 @@ void tst_qqmlecmascript::handleReferenceManagement()
gc(hrmEngine);
QCOMPARE(dtorCount, 2); // both should be cleaned up, since circular references shouldn't keep alive.
object.reset();
- hrmEngine.collectGarbage();
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
- QCoreApplication::processEvents();
+ gc(hrmEngine);
QCOMPARE(dtorCount, 3);
}
@@ -7892,7 +8035,7 @@ public:
void init(QV4::ExecutionEngine *v4, QV4::WeakValue *weakRef, bool *resultPtr)
{
- QV4::QObjectWrapper::wrap(v4, this);
+ (void) QV4::QObjectWrapper::wrap(v4, this); // Intentionally drop the wrapper
QQmlEngine::setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
this->resultPtr = resultPtr;
@@ -7980,12 +8123,9 @@ void tst_qqmlecmascript::onDestructionViaGC()
v4->memoryManager->allocate<QV4::WeakReferenceSentinel>(weakRef.data(), &sentinelResult);
}
gc(engine);
-
+ QVERIFY2(weakRef->isNullOrUndefined(), "The weak value was not cleared");
QVERIFY2(mutatorResult, "We failed to re-assign the weak reference a new value during GC");
- QVERIFY2(!sentinelResult, "The weak value was cleared on first GC run");
- QVERIFY2(!weakRef->isNullOrUndefined(), "The weak value was cleared on first GC run");
- gc(engine);
- QVERIFY2(weakRef->isNullOrUndefined(), "The weak value was not cleared on second gc run");
+ QVERIFY2(sentinelResult, "The weak reference was not cleared properly");
}
struct EventProcessor : public QObject
@@ -8078,7 +8218,9 @@ void tst_qqmlecmascript::qqmldataDestroyed()
QVERIFY2(object, qPrintable(c.errorString()));
// now gc causing the collection of the dynamically constructed object.
engine.collectGarbage();
+ QTRY_VERIFY(gcDone(&engine));
engine.collectGarbage();
+ QTRY_VERIFY(gcDone(&engine));
// now process events to allow deletion (calling qqmldata::destroyed())
QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
QCoreApplication::processEvents();
@@ -9389,6 +9531,32 @@ void tst_qqmlecmascript::qpropertyBindingHandlesUndefinedWithoutResetCorrectly()
QCOMPARE(root->property("value2").toInt(), 2);
}
+void tst_qqmlecmascript::qpropertyBindingRestoresObserverAfterReset()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("restoreObserverAfterReset.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QTRY_COMPARE(o->property("height").toDouble(), 60.0);
+ QVERIFY(o->property("steps").toInt() > 3);
+}
+
+void tst_qqmlecmascript::qpropertyBindingObserverCorrectlyLinkedAfterReset()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("qpropertyResetCorrectlyLinked.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ std::unique_ptr<QObject> o(c.create());
+ QVERIFY(o);
+ QCOMPARE(o->property("width"), 200);
+ auto item = qobject_cast<QQuickItem *>(o.get());
+ auto itemPriv = QQuickItemPrivate::get(item);
+ QBindingStorage *storage = qGetBindingStorage(itemPriv);
+ QPropertyBindingDataPointer ptr { storage->bindingData(&itemPriv->width) };
+ QCOMPARE(ptr.observerCount(), 1);
+}
+
void tst_qqmlecmascript::hugeRegexpQuantifiers()
{
QJSEngine engine;
@@ -9987,6 +10155,17 @@ void tst_qqmlecmascript::proxyHandlerTraps()
QVERIFY(value.isString() && value.toString() == QStringLiteral("SUCCESS"));
}
+void tst_qqmlecmascript::lookupsDoNotBypassProxy()
+{
+ QQmlEngine engine;
+ // we need a component to have a proper compilation to byte code;
+ // otherwise, we don't actually end up with lookups
+ QQmlComponent comp(&engine, testFileUrl("lookupsDoNotBypassProxy.qml"));
+ QVERIFY(comp.isReady());
+ std::unique_ptr<QObject> obj { comp.create() };
+ QCOMPARE(obj->property("result").toInt(), 3);
+}
+
void tst_qqmlecmascript::cmpInThrows()
{
QJSEngine engine;
@@ -10027,6 +10206,9 @@ public:
Q_INVOKABLE void triggerSignal() { emit fooMember2Emitted(&m_fooMember2); }
+ Q_INVOKABLE const FrozenFoo *getConst() { return createFloating(); }
+ Q_INVOKABLE FrozenFoo *getNonConst() { return createFloating(); }
+
FrozenFoo *fooMember() { return &m_fooMember; }
FrozenFoo *fooMember2() { return &m_fooMember2; }
@@ -10036,6 +10218,16 @@ signals:
private:
const FrozenFoo *fooMemberConst() const { return &m_fooMember; }
+ FrozenFoo *createFloating()
+ {
+ if (!m_floating) {
+ m_floating = new FrozenFoo;
+ m_floating->setObjectName(objectName());
+ }
+ return m_floating;
+ }
+
+ FrozenFoo *m_floating = nullptr;
FrozenFoo m_fooMember;
FrozenFoo m_fooMember2;
};
@@ -10058,6 +10250,17 @@ void tst_qqmlecmascript::frozenQObject()
QVERIFY(frozenObjects->property("caughtSignal").toBool());
QCOMPARE(frozenObjects->fooMember()->name(), QStringLiteral("Jane"));
QCOMPARE(frozenObjects->fooMember2()->name(), QStringLiteral("Jane"));
+
+ QQmlComponent component3(&engine, testFileUrl("frozenQObject3.qml"));
+ QScopedPointer<QObject> root3(component3.create());
+ QCOMPARE(root3->objectName(), QLatin1String("a/b"));
+ QVERIFY(root3->property("objConst").value<QObject *>());
+ QVERIFY(root3->property("objNonConst").value<QObject *>());
+
+ QTRY_VERIFY(root3->property("gcs").toInt() > 8);
+
+ QVERIFY(root3->property("objConst").value<QObject *>());
+ QVERIFY(root3->property("objNonConst").value<QObject *>());
}
struct ConstPointer : QObject
@@ -10373,6 +10576,80 @@ void tst_qqmlecmascript::doNotCrashOnReadOnlyBindable()
QCOMPARE(o->property("x").toInt(), 7);
}
+void tst_qqmlecmascript::resetGadget()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("resetGadget.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ auto resettableGadgetHolder = qobject_cast<ResettableGadgetHolder *>(o.get());
+ QVERIFY(resettableGadgetHolder);
+ QCOMPARE(resettableGadgetHolder->g().value(), 0);
+ resettableGadgetHolder->setProperty("trigger", QVariant::fromValue(true));
+ QCOMPARE(resettableGadgetHolder->g().value(), 42);
+}
+
+void tst_qqmlecmascript::assignListPropertyByIndexOnGadget()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFile("AssignListPropertyByIndexOnGadget.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+
+ const auto &gadget = o->property("gadget").value<ListPropertyAssignment_Gadget>();
+ const auto *object = o->property("object").value<ListPropertyAssignment_Object *>();
+ QVERIFY(object);
+
+ QStringList expected{ "Completely new Element", "Element2", "Element3" };
+ QVariantList variants {
+ u"Completely new Element"_s,
+ u"foo"_s,
+ QVariant::fromValue<std::nullptr_t>(nullptr),
+ QVariant::fromValue<bool>(true)
+ };
+
+ QCOMPARE(gadget.gadgetStringList(), expected);
+ QCOMPARE(gadget.gadgetVariantList(), variants);
+ QCOMPARE(object->qobjectStringList(), expected);
+}
+
+void tst_qqmlecmascript::methodCallOnDerivedSingleton()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFile("methodCallOnDerivedSingleton.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(o);
+ auto singleton = engine.singletonInstance<SingletonBase *>("Qt.test", "SingletonInheritanceTest");
+ QVERIFY(singleton);
+ QVERIFY(singleton->m_okay);
+}
+
+void tst_qqmlecmascript::proxyMetaObject()
+{
+ // Verify that TypeWithCustomMetaObject, that extends another type,
+ // thereby triggering a QQmlProxyMetaObject, is still proxied the
+ // QDynamicMetaObjectData::objectDestroyed callback.
+
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.setData(R"(
+ import QtQuick
+ import QtQml
+ import Qt.test
+ Rectangle {
+ TypeWithCustomMetaObject {}
+ }
+ )", QUrl("testData"));
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY(o);
+ QVERIFY(!MetaCallInterceptor::didGetObjectDestroyedCallback);
+ o.reset(nullptr);
+ QVERIFY(MetaCallInterceptor::didGetObjectDestroyedCallback);
+}
+
QTEST_MAIN(tst_qqmlecmascript)
#include "tst_qqmlecmascript.moc"