aboutsummaryrefslogtreecommitdiffstats
path: root/tests/auto/qml
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/qml')
-rw-r--r--tests/auto/qml/CMakeLists.txt1
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp21
-rw-r--r--tests/auto/qml/qqmllanguage/data/BindingOverrider.qml5
-rw-r--r--tests/auto/qml/qqmllanguage/data/SimpleWidget.qml25
-rw-r--r--tests/auto/qml/qqmllanguage/data/badICAnnotation.qml26
-rw-r--r--tests/auto/qml/qqmllanguage/data/typedObjectList.qml13
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp51
-rw-r--r--tests/auto/qml/qqmllistmodel/data/deadModelData.qml45
-rw-r--r--tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp62
-rw-r--r--tests/auto/qml/qv4estable/CMakeLists.txt24
-rw-r--r--tests/auto/qml/qv4estable/tst_qv4estable.cpp40
11 files changed, 313 insertions, 0 deletions
diff --git a/tests/auto/qml/CMakeLists.txt b/tests/auto/qml/CMakeLists.txt
index 5fe0b0950f..4b2fb9c1e7 100644
--- a/tests/auto/qml/CMakeLists.txt
+++ b/tests/auto/qml/CMakeLists.txt
@@ -120,6 +120,7 @@ if(QT_FEATURE_private_tests)
add_subdirectory(qqmltablemodel)
add_subdirectory(qv4assembler)
add_subdirectory(qv4mm)
+ add_subdirectory(qv4estable)
add_subdirectory(qv4identifiertable)
add_subdirectory(qv4regexp)
if(QT_FEATURE_process AND NOT QNX)
diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
index c2c6f3f43f..8d2b5179f8 100644
--- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp
+++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
@@ -293,6 +293,7 @@ private slots:
void spreadNoOverflow();
void deleteDefineCycle();
+ void deleteFromSparseArray();
public:
Q_INVOKABLE QJSValue throwingCppMethod1();
@@ -5837,6 +5838,26 @@ void tst_QJSEngine::deleteDefineCycle()
QVERIFY(stackTrace.isEmpty());
}
+void tst_QJSEngine::deleteFromSparseArray()
+{
+ QJSEngine engine;
+
+ // Should not crash
+ const QJSValue result = engine.evaluate(QLatin1String(R"((function() {
+ let o = [];
+ o[10000] = 10;
+ o[20000] = 20;
+ for (let k in o)
+ delete o[k];
+ return o;
+ })())"));
+
+ QVERIFY(result.isArray());
+ QCOMPARE(result.property("length").toNumber(), 20001);
+ QVERIFY(result.property(10000).isUndefined());
+ QVERIFY(result.property(20000).isUndefined());
+}
+
QTEST_MAIN(tst_QJSEngine)
#include "tst_qjsengine.moc"
diff --git a/tests/auto/qml/qqmllanguage/data/BindingOverrider.qml b/tests/auto/qml/qqmllanguage/data/BindingOverrider.qml
new file mode 100644
index 0000000000..de7e1e96a3
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/BindingOverrider.qml
@@ -0,0 +1,5 @@
+import QtQml 2.15
+
+SimpleWidget {
+ width: 20
+}
diff --git a/tests/auto/qml/qqmllanguage/data/SimpleWidget.qml b/tests/auto/qml/qqmllanguage/data/SimpleWidget.qml
new file mode 100644
index 0000000000..9150ebaa4e
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/SimpleWidget.qml
@@ -0,0 +1,25 @@
+import QtQuick 2.15
+
+Item {
+ id: outer
+
+ property real innerWidth: 0
+
+ Item {
+ id: inner
+ width: style.width
+ onWidthChanged: outer.innerWidth = width
+ }
+
+ width: inner.width
+
+ onWidthChanged: {
+ if (width !== inner.width)
+ inner.width = width // overwrite binding
+ }
+
+ QtObject {
+ id: style
+ property int width: 50
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/badICAnnotation.qml b/tests/auto/qml/qqmllanguage/data/badICAnnotation.qml
new file mode 100644
index 0000000000..78555ac7dc
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/badICAnnotation.qml
@@ -0,0 +1,26 @@
+
+import QtQml
+
+QtObject {
+ id: self
+
+ function doStuff(status: Binding.NotAnInlineComponent) : int {
+ return status
+ }
+
+ function doStuff2(status: InlineComponentBase.IC) : QtObject {
+ return status
+ }
+
+ function doStuff3(status: InlineComponentBase.NotIC) : QtObject {
+ return status
+ }
+
+ property InlineComponentBase.IC ic: InlineComponentBase.IC {}
+
+ property int a: doStuff(5)
+ property QtObject b: doStuff2(ic)
+ property QtObject c: doStuff3(ic)
+ property QtObject d: doStuff2(self)
+}
+
diff --git a/tests/auto/qml/qqmllanguage/data/typedObjectList.qml b/tests/auto/qml/qqmllanguage/data/typedObjectList.qml
new file mode 100644
index 0000000000..89c66249cf
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/typedObjectList.qml
@@ -0,0 +1,13 @@
+import QtQml
+
+QtObject {
+ property var b;
+ property Component c: QtObject {}
+
+ // In 6.5 and earlier we don't have heap-managed QQmlListProperty, yet.
+ property list<Component> ll;
+
+ function returnList(a: Component) : list<Component> { ll.push(a); return ll; }
+
+ Component.onCompleted: b = { b: returnList(c) }
+}
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index 3d1f023425..95e838476f 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -394,6 +394,11 @@ private slots:
void deepAliasOnICOrReadonly();
void writeNumberToEnumAlias();
+ void badInlineComponentAnnotation();
+
+ void typedObjectList();
+
+ void overrideInnerBinding();
private:
QQmlEngine engine;
@@ -6747,6 +6752,52 @@ void tst_qqmllanguage::writeNumberToEnumAlias()
QCOMPARE(o->property("strokeStyle").toInt(), 1);
}
+void tst_qqmllanguage::badInlineComponentAnnotation()
+{
+ QQmlEngine engine;
+ const QUrl url = testFileUrl("badICAnnotation.qml");
+ QQmlComponent c(&engine, testFileUrl("badICAnnotation.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ QCOMPARE(o->property("a").toInt(), 5);
+
+ QObject *ic = o->property("ic").value<QObject *>();
+ QVERIFY(ic);
+
+ QCOMPARE(o->property("b").value<QObject *>(), ic);
+ QCOMPARE(o->property("c").value<QObject *>(), ic);
+}
+
+void tst_qqmllanguage::typedObjectList()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("typedObjectList.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ QJSValue b = o->property("b").value<QJSValue>();
+ auto list = qjsvalue_cast<QQmlListProperty<QQmlComponent>>(b.property(QStringLiteral("b")));
+
+ QCOMPARE(list.count(&list), 1);
+ QVERIFY(list.at(&list, 0) != nullptr);
+}
+
+void tst_qqmllanguage::overrideInnerBinding()
+{
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("BindingOverrider.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ QCOMPARE(o->property("width").toReal(), 20.0);
+ QCOMPARE(o->property("innerWidth").toReal(), 20.0);
+}
+
QTEST_MAIN(tst_qqmllanguage)
#include "tst_qqmllanguage.moc"
diff --git a/tests/auto/qml/qqmllistmodel/data/deadModelData.qml b/tests/auto/qml/qqmllistmodel/data/deadModelData.qml
new file mode 100644
index 0000000000..fc3dd6fa11
--- /dev/null
+++ b/tests/auto/qml/qqmllistmodel/data/deadModelData.qml
@@ -0,0 +1,45 @@
+import QtQml
+
+QtObject {
+ function swapCorpses() {
+ const lhsData = getModelData(lhsButtonListModel);
+ const rhsData = getModelData(rhsButtonListModel);
+
+ lhsButtonListModel.clear();
+ rhsButtonListModel.clear();
+
+ addToModel(lhsButtonListModel, rhsData);
+ addToModel(rhsButtonListModel, lhsData);
+ }
+
+ property ListModel l1: ListModel {
+ id: lhsButtonListModel
+ }
+
+ property ListModel l2: ListModel {
+ id: rhsButtonListModel
+ }
+
+ Component.onCompleted: {
+ lhsButtonListModel.append({ "ident": 1, "buttonText": "B 1"});
+ lhsButtonListModel.append({ "ident": 2, "buttonText": "B 2"});
+ lhsButtonListModel.append({ "ident": 3, "buttonText": "B 3"});
+
+ rhsButtonListModel.append({ "ident": 4, "buttonText": "B 4"});
+ rhsButtonListModel.append({ "ident": 5, "buttonText": "B 5"});
+ rhsButtonListModel.append({ "ident": 6, "buttonText": "B 6"});
+ }
+
+ function getModelData(model) {
+ var dataList = []
+ for (var i = 0; i < model.count; ++i)
+ dataList.push(model.get(i));
+
+ return dataList;
+ }
+
+ function addToModel(model, buttonData) {
+ for (var i = 0; i < buttonData.length; ++i)
+ model.append(buttonData[i]);
+ }
+}
diff --git a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp
index fbdf6d90f3..f440eab6b7 100644
--- a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp
+++ b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp
@@ -140,6 +140,7 @@ private slots:
void destroyComponentObject();
void objectOwnershipFlip();
void protectQObjectFromGC();
+ void deadModelData();
};
bool tst_qqmllistmodel::compareVariantList(const QVariantList &testList, QVariant object)
@@ -1940,6 +1941,67 @@ void tst_qqmllistmodel::protectQObjectFromGC()
}
}
+void tst_qqmllistmodel::deadModelData()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("deadModelData.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY(!o.isNull());
+
+ QQmlListModel *l1 = o->property("l1").value<QQmlListModel *>();
+ QVERIFY(l1);
+ QQmlListModel *l2 = o->property("l2").value<QQmlListModel *>();
+ QVERIFY(l2);
+
+ QCOMPARE(l1->count(), 3);
+ QCOMPARE(l2->count(), 3);
+
+ for (int i = 0; i < 3; ++i) {
+ QObject *i1 = qjsvalue_cast<QObject *>(l1->get(i));
+ QVERIFY(i1);
+ QCOMPARE(i1->property("ident").value<double>(), i + 1);
+ QCOMPARE(i1->property("buttonText").value<QString>(),
+ QLatin1String("B %1").arg(QLatin1Char('0' + i + 1)));
+
+ QObject *i2 = qjsvalue_cast<QObject *>(l2->get(i));
+ QVERIFY(i2);
+ QCOMPARE(i2->property("ident").value<double>(), i + 4);
+ QCOMPARE(i2->property("buttonText").value<QString>(),
+ QLatin1String("B %1").arg(QLatin1Char('0' + i + 4)));
+ }
+
+ for (int i = 0; i < 6; ++i) {
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ QRegularExpression(".*: ident is undefined. Adding an object with a undefined "
+ "member does not create a role for it."));
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ QRegularExpression(".*: buttonText is undefined. Adding an object with a undefined "
+ "member does not create a role for it."));
+ }
+
+ QMetaObject::invokeMethod(o.data(), "swapCorpses");
+
+ // We get default-created values for all the roles now.
+
+ QCOMPARE(l1->count(), 3);
+ QCOMPARE(l2->count(), 3);
+
+ for (int i = 0; i < 3; ++i) {
+ QObject *i1 = qjsvalue_cast<QObject *>(l1->get(i));
+ QVERIFY(i1);
+ QCOMPARE(i1->property("ident").value<double>(), double());
+ QCOMPARE(i1->property("buttonText").value<QString>(), QString());
+
+ QObject *i2 = qjsvalue_cast<QObject *>(l2->get(i));
+ QVERIFY(i2);
+ QCOMPARE(i2->property("ident").value<double>(), double());
+ QCOMPARE(i2->property("buttonText").value<QString>(), QString());
+ }
+}
+
QTEST_MAIN(tst_qqmllistmodel)
#include "tst_qqmllistmodel.moc"
diff --git a/tests/auto/qml/qv4estable/CMakeLists.txt b/tests/auto/qml/qv4estable/CMakeLists.txt
new file mode 100644
index 0000000000..01d2663a04
--- /dev/null
+++ b/tests/auto/qml/qv4estable/CMakeLists.txt
@@ -0,0 +1,24 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## tst_qv4estable Test:
+#####################################################################
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qv4estable LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qv4estable
+ SOURCES
+ tst_qv4estable.cpp
+ LIBRARIES
+ Qt::Gui
+ Qt::Qml
+ Qt::QmlPrivate
+)
+
+## Scopes:
+#####################################################################
diff --git a/tests/auto/qml/qv4estable/tst_qv4estable.cpp b/tests/auto/qml/qv4estable/tst_qv4estable.cpp
new file mode 100644
index 0000000000..eecb672bc6
--- /dev/null
+++ b/tests/auto/qml/qv4estable/tst_qv4estable.cpp
@@ -0,0 +1,40 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <qtest.h>
+#include <private/qv4estable_p.h>
+
+class tst_qv4estable : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void checkRemoveAvoidsHeapBufferOverflow();
+};
+
+// QTBUG-123999
+void tst_qv4estable::checkRemoveAvoidsHeapBufferOverflow()
+{
+ QV4::ESTable estable;
+
+ // Fill the ESTable with values so it is at max capacity.
+ QCOMPARE(estable.m_capacity, 8u);
+ for (uint i = 0; i < estable.m_capacity; ++i) {
+ estable.set(QV4::Value::fromUInt32(i), QV4::Value::fromUInt32(i));
+ }
+ // Our |m_keys| array should now contain eight values.
+ // > [v0, v1, v2, v3, v4, v5, v6, v7]
+ for (uint i = 0; i < estable.m_capacity; ++i) {
+ QVERIFY(estable.m_keys[i].sameValueZero(QV4::Value::fromUInt32(i)));
+ }
+ QCOMPARE(estable.m_capacity, 8u);
+ QCOMPARE(estable.m_size, 8u);
+
+ // Remove the first item from the set to verify that asan does not trip.
+ // Relies on the CI platform propagating asan flag to all tests.
+ estable.remove(QV4::Value::fromUInt32(0));
+}
+
+QTEST_MAIN(tst_qv4estable)
+
+#include "tst_qv4estable.moc"