diff options
Diffstat (limited to 'tests')
23 files changed, 687 insertions, 1 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" diff --git a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp index 30b934eeec..579eeb7c0a 100644 --- a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp +++ b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp @@ -119,6 +119,7 @@ private slots: void cleanupWhenRenderThreadStops(); void infiniteLoopsWithoutFrom(); void targetsDeletedNotRemoved(); + void alwaysRunToEndSetFalseRestartBug(); }; #define QTIMED_COMPARE(lhs, rhs) do { \ for (int ii = 0; ii < 5; ++ii) { \ @@ -2081,6 +2082,41 @@ void tst_qquickanimations::targetsDeletedNotRemoved() } } +//QTBUG-125224 +void tst_qquickanimations::alwaysRunToEndSetFalseRestartBug() +{ + QQuickRectangle rect; + QQuickSequentialAnimation sequential; + QQuickPropertyAnimation beginAnim; + QQuickPropertyAnimation endAnim; + + beginAnim.setTargetObject(&rect); + beginAnim.setProperty("x"); + beginAnim.setTo(200); + beginAnim.setDuration(1000); + + endAnim.setTargetObject(&rect); + endAnim.setProperty("x"); + endAnim.setFrom(200); + endAnim.setDuration(1000); + + beginAnim.setGroup(&sequential); + endAnim.setGroup(&sequential); + + sequential.setLoops(-1); + sequential.setAlwaysRunToEnd(true); + + QCOMPARE(sequential.loops(), -1); + QVERIFY(sequential.alwaysRunToEnd()); + sequential.start(); + sequential.stop(); + sequential.setAlwaysRunToEnd(false); + sequential.start(); + QCOMPARE(sequential.isRunning(), true); + sequential.stop(); + QCOMPARE(sequential.isRunning(), false); +} + QTEST_MAIN(tst_qquickanimations) #include "tst_qquickanimations.moc" diff --git a/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml b/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml index 5e8f6f9bcf..37166af119 100644 --- a/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml +++ b/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml @@ -1308,6 +1308,53 @@ Item { compare(rootRect.item1.width, 100) } + //--------------------------- + // Layout with negative size + Component { + id: negativeSize_Component + Item { + id: rootItem + width: 0 + height: 0 + // default width x height: (0 x 0) + RowLayout { + spacing: 0 + anchors.fill: parent + anchors.leftMargin: 1 // since parent size == (0 x 0), it causes layout size + anchors.bottomMargin: 1 // to become (-1, -1) + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } + } + } + } + + function test_negativeSize() { + let rootItem = createTemporaryObject(negativeSize_Component, container) + let rowLayout = rootItem.children[0] + let item = rowLayout.children[0] + + // 1 doesn't work in 6.2 because that will make the layout of size 0x0 + // Setting the geometry of a layout to 0x0 doesn't work because + // QQuickLayout::geometryChange() early returns if !newGeometry.isValid() + // This check has been later removed. + const arr = [7, /*1,*/ 7, 0] + arr.forEach((n) => { + rootItem.width = n + rootItem.height = n + + // n === 0 is special: It will cause the layout to have a + // negative size. In this case it will simply not rearrange its + // child (and leave it at its previous size, 6) + const expectedItemExtent = n === 0 ? 6 : n - 1 + + compare(item.width, expectedItemExtent) + compare(item.height, expectedItemExtent) + }); + } + + //--------------------------- Component { id: rowlayoutWithTextItems_Component diff --git a/tests/auto/quick/qquicklistview/data/snapToItemWithSectionAtStart.qml b/tests/auto/quick/qquicklistview/data/snapToItemWithSectionAtStart.qml new file mode 100644 index 0000000000..0d4c233345 --- /dev/null +++ b/tests/auto/quick/qquicklistview/data/snapToItemWithSectionAtStart.qml @@ -0,0 +1,52 @@ +import QtQuick + +ListView { + id: listView + width: 240 + height: 300 + + model: ListModel { + ListElement { section: "section 1" } + ListElement { section: "section 1" } + ListElement { section: "section 1" } + ListElement { section: "section 2" } + ListElement { section: "section 2" } + ListElement { section: "section 2" } + ListElement { section: "section 3" } + ListElement { section: "section 3" } + ListElement { section: "section 3" } + ListElement { section: "section 4" } + ListElement { section: "section 4" } + ListElement { section: "section 4" } + ListElement { section: "section 5" } + ListElement { section: "section 5" } + ListElement { section: "section 5" } + } + + section.property: "section" + section.labelPositioning: ViewSection.InlineLabels | ViewSection.CurrentLabelAtStart + section.delegate: Rectangle { + width: listView.width + height: 30 + Text { + anchors.fill: parent + text: section + } + color: "lightblue" + } + + snapMode: ListView.SnapToItem + + delegate: Rectangle { + width: listView.width + height: 30 + Text { + anchors.fill: parent + text: index + } + border { + width: 1 + color: "black" + } + } +} diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index 24196e2ba1..8f3aa551e1 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -193,6 +193,7 @@ private slots: void headerSnapToItem_data(); void headerSnapToItem(); void snapToItemWithSpacing_QTBUG_59852(); + void snapToItemWithSectionAtStart(); void snapOneItemResize_QTBUG_43555(); void snapOneItem_data(); void snapOneItem(); @@ -5407,6 +5408,27 @@ void tst_QQuickListView::snapToItemWithSpacing_QTBUG_59852() releaseView(window); } +void tst_QQuickListView::snapToItemWithSectionAtStart() // QTBUG-30768 +{ + QQuickView window; + QVERIFY(QQuickTest::showView(window, testFileUrl("snapToItemWithSectionAtStart.qml"))); + QQuickListView *listView = qobject_cast<QQuickListView *>(window.rootObject()); + QTRY_VERIFY(listView); + + // Both sections and elements are 30px high. The list height is 300px, so + // it fits exactly 10 elements. We can do some random flicks, but the + // content position always MUST be divisible by 30. + for (int i = 0; i < 10; ++i) { + const bool even = (i % 2 == 0); + const QPoint start = even ? QPoint(20, 100 + i * 5) : QPoint(20, 20 + i * 3); + const QPoint end = even ? start - QPoint(0, 50 + i * 10) : start + QPoint(0, 50 + i * 5); + + flick(&window, start, end, 180); + QTRY_COMPARE(listView->isMoving(), false); // wait until it stops + QCOMPARE(int(listView->contentY()) % 30, 0); + } +} + static void drag_helper(QWindow *window, QPoint *startPos, const QPoint &delta) { QPoint pos = *startPos; diff --git a/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST b/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST index 2488eff270..46db6477f5 100644 --- a/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST +++ b/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST @@ -4,3 +4,5 @@ ubuntu-22.04 [nested] ubuntu-20.04 ubuntu-22.04 +[inFlickable] +* # QTBUG-127628 diff --git a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp index 5991621376..5330d8d01f 100644 --- a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp +++ b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp @@ -185,6 +185,8 @@ private slots: void checkSyncView_pageFlicking(); void checkSyncView_emptyModel(); void checkSyncView_topLeftChanged(); + void checkSyncView_dontRelayoutWhileFlicking(); + void checkSyncView_detectTopLeftPositionChanged(); void delegateWithRequiredProperties(); void checkThatFetchMoreIsCalledWhenScrolledToTheEndOfTable(); void replaceModel(); @@ -3108,6 +3110,85 @@ void tst_QQuickTableView::checkSyncView_topLeftChanged() QCOMPARE(tableViewV->topRow(), tableView->topRow()); } +void tst_QQuickTableView::checkSyncView_dontRelayoutWhileFlicking() +{ + // Check that we don't do a full relayout in a sync child when + // a new row or column is flicked into the view. Normal load + // and unload of edges should suffice, equal to how the main + // TableView (syncView) does it. + LOAD_TABLEVIEW("syncviewsimple.qml"); + GET_QML_TABLEVIEW(tableViewHV); + + auto model = TestModelAsVariant(100, 100); + tableView->setModel(model); + tableViewHV->setModel(model); + + tableView->setColumnWidthProvider(QJSValue()); + tableView->setRowHeightProvider(QJSValue()); + view->rootObject()->setProperty("delegateWidth", 50); + view->rootObject()->setProperty("delegateHeight", 50); + + WAIT_UNTIL_POLISHED; + + // To check that we don't do a relayout when flicking horizontally, we use a "trick" + // where we check the rebuildOptions when we receive the rightColumnChanged + // signal. If this signal is emitted as a part of a relayout, rebuildOptions + // would still be different from RebuildOption::None at that point. + bool columnFlickedIn = false; + connect(tableViewHV, &QQuickTableView::rightColumnChanged, [&] { + columnFlickedIn = true; + QCOMPARE(tableViewHVPrivate->rebuildOptions, QQuickTableViewPrivate::RebuildOption::None); + }); + + // We do the same for vertical flicking + bool rowFlickedIn = false; + connect(tableViewHV, &QQuickTableView::bottomRowChanged, [&] { + rowFlickedIn = true; + QCOMPARE(tableViewHVPrivate->rebuildOptions, QQuickTableViewPrivate::RebuildOption::None); + }); + + // Move the main tableview so that a new column is flicked in + tableView->setContentX(60); + QTRY_VERIFY(columnFlickedIn); + + // Move the main tableview so that a new row is flicked in + tableView->setContentY(60); + QTRY_VERIFY(rowFlickedIn); +} + +void tst_QQuickTableView::checkSyncView_detectTopLeftPositionChanged() +{ + // It can happen that, during a resize of columns or rows from using a float-based + // slider, that the position of the top-left delegate item is shifted a bit left or + // right because of rounding issues. And this again can over time, as you flick, make + // the loadedTableOuterRect get slightly out of sync in the sync child compared to the + // sync view. TableView will detect if this happens (in syncSyncView), and correct for + // it. And this test will test that it works. + LOAD_TABLEVIEW("syncviewsimple.qml"); + GET_QML_TABLEVIEW(tableViewHV); + + auto model = TestModelAsVariant(100, 100); + tableView->setModel(model); + tableViewHV->setModel(model); + + WAIT_UNTIL_POLISHED; + + // Writing an auto test to trigger this rounding issue is very hard. So to keep it + // simple, we cheat by just moving the loadedTableOuterRect directly, and + // check that the syncView child detects it, and corrects it, upon doing a + // forceLayout() + tableViewPrivate->loadedTableOuterRect.moveLeft(20); + tableViewPrivate->loadedTableOuterRect.moveTop(30); + tableViewPrivate->relayoutTableItems(); + tableViewHV->forceLayout(); + + QCOMPARE(tableViewPrivate->loadedTableOuterRect.left(), 20); + QCOMPARE(tableViewHVPrivate->loadedTableOuterRect.left(), 20); + + QCOMPARE(tableViewPrivate->loadedTableOuterRect.top(), 30); + QCOMPARE(tableViewHVPrivate->loadedTableOuterRect.top(), 30); +} + void tst_QQuickTableView::checkThatFetchMoreIsCalledWhenScrolledToTheEndOfTable() { LOAD_TABLEVIEW("plaintableview.qml"); diff --git a/tests/auto/quick/qquicktext/data/elideZeroWidthWithMargins.qml b/tests/auto/quick/qquicktext/data/elideZeroWidthWithMargins.qml new file mode 100644 index 0000000000..bb269e6ad5 --- /dev/null +++ b/tests/auto/quick/qquicktext/data/elideZeroWidthWithMargins.qml @@ -0,0 +1,27 @@ +import QtQuick + +Item { + id: root + property bool ok: false + width: 640 + height: 480 + + Text { + id: text + text: "This is a quite long text. Click me and i should remain visible!!! Sadly this doesn't happen" + elide: Text.ElideRight + anchors { + fill: parent + margins: 1 + } + } + + Component.onCompleted: { + text.width = 300; + text.height = 0; + text.width = 0; + text.height = 30; + text.width = 300; + root.ok = text.paintedWidth > 0 && text.paintedHeight > 0 + } +} diff --git a/tests/auto/quick/qquicktext/tst_qquicktext.cpp b/tests/auto/quick/qquicktext/tst_qquicktext.cpp index 476297918d..ea90fc74d6 100644 --- a/tests/auto/quick/qquicktext/tst_qquicktext.cpp +++ b/tests/auto/quick/qquicktext/tst_qquicktext.cpp @@ -69,6 +69,7 @@ private slots: void wrap(); void elide(); void elideParentChanged(); + void elideRelayoutAfterZeroWidth_data(); void elideRelayoutAfterZeroWidth(); void multilineElide_data(); void multilineElide(); @@ -611,10 +612,19 @@ void tst_qquicktext::elideParentChanged() QCOMPARE(actualItemImageGrab, expectedItemImageGrab); } +void tst_qquicktext::elideRelayoutAfterZeroWidth_data() +{ + QTest::addColumn<QByteArray>("fileName"); + + QTest::newRow("no_margins") << QByteArray("elideZeroWidth.qml"); + QTest::newRow("with_margins") << QByteArray("elideZeroWidthWithMargins.qml"); +} + void tst_qquicktext::elideRelayoutAfterZeroWidth() { + QFETCH(const QByteArray, fileName); QQmlEngine engine; - QQmlComponent component(&engine, testFileUrl("elideZeroWidth.qml")); + QQmlComponent component(&engine, testFileUrl(fileName.constData())); QScopedPointer<QObject> root(component.create()); QVERIFY2(root, qPrintable(component.errorString())); QVERIFY(root->property("ok").toBool()); diff --git a/tests/auto/quickcontrols2/controls/data/tst_control.qml b/tests/auto/quickcontrols2/controls/data/tst_control.qml index e12cbbf4ef..5103d9afd4 100644 --- a/tests/auto/quickcontrols2/controls/data/tst_control.qml +++ b/tests/auto/quickcontrols2/controls/data/tst_control.qml @@ -503,6 +503,36 @@ TestCase { } Component { + id: backgroundTest2 + Button { + id: btn + width: 100 + height: 100 + topInset: 0 + objectName: "" + + background: Rectangle { + id: bg + implicitHeight: 80 + border.color: "red" + y: btn.objectName === "aaa" ? 20 : 0 + } + } + } + + // QTBUG-120033: Make sure that the binding for y on the tab button's background doesn't get removed + function test_background2() { + let button = createTemporaryObject(backgroundTest2, testCase) + verify(button) + + verify(button.background.y === 0) + button.objectName = "aaa" + verify(button.background.y === 20) + button.objectName = "" + verify(button.background.y === 0) + } + + Component { id: component2 T.Control { id: item2 diff --git a/tests/auto/quickcontrols2/qquickpopup/data/resetHoveredForItemsWithinOverlay.qml b/tests/auto/quickcontrols2/qquickpopup/data/resetHoveredForItemsWithinOverlay.qml new file mode 100644 index 0000000000..a7e66718ee --- /dev/null +++ b/tests/auto/quickcontrols2/qquickpopup/data/resetHoveredForItemsWithinOverlay.qml @@ -0,0 +1,28 @@ +import QtQuick +import QtQuick.Controls + +ApplicationWindow { + id: root + width: 100 + height: 100 + property alias controlsPopup: _controlsPopup + property alias blockInputPopup: _blockInputPopup + Popup { + id: _controlsPopup + width: parent.width + height: parent.height + modal: true + Control { + id: controls + anchors.fill: parent + hoverEnabled: true + contentItem: Text { text: "Test Control" } + } + } + Popup { + id: _blockInputPopup + width: parent.width + height: parent.height + modal: true + } +} diff --git a/tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp b/tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp index a587c085a4..2895ac4a83 100644 --- a/tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp +++ b/tests/auto/quickcontrols2/qquickpopup/tst_qquickpopup.cpp @@ -121,6 +121,7 @@ private slots: void shrinkPopupThatWasLargerThanWindow(); void mirroredCombobox(); void rotatedCombobox(); + void resetHoveredStateForItemsWithinPopup(); private: static bool hasWindowActivation(); @@ -2038,6 +2039,40 @@ void tst_QQuickPopup::rotatedCombobox() } } +void tst_QQuickPopup::resetHoveredStateForItemsWithinPopup() +{ + QQuickControlsApplicationHelper helper(this, "resetHoveredForItemsWithinOverlay.qml"); + QVERIFY2(helper.ready, helper.failureMessage()); + + QQuickWindow *window = helper.window; + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + + QQuickPopup *controlsPopup = window->property("controlsPopup").value<QQuickPopup*>(); + QVERIFY(controlsPopup); + + QQuickPopup *blockInputPopup = window->property("blockInputPopup").value<QQuickPopup*>(); + QVERIFY(controlsPopup); + + controlsPopup->open(); + QTRY_VERIFY(controlsPopup->isOpened()); + + QTest::mouseMove(window, QPoint(window->width() / 2 + 2, window->height() / 2 + 2)); + QTest::mouseMove(window, QPoint(window->width() / 2, window->height() / 2)); + + auto *controlItem = qobject_cast<QQuickControl *>(controlsPopup->contentItem()->childItems().at(0)); + QVERIFY(controlItem); + // Check hover enabled for the control item within the popup + QTRY_VERIFY(controlItem->isHovered()); + + // Open the modal popup window over the existing control item + blockInputPopup->open(); + QTRY_VERIFY(blockInputPopup->isOpened()); + + // Control item hovered shall be disabled once we open the modal popup + QTRY_VERIFY(!controlItem->isHovered()); +} + QTEST_QUICKCONTROLS_MAIN(tst_QQuickPopup) #include "tst_qquickpopup.moc" diff --git a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp index 54673b70ce..f0bfd3c79c 100644 --- a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp +++ b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp @@ -711,6 +711,9 @@ void tst_qquickwidget::touchMultipleWidgets() QWidget window; QQuickWidget *leftQuick = new QQuickWidget; leftQuick->setSource(testFileUrl("button.qml")); + if (!leftQuick->testAttribute(Qt::WA_AcceptTouchEvents)) + QSKIP("irrelevant on non-touch platforms"); + QQuickWidget *rightQuick = new QQuickWidget; rightQuick->setSource(testFileUrl("button.qml")); |
