diff options
Diffstat (limited to 'tests/auto/qml')
| -rw-r--r-- | tests/auto/qml/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | tests/auto/qml/qjsengine/tst_qjsengine.cpp | 21 | ||||
| -rw-r--r-- | tests/auto/qml/qqmllanguage/data/BindingOverrider.qml | 5 | ||||
| -rw-r--r-- | tests/auto/qml/qqmllanguage/data/SimpleWidget.qml | 25 | ||||
| -rw-r--r-- | tests/auto/qml/qqmllanguage/data/badICAnnotation.qml | 26 | ||||
| -rw-r--r-- | tests/auto/qml/qqmllanguage/data/typedObjectList.qml | 13 | ||||
| -rw-r--r-- | tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 51 | ||||
| -rw-r--r-- | tests/auto/qml/qqmllistmodel/data/deadModelData.qml | 45 | ||||
| -rw-r--r-- | tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp | 62 | ||||
| -rw-r--r-- | tests/auto/qml/qv4estable/CMakeLists.txt | 24 | ||||
| -rw-r--r-- | tests/auto/qml/qv4estable/tst_qv4estable.cpp | 40 |
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" |
