diff options
Diffstat (limited to 'tests/auto/quick')
89 files changed, 3433 insertions, 297 deletions
diff --git a/tests/auto/quick/drawingmodes/tst_drawingmodes.cpp b/tests/auto/quick/drawingmodes/tst_drawingmodes.cpp index afc66948b0..9bcc21c77d 100644 --- a/tests/auto/quick/drawingmodes/tst_drawingmodes.cpp +++ b/tests/auto/quick/drawingmodes/tst_drawingmodes.cpp @@ -69,6 +69,9 @@ private slots: void triangles(); void triangleStrip(); void triangleFan(); + +private: + bool isRunningOnRhi() const; }; class DrawingModeItem : public QQuickItem @@ -260,6 +263,9 @@ void tst_drawingmodes::lineLoop() || (QGuiApplication::platformName() == QLatin1String("minimal"))) QSKIP("Skipping due to grabWindow not functional on offscreen/minimimal platforms"); + if (isRunningOnRhi()) + QSKIP("Line loops are not supported by some modern graphics APIs - skipping test"); + QImage fb = runTest("DrawingModes.qml"); QCOMPARE(fb.width(), 200); @@ -350,6 +356,9 @@ void tst_drawingmodes::triangleFan() || (QGuiApplication::platformName() == QLatin1String("minimal"))) QSKIP("Skipping due to grabWindow not functional on offscreen/minimimal platforms"); + if (isRunningOnRhi()) + QSKIP("Triangle fans are not supported by some modern graphics APIs - skipping test"); + QImage fb = runTest("DrawingModes.qml"); QCOMPARE(fb.width(), 200); @@ -368,6 +377,23 @@ void tst_drawingmodes::triangleFan() QVERIFY(!hasPixelAround(fb, 37, 100)); } +bool tst_drawingmodes::isRunningOnRhi() const +{ + static bool retval = false; + static bool decided = false; + if (!decided) { + decided = true; + QQuickView dummy; + dummy.show(); + if (QTest::qWaitForWindowExposed(&dummy)) { + QSGRendererInterface::GraphicsApi api = dummy.rendererInterface()->graphicsApi(); + retval = QSGRendererInterface::isApiRhiBased(api); + } + dummy.hide(); + } + return retval; +} + QTEST_MAIN(tst_drawingmodes) diff --git a/tests/auto/quick/examples/tst_examples.cpp b/tests/auto/quick/examples/tst_examples.cpp index 9b3fa8fd2c..fdefa855e4 100644 --- a/tests/auto/quick/examples/tst_examples.cpp +++ b/tests/auto/quick/examples/tst_examples.cpp @@ -74,6 +74,7 @@ tst_examples::tst_examples() { // Add files to exclude here excludedFiles << "snippets/qml/listmodel/listmodel.qml"; //Just a ListModel, no root QQuickItem + excludedFiles << "snippets/qml/tablemodel/fruit-example-delegatechooser.qml"; // Requires QtQuick.Controls import. // Add directories you want excluded here excludedDirs << "shared"; //Not an example diff --git a/tests/auto/quick/nodes/tst_nodestest.cpp b/tests/auto/quick/nodes/tst_nodestest.cpp index 79a9e5f757..bd5e6c6383 100644 --- a/tests/auto/quick/nodes/tst_nodestest.cpp +++ b/tests/auto/quick/nodes/tst_nodestest.cpp @@ -39,7 +39,7 @@ #include <QtQuick/qsgsimplerectnode.h> #include <QtQuick/qsgsimpletexturenode.h> -#include <QtQuick/private/qsgtexture_p.h> +#include <QtQuick/private/qsgplaintexture_p.h> #include <QtGui/private/qguiapplication_p.h> #include <QtGui/qpa/qplatformintegration.h> @@ -99,7 +99,10 @@ void NodesTest::initTestCase() auto rc = renderLoop->createRenderContext(renderLoop->sceneGraphContext()); renderContext = static_cast<QSGDefaultRenderContext *>(rc); QVERIFY(renderContext); - renderContext->initialize(context); + QSGDefaultRenderContext::InitParams rcParams; + rcParams.openGLContext = context; + rcParams.initialSurfacePixelSize = QSize(512, 512); // dummy, make up something + renderContext->initialize(&rcParams); QVERIFY(renderContext->isValid()); } diff --git a/tests/auto/quick/nokeywords/tst_nokeywords.cpp b/tests/auto/quick/nokeywords/tst_nokeywords.cpp index e6655589a3..c0b66f1c3f 100644 --- a/tests/auto/quick/nokeywords/tst_nokeywords.cpp +++ b/tests/auto/quick/nokeywords/tst_nokeywords.cpp @@ -49,7 +49,7 @@ #include <QtQuick/private/qsgcontext_p.h> #include <QtQuick/private/qsgcontextplugin_p.h> #if QT_CONFIG(opengl) -#include <QtQuick/private/qsgdefaultdistancefieldglyphcache_p.h> +#include <QtQuick/private/qsgopengldistancefieldglyphcache_p.h> #include <QtQuick/private/qsgdefaultglyphnode_p.h> #include <QtQuick/private/qsgdefaultinternalimagenode_p.h> #include <QtQuick/private/qsgdefaultinternalrectanglenode_p.h> @@ -65,9 +65,16 @@ #include <QtQuick/private/qsgrendernode_p.h> #include <QtQuick/private/qsgtexturematerial_p.h> #include <QtQuick/private/qsgtexture_p.h> +#include <QtQuick/private/qsgplaintexture_p.h> #include <QtQuick/private/qsgthreadedrenderloop_p.h> #include <QtQuick/private/qsgwindowsrenderloop_p.h> +#include <QtQuick/private/qsgrhiatlastexture_p.h> +#include <QtQuick/private/qsgrhidistancefieldglyphcache_p.h> +#include <QtQuick/private/qsgrhilayer_p.h> +#include <QtQuick/private/qsgrhishadereffectnode_p.h> +#include <QtQuick/private/qsgrhitextureglyphcache_p.h> + #undef signals #undef slots #undef emit diff --git a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp index cd18580ccf..35fed99e8b 100644 --- a/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp +++ b/tests/auto/quick/pointerhandlers/multipointtoucharea_interop/tst_multipointtoucharea_interop.cpp @@ -137,7 +137,6 @@ void tst_MptaInterop::touchDrag() // TODO touchesThenPinch_data with press/release sequences somehow: vectors of touchpoint IDs? or a string representation? void tst_MptaInterop::touchesThenPinch() { - const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); QScopedPointer<QQuickView> windowPtr; createView(windowPtr, "pinchDragMPTA.qml"); QQuickView * window = windowPtr.data(); diff --git a/tests/auto/quick/pointerhandlers/pointerhandlers.pro b/tests/auto/quick/pointerhandlers/pointerhandlers.pro index 950d6835eb..4d6311bdb2 100644 --- a/tests/auto/quick/pointerhandlers/pointerhandlers.pro +++ b/tests/auto/quick/pointerhandlers/pointerhandlers.pro @@ -10,4 +10,5 @@ qtConfig(private_tests) { qquickpointerhandler \ qquickpointhandler \ qquicktaphandler \ + qquickwheelhandler \ } diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/data/snapMode.qml b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/snapMode.qml new file mode 100644 index 0000000000..d6eb791700 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/data/snapMode.qml @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 + +Item { + id: root + objectName: "snapMode" + width: 640 + height: 480 + + Rectangle { + id: rect1 + objectName: "rect1" + width: 90 + height: 100 + x: 100 + y: 100 + color: "teal" + + Rectangle { + width: parent.width/2 + height: parent.width/2 + x: width/2 + y: -x + color: dragHandler1.active ? "red" : "salmon" + + DragHandler { + id: dragHandler1 + objectName: "dragHandler1" + target: rect1 + } + } + } + + + Rectangle { + id: rect2 + objectName: "rect2" + width: 90 + height: 100 + x: 200 + y: 100 + color: "teal" + + DragHandler { + id: dragHandler2 + objectName: "dragHandler2" + target: rect2b + } + + Rectangle { + id: rect2b + width: parent.width/2 + height: parent.width/2 + anchors.horizontalCenter: parent.horizontalCenter + y: -width/2 + color: dragHandler2.active ? "red" : "salmon" + } + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/qquickdraghandler.pro b/tests/auto/quick/pointerhandlers/qquickdraghandler/qquickdraghandler.pro index 42c4e46c4f..6258fb9392 100644 --- a/tests/auto/quick/pointerhandlers/qquickdraghandler/qquickdraghandler.pro +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/qquickdraghandler.pro @@ -19,3 +19,4 @@ OTHER_FILES += data/DragAnywhereSlider.qml \ data/grabberstate.qml \ data/multipleSliders.qml \ data/reparenting.qml \ + data/snapMode.qml \ diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp index fb0192893f..66314f88a2 100644 --- a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp +++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp @@ -57,6 +57,8 @@ private slots: void mouseDrag_data(); void mouseDrag(); void dragFromMargin(); + void snapMode_data(); + void snapMode(); void touchDragMulti(); void touchDragMultiSliders_data(); void touchDragMultiSliders(); @@ -312,6 +314,78 @@ void tst_DragHandler::dragFromMargin() // QTBUG-74966 QCOMPARE(dragHandler->centroid().pressedButtons(), Qt::NoButton); } +void tst_DragHandler::snapMode_data() +{ + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QTest::addColumn<QString>("subTree"); + QTest::addColumn<int>("snapMode"); + QTest::addColumn<QPoint>("startDragPos"); + QTest::addColumn<QPoint>("dragMovement"); + QTest::addColumn<QPoint>("expectedMovement"); + + struct TestEntry { + const char *desc; + const char *subTree; + QQuickDragHandler::SnapMode mode; + QPoint startDragPos; + QPoint dragMovement; + QPoint expectedMovement; + }; + + TestEntry testdata[] = { + {"outside the target", "rect1", QQuickDragHandler::SnapAuto, QPoint(45, -10), QPoint(dragThreshold*2, 0), QPoint(dragThreshold*2, 0)}, + {"inside the target", "rect1", QQuickDragHandler::SnapAuto, QPoint(45, 10), QPoint(dragThreshold*2, 0), QPoint(dragThreshold*2, 0)}, + {"outside the target", "rect1", QQuickDragHandler::SnapAlways, QPoint(45, -10), QPoint(dragThreshold*2, 0), QPoint(dragThreshold*2, -50-10)}, + {"outside the target", "rect1", QQuickDragHandler::NoSnap, QPoint(45, -10), QPoint(dragThreshold*2, 0), QPoint(dragThreshold*2, 0)}, + {"outside the target", "rect1", QQuickDragHandler::SnapIfPressedOutsideTarget, QPoint(45, -10), QPoint(dragThreshold*2, 0), QPoint(dragThreshold*2, -50-10)}, + {"inside the target", "rect1", QQuickDragHandler::SnapIfPressedOutsideTarget, QPoint(45, 10), QPoint(dragThreshold*2, 0), QPoint(dragThreshold*2, 0)}, + //targets y pos moves from -25 to (25 + dragThreshold*2) because of snapping to center: + {"outside target, should snap", "rect2", QQuickDragHandler::SnapAuto, QPoint(45, 50), QPoint(0, dragThreshold*2), QPoint(0, 25 + 25 + dragThreshold*2)}, + {"inside target, shouldn't snap", "rect2", QQuickDragHandler::SnapAuto, QPoint(45, 10), QPoint(0, dragThreshold*2), QPoint(0, dragThreshold*2)} + }; + + for (const TestEntry& e : testdata) { + const QMetaEnum menum = QMetaEnum::fromType<QQuickDragHandler::SnapMode>(); + const QString dataTag = QString::fromLatin1("%1, %2, %3").arg(e.subTree).arg(menum.valueToKey(e.mode)).arg(e.desc); + QTest::newRow(dataTag.toUtf8().constData()) << e.subTree << (int)e.mode + << e.startDragPos << e.dragMovement << e.expectedMovement; + } +} + +void tst_DragHandler::snapMode() +{ + QFETCH(QString, subTree); + QFETCH(QPoint, startDragPos); + QFETCH(QPoint, dragMovement); + QFETCH(int, snapMode); + QFETCH(QPoint, expectedMovement); + + QScopedPointer<QQuickView> windowPtr; + createView(windowPtr, "snapMode.qml"); + QQuickView * window = windowPtr.data(); + + QQuickItem *rect1 = window->rootObject()->findChild<QQuickItem*>(subTree); + QVERIFY(rect1); + QQuickItem *rect1b = rect1->childItems().first(); + QVERIFY(rect1b); + QQuickDragHandler *dragHandler1 = rect1->findChild<QQuickDragHandler*>(); + QVERIFY(dragHandler1); + dragHandler1->setSnapMode((QQuickDragHandler::SnapMode)snapMode); + QQuickItem *dragTarget = dragHandler1->target(); + QPointF oldTargetPos = dragTarget->position(); + + QPoint p1 = rect1->mapToScene(QPointF(startDragPos)).toPoint(); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1); + QVERIFY(!dragHandler1->active()); + p1 += dragMovement; + QTest::mouseMove(window, p1); + QTRY_VERIFY(dragHandler1->active()); + QCOMPARE(dragTarget->position(), oldTargetPos + expectedMovement); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_VERIFY(!dragHandler1->active()); + QCOMPARE(dragHandler1->centroid().pressedButtons(), Qt::NoButton); +} + void tst_DragHandler::touchDragMulti() { const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); diff --git a/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/nested.qml b/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/nested.qml new file mode 100644 index 0000000000..49e44f2b1f --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/nested.qml @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.14 + +Rectangle { + width: 320; height: 240 + color: "lightsteelblue"; antialiasing: true + border.color: outerWheelHandler.active ? "red" : "white" + + WheelHandler { + id: outerWheelHandler + objectName: "outerWheelHandler" + property: "x" + } + + Rectangle { + width: 120; height: 120; x: 100; y: 60 + color: "beige"; antialiasing: true + border.color: innerWheelHandler.active ? "red" : "white" + + WheelHandler { + id: innerWheelHandler + objectName: "innerWheelHandler" + // TODO should ideally deactivate because events go to the outer handler, not because of timeout + activeTimeout: 0.5 + property: "x" + } + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/rectWheel.qml b/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/rectWheel.qml new file mode 100644 index 0000000000..d4875d5313 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickwheelhandler/data/rectWheel.qml @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.14 + +Rectangle { + width: 320; height: 240 + color: "green"; antialiasing: true + + Rectangle { + width: 100; height: 2; anchors.centerIn: parent + Rectangle { + width: 2; height: 100; anchors.centerIn: parent + } + } + + WheelHandler { + activeTimeout: 0.5 + } +} diff --git a/tests/auto/quick/pointerhandlers/qquickwheelhandler/qquickwheelhandler.pro b/tests/auto/quick/pointerhandlers/qquickwheelhandler/qquickwheelhandler.pro new file mode 100644 index 0000000000..7509e38dd3 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickwheelhandler/qquickwheelhandler.pro @@ -0,0 +1,14 @@ +CONFIG += testcase +TARGET = tst_qquickwheelhandler +macos:CONFIG -= app_bundle + +SOURCES += tst_qquickwheelhandler.cpp +OTHER_FILES = \ + data/rectWheel.qml \ + +include (../../../shared/util.pri) +include (../../shared/util.pri) + +TESTDATA = data/* + +QT += core-private gui-private qml-private quick-private testlib diff --git a/tests/auto/quick/pointerhandlers/qquickwheelhandler/tst_qquickwheelhandler.cpp b/tests/auto/quick/pointerhandlers/qquickwheelhandler/tst_qquickwheelhandler.cpp new file mode 100644 index 0000000000..2abf2ea8c3 --- /dev/null +++ b/tests/auto/quick/pointerhandlers/qquickwheelhandler/tst_qquickwheelhandler.cpp @@ -0,0 +1,344 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <QtTest/QSignalSpy> +#include <QtGui/QStyleHints> +#include <qpa/qwindowsysteminterface.h> +#include <private/qquickwheelhandler_p.h> +#include <QtQuick/private/qquickrectangle_p.h> +#include <QtQuick/qquickview.h> +#include <QtQml/qqmlcontext.h> +#include "../../../shared/util.h" +#include "../../shared/viewtestutil.h" + +Q_LOGGING_CATEGORY(lcPointerTests, "qt.quick.pointer.tests") + +class tst_QQuickWheelHandler: public QQmlDataTest +{ + Q_OBJECT +public: + tst_QQuickWheelHandler() { } + +private slots: + void singleHandler_data(); + void singleHandler(); + void nestedHandler_data(); + void nestedHandler(); + +private: + void sendWheelEvent(QQuickView &window, QPoint pos, QPoint angleDelta, + QPoint pixelDelta = QPoint(), Qt::KeyboardModifiers modifiers = Qt::NoModifier, + Qt::ScrollPhase phase = Qt::NoScrollPhase, bool inverted = false); +}; + +void tst_QQuickWheelHandler::sendWheelEvent(QQuickView &window, QPoint pos, QPoint angleDelta, + QPoint pixelDelta, Qt::KeyboardModifiers modifiers, Qt::ScrollPhase phase, bool inverted) +{ + QWheelEvent wheelEvent(pos, window.mapToGlobal(pos), pixelDelta, angleDelta, + Qt::NoButton, modifiers, phase, inverted); + QGuiApplication::sendEvent(&window, &wheelEvent); + qApp->processEvents(); +} + +void tst_QQuickWheelHandler::singleHandler_data() +{ + // handler properties + QTest::addColumn<Qt::Orientation>("orientation"); + QTest::addColumn<bool>("invertible"); + QTest::addColumn<int>("rotationScale"); + QTest::addColumn<QString>("property"); + QTest::addColumn<qreal>("targetScaleMultiplier"); + QTest::addColumn<bool>("targetTransformAroundCursor"); + // event + QTest::addColumn<QPoint>("eventPos"); + QTest::addColumn<QPoint>("eventAngleDelta"); + QTest::addColumn<QPoint>("eventPixelDelta"); + QTest::addColumn<Qt::KeyboardModifiers>("eventModifiers"); + QTest::addColumn<bool>("eventPhases"); + QTest::addColumn<bool>("eventInverted"); + // result + QTest::addColumn<QPoint>("expectedPosition"); + QTest::addColumn<qreal>("expectedScale"); + QTest::addColumn<int>("expectedRotation"); + + // move the item + QTest::newRow("vertical wheel angle delta to adjust x") + << Qt::Vertical << false << 1 << "x" << 1.5 << true + << QPoint(160, 120) << QPoint(-360, 120) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << false + << QPoint(15, 0) << 1.0 << 0; + QTest::newRow("horizontal wheel angle delta to adjust y") + << Qt::Horizontal << false << 1 << "y" << 1.5 << false + << QPoint(160, 120) << QPoint(-360, 120) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << false + << QPoint(0, -45) << 1.0 << 0; + QTest::newRow("vertical wheel angle delta to adjust y, amplified and inverted") + << Qt::Vertical << true << 4 << "y" << 1.5 << true + << QPoint(160, 120) << QPoint(60, 60) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << true + << QPoint(0, 30) << 1.0 << 0; + QTest::newRow("horizontal wheel angle delta to adjust x, amplified and reversed") + << Qt::Horizontal << false << -4 << "x" << 1.5 << false + << QPoint(160, 120) << QPoint(60, 60) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << false + << QPoint(-30, 0) << 1.0 << 0; + QTest::newRow("vertical wheel pixel delta to adjust x") + << Qt::Vertical << false << 1 << "x" << 1.5 << true + << QPoint(160, 120) << QPoint(-360, 120) << QPoint(20, 20) << Qt::KeyboardModifiers(Qt::NoModifier) << true << false + << QPoint(20, 0) << 1.0 << 0; + QTest::newRow("horizontal wheel pixel delta to adjust y") + << Qt::Horizontal << false << 1 << "y" << 1.5 << false + << QPoint(160, 120) << QPoint(-360, 120) << QPoint(20, 20) << Qt::KeyboardModifiers(Qt::NoModifier) << true << false + << QPoint(0, 20) << 1.0 << 0; + QTest::newRow("vertical wheel pixel delta to adjust y, amplified and inverted") + << Qt::Vertical << true << 4 << "y" << 1.5 << true + << QPoint(160, 120) << QPoint(60, 60) << QPoint(20, 20) << Qt::KeyboardModifiers(Qt::NoModifier) << true << true + << QPoint(0, 80) << 1.0 << 0; + QTest::newRow("horizontal wheel pixel delta to adjust x, amplified and reversed") + << Qt::Horizontal << false << -4 << "x" << 1.5 << false + << QPoint(160, 120) << QPoint(60, 60) << QPoint(20, 20) << Qt::KeyboardModifiers(Qt::NoModifier) << true << false + << QPoint(-80, 0) << 1.0 << 0; + + // scale the item + QTest::newRow("vertical wheel angle delta to adjust scale") + << Qt::Vertical << false << 1 << "scale" << 1.5 << true + << QPoint(50, 32) << QPoint(360, 120) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << false + << QPoint(55, 44) << 1.5 << 0; + QTest::newRow("horizontal wheel angle delta to adjust scale, amplified and reversed, don't adjust position") + << Qt::Horizontal << false << -2 << "scale" << 1.5 << false + << QPoint(50, 32) << QPoint(-240, 360) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << false + << QPoint(0, 0) << 5.0625 << 0; + + // rotate the item + QTest::newRow("vertical wheel angle delta to adjust rotation") + << Qt::Vertical << false << 1 << "rotation" << 1.5 << true + << QPoint(50, 32) << QPoint(360, -120) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << false + << QPoint(19, -31) << 1.0 << -15; + QTest::newRow("horizontal wheel angle delta to adjust rotation, amplified and reversed, don't adjust position") + << Qt::Horizontal << false << -2 << "rotation" << 1.5 << false + << QPoint(80, 80) << QPoint(240, 360) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << false + << QPoint(0, 0) << 1.0 << -60; +} + +void tst_QQuickWheelHandler::singleHandler() +{ + // handler properties + QFETCH(Qt::Orientation, orientation); + QFETCH(bool, invertible); + QFETCH(int, rotationScale); + QFETCH(QString, property); + QFETCH(qreal, targetScaleMultiplier); + QFETCH(bool, targetTransformAroundCursor); + // event + QFETCH(QPoint, eventPos); + QFETCH(QPoint, eventAngleDelta); + QFETCH(QPoint, eventPixelDelta); + QFETCH(Qt::KeyboardModifiers, eventModifiers); + QFETCH(bool, eventPhases); + QFETCH(bool, eventInverted); + // result + QFETCH(QPoint, expectedPosition); + QFETCH(qreal, expectedScale); + QFETCH(int, expectedRotation); + + QQuickView window; + QByteArray errorMessage; + QVERIFY2(QQuickTest::initView(window, testFileUrl("rectWheel.qml"), true, &errorMessage), errorMessage.constData()); + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + + QQuickItem *rect = window.rootObject(); + QVERIFY(rect != nullptr); + QQuickWheelHandler *handler = rect->findChild<QQuickWheelHandler*>(); + QVERIFY(handler != nullptr); + handler->setOrientation(orientation); + handler->setInvertible(invertible); + handler->setRotationScale(rotationScale); + handler->setProperty(property); + handler->setTargetScaleMultiplier(targetScaleMultiplier); + handler->setTargetTransformAroundCursor(targetTransformAroundCursor); + QSignalSpy activeChangedSpy(handler, SIGNAL(activeChanged())); + + if (eventPhases) { + sendWheelEvent(window, eventPos, QPoint(), QPoint(), eventModifiers, Qt::ScrollBegin, eventInverted); + sendWheelEvent(window, eventPos, eventAngleDelta, eventPixelDelta, eventModifiers, Qt::ScrollUpdate, eventInverted); + } else { + sendWheelEvent(window, eventPos, eventAngleDelta, eventPixelDelta, eventModifiers, Qt::NoScrollPhase, eventInverted); + } + QCOMPARE(rect->position().toPoint(), expectedPosition); + QCOMPARE(activeChangedSpy.count(), 1); + QCOMPARE(handler->active(), true); + QCOMPARE(rect->scale(), expectedScale); + QCOMPARE(rect->rotation(), expectedRotation); + if (!eventPhases) { + QTRY_COMPARE(handler->active(), false); + QCOMPARE(activeChangedSpy.count(), 2); + } + + // restore by rotating backwards + if (eventPhases) { + sendWheelEvent(window, eventPos, eventAngleDelta * -1, eventPixelDelta * -1, eventModifiers, Qt::ScrollUpdate, eventInverted); + sendWheelEvent(window, eventPos, QPoint(), QPoint(), eventModifiers, Qt::ScrollEnd, eventInverted); + } else { + sendWheelEvent(window, eventPos, eventAngleDelta * -1, eventPixelDelta * -1, eventModifiers, Qt::NoScrollPhase, eventInverted); + } + QCOMPARE(activeChangedSpy.count(), eventPhases ? 2 : 3); + QCOMPARE(handler->active(), !eventPhases); + QCOMPARE(rect->position().toPoint(), QPoint(0, 0)); + QCOMPARE(rect->scale(), 1); + QCOMPARE(rect->rotation(), 0); +} + +void tst_QQuickWheelHandler::nestedHandler_data() +{ + // handler properties + QTest::addColumn<Qt::Orientation>("orientation"); + QTest::addColumn<bool>("invertible"); + QTest::addColumn<int>("rotationScale"); + QTest::addColumn<QString>("property"); + QTest::addColumn<qreal>("targetScaleMultiplier"); + QTest::addColumn<bool>("targetTransformAroundCursor"); + // event + QTest::addColumn<QPoint>("eventPos"); + QTest::addColumn<QPoint>("eventAngleDelta"); + QTest::addColumn<QPoint>("eventPixelDelta"); + QTest::addColumn<Qt::KeyboardModifiers>("eventModifiers"); + QTest::addColumn<bool>("eventPhases"); + QTest::addColumn<bool>("eventInverted"); + QTest::addColumn<int>("eventCount"); + // result: inner handler + QTest::addColumn<QPoint>("innerPosition"); + QTest::addColumn<qreal>("innerScale"); + QTest::addColumn<int>("innerRotation"); + // result: outer handler + QTest::addColumn<QPoint>("outerPosition"); + QTest::addColumn<qreal>("outerScale"); + QTest::addColumn<int>("outerRotation"); + + // move the item + QTest::newRow("vertical wheel angle delta to adjust x") + << Qt::Vertical << false << 1 << "x" << 1.5 << true + << QPoint(160, 120) << QPoint(120, 120) << QPoint() << Qt::KeyboardModifiers(Qt::NoModifier) << false << false << 10 + << QPoint(175,60) << 1.0 << 0 + << QPoint(75, 0) << 1.0 << 0; + QTest::newRow("horizontal wheel pixel delta to adjust y") + << Qt::Horizontal << false << 1 << "y" << 1.5 << false + << QPoint(160, 120) << QPoint(120, 120) << QPoint(50, 50) << Qt::KeyboardModifiers(Qt::NoModifier) << true << false << 4 + << QPoint(100, 160) << 1.0 << 0 + << QPoint(0, 100) << 1.0 << 0; +} + +void tst_QQuickWheelHandler::nestedHandler() +{ + // handler properties + QFETCH(Qt::Orientation, orientation); + QFETCH(bool, invertible); + QFETCH(int, rotationScale); + QFETCH(QString, property); + QFETCH(qreal, targetScaleMultiplier); + QFETCH(bool, targetTransformAroundCursor); + // event + QFETCH(QPoint, eventPos); + QFETCH(QPoint, eventAngleDelta); + QFETCH(QPoint, eventPixelDelta); + QFETCH(Qt::KeyboardModifiers, eventModifiers); + QFETCH(bool, eventPhases); + QFETCH(bool, eventInverted); + QFETCH(int, eventCount); + // result: inner handler + QFETCH(QPoint, innerPosition); + QFETCH(qreal, innerScale); + QFETCH(int, innerRotation); + // result: outer handler + QFETCH(QPoint, outerPosition); + QFETCH(qreal, outerScale); + QFETCH(int, outerRotation); + + QQuickView window; + QByteArray errorMessage; + QVERIFY2(QQuickTest::initView(window, testFileUrl("nested.qml"), true, &errorMessage), errorMessage.constData()); + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + + QQuickItem *outerRect = window.rootObject(); + QVERIFY(outerRect != nullptr); + QQuickWheelHandler *outerHandler = outerRect->findChild<QQuickWheelHandler*>("outerWheelHandler"); + QVERIFY(outerHandler != nullptr); + QQuickWheelHandler *innerHandler = outerRect->findChild<QQuickWheelHandler*>("innerWheelHandler"); + QVERIFY(innerHandler != nullptr); + QQuickItem *innerRect = innerHandler->parentItem(); + QVERIFY(innerRect != nullptr); + innerHandler->setOrientation(orientation); + innerHandler->setInvertible(invertible); + innerHandler->setRotationScale(rotationScale); + innerHandler->setProperty(property); + innerHandler->setTargetScaleMultiplier(targetScaleMultiplier); + innerHandler->setTargetTransformAroundCursor(targetTransformAroundCursor); + outerHandler->setOrientation(orientation); + outerHandler->setInvertible(invertible); + outerHandler->setRotationScale(rotationScale); + outerHandler->setProperty(property); + outerHandler->setTargetScaleMultiplier(targetScaleMultiplier); + outerHandler->setTargetTransformAroundCursor(targetTransformAroundCursor); + QSignalSpy innerActiveChangedSpy(innerHandler, SIGNAL(activeChanged())); + QSignalSpy outerActiveChangedSpy(outerHandler, SIGNAL(activeChanged())); + + if (eventPhases) + sendWheelEvent(window, eventPos, QPoint(), QPoint(), eventModifiers, Qt::ScrollBegin, eventInverted); + for (int i = 0; i < eventCount; ++i) + sendWheelEvent(window, eventPos, eventAngleDelta, eventPixelDelta, eventModifiers, + (eventPhases ? Qt::ScrollUpdate : Qt::NoScrollPhase), eventInverted); + QCOMPARE(innerRect->position().toPoint(), innerPosition); + + /* + If outer is activated, maybe inner should be deactivated? But the event + doesn't get delivered to inner anymore, so it doesn't find out that + it's no longer getting events. It will get deactivated after the + timeout, just as if the user stopped scrolling. + + This situation is similar to QTBUG-50199, but it's questionable whether + that was really so important. So far in Qt Quick, if you move the mouse + while wheel momentum continues, or if the item moves out from under the + mouse, a different item starts getting the events immediately. In + non-Qt applications on most OSes, that's quite normal. + */ + // QCOMPARE(innerActiveChangedSpy.count(), 2); + // QCOMPARE(innerHandler->active(), false); + QCOMPARE(innerRect->scale(), innerScale); + QCOMPARE(innerRect->rotation(), innerRotation); + QCOMPARE(outerRect->position().toPoint(), outerPosition); + QCOMPARE(outerActiveChangedSpy.count(), 1); + QCOMPARE(outerHandler->active(), true); + QCOMPARE(outerRect->scale(), outerScale); + QCOMPARE(outerRect->rotation(), outerRotation); + if (!eventPhases) { + QTRY_COMPARE(outerHandler->active(), false); + QCOMPARE(outerActiveChangedSpy.count(), 2); + } +} + +QTEST_MAIN(tst_QQuickWheelHandler) + +#include "tst_qquickwheelhandler.moc" diff --git a/tests/auto/quick/propertyrequirements/tst_propertyrequirements.cpp b/tests/auto/quick/propertyrequirements/tst_propertyrequirements.cpp index 34be4d98b4..ca348eea03 100644 --- a/tests/auto/quick/propertyrequirements/tst_propertyrequirements.cpp +++ b/tests/auto/quick/propertyrequirements/tst_propertyrequirements.cpp @@ -28,7 +28,6 @@ #include <qtest.h> #include <QtQml/qqmlcomponent.h> #include <QtQml/qqmlengine.h> -#include <QtQml/private/qhashedstring_p.h> #include <QtQml/private/qqmlmetatype_p.h> #include <QtCore/QDebug> #include <QtCore/QHash> @@ -121,7 +120,7 @@ void tst_PropertyRequirements::constantOrNotifyableFull() } static const QString messagePattern("\nProperty %1 neither CONSTANT nor NOTIFYable. Affected types:\n\t%2"); - QStringList occurrencesList = occurrences.toList(); + QStringList occurrencesList = occurrences.values(); occurrencesList.sort(); messages.append(messagePattern.arg(it.key(), occurrencesList.join("\n\t"))); @@ -159,7 +158,8 @@ void tst_PropertyRequirements::testAllQmlTypes(TestDepth testDepth, FailuresByPr testQmlType(testDepth, qmlType, failuresByProperty); } } - seenTypes.unite(QSet<QString>::fromList(QQmlMetaType::qmlTypeNames())); + const auto &typeNameList = QQmlMetaType::qmlTypeNames(); + seenTypes.unite(QSet<QString>(typeNameList.cbegin(), typeNameList.cend())); } } diff --git a/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp b/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp index c5fdb6c1b9..d1f6d67aa1 100644 --- a/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp +++ b/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp @@ -338,7 +338,7 @@ void tst_QQuickAccessible::basicPropertiesTest() QCOMPARE(text2->rect().y(), item->rect().y() + 40); QCOMPARE(text2->role(), QAccessible::StaticText); QCOMPARE(item->indexOfChild(text2), 1); - QCOMPARE(text2->state().editable, 0); + QCOMPARE(text2->state().editable, 0u); QCOMPARE(text2->state().readOnly, 1); QCOMPARE(iface->indexOfChild(text2), -1); diff --git a/tests/auto/quick/qquickanchors/tst_qquickanchors.cpp b/tests/auto/quick/qquickanchors/tst_qquickanchors.cpp index 77fa1292c4..128a154492 100644 --- a/tests/auto/quick/qquickanchors/tst_qquickanchors.cpp +++ b/tests/auto/quick/qquickanchors/tst_qquickanchors.cpp @@ -364,7 +364,7 @@ void tst_qquickanchors::reset() const QMetaObject *meta = itemPrivate->anchors()->metaObject(); QMetaProperty p = meta->property(meta->indexOfProperty(side.toUtf8().constData())); - QVERIFY(p.write(itemPrivate->anchors(), qVariantFromValue(anchorLine))); + QVERIFY(p.write(itemPrivate->anchors(), QVariant::fromValue(anchorLine))); QCOMPARE(itemPrivate->anchors()->usedAnchors().testFlag(anchor), true); QVERIFY(p.reset(itemPrivate->anchors())); @@ -423,7 +423,7 @@ void tst_qquickanchors::nullItem() QMetaProperty p = meta->property(meta->indexOfProperty(side.toUtf8().constData())); QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML Item: Cannot anchor to a null item."); - QVERIFY(p.write(itemPrivate->anchors(), qVariantFromValue(anchor))); + QVERIFY(p.write(itemPrivate->anchors(), QVariant::fromValue(anchor))); delete item; } diff --git a/tests/auto/quick/qquickanimatedimage/data/currentframe.qml b/tests/auto/quick/qquickanimatedimage/data/currentframe.qml new file mode 100644 index 0000000000..b679da2a99 --- /dev/null +++ b/tests/auto/quick/qquickanimatedimage/data/currentframe.qml @@ -0,0 +1,13 @@ +import QtQuick 2.0 + +AnimatedImage { + property int currentFrameChangeCount: 0 + property int frameChangeCount: 0 + source: "stickman.gif" + onCurrentFrameChanged: if (currentFrame > 0) ++currentFrameChangeCount; + onFrameChanged: if (currentFrame > 0) ++frameChangeCount; + function scriptedSetCurrentFrame(frame) { + currentFrame = frame; + } +} + diff --git a/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp b/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp index 8026bafb9e..31c3fb9946 100644 --- a/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp +++ b/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp @@ -27,6 +27,7 @@ ****************************************************************************/ #include <qtest.h> #include <QtQml/qqmlengine.h> +#include <QtQml/qqmlexpression.h> #include <QtQml/qqmlcomponent.h> #include <QtQuick/qquickview.h> #include <QtQuick/private/qquickrectangle_p.h> @@ -40,6 +41,23 @@ Q_DECLARE_METATYPE(QQuickImageBase::Status) +template <typename T> static T evaluate(QObject *scope, const QString &expression) +{ + QQmlExpression expr(qmlContext(scope), scope, expression); + QVariant result = expr.evaluate(); + if (expr.hasError()) + qWarning() << expr.error().toString(); + return result.value<T>(); +} + +template <> void evaluate<void>(QObject *scope, const QString &expression) +{ + QQmlExpression expr(qmlContext(scope), scope, expression); + expr.evaluate(); + if (expr.hasError()) + qWarning() << expr.error().toString(); +} + class tst_qquickanimatedimage : public QQmlDataTest { Q_OBJECT @@ -68,6 +86,7 @@ private slots: void playingAndPausedChanges(); void noCaching(); void sourceChangesOnFrameChanged(); + void currentFrame(); }; void tst_qquickanimatedimage::cleanup() @@ -618,6 +637,33 @@ void tst_qquickanimatedimage::sourceChangesOnFrameChanged() qDeleteAll(images); } +void tst_qquickanimatedimage::currentFrame() +{ + QQuickView window; + window.setSource(testFileUrl("currentframe.qml")); + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + + QQuickAnimatedImage *anim = qobject_cast<QQuickAnimatedImage *>(window.rootObject()); + QVERIFY(anim); + QSignalSpy frameChangedSpy(anim, SIGNAL(frameChanged())); + QSignalSpy currentFrameChangedSpy(anim, SIGNAL(currentFrameChanged())); + + anim->setCurrentFrame(1); + QCOMPARE(anim->currentFrame(), 1); + QCOMPARE(frameChangedSpy.count(), 1); + QCOMPARE(currentFrameChangedSpy.count(), 1); + QCOMPARE(anim->property("currentFrameChangeCount"), 1); + QCOMPARE(anim->property("frameChangeCount"), 1); + + evaluate<void>(anim, "scriptedSetCurrentFrame(2)"); + QCOMPARE(anim->currentFrame(), 2); + QCOMPARE(frameChangedSpy.count(), 2); + QCOMPARE(currentFrameChangedSpy.count(), 2); + QCOMPARE(anim->property("currentFrameChangeCount"), 2); + QCOMPARE(anim->property("frameChangeCount"), 2); +} + QTEST_MAIN(tst_qquickanimatedimage) #include "tst_qquickanimatedimage.moc" diff --git a/tests/auto/quick/qquickanimations/qquickanimations.pro b/tests/auto/quick/qquickanimations/qquickanimations.pro index 1c5494a24a..94f694181d 100644 --- a/tests/auto/quick/qquickanimations/qquickanimations.pro +++ b/tests/auto/quick/qquickanimations/qquickanimations.pro @@ -8,7 +8,7 @@ macx:CONFIG -= app_bundle TESTDATA = data/* -QT += core-private gui-private qml-private quick-private testlib +QT += core-private gui-private qml-private quick-private testlib qmlmodels-private DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 OTHER_FILES += \ diff --git a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp index 17dfa4a3d7..48f779a490 100644 --- a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp +++ b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp @@ -30,7 +30,7 @@ #include <QtQml/qqmlcomponent.h> #include <QtQuick/qquickview.h> #include <QtQml/private/qqmltimer_p.h> -#include <QtQml/private/qqmllistmodel_p.h> +#include <QtQmlModels/private/qqmllistmodel_p.h> #include <QtQml/private/qanimationgroupjob_p.h> #include <QtQuick/private/qquickrectangle_p.h> #include <QtQuick/private/qquickitemanimation_p.h> diff --git a/tests/auto/quick/qquickbehaviors/data/delete.qml b/tests/auto/quick/qquickbehaviors/data/delete.qml new file mode 100644 index 0000000000..1bf0267b84 --- /dev/null +++ b/tests/auto/quick/qquickbehaviors/data/delete.qml @@ -0,0 +1,37 @@ +import QtQuick 2.12 + +Item { + visible: true + width: 640 + height: 480 + + Component.onCompleted: { + myLoader.active = false + } + + Loader { + id: myLoader + + active: true + sourceComponent: Item { + width: 100 + height: 100 + id: myPopup + + NumberAnimation { + id: anim + } + + Rectangle { + color: "black" + Component.onCompleted: { + opacity = 20 + } + + Behavior on opacity { + animation: anim + } + } + } + } +} diff --git a/tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp b/tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp index 6367f327da..64e32dcdfd 100644 --- a/tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp +++ b/tests/auto/quick/qquickbehaviors/tst_qquickbehaviors.cpp @@ -74,6 +74,7 @@ private slots: void aliasedProperty(); void innerBehaviorOverwritten(); void oneWay(); + void safeToDelete(); }; void tst_qquickbehaviors::simpleBehavior() @@ -647,6 +648,16 @@ void tst_qquickbehaviors::oneWay() QCOMPARE(myAnimation->isRunning(), false); } +// QTBUG-76749 +void tst_qquickbehaviors::safeToDelete() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("delete.qml")); + QVERIFY(c.create()); +} + + + QTEST_MAIN(tst_qquickbehaviors) #include "tst_qquickbehaviors.moc" diff --git a/tests/auto/quick/qquickborderimage/data/multi.ico b/tests/auto/quick/qquickborderimage/data/multi.ico Binary files differnew file mode 100644 index 0000000000..b748ceaa29 --- /dev/null +++ b/tests/auto/quick/qquickborderimage/data/multi.ico diff --git a/tests/auto/quick/qquickborderimage/data/multiframe.qml b/tests/auto/quick/qquickborderimage/data/multiframe.qml new file mode 100644 index 0000000000..8bd32da5a6 --- /dev/null +++ b/tests/auto/quick/qquickborderimage/data/multiframe.qml @@ -0,0 +1,8 @@ +import QtQuick 2.14 + +BorderImage { + source: "multi.ico" + border { left: 19; top: 19; right: 19; bottom: 19 } + width: 160; height: 160 + horizontalTileMode: BorderImage.Stretch +} diff --git a/tests/auto/quick/qquickborderimage/data/multiframeAsync.qml b/tests/auto/quick/qquickborderimage/data/multiframeAsync.qml new file mode 100644 index 0000000000..059e4becf3 --- /dev/null +++ b/tests/auto/quick/qquickborderimage/data/multiframeAsync.qml @@ -0,0 +1,9 @@ +import QtQuick 2.14 + +BorderImage { + source: "multi.ico" + asynchronous: true + border { left: 19; top: 19; right: 19; bottom: 19 } + width: 160; height: 160 + horizontalTileMode: BorderImage.Stretch +} diff --git a/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp b/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp index 9292e1886a..dc3a783600 100644 --- a/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp +++ b/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp @@ -46,6 +46,8 @@ #include "../../shared/util.h" #include "../shared/visualtestutil.h" +Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests") + Q_DECLARE_METATYPE(QQuickImageBase::Status) class tst_qquickborderimage : public QQmlDataTest @@ -79,6 +81,8 @@ private slots: #if QT_CONFIG(opengl) void borderImageMesh(); #endif + void multiFrame_data(); + void multiFrame(); private: QQmlEngine engine; @@ -601,6 +605,67 @@ void tst_qquickborderimage::borderImageMesh() qPrintable(errorMessage)); } #endif + +void tst_qquickborderimage::multiFrame_data() +{ + QTest::addColumn<QString>("qmlfile"); + QTest::addColumn<bool>("asynchronous"); + + QTest::addRow("default") << "multiframe.qml" << false; + QTest::addRow("async") << "multiframeAsync.qml" << true; +} + +void tst_qquickborderimage::multiFrame() +{ + if ((QGuiApplication::platformName() == QLatin1String("offscreen")) + || (QGuiApplication::platformName() == QLatin1String("minimal"))) + QSKIP("Skipping due to grabWindow not functional on offscreen/minimimal platforms"); + + QFETCH(QString, qmlfile); + QFETCH(bool, asynchronous); + Q_UNUSED(asynchronous) + + QQuickView view(testFileUrl(qmlfile)); + QQuickBorderImage *image = qobject_cast<QQuickBorderImage*>(view.rootObject()); + QVERIFY(image); + QSignalSpy countSpy(image, SIGNAL(frameCountChanged())); + QSignalSpy currentSpy(image, SIGNAL(currentFrameChanged())); + if (asynchronous) { + QCOMPARE(image->frameCount(), 0); + QTRY_COMPARE(image->frameCount(), 4); + QCOMPARE(countSpy.count(), 1); + } else { + QCOMPARE(image->frameCount(), 4); + } + QCOMPARE(image->currentFrame(), 0); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + QCoreApplication::processEvents(); // Process all queued events + + QImage contents = view.grabWindow(); + if (contents.width() < 160) + QSKIP("Skipping due to grabWindow not functional"); + + // The middle of the first frame looks blue, approximately qRgba(0x43, 0x7e, 0xd6, 0xff) + QColor color = contents.pixelColor(60, 60); + qCDebug(lcTests) << "expected bluish color, got" << color; + QVERIFY(color.redF() < 0.75); + QVERIFY(color.greenF() < 0.75); + QVERIFY(color.blueF() > 0.75); + + image->setCurrentFrame(1); + QTRY_COMPARE(image->status(), QQuickImageBase::Ready); + QCOMPARE(currentSpy.count(), 1); + QCOMPARE(image->currentFrame(), 1); + contents = view.grabWindow(); + // The middle of the second frame looks green, approximately qRgba(0x3a, 0xd2, 0x31, 0xff) + color = contents.pixelColor(60, 60); + qCDebug(lcTests) << "expected greenish color, got" << color; + QVERIFY(color.redF() < 0.75); + QVERIFY(color.green() > 0.75); + QVERIFY(color.blueF() < 0.75); +} + QTEST_MAIN(tst_qquickborderimage) #include "tst_qquickborderimage.moc" diff --git a/tests/auto/quick/qquickboundaryrule/data/dragHandler.qml b/tests/auto/quick/qquickboundaryrule/data/dragHandler.qml new file mode 100644 index 0000000000..c66fd76ff1 --- /dev/null +++ b/tests/auto/quick/qquickboundaryrule/data/dragHandler.qml @@ -0,0 +1,23 @@ +import QtQuick 2.14 +import Qt.labs.animation 1.0 + +Rectangle { + id: root + width: 240; height: 120 + color: "green" + + DragHandler { + id: dragHandler + yAxis.minimum: -1000 + xAxis.minimum: -1000 + onActiveChanged: if (!active) xbr.returnToBounds(); + } + + BoundaryRule on x { + id: xbr + minimum: -50 + maximum: 100 + minimumOvershoot: 40 + maximumOvershoot: 40 + } +} diff --git a/tests/auto/quick/qquickboundaryrule/qquickboundaryrule.pro b/tests/auto/quick/qquickboundaryrule/qquickboundaryrule.pro new file mode 100644 index 0000000000..ef43f4526a --- /dev/null +++ b/tests/auto/quick/qquickboundaryrule/qquickboundaryrule.pro @@ -0,0 +1,12 @@ +CONFIG += testcase +TARGET = tst_qquickboundaryrule +macx:CONFIG -= app_bundle + +SOURCES += tst_qquickboundaryrule.cpp + +include (../../shared/util.pri) +include (../shared/util.pri) + +TESTDATA = data/* + +QT += core-private gui-private qml-private quick-private testlib diff --git a/tests/auto/quick/qquickboundaryrule/tst_qquickboundaryrule.cpp b/tests/auto/quick/qquickboundaryrule/tst_qquickboundaryrule.cpp new file mode 100644 index 0000000000..44f1c9a2f9 --- /dev/null +++ b/tests/auto/quick/qquickboundaryrule/tst_qquickboundaryrule.cpp @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <QtTest/QtTest> +#include <qsignalspy.h> +#include <QtQml/qqmlengine.h> +#include <QtQml/qqmlcomponent.h> +#include <QtQuick/qquickview.h> +#include <QtQuick/private/qquickboundaryrule_p.h> +#include <QtQuick/private/qquickdraghandler_p.h> +#include "../../shared/util.h" +#include "../shared/viewtestutil.h" + +class tst_qquickboundaryrule : public QQmlDataTest +{ + Q_OBJECT +public: + tst_qquickboundaryrule() {} + +private slots: + void init() { qApp->processEvents(); } //work around animation timer bug (QTBUG-22865) + void dragHandler(); +}; + +void tst_qquickboundaryrule::dragHandler() +{ + QQuickView window; + QByteArray errorMessage; + QVERIFY2(QQuickTest::initView(window, testFileUrl("dragHandler.qml"), true, &errorMessage), errorMessage.constData()); + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + QQuickItem *target = window.rootObject(); + QVERIFY(target); + QQuickDragHandler *dragHandler = target->findChild<QQuickDragHandler*>(); + QVERIFY(dragHandler); + QQuickBoundaryRule *boundaryRule = target->findChild<QQuickBoundaryRule*>(); + QVERIFY(boundaryRule); + QSignalSpy overshootChangedSpy(boundaryRule, SIGNAL(currentOvershootChanged())); + + QPoint p1(10, 10); + QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, p1); + // unrestricted drag + p1 += QPoint(100, 0); + QTest::mouseMove(&window, p1); + QTRY_VERIFY(dragHandler->active()); + QCOMPARE(target->position().x(), 100); + QCOMPARE(boundaryRule->currentOvershoot(), 0); + QCOMPARE(boundaryRule->peakOvershoot(), 0); + QCOMPARE(overshootChangedSpy.count(), 0); + // restricted drag: halfway into overshoot + p1 += QPoint(20, 0); + QTest::mouseMove(&window, p1); + QCOMPARE(target->position().x(), 117.5); + QCOMPARE(boundaryRule->currentOvershoot(), 20); + QCOMPARE(boundaryRule->peakOvershoot(), 20); + QCOMPARE(overshootChangedSpy.count(), 1); + // restricted drag: maximum overshoot + p1 += QPoint(80, 0); + QTest::mouseMove(&window, p1); + QCOMPARE(target->position().x(), 140); + QCOMPARE(boundaryRule->currentOvershoot(), 100); + QCOMPARE(boundaryRule->peakOvershoot(), 100); + QCOMPARE(overshootChangedSpy.count(), 2); + // release and let it return to bounds + QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, p1); + QTRY_COMPARE(dragHandler->active(), false); + QTRY_COMPARE(overshootChangedSpy.count(), 3); + QCOMPARE(boundaryRule->currentOvershoot(), 0); + QCOMPARE(boundaryRule->peakOvershoot(), 0); + QCOMPARE(target->position().x(), 100); +} + +QTEST_MAIN(tst_qquickboundaryrule) + +#include "tst_qquickboundaryrule.moc" diff --git a/tests/auto/quick/qquickdrag/tst_qquickdrag.cpp b/tests/auto/quick/qquickdrag/tst_qquickdrag.cpp index d3d3505e85..f1288c2dbe 100644 --- a/tests/auto/quick/qquickdrag/tst_qquickdrag.cpp +++ b/tests/auto/quick/qquickdrag/tst_qquickdrag.cpp @@ -599,7 +599,7 @@ void tst_QQuickDrag::move() QCoreApplication::processEvents(); QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(&rightTarget)); QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(&rightTarget)); - QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.moveEvents, 0); + QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 1); QCOMPARE(outerTarget.moveEvents, 0); QCOMPARE(leftTarget .enterEvents, 0); QCOMPARE(leftTarget .leaveEvents, 0); QCOMPARE(leftTarget .moveEvents, 0); QCOMPARE(rightTarget.enterEvents, 1); QCOMPARE(rightTarget.leaveEvents, 0); QCOMPARE(rightTarget.moveEvents, 0); QCOMPARE(rightTarget.position.x(), qreal(5)); QCOMPARE(rightTarget.position.y(), qreal(15)); @@ -621,10 +621,10 @@ void tst_QQuickDrag::move() QCoreApplication::processEvents(); QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(&leftTarget)); QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(&leftTarget)); - QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.moveEvents, 1); + QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.moveEvents, 0); QCOMPARE(leftTarget .enterEvents, 0); QCOMPARE(leftTarget .leaveEvents, 0); QCOMPARE(leftTarget .moveEvents, 1); QCOMPARE(rightTarget.enterEvents, 0); QCOMPARE(rightTarget.leaveEvents, 0); QCOMPARE(rightTarget.moveEvents, 0); - QCOMPARE(outerTarget.position.x(), qreal(25)); QCOMPARE(outerTarget.position.y(), qreal(40)); + QCOMPARE(outerTarget.position.x(), qreal(60)); QCOMPARE(outerTarget.position.y(), qreal(50)); QCOMPARE(leftTarget.position.x(), qreal(25)); QCOMPARE(leftTarget.position.y(), qreal(5)); // Move out of all targets. @@ -633,7 +633,7 @@ void tst_QQuickDrag::move() QCoreApplication::processEvents(); QCOMPARE(evaluate<QObject *>(item, "Drag.target"), static_cast<QObject *>(nullptr)); QCOMPARE(evaluate<QObject *>(item, "dragTarget"), static_cast<QObject *>(nullptr)); - QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 1); QCOMPARE(outerTarget.moveEvents, 0); + QCOMPARE(outerTarget.enterEvents, 0); QCOMPARE(outerTarget.leaveEvents, 0); QCOMPARE(outerTarget.moveEvents, 0); QCOMPARE(leftTarget .enterEvents, 0); QCOMPARE(leftTarget .leaveEvents, 1); QCOMPARE(leftTarget .moveEvents, 0); QCOMPARE(rightTarget.enterEvents, 0); QCOMPARE(rightTarget.leaveEvents, 0); QCOMPARE(rightTarget.moveEvents, 0); diff --git a/tests/auto/quick/qquickdroparea/data/nested1.qml b/tests/auto/quick/qquickdroparea/data/nested1.qml new file mode 100644 index 0000000000..de6ac70d08 --- /dev/null +++ b/tests/auto/quick/qquickdroparea/data/nested1.qml @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.0 + +Item { + width: 200; height: 200 + property int outerEnterEvents: 0 + property int outerExitEvents: 0 + property int innerEnterEvents: 0 + property int innerExitEvents: 0 + + DropArea { + objectName: "outerDropArea" + x: 75; y: 75 + width: 100; height: 100 + Rectangle { + anchors.fill: parent + color: "green" + } + onEntered: ++outerEnterEvents + onExited: ++outerExitEvents + + DropArea { + objectName: "innerDropArea" + width: 50; height: 50 + Rectangle { + anchors.fill: parent + color: "blue" + } + onEntered: ++innerEnterEvents + onExited: ++innerExitEvents + } + } + + Rectangle { + width: 20; height: 20 + color: dragArea.pressed ? "red" : "brown" + Drag.active: dragArea.drag.active + MouseArea { + id: dragArea + objectName: "dragArea" + anchors.fill: parent + drag.target: parent + } + } +} diff --git a/tests/auto/quick/qquickdroparea/data/nested2.qml b/tests/auto/quick/qquickdroparea/data/nested2.qml new file mode 100644 index 0000000000..93630c3779 --- /dev/null +++ b/tests/auto/quick/qquickdroparea/data/nested2.qml @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.0 + +Item { + width: 200; height: 200 + property int outerEnterEvents: 0 + property int outerExitEvents: 0 + property int innerEnterEvents: 0 + property int innerExitEvents: 0 + + Rectangle { + x: 75; y: 75 + width: 100; height: 100 + color: "green" + DropArea { + objectName: "outerDropArea" + anchors.fill: parent + onEntered: ++outerEnterEvents + onExited: ++outerExitEvents + } + + Rectangle { + width: 50; height: 50 + color: "blue" + DropArea { + objectName: "innerDropArea" + anchors.fill: parent + onEntered: ++innerEnterEvents + onExited: ++innerExitEvents + } + } + } + + Rectangle { + width: 20; height: 20 + color: dragArea.pressed ? "red" : "brown" + Drag.active: dragArea.drag.active + MouseArea { + id: dragArea + objectName: "dragArea" + anchors.fill: parent + drag.target: parent + } + } +} diff --git a/tests/auto/quick/qquickdroparea/qquickdroparea.pro b/tests/auto/quick/qquickdroparea/qquickdroparea.pro index a34d5ad009..7a8fdef7b9 100644 --- a/tests/auto/quick/qquickdroparea/qquickdroparea.pro +++ b/tests/auto/quick/qquickdroparea/qquickdroparea.pro @@ -4,4 +4,11 @@ macx:CONFIG -= app_bundle SOURCES += tst_qquickdroparea.cpp +OTHER_FILES += $$files(data/*.qml) + +include (../../shared/util.pri) +include (../shared/util.pri) + +TESTDATA = data/* + QT += core-private gui-private qml-private quick-private network testlib diff --git a/tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp b/tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp index cf01cc927b..dcba4c872e 100644 --- a/tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp +++ b/tests/auto/quick/qquickdroparea/tst_qquickdroparea.cpp @@ -28,6 +28,7 @@ #include <QtTest/QtTest> #include <QtTest/QSignalSpy> +#include <QtGui/qstylehints.h> #include <QtQuick/qquickitem.h> #include <QtQuick/qquickview.h> #include <QtQml/qqmlcontext.h> @@ -36,6 +37,8 @@ #include <qpa/qplatformdrag.h> #include <qpa/qwindowsysteminterface.h> +#include "../../shared/util.h" +#include "../shared/viewtestutil.h" template <typename T> static T evaluate(QObject *scope, const QString &expression) { @@ -54,13 +57,10 @@ template <> void evaluate<void>(QObject *scope, const QString &expression) qWarning() << expr.error().toString(); } -class tst_QQuickDropArea: public QObject +class tst_QQuickDropArea: public QQmlDataTest { Q_OBJECT private slots: - void initTestCase(); - void cleanupTestCase(); - void containsDrag_internal(); void containsDrag_external(); void keys_internal(); @@ -74,21 +74,13 @@ private slots: void competingDrags(); void simultaneousDrags(); void dropStuff(); + void nestedDropAreas_data(); + void nestedDropAreas(); private: QQmlEngine engine; }; -void tst_QQuickDropArea::initTestCase() -{ - -} - -void tst_QQuickDropArea::cleanupTestCase() -{ - -} - void tst_QQuickDropArea::containsDrag_internal() { QQuickWindow window; @@ -1224,6 +1216,74 @@ void tst_QQuickDropArea::dropStuff() QCOMPARE(evaluate<QByteArray>(dropArea, "array"), QByteArray("red")); } +void tst_QQuickDropArea::nestedDropAreas_data() +{ + QTest::addColumn<QString>("qmlFile"); + + QTest::newRow("dropRectDropRect") << "nested1.qml"; + QTest::newRow("rectDropRectDrop") << "nested2.qml"; +} + +void tst_QQuickDropArea::nestedDropAreas() +{ + QFETCH(QString, qmlFile); + + const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); + QQuickView window; + QByteArray errorMessage; + QVERIFY2(QQuickTest::initView(window, testFileUrl(qmlFile.toLatin1().data()), true, &errorMessage), errorMessage.constData()); + + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + QVERIFY(window.rootObject() != nullptr); + + QQuickItem *dragArea = window.rootObject()->findChild<QQuickItem*>("dragArea"); + QVERIFY(dragArea); + QQuickItem *outerDropArea = window.rootObject()->findChild<QQuickItem*>("outerDropArea"); + QVERIFY(outerDropArea); + QQuickItem *innerDropArea = window.rootObject()->findChild<QQuickItem*>("innerDropArea"); + QVERIFY(innerDropArea); + + QPoint p = QPoint(10,10); + QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, p); + + // move the minimum distance to activate drag + p += QPoint(dragThreshold + 1, dragThreshold + 1); + QTest::mouseMove(&window, p); + + // drag the red rectangle into the inner DropArea + p += QPoint(100, 100); + QTest::mouseMove(&window, p); + QCOMPARE(window.rootObject()->property("outerEnterEvents"), 0); + QCOMPARE(window.rootObject()->property("outerExitEvents"), 0); + QCOMPARE(window.rootObject()->property("innerEnterEvents"), 1); + QCOMPARE(window.rootObject()->property("innerExitEvents"), 0); + + // drag the red rectangle into the outer DropArea + p += QPoint(0, 50); + QTest::mouseMove(&window, p); + QCOMPARE(window.rootObject()->property("outerEnterEvents"), 1); + QCOMPARE(window.rootObject()->property("outerExitEvents"), 0); + QCOMPARE(window.rootObject()->property("innerEnterEvents"), 1); + QCOMPARE(window.rootObject()->property("innerExitEvents"), 1); + + // drag the red rectangle into the inner DropArea + p -= QPoint(0, 50); + QTest::mouseMove(&window, p); + QCOMPARE(window.rootObject()->property("outerEnterEvents"), 1); + QCOMPARE(window.rootObject()->property("outerExitEvents"), 1); + QCOMPARE(window.rootObject()->property("innerEnterEvents"), 2); + QCOMPARE(window.rootObject()->property("innerExitEvents"), 1); + + // drag the red rectangle back out of both + p -= QPoint(100, 100); + QTest::mouseMove(&window, p); + QCOMPARE(window.rootObject()->property("outerEnterEvents"), 1); + QCOMPARE(window.rootObject()->property("outerExitEvents"), 1); + QCOMPARE(window.rootObject()->property("innerEnterEvents"), 2); + QCOMPARE(window.rootObject()->property("innerExitEvents"), 2); +} + QTEST_MAIN(tst_QQuickDropArea) #include "tst_qquickdroparea.moc" diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp index 2314b82e8c..c104eecbcd 100644 --- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp +++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp @@ -874,7 +874,8 @@ void tst_qquickflickable::wheel() // test a vertical flick { QPoint pos(200, 200); - QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(), QPoint(0,-120), -120, Qt::Vertical, Qt::NoButton, Qt::NoModifier); + QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(), QPoint(0,-120), + Qt::NoButton, Qt::NoModifier, Qt::NoScrollPhase, false); event.setAccepted(false); QGuiApplication::sendEvent(window.data(), &event); } @@ -897,7 +898,8 @@ void tst_qquickflickable::wheel() // test a horizontal flick { QPoint pos(200, 200); - QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(), QPoint(-120,0), -120, Qt::Horizontal, Qt::NoButton, Qt::NoModifier); + QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(), QPoint(-120,0), + Qt::NoButton, Qt::NoModifier, Qt::NoScrollPhase, false); event.setAccepted(false); QGuiApplication::sendEvent(window.data(), &event); @@ -926,7 +928,8 @@ void tst_qquickflickable::trackpad() QPoint pos(200, 200); { - QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(0,-100), QPoint(0,-120), -120, Qt::Vertical, Qt::NoButton, Qt::NoModifier, Qt::ScrollBegin); + QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(0,-100), QPoint(0,-120), + Qt::NoButton, Qt::NoModifier, Qt::ScrollBegin, false); event.setAccepted(false); QGuiApplication::sendEvent(window.data(), &event); } @@ -938,7 +941,8 @@ void tst_qquickflickable::trackpad() QCOMPARE(flick->contentY(), qreal(0)); { - QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(-100,0), QPoint(-120,0), -120, Qt::Horizontal, Qt::NoButton, Qt::NoModifier, Qt::ScrollUpdate); + QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(-100,0), QPoint(-120,0), + Qt::NoButton, Qt::NoModifier, Qt::ScrollUpdate, false); event.setAccepted(false); QGuiApplication::sendEvent(window.data(), &event); } @@ -947,7 +951,8 @@ void tst_qquickflickable::trackpad() QCOMPARE(flick->contentY(), qreal(0)); { - QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(0,0), QPoint(0,0), 0, Qt::Horizontal, Qt::NoButton, Qt::NoModifier, Qt::ScrollEnd); + QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(0,0), QPoint(0,0), + Qt::NoButton, Qt::NoModifier, Qt::ScrollEnd, false); event.setAccepted(false); QGuiApplication::sendEvent(window.data(), &event); } diff --git a/tests/auto/quick/qquickfontloader_static/tst_qquickfontloader_static.cpp b/tests/auto/quick/qquickfontloader_static/tst_qquickfontloader_static.cpp index c0f66bd709..7e848ef2fc 100644 --- a/tests/auto/quick/qquickfontloader_static/tst_qquickfontloader_static.cpp +++ b/tests/auto/quick/qquickfontloader_static/tst_qquickfontloader_static.cpp @@ -59,6 +59,8 @@ int main(int argc, char **argv) component.setData(qmltemplate, current); window.setContent(current, &component, component.create()); window.show(); - QTest::qWaitForWindowActive(&window); + if (!QTest::qWaitForWindowActive(&window)) + return EXIT_FAILURE; } + return 0; } diff --git a/tests/auto/quick/qquickgridview/qquickgridview.pro b/tests/auto/quick/qquickgridview/qquickgridview.pro index 5051f8bc62..0390637058 100644 --- a/tests/auto/quick/qquickgridview/qquickgridview.pro +++ b/tests/auto/quick/qquickgridview/qquickgridview.pro @@ -10,5 +10,5 @@ include (../shared/util.pri) TESTDATA = data/* -QT += core-private gui-private qml-private quick-private testlib qmltest +QT += core-private gui-private qml-private quick-private testlib qmltest qmlmodels-private diff --git a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp index 448096720c..46e3457d6e 100644 --- a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp +++ b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp @@ -40,7 +40,7 @@ #include <QtQuick/private/qquickitemview_p_p.h> #include <QtQuick/private/qquickgridview_p.h> #include <QtQuick/private/qquicktext_p.h> -#include <QtQml/private/qqmllistmodel_p.h> +#include <QtQmlModels/private/qqmllistmodel_p.h> #include "../../shared/util.h" #include "../shared/viewtestutil.h" #include "../shared/visualtestutil.h" @@ -6433,11 +6433,13 @@ QList<int> tst_QQuickGridView::toIntList(const QVariantList &list) void tst_QQuickGridView::matchIndexLists(const QVariantList &indexLists, const QList<int> &expectedIndexes) { + const QSet<int> expectedIndexSet(expectedIndexes.cbegin(), expectedIndexes.cend()); for (int i=0; i<indexLists.count(); i++) { - QSet<int> current = indexLists[i].value<QList<int> >().toSet(); - if (current != expectedIndexes.toSet()) + const auto ¤tList = indexLists[i].value<QList<int> >(); + const QSet<int> current(currentList.cbegin(), currentList.cend()); + if (current != expectedIndexSet) qDebug() << "Cannot match actual targets" << current << "with expected" << expectedIndexes; - QCOMPARE(current, expectedIndexes.toSet()); + QCOMPARE(current, expectedIndexSet); } } diff --git a/tests/auto/quick/qquickimage/data/multi.ico b/tests/auto/quick/qquickimage/data/multi.ico Binary files differnew file mode 100644 index 0000000000..b748ceaa29 --- /dev/null +++ b/tests/auto/quick/qquickimage/data/multi.ico diff --git a/tests/auto/quick/qquickimage/data/multiframe.qml b/tests/auto/quick/qquickimage/data/multiframe.qml new file mode 100644 index 0000000000..df70bc784c --- /dev/null +++ b/tests/auto/quick/qquickimage/data/multiframe.qml @@ -0,0 +1,5 @@ +import QtQuick 2.14 + +Image { + source: "multi.ico" +} diff --git a/tests/auto/quick/qquickimage/data/multiframeAsync.qml b/tests/auto/quick/qquickimage/data/multiframeAsync.qml new file mode 100644 index 0000000000..167b4a3e57 --- /dev/null +++ b/tests/auto/quick/qquickimage/data/multiframeAsync.qml @@ -0,0 +1,6 @@ +import QtQuick 2.14 + +Image { + source: "multi.ico" + asynchronous: true +} diff --git a/tests/auto/quick/qquickimage/tst_qquickimage.cpp b/tests/auto/quick/qquickimage/tst_qquickimage.cpp index d1f46a3912..abc7cd86bd 100644 --- a/tests/auto/quick/qquickimage/tst_qquickimage.cpp +++ b/tests/auto/quick/qquickimage/tst_qquickimage.cpp @@ -45,6 +45,7 @@ #include <QQuickWindow> #include <QQuickView> #include <QQuickImageProvider> +#include <QQmlAbstractUrlInterceptor> #include "../../shared/util.h" #include "../../shared/testhttpserver.h" @@ -95,6 +96,9 @@ private slots: void highDpiFillModesAndSizes_data(); void highDpiFillModesAndSizes(); void hugeImages(); + void urlInterceptor(); + void multiFrame_data(); + void multiFrame(); private: QQmlEngine engine; @@ -1100,6 +1104,92 @@ void tst_qquickimage::hugeImages() QCOMPARE(contents.pixel(199, 99), qRgba(0, 0, 255, 255)); } + +class MyInterceptor : public QQmlAbstractUrlInterceptor +{ +public: + MyInterceptor(QUrl url) : QQmlAbstractUrlInterceptor(), m_url(url) {} + QUrl intercept(const QUrl &url, QQmlAbstractUrlInterceptor::DataType) + { + if (url.scheme() == "interceptthis") + return m_url; + return url; + } + + QUrl m_url; +}; + +void tst_qquickimage::urlInterceptor() +{ + QQmlEngine engine; + MyInterceptor interceptor {testFileUrl("colors.png")}; + engine.setUrlInterceptor(&interceptor); + + QQmlComponent c(&engine); + + c.setData("import QtQuick 2.12; Image { objectName: \"item\"; source: width == 0 ? \"interceptthis:doesNotExist\" : \"interceptthis:doesNotExist\"}", QUrl{}); + QScopedPointer<QQuickImage> object { qobject_cast<QQuickImage*>(c.create())}; + QVERIFY(object); + QTRY_COMPARE(object->status(), QQuickImage::Ready); + QTRY_COMPARE(object->progress(), 1.0); +} + +void tst_qquickimage::multiFrame_data() +{ + QTest::addColumn<QString>("qmlfile"); + QTest::addColumn<bool>("asynchronous"); + + QTest::addRow("default") << "multiframe.qml" << false; + QTest::addRow("async") << "multiframeAsync.qml" << true; +} + +void tst_qquickimage::multiFrame() +{ + if ((QGuiApplication::platformName() == QLatin1String("offscreen")) + || (QGuiApplication::platformName() == QLatin1String("minimal"))) + QSKIP("Skipping due to grabWindow not functional on offscreen/minimimal platforms"); + + QFETCH(QString, qmlfile); + QFETCH(bool, asynchronous); + Q_UNUSED(asynchronous) + + QQuickView view(testFileUrl(qmlfile)); + QQuickImage *image = qobject_cast<QQuickImage*>(view.rootObject()); + QVERIFY(image); + QSignalSpy countSpy(image, SIGNAL(frameCountChanged())); + QSignalSpy currentSpy(image, SIGNAL(currentFrameChanged())); + if (asynchronous) { + QCOMPARE(image->frameCount(), 0); + QTRY_COMPARE(image->frameCount(), 4); + QCOMPARE(countSpy.count(), 1); + } else { + QCOMPARE(image->frameCount(), 4); + } + QCOMPARE(image->currentFrame(), 0); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + QImage contents = view.grabWindow(); + if (contents.width() < 40) + QSKIP("Skipping due to grabWindow not functional"); + // The first frame is a blue ball, approximately qRgba(0x33, 0x6d, 0xcc, 0xff) + QRgb color = contents.pixel(16, 16); + QVERIFY(qRed(color) < 0xc0); + QVERIFY(qGreen(color) < 0xc0); + QVERIFY(qBlue(color) > 0xc0); + + image->setCurrentFrame(1); + QTRY_COMPARE(image->status(), QQuickImageBase::Ready); + QCOMPARE(currentSpy.count(), 1); + QCOMPARE(image->currentFrame(), 1); + contents = view.grabWindow(); + // The second frame is a green ball, approximately qRgba(0x27, 0xc8, 0x22, 0xff) + color = contents.pixel(16, 16); + QVERIFY(qRed(color) < 0xc0); + QVERIFY(qGreen(color) > 0xc0); + QVERIFY(qBlue(color) < 0xc0); +} + QTEST_MAIN(tst_qquickimage) #include "tst_qquickimage.moc" diff --git a/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp b/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp index 4b75a7e008..9dc9ad53ea 100644 --- a/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp +++ b/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp @@ -33,6 +33,7 @@ #include <QImageReader> #include <QWaitCondition> #include <QThreadPool> +#include <private/qqmlengine_p.h> Q_DECLARE_METATYPE(QQuickImageProvider*); @@ -67,6 +68,8 @@ private slots: void asyncTextureTest(); void instantAsyncTextureTest(); + void asyncImageThreadSafety(); + private: QString newImageFileName() const; void fillRequestTestsData(const QString &id); @@ -448,21 +451,56 @@ void tst_qquickimageprovider::threadTest() foreach (QQuickImage *img, images) { QCOMPARE(img->status(), QQuickImage::Loading); } - provider->ok = true; - provider->cond.wakeAll(); + { + QMutexLocker lock(&provider->mutex); + provider->ok = true; + provider->cond.wakeAll(); + } QTest::qWait(250); foreach (QQuickImage *img, images) { QTRY_COMPARE(img->status(), QQuickImage::Ready); } } -class TestImageResponse : public QQuickImageResponse, public QRunnable +class TestImageResponseRunner : public QObject, public QRunnable { + + Q_OBJECT + +public: + Q_SIGNAL void finished(QQuickTextureFactory *texture); + TestImageResponseRunner(QMutex *lock, QWaitCondition *condition, bool *ok, const QString &id, const QSize &requestedSize) + : m_lock(lock), m_condition(condition), m_ok(ok), m_id(id), m_requestedSize(requestedSize) {} + void run() + { + m_lock->lock(); + if (!(*m_ok)) { + m_condition->wait(m_lock); + } + m_lock->unlock(); + QImage image(50, 50, QImage::Format_RGB32); + image.fill(QColor(m_id).rgb()); + if (m_requestedSize.isValid()) + image = image.scaled(m_requestedSize); + emit finished(QQuickTextureFactory::textureFactoryForImage(image)); + } + +private: + QMutex *m_lock; + QWaitCondition *m_condition; + bool *m_ok; + QString m_id; + QSize m_requestedSize; +}; + +class TestImageResponse : public QQuickImageResponse { public: - TestImageResponse(QMutex *lock, QWaitCondition *condition, bool *ok, const QString &id, const QSize &requestedSize) + TestImageResponse(QMutex *lock, QWaitCondition *condition, bool *ok, const QString &id, const QSize &requestedSize, QThreadPool *pool) : m_lock(lock), m_condition(condition), m_ok(ok), m_id(id), m_requestedSize(requestedSize), m_texture(nullptr) { - setAutoDelete(false); + auto runnable = new TestImageResponseRunner(m_lock, m_condition, m_ok, m_id, m_requestedSize); + QObject::connect(runnable, &TestImageResponseRunner::finished, this, &TestImageResponse::handleResponse); + pool->start(runnable); } QQuickTextureFactory *textureFactory() const @@ -470,18 +508,8 @@ class TestImageResponse : public QQuickImageResponse, public QRunnable return m_texture; } - void run() - { - m_lock->lock(); - if (!(*m_ok)) { - m_condition->wait(m_lock); - } - m_lock->unlock(); - QImage image(50, 50, QImage::Format_RGB32); - image.fill(QColor(m_id).rgb()); - if (m_requestedSize.isValid()) - image = image.scaled(m_requestedSize); - m_texture = QQuickTextureFactory::textureFactoryForImage(image); + void handleResponse(QQuickTextureFactory *factory) { + this->m_texture = factory; emit finished(); } @@ -505,8 +533,7 @@ class TestAsyncProvider : public QQuickAsyncImageProvider QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) { - TestImageResponse *response = new TestImageResponse(&lock, &condition, &ok, id, requestedSize); - pool.start(response); + TestImageResponse *response = new TestImageResponse(&lock, &condition, &ok, id, requestedSize, &pool); return response; } @@ -544,8 +571,11 @@ void tst_qquickimageprovider::asyncTextureTest() foreach (QQuickImage *img, images) { QTRY_COMPARE(img->status(), QQuickImage::Loading); } - provider->ok = true; - provider->condition.wakeAll(); + { + QMutexLocker lock(&provider->lock); + provider->ok = true; + provider->condition.wakeAll(); + } foreach (QQuickImage *img, images) { QTRY_COMPARE(img->status(), QQuickImage::Ready); } @@ -616,6 +646,115 @@ void tst_qquickimageprovider::instantAsyncTextureTest() } +class WaitingAsyncImageResponse : public QQuickImageResponse, public QRunnable +{ +public: + WaitingAsyncImageResponse(QMutex *providerRemovedMutex, QWaitCondition *providerRemovedCond, bool *providerRemoved, QMutex *imageRequestedMutex, QWaitCondition *imageRequestedCond, bool *imageRequested) + : m_providerRemovedMutex(providerRemovedMutex), m_providerRemovedCond(providerRemovedCond), m_providerRemoved(providerRemoved), + m_imageRequestedMutex(imageRequestedMutex), m_imageRequestedCondition(imageRequestedCond), m_imageRequested(imageRequested) + { + setAutoDelete(false); + } + + void run() override + { + m_imageRequestedMutex->lock(); + *m_imageRequested = true; + m_imageRequestedCondition->wakeAll(); + m_imageRequestedMutex->unlock(); + m_providerRemovedMutex->lock(); + while (!*m_providerRemoved) + m_providerRemovedCond->wait(m_providerRemovedMutex); + m_providerRemovedMutex->unlock(); + emit finished(); + } + + QQuickTextureFactory *textureFactory() const override + { + QImage image(50, 50, QImage::Format_RGB32); + auto texture = QQuickTextureFactory::textureFactoryForImage(image); + return texture; + } + + QMutex *m_providerRemovedMutex; + QWaitCondition *m_providerRemovedCond; + bool *m_providerRemoved; + QMutex *m_imageRequestedMutex; + QWaitCondition *m_imageRequestedCondition; + bool *m_imageRequested; + +}; + +class WaitingAsyncProvider : public QQuickAsyncImageProvider +{ +public: + WaitingAsyncProvider(QMutex *providerRemovedMutex, QWaitCondition *providerRemovedCond, bool *providerRemoved, QMutex *imageRequestedMutex, QWaitCondition *imageRequestedCond, bool *imageRequested) + : m_providerRemovedMutex(providerRemovedMutex), m_providerRemovedCond(providerRemovedCond), m_providerRemoved(providerRemoved), + m_imageRequestedMutex(imageRequestedMutex), m_imageRequestedCondition(imageRequestedCond), m_imageRequested(imageRequested) + { + } + + ~WaitingAsyncProvider() {} + + QQuickImageResponse *requestImageResponse(const QString & /* id */, const QSize & /* requestedSize */) + { + auto response = new WaitingAsyncImageResponse(m_providerRemovedMutex, m_providerRemovedCond, m_providerRemoved, m_imageRequestedMutex, m_imageRequestedCondition, m_imageRequested); + pool.start(response); + return response; + } + + QMutex *m_providerRemovedMutex; + QWaitCondition *m_providerRemovedCond; + bool *m_providerRemoved; + QMutex *m_imageRequestedMutex; + QWaitCondition *m_imageRequestedCondition; + bool *m_imageRequested; + QThreadPool pool; +}; + + +// QTBUG-76527 +void tst_qquickimageprovider::asyncImageThreadSafety() +{ + QQmlEngine engine; + QMutex providerRemovedMutex; + bool providerRemoved = false; + QWaitCondition providerRemovedCond; + QMutex imageRequestedMutex; + bool imageRequested = false; + QWaitCondition imageRequestedCond; + auto imageProvider = new WaitingAsyncProvider(&providerRemovedMutex, &providerRemovedCond, &providerRemoved, &imageRequestedMutex, &imageRequestedCond, &imageRequested); + engine.addImageProvider("test_waiting", imageProvider); + QVERIFY(engine.imageProvider("test_waiting") != nullptr); + auto privateEngine = QQmlEnginePrivate::get(&engine); + + QString componentStr = "import QtQuick 2.0\nItem { \n" + "Image { source: \"image://test_waiting/blue\"; }\n" + " }"; + QQmlComponent component(&engine); + component.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QWeakPointer<QQmlImageProviderBase> observer = privateEngine->imageProvider("test_waiting").toWeakRef(); + QVERIFY(!observer.isNull()); // engine still own the object + imageRequestedMutex.lock(); + while (!imageRequested) + imageRequestedCond.wait(&imageRequestedMutex); + imageRequestedMutex.unlock(); + engine.removeImageProvider("test_waiting"); + + QVERIFY(engine.imageProvider("test_waiting") == nullptr); + QVERIFY(!observer.isNull()); // lifetime has been extended + + providerRemovedMutex.lock(); + providerRemoved = true; + providerRemovedCond.wakeAll(); + providerRemovedMutex.unlock(); + + QTRY_VERIFY(observer.isNull()); // once the reply has finished, the imageprovider gets deleted +} + + QTEST_MAIN(tst_qquickimageprovider) #include "tst_qquickimageprovider.moc" diff --git a/tests/auto/quick/qquickitem/tst_qquickitem.cpp b/tests/auto/quick/qquickitem/tst_qquickitem.cpp index 9ce9766c92..8aab13e095 100644 --- a/tests/auto/quick/qquickitem/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem/tst_qquickitem.cpp @@ -83,8 +83,8 @@ protected: event->accept(); ++wheelCount; timestamp = event->timestamp(); - lastWheelEventPos = event->pos(); - lastWheelEventGlobalPos = event->globalPos(); + lastWheelEventPos = event->position().toPoint(); + lastWheelEventGlobalPos = event->globalPosition().toPoint(); } }; @@ -1464,7 +1464,8 @@ void tst_qquickitem::wheelEvent() QPoint localPoint(width / 2, height / 2); QPoint globalPoint = window.mapToGlobal(localPoint); - QWheelEvent event(localPoint, globalPoint, -120, Qt::NoButton, Qt::NoModifier, Qt::Vertical); + QWheelEvent event(localPoint, globalPoint, QPoint(0, 0), QPoint(0, -120), + Qt::NoButton, Qt::NoModifier, Qt::NoScrollPhase, false); event.setTimestamp(123456UL); event.setAccepted(false); QGuiApplication::sendEvent(&window, &event); diff --git a/tests/auto/quick/qquicklistview/BLACKLIST b/tests/auto/quick/qquicklistview/BLACKLIST index 42658dca7a..1f3736328a 100644 --- a/tests/auto/quick/qquicklistview/BLACKLIST +++ b/tests/auto/quick/qquicklistview/BLACKLIST @@ -1,15 +1,11 @@ [enforceRange_withoutHighlight] -osx opensuse-42.3 opensuse-leap #QTBUG-53863 [populateTransitions] opensuse-42.1 -#QTBUG-65964 - [contentHeightWithDelayRemove] osx-10.12 - #QTBUG-75960 #QTBUG-76652 [currentIndex] diff --git a/tests/auto/quick/qquicklistview/qquicklistview.pro b/tests/auto/quick/qquicklistview/qquicklistview.pro index fd96c269a2..b08fca2b1d 100644 --- a/tests/auto/quick/qquicklistview/qquicklistview.pro +++ b/tests/auto/quick/qquicklistview/qquicklistview.pro @@ -18,5 +18,5 @@ include (../shared/util.pri) TESTDATA = data/* DISTFILES += data/* -QT += core-private gui-private qml-private quick-private testlib qmltest +QT += core-private gui-private qml-private quick-private testlib qmltest qmlmodels-private diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index 9419dae362..ca438a9cd5 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -40,9 +40,9 @@ #include <QtQuick/private/qquicklistview_p.h> #include <QtQuick/private/qquickmousearea_p.h> #include <QtQuick/private/qquicktext_p.h> -#include <QtQml/private/qqmlobjectmodel_p.h> -#include <QtQml/private/qqmllistmodel_p.h> -#include <QtQml/private/qqmldelegatemodel_p.h> +#include <QtQmlModels/private/qqmlobjectmodel_p.h> +#include <QtQmlModels/private/qqmllistmodel_p.h> +#include <QtQmlModels/private/qqmldelegatemodel_p.h> #include "../../shared/util.h" #include "../shared/viewtestutil.h" #include "../shared/visualtestutil.h" @@ -7307,11 +7307,13 @@ QList<int> tst_QQuickListView::toIntList(const QVariantList &list) void tst_QQuickListView::matchIndexLists(const QVariantList &indexLists, const QList<int> &expectedIndexes) { + const QSet<int> expectedIndexSet(expectedIndexes.cbegin(), expectedIndexes.cend()); for (int i=0; i<indexLists.count(); i++) { - QSet<int> current = indexLists[i].value<QList<int> >().toSet(); - if (current != expectedIndexes.toSet()) + const auto ¤tList = indexLists[i].value<QList<int> >(); + const QSet<int> current(currentList.cbegin(), currentList.cend()); + if (current != expectedIndexSet) qDebug() << "Cannot match actual targets" << current << "with expected" << expectedIndexes; - QCOMPARE(current, expectedIndexes.toSet()); + QCOMPARE(current, expectedIndexSet); } } diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index 52d1458a53..17553ee6c4 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -393,10 +393,20 @@ void tst_QQuickMouseArea::dragging() QTRY_COMPARE(blackRect->x(), 61.0); QCOMPARE(blackRect->y(), 61.0); + qreal relativeX = mouseRegion->mouseX(); + qreal relativeY = mouseRegion->mouseY(); + for (int i = 0; i < 20; i++) { + p += QPoint(1, 1); + QTest::mouseMove(&window, p); + } + QTRY_VERIFY(drag->active()); + QTRY_COMPARE(mouseRegion->mouseX(), relativeX); + QCOMPARE(mouseRegion->mouseY(), relativeY); + QTest::mouseRelease(&window, button, Qt::NoModifier, p); QTRY_VERIFY(!drag->active()); - QTRY_COMPARE(blackRect->x(), 61.0); - QCOMPARE(blackRect->y(), 61.0); + QTRY_COMPARE(blackRect->x(), 81.0); + QCOMPARE(blackRect->y(), 81.0); } void tst_QQuickMouseArea::dragSmoothed() @@ -1349,6 +1359,34 @@ void tst_QQuickMouseArea::hoverVisible() QCOMPARE(enteredSpy.count(), 1); QCOMPARE(QPointF(mouseTracker->mouseX(), mouseTracker->mouseY()), QPointF(11,33)); + + // QTBUG-77983 + mouseTracker->setVisible(false); + mouseTracker->setEnabled(false); + + QCOMPARE(mouseTracker->hovered(), false); + mouseTracker->setVisible(true); + // if the enabled property is false, the containsMouse property shouldn't become true + // when an invisible mousearea become visible + QCOMPARE(mouseTracker->hovered(), false); + + mouseTracker->parentItem()->setEnabled(false); + mouseTracker->setVisible(false); + mouseTracker->setEnabled(true); + + QCOMPARE(mouseTracker->hovered(), false); + mouseTracker->setVisible(true); + // if the parent item is not enabled, the containsMouse property will be false, even if + // the mousearea is enabled + QCOMPARE(mouseTracker->hovered(), false); + + mouseTracker->parentItem()->setEnabled(true); + mouseTracker->setVisible(false); + mouseTracker->setEnabled(true); + + QCOMPARE(mouseTracker->hovered(), false); + mouseTracker->setVisible(true); + QCOMPARE(mouseTracker->hovered(), true); } void tst_QQuickMouseArea::hoverAfterPress() @@ -1509,7 +1547,7 @@ void tst_QQuickMouseArea::onWheel() QVERIFY(root != nullptr); QWheelEvent wheelEvent(QPoint(10, 32), QPoint(10, 32), QPoint(60, 20), QPoint(0, 120), - 0, Qt::Vertical,Qt::NoButton, Qt::ControlModifier); + Qt::NoButton, Qt::ControlModifier, Qt::NoScrollPhase, false); QGuiApplication::sendEvent(&window, &wheelEvent); QCOMPARE(root->property("angleDeltaY").toInt(), 120); diff --git a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp index cd66fc4ede..e96b892b54 100644 --- a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp +++ b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp @@ -72,6 +72,7 @@ private slots: void mouseGestureStarted_data(); void mouseGestureStarted(); void cancel(); + void stationaryTouchWithChangingPressure(); private: QQuickView *createAndShowView(const QString &file); @@ -1305,6 +1306,42 @@ void tst_QQuickMultiPointTouchArea::cancel() } +void tst_QQuickMultiPointTouchArea::stationaryTouchWithChangingPressure() // QTBUG-77142 +{ + QScopedPointer<QQuickView> window(createAndShowView("basic.qml")); + QVERIFY(window->rootObject() != nullptr); + + QQuickTouchPoint *point1 = window->rootObject()->findChild<QQuickTouchPoint*>("point1"); + QCOMPARE(point1->pressed(), false); + + QPoint p1(20,100); + QTouchEvent::TouchPoint tp1(1); + + tp1.setScreenPos(window->mapToGlobal(p1)); + tp1.setState(Qt::TouchPointPressed); + tp1.setPressure(0.5); + qt_handleTouchEvent(window.data(), device, {tp1}); + QQuickTouchUtils::flush(window.data()); + + QCOMPARE(point1->pressed(), true); + QCOMPARE(point1->pressure(), 0.5); + + tp1.setState(Qt::TouchPointStationary); + tp1.setPressure(0.6); + qt_handleTouchEvent(window.data(), device, {tp1}); + QQuickTouchUtils::flush(window.data()); + + QCOMPARE(point1->pressure(), 0.6); + + tp1.setState(Qt::TouchPointReleased); + tp1.setPressure(0); + qt_handleTouchEvent(window.data(), device, {tp1}); + QQuickTouchUtils::flush(window.data()); + + QCOMPARE(point1->pressed(), false); + QCOMPARE(point1->pressure(), 0); +} + QTEST_MAIN(tst_QQuickMultiPointTouchArea) diff --git a/tests/auto/quick/qquickpath/qquickpath.pro b/tests/auto/quick/qquickpath/qquickpath.pro index 492f82f53d..ef110a8331 100644 --- a/tests/auto/quick/qquickpath/qquickpath.pro +++ b/tests/auto/quick/qquickpath/qquickpath.pro @@ -7,5 +7,6 @@ SOURCES += tst_qquickpath.cpp include (../../shared/util.pri) TESTDATA = data/* +DISTFILES = data/* QT += core-private gui-private qml-private quick-private testlib diff --git a/tests/auto/quick/qquickpath/tst_qquickpath.cpp b/tests/auto/quick/qquickpath/tst_qquickpath.cpp index 12a8c673b0..c89ce730a8 100644 --- a/tests/auto/quick/qquickpath/tst_qquickpath.cpp +++ b/tests/auto/quick/qquickpath/tst_qquickpath.cpp @@ -42,18 +42,44 @@ public: private slots: void arc(); void angleArc(); - void catmullromCurve(); - void closedCatmullromCurve(); + void catmullRomCurve(); + void closedCatmullRomCurve(); void svg(); void line(); + +private: + void arc(QSizeF scale); + void angleArc(QSizeF scale); + void catmullRomCurve(QSizeF scale, const QVector<QPointF> &points); + void closedCatmullRomCurve(QSizeF scale, const QVector<QPointF> &points); + void svg(QSizeF scale); + void line(QSizeF scale); }; -void tst_QuickPath::arc() +static void compare(const QPointF &point, const QSizeF &scale, int line, double x, double y) +{ + QVERIFY2(qFuzzyCompare(float(point.x()), float(x * scale.width())), + (QStringLiteral("Actual: ") + QString::number(point.x(),'g',14) + + QStringLiteral(" Expected: ") + QString::number(x * scale.width(),'g',14) + + QStringLiteral(" At: ") + QString::number(line)).toLatin1().data()); + QVERIFY2(qFuzzyCompare(float(point.y()), float(y * scale.height())), + (QStringLiteral("Actual: ") + QString::number(point.y(),'g',14) + + QStringLiteral(" Expected: ") + QString::number(y * scale.height(),'g',14) + + QStringLiteral(" At: ") + QString::number(line)).toLatin1().data()); +} +static void compare(const QPointF &point, int line, const QPointF &pt) +{ + return compare(point, QSizeF(1,1), line, pt.x(), pt.y()); +} + +void tst_QuickPath::arc(QSizeF scale) { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("arc.qml")); QQuickPath *obj = qobject_cast<QQuickPath*>(c.create()); QVERIFY(obj != nullptr); + if (scale != QSizeF(1,1)) + obj->setProperty("scale", scale); QCOMPARE(obj->startX(), 0.); QCOMPARE(obj->startY(), 0.); @@ -73,22 +99,30 @@ void tst_QuickPath::arc() QPainterPath path = obj->path(); QVERIFY(path != QPainterPath()); - QPointF pos = obj->pointAt(0); + QPointF pos = obj->pointAtPercent(0); QCOMPARE(pos, QPointF(0,0)); - pos = obj->pointAt(.25); - QCOMPARE(pos.toPoint(), QPoint(39,8)); //fuzzy compare - pos = obj->pointAt(.75); - QCOMPARE(pos.toPoint(), QPoint(92,61)); //fuzzy compare - pos = obj->pointAt(1); - QCOMPARE(pos, QPointF(100,100)); + pos = obj->pointAtPercent(.25); + compare(pos, scale, __LINE__, 38.9244897744, 7.85853964341); + pos = obj->pointAtPercent(.75); + compare(pos, scale, __LINE__, 92.141460356592, 61.07551022559); + pos = obj->pointAtPercent(1); + QCOMPARE(pos, QPointF(100 * scale.width(), 100 * scale.height())); } -void tst_QuickPath::angleArc() +void tst_QuickPath::arc() +{ + arc(QSizeF(1,1)); + arc(QSizeF(2.2,3.4)); +} + +void tst_QuickPath::angleArc(QSizeF scale) { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("anglearc.qml")); QQuickPath *obj = qobject_cast<QQuickPath*>(c.create()); QVERIFY(obj != nullptr); + if (scale != QSizeF(1,1)) + obj->setProperty("scale", scale); QQmlListReference list(obj, "pathElements"); QCOMPARE(list.count(), 1); @@ -106,28 +140,35 @@ void tst_QuickPath::angleArc() QPainterPath path = obj->path(); QVERIFY(path != QPainterPath()); - // using QPoint to do fuzzy compare - QPointF pos = obj->pointAt(0); - QCOMPARE(pos.toPoint(), QPoint(135,135)); - pos = obj->pointAt(.25); - QCOMPARE(pos.toPoint(), QPoint(119,146)); - pos = obj->pointAt(.75); - QCOMPARE(pos.toPoint(), QPoint(81,146)); - pos = obj->pointAt(1); - QCOMPARE(pos.toPoint(), QPoint(65,135)); + QPointF pos = obj->pointAtPercent(0); + compare(pos, scale, __LINE__, 135.35533905867, 135.35533905867); + pos = obj->pointAtPercent(.25); + compare(pos, scale, __LINE__, 119.46222180396, 146.07068621369); + pos = obj->pointAtPercent(.75); + compare(pos, scale, __LINE__, 80.537778196007, 146.07068621366); + pos = obj->pointAtPercent(1); + compare(pos, scale, __LINE__, 64.644660941173, 135.35533905867); // if moveToStart is false, we should have a line starting from startX/Y arc->setMoveToStart(false); - pos = obj->pointAt(0); + pos = obj->pointAtPercent(0); QCOMPARE(pos, QPointF(0,0)); } -void tst_QuickPath::catmullromCurve() +void tst_QuickPath::angleArc() +{ + angleArc(QSizeF(1,1)); + angleArc(QSizeF(2.7,0.92)); +} + +void tst_QuickPath::catmullRomCurve(QSizeF scale, const QVector<QPointF> &points) { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("curve.qml")); QQuickPath *obj = qobject_cast<QQuickPath*>(c.create()); QVERIFY(obj != nullptr); + if (scale != QSizeF(1,1)) + obj->setProperty("scale", scale); QCOMPARE(obj->startX(), 0.); QCOMPARE(obj->startY(), 0.); @@ -148,22 +189,36 @@ void tst_QuickPath::catmullromCurve() QPainterPath path = obj->path(); QVERIFY(path != QPainterPath()); - QPointF pos = obj->pointAt(0); - QCOMPARE(pos, QPointF(0,0)); - pos = obj->pointAt(.25); - QCOMPARE(pos.toPoint(), QPoint(63,26)); //fuzzy compare - pos = obj->pointAt(.75); - QCOMPARE(pos.toPoint(), QPoint(51,105)); //fuzzy compare - pos = obj->pointAt(1); - QCOMPARE(pos.toPoint(), QPoint(100,150)); + QPointF pos = path.pointAtPercent(0); + QCOMPARE(pos, points.at(0)); + pos = path.pointAtPercent(.25); + compare(pos, __LINE__, points.at(1)); + pos = path.pointAtPercent(.75); + compare(pos, __LINE__, points.at(2)); + pos = path.pointAtPercent(1); + compare(pos, __LINE__, points.at(3)); +} + +void tst_QuickPath::catmullRomCurve() +{ + catmullRomCurve(QSizeF(1,1), { QPointF(0,0), + QPointF(62.917022919131, 26.175485291549), + QPointF(51.194527196674 , 105.27985623074), + QPointF(100, 150) }); + catmullRomCurve(QSizeF(2,5.3), { QPointF(0,0), + QPointF(150.80562419914, 170.34065984615), + QPointF(109.08400252853 , 588.35165918579), + QPointF(200, 795) }); } -void tst_QuickPath::closedCatmullromCurve() +void tst_QuickPath::closedCatmullRomCurve(QSizeF scale, const QVector<QPointF> &points) { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("closedcurve.qml")); QQuickPath *obj = qobject_cast<QQuickPath*>(c.create()); QVERIFY(obj != nullptr); + if (scale != QSizeF(1,1)) + obj->setProperty("scale", scale); QCOMPARE(obj->startX(), 50.); QCOMPARE(obj->startY(), 50.); @@ -181,22 +236,36 @@ void tst_QuickPath::closedCatmullromCurve() QPainterPath path = obj->path(); QVERIFY(path != QPainterPath()); - QPointF pos = obj->pointAt(0); - QCOMPARE(pos, QPointF(50,50)); - pos = obj->pointAt(.1); - QCOMPARE(pos.toPoint(), QPoint(67,56)); //fuzzy compare - pos = obj->pointAt(.75); - QCOMPARE(pos.toPoint(), QPoint(44,116)); //fuzzy compare - pos = obj->pointAt(1); - QCOMPARE(pos, QPointF(50,50)); + QPointF pos = path.pointAtPercent(0); + QCOMPARE(pos, points.at(0)); + pos = path.pointAtPercent(.1); + compare(pos, __LINE__, points.at(1)); + pos = path.pointAtPercent(.75); + compare(pos, __LINE__, points.at(2)); + pos = path.pointAtPercent(1); + compare(pos, __LINE__, points.at(3)); } -void tst_QuickPath::svg() +void tst_QuickPath::closedCatmullRomCurve() +{ + closedCatmullRomCurve(QSizeF(1,1), { QPointF(50,50), + QPointF(66.776225481812, 55.617435304145), + QPointF(44.10269379731 , 116.33512508175), + QPointF(50, 50) }); + closedCatmullRomCurve(QSizeF(2,3), { QPointF(100,150), + QPointF(136.49725836178, 170.25466686363), + QPointF(87.713232151943 , 328.29232737977), + QPointF(100, 150) }); +} + +void tst_QuickPath::svg(QSizeF scale) { QQmlEngine engine; QQmlComponent c(&engine, testFileUrl("svg.qml")); QQuickPath *obj = qobject_cast<QQuickPath*>(c.create()); QVERIFY(obj != nullptr); + if (scale != QSizeF(1,1)) + obj->setProperty("scale", scale); QCOMPARE(obj->startX(), 0.); QCOMPARE(obj->startY(), 0.); @@ -211,17 +280,23 @@ void tst_QuickPath::svg() QPainterPath path = obj->path(); QVERIFY(path != QPainterPath()); - QPointF pos = obj->pointAt(0); - QCOMPARE(pos, QPointF(200,300)); - pos = obj->pointAt(.25); - QCOMPARE(pos.toPoint(), QPoint(400,175)); //fuzzy compare - pos = obj->pointAt(.75); - QCOMPARE(pos.toPoint(), QPoint(800,425)); //fuzzy compare - pos = obj->pointAt(1); - QCOMPARE(pos, QPointF(1000,300)); + QPointF pos = obj->pointAtPercent(0); + QCOMPARE(pos, QPointF(200 * scale.width(),300 * scale.height())); + pos = obj->pointAtPercent(.25); + QCOMPARE(pos.toPoint(), QPoint(400 * scale.width(),175 * scale.height())); //fuzzy compare + pos = obj->pointAtPercent(.75); + QCOMPARE(pos.toPoint(), QPoint(800 * scale.width(),425 * scale.height())); //fuzzy compare + pos = obj->pointAtPercent(1); + QCOMPARE(pos, QPointF(1000 * scale.width(),300 * scale.height())); } -void tst_QuickPath::line() +void tst_QuickPath::svg() +{ + svg(QSizeF(1,1)); + svg(QSizeF(5,3)); +} + +void tst_QuickPath::line(QSizeF scale) { QQmlEngine engine; QQmlComponent c1(&engine); @@ -234,6 +309,8 @@ void tst_QuickPath::line() QScopedPointer<QObject> o1(c1.create()); QQuickPath *path1 = qobject_cast<QQuickPath *>(o1.data()); QVERIFY(path1); + if (scale != QSizeF(1,1)) + path1->setProperty("scale", scale); QQmlComponent c2(&engine); c2.setData( @@ -246,18 +323,25 @@ void tst_QuickPath::line() QScopedPointer<QObject> o2(c2.create()); QQuickPath *path2 = qobject_cast<QQuickPath *>(o2.data()); QVERIFY(path2); + if (scale != QSizeF(1,1)) + path2->setProperty("scale", scale); for (int i = 0; i < 167; ++i) { qreal t = i / 167.0; - QPointF p1 = path1->pointAt(t); + QPointF p1 = path1->pointAtPercent(t); QCOMPARE(p1.x(), p1.y()); - QPointF p2 = path2->pointAt(t); + QPointF p2 = path2->pointAtPercent(t); QCOMPARE(p1.toPoint(), p2.toPoint()); } } +void tst_QuickPath::line() +{ + line(QSizeF(1,1)); + line(QSizeF(7.23,7.23)); +} QTEST_MAIN(tst_QuickPath) diff --git a/tests/auto/quick/qquickpathview/qquickpathview.pro b/tests/auto/quick/qquickpathview/qquickpathview.pro index f21fb64fa4..5eb24b89bd 100644 --- a/tests/auto/quick/qquickpathview/qquickpathview.pro +++ b/tests/auto/quick/qquickpathview/qquickpathview.pro @@ -9,5 +9,5 @@ include (../shared/util.pri) TESTDATA = data/* -QT += core-private gui-private qml-private quick-private testlib qmltest +QT += core-private gui-private qml-private quick-private testlib qmltest qmlmodels-private qtHaveModule(widgets): QT += widgets diff --git a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp index e2d3253e44..4498548d45 100644 --- a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp +++ b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp @@ -39,7 +39,7 @@ #include <QtQuick/private/qquicktext_p.h> #include <QtQuick/private/qquickrectangle_p.h> #include <QtQuickTest/QtQuickTest> -#include <QtQml/private/qqmllistmodel_p.h> +#include <QtQmlModels/private/qqmllistmodel_p.h> #include <QtQml/private/qqmlvaluetype_p.h> #include <QtGui/qstandarditemmodel.h> #include <QStringListModel> @@ -242,7 +242,7 @@ void tst_QQuickPathView::items() QVERIFY(path); QVERIFY(pathview->highlightItem()); - QPointF start = path->pointAt(0.0); + QPointF start = path->pointAtPercent(0.0); QPointF offset; offset.setX(pathview->highlightItem()->width()/2); offset.setY(pathview->highlightItem()->height()/2); @@ -920,7 +920,7 @@ void tst_QQuickPathView::pathMoved() QVERIFY(firstItem); QQuickPath *path = qobject_cast<QQuickPath*>(pathview->path()); QVERIFY(path); - QPointF start = path->pointAt(0.0); + QPointF start = path->pointAtPercent(0.0); QPointF offset;//Center of item is at point, but pos is from corner offset.setX(firstItem->width()/2); offset.setY(firstItem->height()/2); @@ -929,7 +929,7 @@ void tst_QQuickPathView::pathMoved() for (int i=0; i<model.count(); i++) { QQuickRectangle *curItem = findItem<QQuickRectangle>(pathview, "wrapper", i); - QPointF itemPos(path->pointAt(0.25 + i*0.25)); + QPointF itemPos(path->pointAtPercent(0.25 + i*0.25)); QCOMPARE(curItem->position() + offset, QPointF(itemPos.x(), itemPos.y())); } @@ -1008,7 +1008,7 @@ void tst_QQuickPathView::setCurrentIndex() QVERIFY(firstItem); QQuickPath *path = qobject_cast<QQuickPath*>(pathview->path()); QVERIFY(path); - QPointF start = path->pointAt(0.0); + QPointF start = path->pointAtPercent(0.0); QPointF offset;//Center of item is at point, but pos is from corner offset.setX(firstItem->width()/2); offset.setY(firstItem->height()/2); @@ -1714,7 +1714,7 @@ void tst_QQuickPathView::changePreferredHighlight() QVERIFY(firstItem); QQuickPath *path = qobject_cast<QQuickPath*>(pathview->path()); QVERIFY(path); - QPointF start = path->pointAt(0.5); + QPointF start = path->pointAtPercent(0.5); QPointF offset;//Center of item is at point, but pos is from corner offset.setX(firstItem->width()/2); offset.setY(firstItem->height()/2); @@ -1722,7 +1722,7 @@ void tst_QQuickPathView::changePreferredHighlight() pathview->setPreferredHighlightBegin(0.8); pathview->setPreferredHighlightEnd(0.8); - start = path->pointAt(0.8); + start = path->pointAtPercent(0.8); QTRY_COMPARE(firstItem->position() + offset, start); QCOMPARE(pathview->currentIndex(), 0); @@ -1775,7 +1775,7 @@ void tst_QQuickPathView::currentOffsetOnInsertion() QQuickPath *path = qobject_cast<QQuickPath*>(pathview->path()); QVERIFY(path); - QPointF start = path->pointAt(0.5); + QPointF start = path->pointAtPercent(0.5); QPointF offset;//Center of item is at point, but pos is from corner offset.setX(item->width()/2); offset.setY(item->height()/2); @@ -1864,7 +1864,7 @@ void tst_QQuickPathView::asynchronous() QVERIFY(firstItem); QQuickPath *path = qobject_cast<QQuickPath*>(pathview->path()); QVERIFY(path); - QPointF start = path->pointAt(0.0); + QPointF start = path->pointAtPercent(0.0); QPointF offset;//Center of item is at point, but pos is from corner offset.setX(firstItem->width()/2); offset.setY(firstItem->height()/2); @@ -1873,7 +1873,7 @@ void tst_QQuickPathView::asynchronous() for (int i=0; i<5; i++) { QQuickItem *curItem = findItem<QQuickItem>(pathview, "wrapper", i); - QPointF itemPos(path->pointAt(0.2 + i*0.2)); + QPointF itemPos(path->pointAtPercent(0.2 + i*0.2)); QCOMPARE(curItem->position() + offset, itemPos); } diff --git a/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp b/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp index 80be25d1b0..d3c0f345b9 100644 --- a/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp +++ b/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp @@ -4029,11 +4029,13 @@ QQuickView *tst_qquickpositioners::createView(const QString &filename, bool wait void tst_qquickpositioners::matchIndexLists(const QVariantList &indexLists, const QList<int> &expectedIndexes) { + const QSet<int> expectedIndexSet(expectedIndexes.cbegin(), expectedIndexes.cend()); for (int i=0; i<indexLists.count(); i++) { - QSet<int> current = indexLists[i].value<QList<int> >().toSet(); - if (current != expectedIndexes.toSet()) + const auto ¤tList = indexLists[i].value<QList<int> >(); + const QSet<int> current(currentList.cbegin(), currentList.cend()); + if (current != expectedIndexSet) qDebug() << "Cannot match actual targets" << current << "with expected" << expectedIndexes; - QCOMPARE(current, expectedIndexes.toSet()); + QCOMPARE(current, expectedIndexSet); } } diff --git a/tests/auto/quick/qquickrectangle/data/gradient-preset.qml b/tests/auto/quick/qquickrectangle/data/gradient-preset.qml index b740bdd610..c046dc4c05 100644 --- a/tests/auto/quick/qquickrectangle/data/gradient-preset.qml +++ b/tests/auto/quick/qquickrectangle/data/gradient-preset.qml @@ -10,7 +10,23 @@ Item { gradient: "NightFade" } Rectangle { - objectName: "invalid" + objectName: "invalid1" gradient: -1 } + Rectangle { + objectName: "invalid2" + gradient: 123456789 + } + Rectangle { + objectName: "invalid3" + gradient: "NOT_EXISTING" + } + Rectangle { + objectName: "invalid4" + gradient: "NumPresets" + } + Rectangle { + objectName: "invalid5" + gradient: Gradient.NumPresets + } } diff --git a/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp b/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp index 710caaa734..e4d790f466 100644 --- a/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp +++ b/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp @@ -189,9 +189,11 @@ void tst_qquickrectangle::gradient_preset() QVERIFY(stringRect->gradient().isString()); QCOMPARE(stringRect->gradient().toString(), QLatin1String("NightFade")); - QQuickRectangle *invalidRect = view.rootObject()->findChild<QQuickRectangle *>("invalid"); - QVERIFY(invalidRect); - QVERIFY(invalidRect->gradient().isUndefined()); + for (int i = 1; i <= 5; ++i) { + QQuickRectangle *invalidRect = view.rootObject()->findChild<QQuickRectangle *>(qPrintable(QString("invalid%1").arg(i))); + QVERIFY(invalidRect); + QVERIFY(invalidRect->gradient().isUndefined()); + } } void tst_qquickrectangle::antialiasing() diff --git a/tests/auto/quick/qquickrepeater/qquickrepeater.pro b/tests/auto/quick/qquickrepeater/qquickrepeater.pro index 5554342943..aed5702266 100644 --- a/tests/auto/quick/qquickrepeater/qquickrepeater.pro +++ b/tests/auto/quick/qquickrepeater/qquickrepeater.pro @@ -9,4 +9,4 @@ include (../shared/util.pri) TESTDATA = data/* -QT += core-private gui-private qml-private quick-private testlib +QT += core-private gui-private qml-private quick-private testlib qmlmodels-private diff --git a/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp b/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp index e4b427f6ec..65e7d29595 100644 --- a/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp +++ b/tests/auto/quick/qquickrepeater/tst_qquickrepeater.cpp @@ -35,8 +35,8 @@ #include <QtQml/qqmlincubator.h> #include <private/qquickrepeater_p.h> #include <QtQuick/private/qquicktext_p.h> -#include <QtQml/private/qqmllistmodel_p.h> -#include <QtQml/private/qqmlobjectmodel_p.h> +#include <QtQmlModels/private/qqmllistmodel_p.h> +#include <QtQmlModels/private/qqmlobjectmodel_p.h> #include <QtGui/qstandarditemmodel.h> #include "../../shared/util.h" @@ -899,15 +899,15 @@ void tst_QQuickRepeater::destroyCount() QQuickRepeater *repeater = findItem<QQuickRepeater>(rootObject, "repeater"); QVERIFY(repeater); - repeater->setProperty("model", qVariantFromValue<int>(3)); + repeater->setProperty("model", QVariant::fromValue<int>(3)); QCOMPARE(repeater->property("componentCount").toInt(), 3); - repeater->setProperty("model", qVariantFromValue<int>(0)); + repeater->setProperty("model", QVariant::fromValue<int>(0)); QCOMPARE(repeater->property("componentCount").toInt(), 0); - repeater->setProperty("model", qVariantFromValue<int>(4)); + repeater->setProperty("model", QVariant::fromValue<int>(4)); QCOMPARE(repeater->property("componentCount").toInt(), 4); QStringListModel model; - repeater->setProperty("model", qVariantFromValue<QStringListModel *>(&model)); + repeater->setProperty("model", QVariant::fromValue<QStringListModel *>(&model)); QCOMPARE(repeater->property("componentCount").toInt(), 0); QStringList list; list << "1" << "2" << "3" << "4"; @@ -915,7 +915,7 @@ void tst_QQuickRepeater::destroyCount() QCOMPARE(repeater->property("componentCount").toInt(), 4); model.insertRows(2,1); QModelIndex index = model.index(2); - model.setData(index, qVariantFromValue<QString>(QStringLiteral("foobar"))); + model.setData(index, QVariant::fromValue<QString>(QStringLiteral("foobar"))); QCOMPARE(repeater->property("componentCount").toInt(), 5); model.removeRows(2,1); diff --git a/tests/auto/quick/qquickshape/BLACKLIST b/tests/auto/quick/qquickshape/BLACKLIST deleted file mode 100644 index d0ebc2f505..0000000000 --- a/tests/auto/quick/qquickshape/BLACKLIST +++ /dev/null @@ -1,8 +0,0 @@ -[render] -osx ci -[renderWithMultipleSp] -osx ci -[radialGrad] -osx ci -[conicalGrad] -osx ci diff --git a/tests/auto/quick/qquickshape/data/multiline.png b/tests/auto/quick/qquickshape/data/multiline.png Binary files differnew file mode 100644 index 0000000000..ae25d111df --- /dev/null +++ b/tests/auto/quick/qquickshape/data/multiline.png diff --git a/tests/auto/quick/qquickshape/data/multiline.qml b/tests/auto/quick/qquickshape/data/multiline.qml new file mode 100644 index 0000000000..ad07506972 --- /dev/null +++ b/tests/auto/quick/qquickshape/data/multiline.qml @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.14 +import QtQuick.Shapes 1.14 + +Shape { + width: 200 + height: 150 + vendorExtensionsEnabled: false + objectName: "shape" + id: shape + property alias paths: multiline.paths + property point vertexBeingChecked; + + function checkVertexAt(i, j) { + vertexBeingChecked = multiline.paths[i][j] + } + + ShapePath { + strokeWidth: 4 + strokeColor: "green" + PathMultiline { + id: multiline + } + } +} diff --git a/tests/auto/quick/qquickshape/data/multilineStronglyTyped.qml b/tests/auto/quick/qquickshape/data/multilineStronglyTyped.qml new file mode 100644 index 0000000000..46d650e053 --- /dev/null +++ b/tests/auto/quick/qquickshape/data/multilineStronglyTyped.qml @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.14 +import QtQuick.Shapes 1.14 +import Qt.test 1.0 + +Shape { + width: 200 + height: 150 + vendorExtensionsEnabled: false + objectName: "shape" + id: shape + property alias paths: multiline.paths + property point vertexBeingChecked; + + function checkVertexAt(i, j) { + vertexBeingChecked = multiline.paths[i][j] + } + + PolygonProvider { + id: provider + objectName: "provider" + } + + ShapePath { + strokeWidth: 4 + strokeColor: "green" + PathMultiline { + id: multiline + paths: provider.paths + } + } +} diff --git a/tests/auto/quick/qquickshape/data/pathitem7.qml b/tests/auto/quick/qquickshape/data/pathitem7.qml new file mode 100644 index 0000000000..b3ef47a4dd --- /dev/null +++ b/tests/auto/quick/qquickshape/data/pathitem7.qml @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.14 +import tst_qquickpathitem 1.0 + +Item { + id: item + width: 200 + height: 150 + + Shape { + vendorExtensionsEnabled: false + objectName: "shape" + id: shape + anchors.fill: parent + + ShapePath { + strokeWidth: 4 + strokeColor: "red" + scale: Qt.size(shape.width - 1, shape.height - 1) + fillGradient: LinearGradient { + x1: 20; y1: 20 + x2: 180; y2: 130 + GradientStop { position: 0; color: "blue" } + GradientStop { position: 0.2; color: "green" } + GradientStop { position: 0.4; color: "red" } + GradientStop { position: 0.6; color: "yellow" } + GradientStop { position: 1; color: "cyan" } + } + strokeStyle: ShapePath.DashLine + dashPattern: [ 1, 4 ] + startX: 20; startY: 20 // unnecessary, PathPolyline moves to the first vertex. + PathPolyline { + path: [ Qt.point(20.0 / (item.width - 1.0), 20.0 / (item.height - 1.0)), + Qt.point(180.0 / (item.width - 1.0), 130.0 / (item.height - 1.0)), + Qt.point(20.0 / (item.width - 1.0), 130.0 / (item.height - 1.0)), + Qt.point(20.0 / (item.width - 1.0), 20.0 / (item.height - 1.0)) ] + } + } + } +} diff --git a/tests/auto/quick/qquickshape/data/pathitem8.png b/tests/auto/quick/qquickshape/data/pathitem8.png Binary files differnew file mode 100644 index 0000000000..ee585958ec --- /dev/null +++ b/tests/auto/quick/qquickshape/data/pathitem8.png diff --git a/tests/auto/quick/qquickshape/data/pathitem8.qml b/tests/auto/quick/qquickshape/data/pathitem8.qml new file mode 100644 index 0000000000..9789ff90e0 --- /dev/null +++ b/tests/auto/quick/qquickshape/data/pathitem8.qml @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.14 +import QtQuick.Shapes 1.14 + +Item { + id: item + width: 200 + height: 150 + + Shape { + vendorExtensionsEnabled: false + objectName: "shape" + id: shape + anchors.fill: parent + + ShapePath { + strokeWidth: 4 + strokeColor: "red" + scale: Qt.size(shape.width - 1, shape.height - 1) + fillGradient: LinearGradient { + x1: 20; y1: 20 + x2: 180; y2: 130 + GradientStop { position: 0; color: "blue" } + GradientStop { position: 0.2; color: "green" } + GradientStop { position: 0.4; color: "red" } + GradientStop { position: 0.6; color: "yellow" } + GradientStop { position: 1; color: "cyan" } + } + strokeStyle: ShapePath.DashLine + dashPattern: [ 1, 4 ] + PathMultiline { + paths: [[Qt.point(20.0 / (item.width - 1.0), 20.0 / (item.height - 1.0)), + Qt.point(180.0 / (item.width - 1.0), 130.0 / (item.height - 1.0)), + Qt.point(20.0 / (item.width - 1.0), 130.0 / (item.height - 1.0)), + Qt.point(20.0 / (item.width - 1.0), 20.0 / (item.height - 1.0)) ], + [Qt.point( 0.45 , 0.67 ), + Qt.point( 0.414906666468 , 0.573581858547 ), + Qt.point( 0.32604722665 , 0.522278837048 ), + Qt.point( 0.225 , 0.540096189432 ), + Qt.point( 0.159046106882 , 0.618696978501 ), + Qt.point( 0.159046106882 , 0.721303021499 ), + Qt.point( 0.225 , 0.799903810568 ), + Qt.point( 0.32604722665 , 0.817721162952 ), + Qt.point( 0.414906666468 , 0.766418141453 ), + Qt.point( 0.45 , 0.67 ), + ], + [Qt.point( 0.69 , 0.75 ), + Qt.point( 0.668943999881 , 0.692149115128 ), + Qt.point( 0.61562833599 , 0.661367302229 ), + Qt.point( 0.555 , 0.672057713659 ), + Qt.point( 0.515427664129 , 0.719218187101 ), + Qt.point( 0.515427664129 , 0.780781812899 ), + Qt.point( 0.555 , 0.827942286341 ), + Qt.point( 0.61562833599 , 0.838632697771 ), + Qt.point( 0.668943999881 , 0.807850884872 ), + Qt.point( 0.69 , 0.75 ), + ]] + } + } + } +} diff --git a/tests/auto/quick/qquickshape/data/polyline.png b/tests/auto/quick/qquickshape/data/polyline.png Binary files differnew file mode 100644 index 0000000000..00123fba44 --- /dev/null +++ b/tests/auto/quick/qquickshape/data/polyline.png diff --git a/tests/auto/quick/qquickshape/data/polyline.qml b/tests/auto/quick/qquickshape/data/polyline.qml new file mode 100644 index 0000000000..e62d952ae7 --- /dev/null +++ b/tests/auto/quick/qquickshape/data/polyline.qml @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.14 +import QtQuick.Shapes 1.14 + +Shape { + width: 200 + height: 150 + vendorExtensionsEnabled: false + objectName: "shape" + id: shape + property alias path: polyline.path + property point vertexBeingChecked; + + function checkVertexAt(i) { + vertexBeingChecked = polyline.path[i] + } + + ShapePath { + strokeWidth: 4 + strokeColor: "green" + PathPolyline { + id: polyline + } + } +} diff --git a/tests/auto/quick/qquickshape/qquickshape.pro b/tests/auto/quick/qquickshape/qquickshape.pro index a0e5c002e0..3cf79426c5 100644 --- a/tests/auto/quick/qquickshape/qquickshape.pro +++ b/tests/auto/quick/qquickshape/qquickshape.pro @@ -8,6 +8,7 @@ include (../../shared/util.pri) include (../shared/util.pri) TESTDATA = data/* +DISTFILES = data/* QT += core-private gui-private qml-private quick-private testlib quickshapes-private qtHaveModule(widgets): QT += widgets diff --git a/tests/auto/quick/qquickshape/tst_qquickshape.cpp b/tests/auto/quick/qquickshape/tst_qquickshape.cpp index 61fb260612..851475e2cd 100644 --- a/tests/auto/quick/qquickshape/tst_qquickshape.cpp +++ b/tests/auto/quick/qquickshape/tst_qquickshape.cpp @@ -1,6 +1,6 @@ -/**************************************************************************** +/**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. @@ -34,6 +34,7 @@ #include <QtQml/qqmlexpression.h> #include <QtQml/qqmlincubator.h> #include <QtQuickShapes/private/qquickshape_p.h> +#include <QStandardPaths> #include "../../shared/util.h" #include "../shared/viewtestutil.h" @@ -42,6 +43,28 @@ using namespace QQuickViewTestUtil; using namespace QQuickVisualTestUtil; +class PolygonProvider : public QObject +{ + Q_OBJECT + Q_PROPERTY(QVector<QPolygonF> paths READ paths WRITE setPaths NOTIFY pathsChanged) + +public: + QVector<QPolygonF> paths() const { return m_paths; } + void setPaths(QVector<QPolygonF> paths) + { + if (m_paths == paths) + return; + m_paths = paths; + emit pathsChanged(); + } + +signals: + void pathsChanged(); + +private: + QVector<QPolygonF> m_paths; +}; + class tst_QQuickShape : public QQmlDataTest { Q_OBJECT @@ -57,6 +80,16 @@ private slots: void renderWithMultipleSp(); void radialGrad(); void conicalGrad(); + void renderPolyline(); + void renderMultiline(); + void polylineDataTypes_data(); + void polylineDataTypes(); + void multilineDataTypes_data(); + void multilineDataTypes(); + void multilineStronglyTyped(); + +private: + QVector<QPolygonF> m_lowPolyLogo; }; tst_QQuickShape::tst_QQuickShape() @@ -66,11 +99,36 @@ tst_QQuickShape::tst_QQuickShape() const char *uri = "tst_qquickpathitem"; qmlRegisterType<QQuickShape>(uri, 1, 0, "Shape"); - qmlRegisterType<QQuickShapePath>(uri, 1, 0, "ShapePath"); + qmlRegisterType<QQuickShapePath, 14>(uri, 1, 0, "ShapePath"); qmlRegisterUncreatableType<QQuickShapeGradient>(uri, 1, 0, "ShapeGradient", QQuickShapeGradient::tr("ShapeGradient is an abstract base class")); qmlRegisterType<QQuickShapeLinearGradient>(uri, 1, 0, "LinearGradient"); qmlRegisterType<QQuickShapeRadialGradient>(uri, 1, 0, "RadialGradient"); qmlRegisterType<QQuickShapeConicalGradient>(uri, 1, 0, "ConicalGradient"); + qmlRegisterType<QQuickPathPolyline>(uri, 1, 0, "PathPolyline"); + qmlRegisterType<PolygonProvider>("Qt.test", 1, 0, "PolygonProvider"); + + m_lowPolyLogo << (QPolygonF() << + QPointF(20, 0) << + QPointF(140, 0) << + QPointF(140, 80) << + QPointF(120, 100) << + QPointF(0, 100) << + QPointF(0, 20) << + QPointF(20, 0) ) + << (QPolygonF() << QPointF(20, 80) << + QPointF(60, 80) << + QPointF(80, 60) << + QPointF(80, 20) << + QPointF(40, 20) << + QPointF(20, 40) << + QPointF(20, 80) ) + << (QPolygonF() << QPointF(80, 80) << + QPointF(70, 70) ) + << (QPolygonF() << QPointF(120, 80) << + QPointF(100, 60) << + QPointF(100, 20) ) + << (QPolygonF() << QPointF(100, 40) << + QPointF(120, 40) ); } void tst_QQuickShape::initValues() @@ -311,6 +369,344 @@ void tst_QQuickShape::conicalGrad() qPrintable(errorMessage)); } +void tst_QQuickShape::renderPolyline() +{ + QScopedPointer<QQuickView> window(createView()); + + window->setSource(testFileUrl("pathitem7.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + if ((QGuiApplication::platformName() == QLatin1String("offscreen")) + || (QGuiApplication::platformName() == QLatin1String("minimal"))) + QEXPECT_FAIL("", "Failure due to grabWindow not functional on offscreen/minimimal platforms", Abort); + + QImage img = window->grabWindow(); + QVERIFY(!img.isNull()); + + QImage refImg(testFileUrl("pathitem3.png").toLocalFile()); // It's a recreation of pathitem3 using PathPolyline + QVERIFY(!refImg.isNull()); + + QString errorMessage; + const QImage actualImg = img.convertToFormat(refImg.format()); + const bool res = QQuickVisualTestUtil::compareImages(actualImg, refImg, &errorMessage); + if (!res) { // For visual inspection purposes. + QTest::qWait(5000); + const QString &tempLocation = QStandardPaths::writableLocation(QStandardPaths::TempLocation); + actualImg.save(tempLocation + QLatin1String("/pathitem7.png")); + } + QVERIFY2(res, qPrintable(errorMessage)); +} + +void tst_QQuickShape::renderMultiline() +{ + QScopedPointer<QQuickView> window(createView()); + + window->setSource(testFileUrl("pathitem8.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + if ((QGuiApplication::platformName() == QLatin1String("offscreen")) + || (QGuiApplication::platformName() == QLatin1String("minimal"))) + QEXPECT_FAIL("", "Failure due to grabWindow not functional on offscreen/minimimal platforms", Abort); + + QImage img = window->grabWindow(); + QVERIFY(!img.isNull()); + + QImage refImg(testFileUrl("pathitem8.png").toLocalFile()); + QVERIFY(!refImg.isNull()); + + QString errorMessage; + const QImage actualImg = img.convertToFormat(refImg.format()); + const bool res = QQuickVisualTestUtil::compareImages(actualImg, refImg, &errorMessage); + if (!res) { // For visual inspection purposes. + QTest::qWait(5000); + const QString &tempLocation = QStandardPaths::writableLocation(QStandardPaths::TempLocation); + actualImg.save(tempLocation + QLatin1String("/pathitem8.png")); + } + QVERIFY2(res, qPrintable(errorMessage)); +} + +void tst_QQuickShape::polylineDataTypes_data() +{ + QTest::addColumn<QVariant>("path"); + + QTest::newRow("polygon") << QVariant::fromValue(m_lowPolyLogo.first()); + { + QVector<QPointF> points; + points << m_lowPolyLogo.first(); + QTest::newRow("vector of points") << QVariant::fromValue(points); + } + { + QList<QPointF> points; + for (const auto &point : m_lowPolyLogo.first()) + points << point; + QTest::newRow("list of points") << QVariant::fromValue(points); + } + { + QVariantList points; + for (const auto &point : m_lowPolyLogo.first()) + points << point; + QTest::newRow("QVariantList of points") << QVariant::fromValue(points); + } + { + QVector<QPoint> points; + for (const auto &point : m_lowPolyLogo.first()) + points << point.toPoint(); + QTest::newRow("vector of QPoint (integer points)") << QVariant::fromValue(points); + } + // Oddly, QPolygon is not supported, even though it's really QVector<QPoint>. + // We don't want to have a special case for it in QQuickPathPolyline::setPath(), + // but it could potentially be supported by fixing one of the QVariant conversions. +} + +void tst_QQuickShape::polylineDataTypes() +{ + QFETCH(QVariant, path); + + QScopedPointer<QQuickView> window(createView()); + window->setSource(testFileUrl("polyline.qml")); + QQuickShape *shape = qobject_cast<QQuickShape *>(window->rootObject()); + QVERIFY(shape); + shape->setProperty("path", path); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + if ((QGuiApplication::platformName() == QLatin1String("offscreen")) + || (QGuiApplication::platformName() == QLatin1String("minimal"))) + QEXPECT_FAIL("", "Failure due to grabWindow not functional on offscreen/minimimal platforms", Abort); + + QImage img = window->grabWindow(); + QVERIFY(!img.isNull()); + + QImage refImg(testFileUrl("polyline.png").toLocalFile()); + QVERIFY(!refImg.isNull()); + + QString errorMessage; + const QImage actualImg = img.convertToFormat(refImg.format()); + const bool res = QQuickVisualTestUtil::compareImages(actualImg, refImg, &errorMessage); + if (!res) { // For visual inspection purposes. + QTest::qWait(5000); + const QString &tempLocation = QStandardPaths::writableLocation(QStandardPaths::TempLocation); + actualImg.save(tempLocation + QLatin1String("/polyline.png")); + } + QVERIFY2(res, qPrintable(errorMessage)); + + QCOMPARE(shape->property("path").value<QVector<QPointF>>(), m_lowPolyLogo.first()); + // Verify that QML sees it as an array of points + int i = 0; + for (QPointF p : m_lowPolyLogo.first()) { + QMetaObject::invokeMethod(shape, "checkVertexAt", Q_ARG(QVariant, QVariant::fromValue<int>(i++))); + QCOMPARE(shape->property("vertexBeingChecked").toPointF(), p); + } +} + +void tst_QQuickShape::multilineDataTypes_data() +{ + QTest::addColumn<QVariant>("paths"); + + QTest::newRow("vector of polygons") << QVariant::fromValue(m_lowPolyLogo); + { + QVector<QVector<QPointF>> paths; + for (const auto &poly : m_lowPolyLogo) { + QVector<QPointF> points; + points << poly; + paths << points; + } + QTest::newRow("vector of point vectors") << QVariant::fromValue(paths); + } + { + QList<QVector<QPointF>> paths; + for (const auto &poly : m_lowPolyLogo) { + QVector<QPointF> points; + points << poly; + paths << points; + } + QTest::newRow("list of point vectors") << QVariant::fromValue(paths); + } + { + QList<QList<QPointF>> paths; + for (const auto &poly : m_lowPolyLogo) { + QList<QPointF> points; + for (const auto &point : poly) + points << point; + paths << points; + } + QTest::newRow("list of point lists") << QVariant::fromValue(paths); + } + { + QVariantList paths; + for (const auto &poly : m_lowPolyLogo) { + QVector<QPointF> points; + points << poly; + paths << QVariant::fromValue(points); + } + QTest::newRow("QVariantList of point vectors") << QVariant::fromValue(paths); + } + { + QVariantList paths; + for (const auto &poly : m_lowPolyLogo) { + QList<QPointF> points; + for (const auto &point : poly) + points << point; + paths << QVariant::fromValue(points); + } + QTest::newRow("QVariantList of point lists") << QVariant::fromValue(paths); + } + { + QVariantList paths; + for (const auto &poly : m_lowPolyLogo) { + QVariantList points; + for (const auto &point : poly) + points << point; + paths << QVariant::fromValue(points); + } + QTest::newRow("QVariantList of QVariantLists") << QVariant::fromValue(paths); + } + /* These could be supported if QVariant knew how to convert lists and vectors of QPolygon to QPolygonF. + But they are omitted for now because we don't want to have special cases for them + in QQuickPathMultiline::setPaths(). Floating point is preferred for geometry in Qt Quick. + { + QList<QPolygon> paths; + for (const auto &poly : m_lowPolyLogo) + paths << poly.toPolygon(); + QTest::newRow("list of QPolygon (integer points)") << QVariant::fromValue(paths); + } + { + QVector<QPolygon> paths; + for (const auto &poly : m_lowPolyLogo) + paths << poly.toPolygon(); + QTest::newRow("vector of QPolygon (integer points)") << QVariant::fromValue(paths); + } + */ + { + QList<QList<QPoint>> paths; + for (const auto &poly : m_lowPolyLogo) { + QList<QPoint> points; + for (const auto &point : poly) + points << point.toPoint(); + paths << points; + } + QTest::newRow("list of integer point lists") << QVariant::fromValue(paths); + } + { + QVector<QList<QPoint>> paths; + for (const auto &poly : m_lowPolyLogo) { + QList<QPoint> points; + for (const auto &point : poly) + points << point.toPoint(); + paths << points; + } + QTest::newRow("vector of integer point lists") << QVariant::fromValue(paths); + } + { + QList<QVector<QPoint>> paths; + for (const auto &poly : m_lowPolyLogo) { + QVector<QPoint> points; + for (const auto &point : poly) + points << point.toPoint(); + paths << points; + } + QTest::newRow("list of integer point vectors") << QVariant::fromValue(paths); + } +} + +void tst_QQuickShape::multilineDataTypes() +{ + QFETCH(QVariant, paths); + + QScopedPointer<QQuickView> window(createView()); + window->setSource(testFileUrl("multiline.qml")); + QQuickShape *shape = qobject_cast<QQuickShape *>(window->rootObject()); + QVERIFY(shape); + shape->setProperty("paths", paths); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + if ((QGuiApplication::platformName() == QLatin1String("offscreen")) + || (QGuiApplication::platformName() == QLatin1String("minimal"))) + QEXPECT_FAIL("", "Failure due to grabWindow not functional on offscreen/minimimal platforms", Abort); + + QImage img = window->grabWindow(); + QVERIFY(!img.isNull()); + + QImage refImg(testFileUrl("multiline.png").toLocalFile()); + QVERIFY(!refImg.isNull()); + + QString errorMessage; + const QImage actualImg = img.convertToFormat(refImg.format()); + const bool res = QQuickVisualTestUtil::compareImages(actualImg, refImg, &errorMessage); + if (!res) { // For visual inspection purposes. + QTest::qWait(5000); + const QString &tempLocation = QStandardPaths::writableLocation(QStandardPaths::TempLocation); + actualImg.save(tempLocation + QLatin1String("/multiline.png")); + } + QVERIFY2(res, qPrintable(errorMessage)); + + QVector<QVector<QPointF>> pointVectors; + for (auto v : m_lowPolyLogo) + pointVectors << v; + QCOMPARE(shape->property("paths").value<QVector<QVector<QPointF>>>(), pointVectors); + // Verify that QML sees it as an array of arrays of points + int i = 0; + for (auto pv : m_lowPolyLogo) { + int j = 0; + for (QPointF p : pv) { + QMetaObject::invokeMethod(shape, "checkVertexAt", Q_ARG(QVariant, QVariant::fromValue<int>(i)), Q_ARG(QVariant, QVariant::fromValue<int>(j++))); + QCOMPARE(shape->property("vertexBeingChecked").toPointF(), p); + } + ++i; + } +} + +void tst_QQuickShape::multilineStronglyTyped() +{ + QScopedPointer<QQuickView> window(createView()); + window->setSource(testFileUrl("multilineStronglyTyped.qml")); + QQuickShape *shape = qobject_cast<QQuickShape *>(window->rootObject()); + QVERIFY(shape); + PolygonProvider *provider = shape->findChild<PolygonProvider*>("provider"); + QVERIFY(provider); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + provider->setPaths(m_lowPolyLogo); + + if ((QGuiApplication::platformName() == QLatin1String("offscreen")) + || (QGuiApplication::platformName() == QLatin1String("minimal"))) + QEXPECT_FAIL("", "Failure due to grabWindow not functional on offscreen/minimimal platforms", Abort); + + QImage img = window->grabWindow(); + QVERIFY(!img.isNull()); + + QImage refImg(testFileUrl("multiline.png").toLocalFile()); + QVERIFY(!refImg.isNull()); + + QString errorMessage; + const QImage actualImg = img.convertToFormat(refImg.format()); + const bool res = QQuickVisualTestUtil::compareImages(actualImg, refImg, &errorMessage); + if (!res) { // For visual inspection purposes. + QTest::qWait(5000); + const QString &tempLocation = QStandardPaths::writableLocation(QStandardPaths::TempLocation); + actualImg.save(tempLocation + QLatin1String("/multilineStronglyTyped.png")); + } + QVERIFY2(res, qPrintable(errorMessage)); + + QVector<QVector<QPointF>> pointVectors; + for (auto v : m_lowPolyLogo) + pointVectors << v; + QCOMPARE(shape->property("paths").value<QVector<QVector<QPointF>>>(), pointVectors); + // Verify that QML sees it as an array of arrays of points + int i = 0; + for (auto pv : m_lowPolyLogo) { + int j = 0; + for (QPointF p : pv) { + QMetaObject::invokeMethod(shape, "checkVertexAt", Q_ARG(QVariant, QVariant::fromValue<int>(i)), Q_ARG(QVariant, QVariant::fromValue<int>(j++))); + QCOMPARE(shape->property("vertexBeingChecked").toPointF(), p); + } + ++i; + } +} + QTEST_MAIN(tst_QQuickShape) #include "tst_qquickshape.moc" diff --git a/tests/auto/quick/qquickstates/data/trivialWhen.qml b/tests/auto/quick/qquickstates/data/trivialWhen.qml new file mode 100644 index 0000000000..9f7f3161e9 --- /dev/null +++ b/tests/auto/quick/qquickstates/data/trivialWhen.qml @@ -0,0 +1,5 @@ +import QtQuick 2.12 + +State { + when: true +} diff --git a/tests/auto/quick/qquickstates/tst_qquickstates.cpp b/tests/auto/quick/qquickstates/tst_qquickstates.cpp index 50554f6333..1eb797f54f 100644 --- a/tests/auto/quick/qquickstates/tst_qquickstates.cpp +++ b/tests/auto/quick/qquickstates/tst_qquickstates.cpp @@ -138,6 +138,7 @@ private slots: void QTBUG_38492(); void revertListMemoryLeak(); void duplicateStateName(); + void trivialWhen(); }; void tst_qquickstates::initTestCase() @@ -1665,6 +1666,15 @@ void tst_qquickstates::duplicateStateName() QVERIFY(!item.isNull()); } +// QTBUG-76838 +void tst_qquickstates::trivialWhen() +{ + QQmlEngine engine; + + QQmlComponent c(&engine, testFileUrl("trivialWhen.qml")); + QVERIFY(c.create()); +} + QTEST_MAIN(tst_qquickstates) diff --git a/tests/auto/quick/qquicktableview/data/syncviewsimple.qml b/tests/auto/quick/qquicktableview/data/syncviewsimple.qml new file mode 100644 index 0000000000..012cceaa9d --- /dev/null +++ b/tests/auto/quick/qquicktableview/data/syncviewsimple.qml @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.14 +import QtQuick.Window 2.3 + +Item { + width: 640 + height: 450 + + property alias tableView: mainTableView + property alias tableViewH: tableViewH + property alias tableViewV: tableViewV + property alias tableViewHV: tableViewHV + + Column { + spacing: 10 + TableView { + id: tableViewH + objectName: "Hor" + width: 600 + height: 100 + anchors.margins: 1 + clip: true + delegate: tableViewDelegate + syncView: mainTableView + syncDirection: Qt.Horizontal + } + + TableView { + id: tableViewV + objectName: "Ver" + width: 600 + height: 100 + anchors.margins: 1 + clip: true + delegate: tableViewDelegate + syncView: mainTableView + syncDirection: Qt.Vertical + } + + TableView { + id: tableViewHV + objectName: "HorVer" + width: 600 + height: 100 + anchors.margins: 1 + clip: true + delegate: tableViewDelegate + syncView: mainTableView + } + + TableView { + id: mainTableView + objectName: "root" + width: 600 + height: 100 + anchors.margins: 1 + clip: true + delegate: tableViewDelegate + columnSpacing: 1 + rowSpacing: 1 + + columnWidthProvider: function(c) { return 50 + c } + rowHeightProvider: function(r) { return 25 + r } + } + } + + Component { + id: tableViewDelegate + Rectangle { + objectName: "tableViewDelegate" + color: "lightgray" + border.width: 1 + implicitWidth: 30 + implicitHeight: 60 + + Text { + anchors.centerIn: parent + font.pixelSize: 10 + text: parent.TableView.view.objectName + "\n" + column + ", " + row + } + } + } + +} diff --git a/tests/auto/quick/qquicktableview/qquicktableview.pro b/tests/auto/quick/qquicktableview/qquicktableview.pro index cf831ed5b5..da0c0b01d0 100644 --- a/tests/auto/quick/qquicktableview/qquicktableview.pro +++ b/tests/auto/quick/qquicktableview/qquicktableview.pro @@ -11,5 +11,5 @@ include (../shared/util.pri) TESTDATA = data/* -QT += core-private gui-private qml-private quick-private testlib +QT += core-private gui-private qml-private quick-private testlib qmlmodels-private diff --git a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp index acb475b1c5..889175a228 100644 --- a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp +++ b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp @@ -38,8 +38,8 @@ #include <QtQml/qqmlcontext.h> #include <QtQml/qqmlexpression.h> #include <QtQml/qqmlincubator.h> -#include <QtQml/private/qqmlobjectmodel_p.h> -#include <QtQml/private/qqmllistmodel_p.h> +#include <QtQmlModels/private/qqmlobjectmodel_p.h> +#include <QtQmlModels/private/qqmllistmodel_p.h> #include "testmodel.h" @@ -50,24 +50,23 @@ using namespace QQuickViewTestUtil; using namespace QQuickVisualTestUtil; -static const char* kTableViewPropName = "tableView"; static const char* kDelegateObjectName = "tableViewDelegate"; static const char *kDelegatesCreatedCountProp = "delegatesCreatedCount"; static const char *kModelDataBindingProp = "modelDataBinding"; Q_DECLARE_METATYPE(QMarginsF); -#define DECLARE_TABLEVIEW_VARIABLES \ - auto tableView = view->rootObject()->property(kTableViewPropName).value<QQuickTableView *>(); \ - QVERIFY(tableView); \ - auto tableViewPrivate = QQuickTableViewPrivate::get(tableView); \ - Q_UNUSED(tableViewPrivate) +#define GET_QML_TABLEVIEW(PROPNAME) \ + auto PROPNAME = view->rootObject()->property(#PROPNAME).value<QQuickTableView *>(); \ + QVERIFY(PROPNAME); \ + auto PROPNAME ## Private = QQuickTableViewPrivate::get(PROPNAME); \ + Q_UNUSED(PROPNAME ## Private) #define LOAD_TABLEVIEW(fileName) \ view->setSource(testFileUrl(fileName)); \ view->show(); \ QVERIFY(QTest::qWaitForWindowActive(view)); \ - DECLARE_TABLEVIEW_VARIABLES + GET_QML_TABLEVIEW(tableView) #define LOAD_TABLEVIEW_ASYNC(fileName) \ view->setSource(testFileUrl("asyncloader.qml")); \ @@ -77,11 +76,12 @@ Q_DECLARE_METATYPE(QMarginsF); loader->setSource(QUrl::fromLocalFile("data/" fileName)); \ QTRY_VERIFY(loader->item()); \ QCOMPARE(loader->status(), QQuickLoader::Status::Ready); \ - DECLARE_TABLEVIEW_VARIABLES + GET_QML_TABLEVIEW(tableView) -#define WAIT_UNTIL_POLISHED \ - QVERIFY(QQuickTest::qIsPolishScheduled(tableView)); \ - QVERIFY(QQuickTest::qWaitForItemPolished(tableView)) +#define WAIT_UNTIL_POLISHED_ARG(item) \ + QVERIFY(QQuickTest::qIsPolishScheduled(item)); \ + QVERIFY(QQuickTest::qWaitForItemPolished(item)) +#define WAIT_UNTIL_POLISHED WAIT_UNTIL_POLISHED_ARG(tableView) class tst_QQuickTableView : public QQmlDataTest { @@ -123,6 +123,9 @@ private slots: void checkContentWidthAndHeight(); void checkPageFlicking(); void checkExplicitContentWidthAndHeight(); + void checkExtents_origin(); + void checkExtents_endExtent(); + void checkExtents_moveTableToEdge(); void checkContentXY(); void noDelegate(); void changeDelegateDuringUpdate(); @@ -163,6 +166,13 @@ private slots: void hideRowsAndColumns_data(); void hideRowsAndColumns(); void checkThatRevisionedPropertiesCannotBeUsedInOldImports(); + void checkSyncView_rootView_data(); + void checkSyncView_rootView(); + void checkSyncView_childViews_data(); + void checkSyncView_childViews(); + void checkSyncView_differentSizedModels(); + void checkSyncView_connect_late_data(); + void checkSyncView_connect_late(); }; tst_QQuickTableView::tst_QQuickTableView() @@ -579,7 +589,7 @@ void tst_QQuickTableView::checkForceLayoutEndUpDoingALayout() void tst_QQuickTableView::checkContentWidthAndHeight() { - // Check that contentWidth/Height reports the correct size of the the + // Check that contentWidth/Height reports the correct size of the // table, based on knowledge of the rows and columns that has been loaded. LOAD_TABLEVIEW("contentwidthheight.qml"); @@ -590,11 +600,7 @@ void tst_QQuickTableView::checkContentWidthAndHeight() const int tableSize = 100; const int cellSizeSmall = 100; - const int cellSizeLarge = 200; const int spacing = 1; - const int smallCellCount = 20; - const int largeCellCount = tableSize - smallCellCount; - const qreal accumulatedSpacing = ((tableSize - 1) * spacing); auto model = TestModelAsVariant(tableSize, tableSize); tableView->setModel(model); @@ -607,72 +613,12 @@ void tst_QQuickTableView::checkContentWidthAndHeight() QCOMPARE(tableViewPrivate->averageEdgeSize.width(), cellSizeSmall); QCOMPARE(tableViewPrivate->averageEdgeSize.height(), cellSizeSmall); - // Flick in 5 more rows and columns, but not so far that we start loading in - // the ones that are bigger. Loading in more rows and columns of the same - // size as the initial ones should not change the first prediction. - qreal flickTo = ((cellSizeSmall + spacing) * 5); - tableView->setContentX(flickTo); - tableView->setContentY(flickTo); + // Flick to the end, and check that content width/height stays unchanged + tableView->setContentX(tableView->contentWidth() - tableView->width()); + tableView->setContentY(tableView->contentHeight() - tableView->height()); QCOMPARE(tableView->contentWidth(), expectedSizeInit); QCOMPARE(tableView->contentHeight(), expectedSizeInit); - QCOMPARE(tableViewPrivate->averageEdgeSize.width(), cellSizeSmall); - QCOMPARE(tableViewPrivate->averageEdgeSize.height(), cellSizeSmall); - - // Flick to row and column 20 (smallCellCount), since there the row and - // column sizes increases with 100. Check that TableView then adjusts - // contentWidth and contentHeight accordingly. - flickTo = ((cellSizeSmall + spacing) * smallCellCount) - spacing; - tableView->setContentX(flickTo); - tableView->setContentY(flickTo); - - // Since we move the viewport more than a page, tableview - // will jump to the new position and do a rebuild. - QVERIFY(tableViewPrivate->polishScheduled); - QVERIFY(tableViewPrivate->rebuildScheduled); - WAIT_UNTIL_POLISHED; - - // Check that the average cell size is now matching the - // large cells since they fill up the whole view. - QCOMPARE(tableViewPrivate->averageEdgeSize.width(), cellSizeLarge); - QCOMPARE(tableViewPrivate->averageEdgeSize.height(), cellSizeLarge); - - const int largeSizeCellCountInView = qCeil(tableView->width() / cellSizeLarge); - const int columnCount = smallCellCount + largeSizeCellCountInView; - QCOMPARE(tableViewPrivate->leftColumn(), smallCellCount); - QCOMPARE(tableViewPrivate->rightColumn(), columnCount - 1); - - const qreal firstHalfLength = smallCellCount * cellSizeSmall; - const qreal secondHalfOneScreenLength = largeSizeCellCountInView * cellSizeLarge; - const qreal lengthAfterFlick = firstHalfLength + secondHalfOneScreenLength; - - // Check that loadedTableOuterRect has been calculated correct thus far - const qreal spacingAfterFlick = (smallCellCount + largeSizeCellCountInView - 1) * spacing; - QCOMPARE(tableViewPrivate->loadedTableOuterRect.left(), flickTo + spacing); - QCOMPARE(tableViewPrivate->loadedTableOuterRect.right(), lengthAfterFlick + spacingAfterFlick); - QCOMPARE(tableViewPrivate->loadedTableOuterRect.top(), flickTo + spacing); - QCOMPARE(tableViewPrivate->loadedTableOuterRect.bottom(), lengthAfterFlick + spacingAfterFlick); - - // At this point, we should have the exact content width/height set, because - // TableView knows where the large cells start in the viewport, and how many - // columns that remain in the model. It will assume that the rest of the the - // columns have the same average size as the ones currently inside the viewport. - const qreal expectedContentSize = (smallCellCount * cellSizeSmall) + (largeCellCount * cellSizeLarge) + accumulatedSpacing; - QCOMPARE(tableView->contentWidth(), expectedContentSize); - QCOMPARE(tableView->contentHeight(), expectedContentSize); - - // Flick to the end (row/column 100, and overshoot a bit), and - // check that we then end up with the exact content width/height. - const qreal secondHalfLength = largeCellCount * cellSizeLarge; - const qreal expectedFullSize = (firstHalfLength + secondHalfLength) + accumulatedSpacing; - const qreal overshoot = 100; - const qreal endPosX = expectedFullSize - tableView->width() + overshoot; - const qreal endPosY = expectedFullSize - tableView->height() + overshoot; - tableView->setContentX(endPosX); - tableView->setContentY(endPosY); - - QCOMPARE(tableView->contentWidth(), expectedFullSize); - QCOMPARE(tableView->contentHeight(), expectedFullSize); // Flick back to start tableView->setContentX(0); @@ -681,10 +627,10 @@ void tst_QQuickTableView::checkContentWidthAndHeight() // Since we move the viewport more than a page, tableview // will jump to the new position and do a rebuild. QVERIFY(tableViewPrivate->polishScheduled); - QVERIFY(tableViewPrivate->rebuildScheduled); + QVERIFY(tableViewPrivate->scheduledRebuildOptions); WAIT_UNTIL_POLISHED; - // We should now have the same content width/height as when we started + // We should still have the same content width/height as when we started QCOMPARE(tableView->contentWidth(), expectedSizeInit); QCOMPARE(tableView->contentHeight(), expectedSizeInit); } @@ -716,7 +662,6 @@ void tst_QQuickTableView::checkPageFlicking() QCOMPARE(tableViewPrivate->averageEdgeSize.width(), cellWidth); QCOMPARE(tableViewPrivate->averageEdgeSize.height(), cellHeight); - QVERIFY(!tableViewPrivate->rebuildScheduled); QCOMPARE(tableViewPrivate->scheduledRebuildOptions, QQuickTableViewPrivate::RebuildOption::None); // Flick 5000 columns to the right, and check that this triggers a @@ -726,7 +671,6 @@ void tst_QQuickTableView::checkPageFlicking() const qreal flickToColumnInPixels = ((cellWidth + columnSpacing) * flickToColumn) - columnSpacing; tableView->setContentX(flickToColumnInPixels); - QVERIFY(tableViewPrivate->rebuildScheduled); QVERIFY(tableViewPrivate->scheduledRebuildOptions & QQuickTableViewPrivate::RebuildOption::ViewportOnly); QVERIFY(tableViewPrivate->scheduledRebuildOptions & QQuickTableViewPrivate::RebuildOption::CalculateNewTopLeftColumn); QVERIFY(!(tableViewPrivate->scheduledRebuildOptions & QQuickTableViewPrivate::RebuildOption::CalculateNewTopLeftRow)); @@ -748,7 +692,6 @@ void tst_QQuickTableView::checkPageFlicking() const qreal flickToRowInPixels = ((cellHeight + rowSpacing) * flickToRow) - rowSpacing; tableView->setContentY(flickToRowInPixels); - QVERIFY(tableViewPrivate->rebuildScheduled); QVERIFY(tableViewPrivate->scheduledRebuildOptions & QQuickTableViewPrivate::RebuildOption::ViewportOnly); QVERIFY(!(tableViewPrivate->scheduledRebuildOptions & QQuickTableViewPrivate::RebuildOption::CalculateNewTopLeftColumn)); QVERIFY(tableViewPrivate->scheduledRebuildOptions & QQuickTableViewPrivate::RebuildOption::CalculateNewTopLeftRow); @@ -783,6 +726,164 @@ void tst_QQuickTableView::checkExplicitContentWidthAndHeight() QCOMPARE(tableView->contentHeight(), 1000); } +void tst_QQuickTableView::checkExtents_origin() +{ + // Check that if the beginning of the content view doesn't match the + // actual size of the table, origin will be adjusted to make it fit. + LOAD_TABLEVIEW("contentwidthheight.qml"); + + const int rows = 10; + const int columns = rows; + const qreal columnWidth = 100; + const qreal rowHeight = 100; + const qreal actualTableSize = columns * columnWidth; + + // Set a content size that is far too large + // compared to the size of the table. + tableView->setContentWidth(actualTableSize * 2); + tableView->setContentHeight(actualTableSize * 2); + tableView->setRowSpacing(0); + tableView->setColumnSpacing(0); + tableView->setLeftMargin(0); + tableView->setRightMargin(0); + tableView->setTopMargin(0); + tableView->setBottomMargin(0); + + auto model = TestModelAsVariant(rows, columns); + tableView->setModel(model); + + WAIT_UNTIL_POLISHED; + + // Flick slowly to column 5 (to avoid rebuilds). Flick two columns at a + // time to ensure that we create a gap before TableView gets a chance to + // adjust endExtent first. This gap on the right side will make TableView + // move the table to move to the edge. Because of this, the table will not + // be aligned at the start of the content view when we next flick back again. + // And this will cause origin to move. + for (int x = 0; x <= 6; x += 2) { + tableView->setContentX(x * columnWidth); + tableView->setContentY(x * rowHeight); + } + + // Check that the table has now been moved one column to the right + // (One column because that's how far outside the table we ended up flicking above). + QCOMPARE(tableViewPrivate->loadedTableOuterRect.right(), actualTableSize + columnWidth); + + // Flick back one column at a time so that TableView detects that the first + // column is not at the origin before the "table move" logic kicks in. This + // will make TableView adjust the origin. + for (int x = 6; x >= 0; x -= 1) { + tableView->setContentX(x * columnWidth); + tableView->setContentY(x * rowHeight); + } + + // The origin will be moved with the same offset that the table was + // moved on the right side earlier, which is one column length. + QCOMPARE(tableViewPrivate->origin.x(), columnWidth); + QCOMPARE(tableViewPrivate->origin.y(), rowHeight); +} + +void tst_QQuickTableView::checkExtents_endExtent() +{ + // Check that if we the content view size doesn't match the actual size + // of the table, endExtent will be adjusted to make it fit (so that + // e.g the the flicking will bounce to a stop at the edge of the table). + LOAD_TABLEVIEW("contentwidthheight.qml"); + + const int rows = 10; + const int columns = rows; + const qreal columnWidth = 100; + const qreal rowHeight = 100; + const qreal actualTableSize = columns * columnWidth; + + // Set a content size that is far too large + // compared to the size of the table. + tableView->setContentWidth(actualTableSize * 2); + tableView->setContentHeight(actualTableSize * 2); + tableView->setRowSpacing(0); + tableView->setColumnSpacing(0); + tableView->setLeftMargin(0); + tableView->setRightMargin(0); + tableView->setTopMargin(0); + tableView->setBottomMargin(0); + + auto model = TestModelAsVariant(rows, columns); + tableView->setModel(model); + + WAIT_UNTIL_POLISHED; + + // Flick slowly to column 5 (to avoid rebuilds). This will flick the table to + // the last column in the model. But since there still is a lot space left in + // the content view, endExtent will be set accordingly to compensate. + for (int x = 1; x <= 5; x++) + tableView->setContentX(x * columnWidth); + QCOMPARE(tableViewPrivate->rightColumn(), columns - 1); + qreal expectedEndExtentWidth = actualTableSize - tableView->contentWidth(); + QCOMPARE(tableViewPrivate->endExtent.width(), expectedEndExtentWidth); + + for (int y = 1; y <= 5; y++) + tableView->setContentY(y * rowHeight); + QCOMPARE(tableViewPrivate->bottomRow(), rows - 1); + qreal expectedEndExtentHeight = actualTableSize - tableView->contentHeight(); + QCOMPARE(tableViewPrivate->endExtent.height(), expectedEndExtentHeight); +} + +void tst_QQuickTableView::checkExtents_moveTableToEdge() +{ + // Check that if we the content view size doesn't match the actual + // size of the table, and we fast-flick the viewport to outside + // the table, we end up moving the table back into the viewport to + // avoid any visual glitches. + LOAD_TABLEVIEW("contentwidthheight.qml"); + + const int rows = 10; + const int columns = rows; + const qreal columnWidth = 100; + const qreal rowHeight = 100; + const qreal actualTableSize = columns * columnWidth; + + // Set a content size that is far to large + // compared to the size of the table. + tableView->setContentWidth(actualTableSize * 2); + tableView->setContentHeight(actualTableSize * 2); + tableView->setRowSpacing(0); + tableView->setColumnSpacing(0); + tableView->setLeftMargin(0); + tableView->setRightMargin(0); + tableView->setTopMargin(0); + tableView->setBottomMargin(0); + + auto model = TestModelAsVariant(rows, columns); + tableView->setModel(model); + + WAIT_UNTIL_POLISHED; + + // Flick slowly to column 5 (to avoid rebuilds). Flick two columns at a + // time to ensure that we create a gap before TableView gets a chance to + // adjust endExtent first. This gap on the right side will make TableView + // move the table to the edge (in addition to adjusting the extents, but that + // will happen in a subsequent polish, and is not for this test verify). + for (int x = 0; x <= 6; x += 2) + tableView->setContentX(x * columnWidth); + QCOMPARE(tableViewPrivate->rightColumn(), columns - 1); + QCOMPARE(tableViewPrivate->loadedTableOuterRect, tableViewPrivate->viewportRect); + + for (int y = 0; y <= 6; y += 2) + tableView->setContentY(y * rowHeight); + QCOMPARE(tableViewPrivate->bottomRow(), rows - 1); + QCOMPARE(tableViewPrivate->loadedTableOuterRect, tableViewPrivate->viewportRect); + + for (int x = 6; x >= 0; x -= 2) + tableView->setContentX(x * columnWidth); + QCOMPARE(tableViewPrivate->leftColumn(), 0); + QCOMPARE(tableViewPrivate->loadedTableOuterRect, tableViewPrivate->viewportRect); + + for (int y = 6; y >= 0; y -= 2) + tableView->setContentY(y * rowHeight); + QCOMPARE(tableViewPrivate->topRow(), 0); + QCOMPARE(tableViewPrivate->loadedTableOuterRect, tableViewPrivate->viewportRect); +} + void tst_QQuickTableView::checkContentXY() { // Check that you can bind contentX and contentY to @@ -1165,8 +1266,11 @@ void tst_QQuickTableView::checkSpacingValues() tableView->polish(); WAIT_UNTIL_POLISHED; - QCOMPARE(tableView->contentWidth(), columnCount * (delegateWidth + tableView->columnSpacing()) - tableView->columnSpacing()); - QCOMPARE(tableView->contentHeight(), rowCount * (delegateHeight + tableView->rowSpacing()) - tableView->rowSpacing()); + + const qreal expectedInitialContentWidth = columnCount * (delegateWidth + tableView->columnSpacing()) - tableView->columnSpacing(); + const qreal expectedInitialContentHeight = rowCount * (delegateHeight + tableView->rowSpacing()) - tableView->rowSpacing(); + QCOMPARE(tableView->contentWidth(), expectedInitialContentWidth); + QCOMPARE(tableView->contentHeight(), expectedInitialContentHeight); // Valid spacing assignment tableView->setRowSpacing(42); @@ -1176,8 +1280,13 @@ void tst_QQuickTableView::checkSpacingValues() tableView->polish(); WAIT_UNTIL_POLISHED; - QCOMPARE(tableView->contentWidth(), columnCount * (delegateWidth + tableView->columnSpacing()) - tableView->columnSpacing()); - QCOMPARE(tableView->contentHeight(), rowCount * (delegateHeight + tableView->rowSpacing()) - tableView->rowSpacing()); + + // Even after changing spacing, TableView will keep the initial guesstimated content size. The + // reason is that deciding the content size based on the currently visible row/columns/spacing + // will almost always be at a little bit wrong at best. So instead of pretending that TableView + // knows what the size of the full table is, it sticks with the first guesstimate. + QCOMPARE(tableView->contentWidth(), expectedInitialContentWidth); + QCOMPARE(tableView->contentHeight(), expectedInitialContentHeight); // Invalid assignments (should ignore) tableView->setRowSpacing(-1); @@ -1969,7 +2078,7 @@ void tst_QQuickTableView::checkChangingModelFromDelegate() // And since the QML code tried to add another row as well, we // expect rebuildScheduled to be true, and a polish event to be pending. - QCOMPARE(tableViewPrivate->rebuildScheduled, true); + QVERIFY(tableViewPrivate->scheduledRebuildOptions); QCOMPARE(tableViewPrivate->polishScheduled, true); WAIT_UNTIL_POLISHED; @@ -2052,7 +2161,7 @@ void tst_QQuickTableView::checkTableviewInsideAsyncLoader() QCOMPARE(loader->status(), QQuickLoader::Ready); // Check that TableView has finished building - QCOMPARE(tableViewPrivate->rebuildScheduled, false); + QVERIFY(!tableViewPrivate->scheduledRebuildOptions); QCOMPARE(tableViewPrivate->rebuildState, QQuickTableViewPrivate::RebuildState::Done); // Check that all expected delegate items have been loaded @@ -2179,6 +2288,337 @@ void tst_QQuickTableView::checkThatRevisionedPropertiesCannotBeUsedInOldImports( QCOMPARE(resolvedColumn, 42); } +void tst_QQuickTableView::checkSyncView_rootView_data() +{ + QTest::addColumn<qreal>("flickToPos"); + + QTest::newRow("pos:110") << 110.; + QTest::newRow("pos:2010") << 2010.; +} + +void tst_QQuickTableView::checkSyncView_rootView() +{ + // Check that if you flick on the root tableview (the view that has + // no other view as syncView), all the other tableviews will sync + // their content view position according to their syncDirection flag. + QFETCH(qreal, flickToPos); + LOAD_TABLEVIEW("syncviewsimple.qml"); + GET_QML_TABLEVIEW(tableViewH); + GET_QML_TABLEVIEW(tableViewV); + GET_QML_TABLEVIEW(tableViewHV); + QQuickTableView *views[] = {tableViewH, tableViewV, tableViewHV}; + + auto model = TestModelAsVariant(100, 100); + + tableView->setModel(model); + for (auto view : views) + view->setModel(model); + + tableView->setContentX(flickToPos); + tableView->setContentY(flickToPos); + + WAIT_UNTIL_POLISHED; + + // Check that geometry properties are mirrored + QCOMPARE(tableViewH->columnSpacing(), tableView->columnSpacing()); + QCOMPARE(tableViewH->rowSpacing(), 0); + QCOMPARE(tableViewH->contentWidth(), tableView->contentWidth()); + QCOMPARE(tableViewV->columnSpacing(), 0); + QCOMPARE(tableViewV->rowSpacing(), tableView->rowSpacing()); + QCOMPARE(tableViewV->contentHeight(), tableView->contentHeight()); + + // Check that viewport is in sync after the flick + QCOMPARE(tableView->contentX(), flickToPos); + QCOMPARE(tableView->contentY(), flickToPos); + QCOMPARE(tableViewH->contentX(), tableView->contentX()); + QCOMPARE(tableViewH->contentY(), 0); + QCOMPARE(tableViewV->contentX(), 0); + QCOMPARE(tableViewV->contentY(), tableView->contentY()); + QCOMPARE(tableViewHV->contentX(), tableView->contentX()); + QCOMPARE(tableViewHV->contentY(), tableView->contentY()); + + // Check that topLeft cell is in sync after the flick + QCOMPARE(tableViewHPrivate->leftColumn(), tableViewPrivate->leftColumn()); + QCOMPARE(tableViewHPrivate->rightColumn(), tableViewPrivate->rightColumn()); + QCOMPARE(tableViewHPrivate->topRow(), 0); + QCOMPARE(tableViewVPrivate->leftColumn(), 0); + QCOMPARE(tableViewVPrivate->topRow(), tableViewPrivate->topRow()); + QCOMPARE(tableViewHVPrivate->leftColumn(), tableViewPrivate->leftColumn()); + QCOMPARE(tableViewHVPrivate->topRow(), tableViewPrivate->topRow()); + + // Check that the geometry of the tables are in sync after the flick + QCOMPARE(tableViewHPrivate->loadedTableOuterRect.left(), tableViewPrivate->loadedTableOuterRect.left()); + QCOMPARE(tableViewHPrivate->loadedTableOuterRect.right(), tableViewPrivate->loadedTableOuterRect.right()); + QCOMPARE(tableViewHPrivate->loadedTableOuterRect.top(), 0); + + QCOMPARE(tableViewVPrivate->loadedTableOuterRect.top(), tableViewPrivate->loadedTableOuterRect.top()); + QCOMPARE(tableViewVPrivate->loadedTableOuterRect.bottom(), tableViewPrivate->loadedTableOuterRect.bottom()); + QCOMPARE(tableViewVPrivate->loadedTableOuterRect.left(), 0); + + QCOMPARE(tableViewHVPrivate->loadedTableOuterRect, tableViewPrivate->loadedTableOuterRect); +} + +void tst_QQuickTableView::checkSyncView_childViews_data() +{ + QTest::addColumn<int>("viewIndexToFlick"); + QTest::addColumn<qreal>("flickToPos"); + + QTest::newRow("tableViewH, pos:100") << 0 << 100.; + QTest::newRow("tableViewV, pos:100") << 1 << 100.; + QTest::newRow("tableViewHV, pos:100") << 2 << 100.; + QTest::newRow("tableViewH, pos:2000") << 0 << 2000.; + QTest::newRow("tableViewV, pos:2000") << 1 << 2000.; + QTest::newRow("tableViewHV, pos:2000") << 2 << 2000.; +} + +void tst_QQuickTableView::checkSyncView_childViews() +{ + // Check that if you flick on a tableview that has a syncView, the + // syncView will move to the new position as well, which will also + // recursivly move all other connected child views of the syncView. + QFETCH(int, viewIndexToFlick); + QFETCH(qreal, flickToPos); + LOAD_TABLEVIEW("syncviewsimple.qml"); + GET_QML_TABLEVIEW(tableViewH); + GET_QML_TABLEVIEW(tableViewV); + GET_QML_TABLEVIEW(tableViewHV); + QQuickTableView *views[] = {tableViewH, tableViewV, tableViewHV}; + QQuickTableView *viewToFlick = views[viewIndexToFlick]; + QQuickTableViewPrivate *viewToFlickPrivate = QQuickTableViewPrivate::get(viewToFlick); + + auto model = TestModelAsVariant(100, 100); + + tableView->setModel(model); + for (auto view : views) + view->setModel(model); + + viewToFlick->setContentX(flickToPos); + viewToFlick->setContentY(flickToPos); + + WAIT_UNTIL_POLISHED; + + // The view the user flicks on can always be flicked in both directions + // (unless is has a flickingDirection set, which is not the case here). + QCOMPARE(viewToFlick->contentX(), flickToPos); + QCOMPARE(viewToFlick->contentY(), flickToPos); + + // The root view (tableView) will move in sync according + // to the syncDirection of the view being flicked. + if (viewToFlick->syncDirection() & Qt::Horizontal) { + QCOMPARE(tableView->contentX(), flickToPos); + QCOMPARE(tableViewPrivate->leftColumn(), viewToFlickPrivate->leftColumn()); + QCOMPARE(tableViewPrivate->rightColumn(), viewToFlickPrivate->rightColumn()); + QCOMPARE(tableViewPrivate->loadedTableOuterRect.left(), viewToFlickPrivate->loadedTableOuterRect.left()); + QCOMPARE(tableViewPrivate->loadedTableOuterRect.right(), viewToFlickPrivate->loadedTableOuterRect.right()); + } else { + QCOMPARE(tableView->contentX(), 0); + QCOMPARE(tableViewPrivate->leftColumn(), 0); + QCOMPARE(tableViewPrivate->loadedTableOuterRect.left(), 0); + } + + if (viewToFlick->syncDirection() & Qt::Vertical) { + QCOMPARE(tableView->contentY(), flickToPos); + QCOMPARE(tableViewPrivate->topRow(), viewToFlickPrivate->topRow()); + QCOMPARE(tableViewPrivate->bottomRow(), viewToFlickPrivate->bottomRow()); + QCOMPARE(tableViewPrivate->loadedTableOuterRect.top(), viewToFlickPrivate->loadedTableOuterRect.top()); + QCOMPARE(tableViewPrivate->loadedTableOuterRect.bottom(), viewToFlickPrivate->loadedTableOuterRect.bottom()); + } else { + QCOMPARE(tableView->contentY(), 0); + QCOMPARE(tableViewPrivate->topRow(), 0); + QCOMPARE(tableViewPrivate->loadedTableOuterRect.top(), 0); + } + + // The other views should continue to stay in sync with + // the root view, unless it was the view being flicked. + if (viewToFlick != tableViewH) { + QCOMPARE(tableViewH->contentX(), tableView->contentX()); + QCOMPARE(tableViewH->contentY(), 0); + QCOMPARE(tableViewHPrivate->leftColumn(), tableViewPrivate->leftColumn()); + QCOMPARE(tableViewHPrivate->rightColumn(), tableViewPrivate->rightColumn()); + QCOMPARE(tableViewHPrivate->loadedTableOuterRect.left(), tableViewPrivate->loadedTableOuterRect.left()); + QCOMPARE(tableViewHPrivate->loadedTableOuterRect.right(), tableViewPrivate->loadedTableOuterRect.right()); + QCOMPARE(tableViewHPrivate->topRow(), 0); + QCOMPARE(tableViewHPrivate->loadedTableOuterRect.top(), 0); + } + + if (viewToFlick != tableViewV) { + QCOMPARE(tableViewV->contentX(), 0); + QCOMPARE(tableViewV->contentY(), tableView->contentY()); + QCOMPARE(tableViewVPrivate->topRow(), tableViewPrivate->topRow()); + QCOMPARE(tableViewVPrivate->bottomRow(), tableViewPrivate->bottomRow()); + QCOMPARE(tableViewVPrivate->loadedTableOuterRect.top(), tableViewPrivate->loadedTableOuterRect.top()); + QCOMPARE(tableViewVPrivate->loadedTableOuterRect.bottom(), tableViewPrivate->loadedTableOuterRect.bottom()); + QCOMPARE(tableViewVPrivate->leftColumn(), 0); + QCOMPARE(tableViewVPrivate->loadedTableOuterRect.left(), 0); + } + + if (viewToFlick != tableViewHV) { + QCOMPARE(tableViewHV->contentX(), tableView->contentX()); + QCOMPARE(tableViewHV->contentY(), tableView->contentY()); + QCOMPARE(tableViewHVPrivate->leftColumn(), tableViewPrivate->leftColumn()); + QCOMPARE(tableViewHVPrivate->rightColumn(), tableViewPrivate->rightColumn()); + QCOMPARE(tableViewHVPrivate->topRow(), tableViewPrivate->topRow()); + QCOMPARE(tableViewHVPrivate->bottomRow(), tableViewPrivate->bottomRow()); + QCOMPARE(tableViewHVPrivate->loadedTableOuterRect, tableViewPrivate->loadedTableOuterRect); + } +} + +void tst_QQuickTableView::checkSyncView_differentSizedModels() +{ + // Check that you can have two tables in a syncView relation, where + // the sync "child" has fewer rows/columns than the syncView. In that + // case, it will be possible to flick the syncView further out than + // the child have rows/columns to follow. This causes some extra + // challenges for TableView to ensure that they are still kept in + // sync once you later flick the syncView back to a point where both + // tables ends up visible. This test will check this sitiation. + LOAD_TABLEVIEW("syncviewsimple.qml"); + GET_QML_TABLEVIEW(tableViewH); + GET_QML_TABLEVIEW(tableViewV); + GET_QML_TABLEVIEW(tableViewHV); + + auto tableViewModel = TestModelAsVariant(100, 100); + auto tableViewHModel = TestModelAsVariant(100, 50); + auto tableViewVModel = TestModelAsVariant(50, 100); + auto tableViewHVModel = TestModelAsVariant(5, 5); + + tableView->setModel(tableViewModel); + tableViewH->setModel(tableViewHModel); + tableViewV->setModel(tableViewVModel); + tableViewHV->setModel(tableViewHVModel); + + WAIT_UNTIL_POLISHED; + + // Flick far out, beyond the smaller tables, which will + // also force a rebuild (and as such, cause layout properties + // like average cell width to be temporarily out of sync). + tableView->setContentX(5000); + QVERIFY(tableViewPrivate->scheduledRebuildOptions); + + WAIT_UNTIL_POLISHED; + + // Check that the smaller tables are now flicked out of view + qreal leftEdge = tableViewPrivate->loadedTableOuterRect.left(); + QVERIFY(tableViewHPrivate->loadedTableOuterRect.right() < leftEdge); + QVERIFY(tableViewHVPrivate->loadedTableOuterRect.right() < leftEdge); + + // Flick slowly back so that we don't trigger a rebuild (since + // we want to check that we stay in sync also when not rebuilding). + while (tableView->contentX() > 200) { + tableView->setContentX(tableView->contentX() - 200); + QVERIFY(!tableViewPrivate->rebuildOptions); + QVERIFY(!tableViewPrivate->polishScheduled); + } + + leftEdge = tableViewPrivate->loadedTableOuterRect.left(); + const int leftColumn = tableViewPrivate->leftColumn(); + QCOMPARE(tableViewHPrivate->loadedTableOuterRect.left(), leftEdge); + QCOMPARE(tableViewHPrivate->leftColumn(), leftColumn); + + // Because the tableView was fast flicked and then slowly flicked back, the + // left column is now 49, which is actually far too high, since we're almost + // at the beginning of the content view. But this "miscalculation" is expected + // when the column widths are increasing for each column, like they do in this + // test. In that case, the algorithm that predicts where each column should end + // up gets slightly confused. Anyway, check that tableViewHV, that has only + // 5 columns, is not showing any columns, since it should always stay in sync with + // syncView regardless of the content view position. + QVERIFY(tableViewHVPrivate->loadedColumns.isEmpty()); +} + +void tst_QQuickTableView::checkSyncView_connect_late_data() +{ + QTest::addColumn<qreal>("flickToPos"); + + QTest::newRow("pos:110") << 110.; + QTest::newRow("pos:2010") << 2010.; +} + +void tst_QQuickTableView::checkSyncView_connect_late() +{ + // Check that if you assign a syncView to a TableView late, and + // after the views have been flicked around, they will still end up in sync. + QFETCH(qreal, flickToPos); + LOAD_TABLEVIEW("syncviewsimple.qml"); + GET_QML_TABLEVIEW(tableViewH); + GET_QML_TABLEVIEW(tableViewV); + GET_QML_TABLEVIEW(tableViewHV); + QQuickTableView *views[] = {tableViewH, tableViewV, tableViewHV}; + + auto model = TestModelAsVariant(100, 100); + + tableView->setModel(model); + + // Start with no syncView connections + for (auto view : views) { + view->setSyncView(nullptr); + view->setModel(model); + } + + WAIT_UNTIL_POLISHED; + + tableView->setContentX(flickToPos); + tableView->setContentY(flickToPos); + + WAIT_UNTIL_POLISHED; + + // Check that viewport is not in sync after the flick + QCOMPARE(tableView->contentX(), flickToPos); + QCOMPARE(tableView->contentY(), flickToPos); + QCOMPARE(tableViewH->contentX(), 0); + QCOMPARE(tableViewH->contentY(), 0); + QCOMPARE(tableViewV->contentX(), 0); + QCOMPARE(tableViewV->contentY(), 0); + QCOMPARE(tableViewHV->contentX(), 0); + QCOMPARE(tableViewHV->contentY(), 0); + + // Assign the syncView late. This should make + // all the views end up in sync. + for (auto view : views) { + view->setSyncView(tableView); + WAIT_UNTIL_POLISHED_ARG(view); + } + + // Check that geometry properties are mirrored + QCOMPARE(tableViewH->columnSpacing(), tableView->columnSpacing()); + QCOMPARE(tableViewH->rowSpacing(), 0); + QCOMPARE(tableViewH->contentWidth(), tableView->contentWidth()); + QCOMPARE(tableViewV->columnSpacing(), 0); + QCOMPARE(tableViewV->rowSpacing(), tableView->rowSpacing()); + QCOMPARE(tableViewV->contentHeight(), tableView->contentHeight()); + + // Check that viewport is in sync after the flick + QCOMPARE(tableView->contentX(), flickToPos); + QCOMPARE(tableView->contentY(), flickToPos); + QCOMPARE(tableViewH->contentX(), tableView->contentX()); + QCOMPARE(tableViewH->contentY(), 0); + QCOMPARE(tableViewV->contentX(), 0); + QCOMPARE(tableViewV->contentY(), tableView->contentY()); + QCOMPARE(tableViewHV->contentX(), tableView->contentX()); + QCOMPARE(tableViewHV->contentY(), tableView->contentY()); + + // Check that topLeft cell is in sync after the flick + QCOMPARE(tableViewHPrivate->leftColumn(), tableViewPrivate->leftColumn()); + QCOMPARE(tableViewHPrivate->rightColumn(), tableViewPrivate->rightColumn()); + QCOMPARE(tableViewHPrivate->topRow(), 0); + QCOMPARE(tableViewVPrivate->leftColumn(), 0); + QCOMPARE(tableViewVPrivate->topRow(), tableViewPrivate->topRow()); + QCOMPARE(tableViewHVPrivate->leftColumn(), tableViewPrivate->leftColumn()); + QCOMPARE(tableViewHVPrivate->topRow(), tableViewPrivate->topRow()); + + // Check that the geometry of the tables are in sync after the flick + QCOMPARE(tableViewHPrivate->loadedTableOuterRect.left(), tableViewPrivate->loadedTableOuterRect.left()); + QCOMPARE(tableViewHPrivate->loadedTableOuterRect.right(), tableViewPrivate->loadedTableOuterRect.right()); + QCOMPARE(tableViewHPrivate->loadedTableOuterRect.top(), 0); + + QCOMPARE(tableViewVPrivate->loadedTableOuterRect.top(), tableViewPrivate->loadedTableOuterRect.top()); + QCOMPARE(tableViewVPrivate->loadedTableOuterRect.bottom(), tableViewPrivate->loadedTableOuterRect.bottom()); + QCOMPARE(tableViewVPrivate->loadedTableOuterRect.left(), 0); + + QCOMPARE(tableViewHVPrivate->loadedTableOuterRect, tableViewPrivate->loadedTableOuterRect); + +} + QTEST_MAIN(tst_QQuickTableView) #include "tst_qquicktableview.moc" diff --git a/tests/auto/quick/qquicktext/BLACKLIST b/tests/auto/quick/qquicktext/BLACKLIST index 531d981159..6c3c3af154 100644 --- a/tests/auto/quick/qquicktext/BLACKLIST +++ b/tests/auto/quick/qquicktext/BLACKLIST @@ -1,6 +1,6 @@ [dependentImplicitSizes] -* -[lineLaidOutRelayout] -msvc-2015 +b2qt +qemu +osx-10.12 [fontSizeMode] opensuse-42.1 diff --git a/tests/auto/quick/qquicktextedit/BLACKLIST b/tests/auto/quick/qquicktextedit/BLACKLIST new file mode 100644 index 0000000000..9df9c7d75a --- /dev/null +++ b/tests/auto/quick/qquicktextedit/BLACKLIST @@ -0,0 +1,2 @@ +[mouseSelection] +opensuse-leap diff --git a/tests/auto/quick/qquickview/tst_qquickview.cpp b/tests/auto/quick/qquickview/tst_qquickview.cpp index e259ed1ae7..dbce0d308c 100644 --- a/tests/auto/quick/qquickview/tst_qquickview.cpp +++ b/tests/auto/quick/qquickview/tst_qquickview.cpp @@ -49,6 +49,7 @@ private slots: void errors(); void engine(); void findChild(); + void setInitialProperties(); }; @@ -283,6 +284,17 @@ void tst_QQuickView::findChild() QVERIFY(!view.rootObject()->findChild<QObject *>("rootObject")); // self } +void tst_QQuickView::setInitialProperties() +{ + QQuickView view; + view.setInitialProperties({{"z", 4}, {"width", 100}}); + view.setSource(testFileUrl("resizemodeitem.qml")); + QObject *rootObject = view.rootObject(); + QVERIFY(rootObject); + QCOMPARE(rootObject->property("z").toInt(), 4); + QCOMPARE(rootObject->property("width").toInt(), 100); +} + QTEST_MAIN(tst_QQuickView) #include "tst_qquickview.moc" diff --git a/tests/auto/quick/qquickvisualdatamodel/qquickvisualdatamodel.pro b/tests/auto/quick/qquickvisualdatamodel/qquickvisualdatamodel.pro index 9222e39477..5445f6768d 100644 --- a/tests/auto/quick/qquickvisualdatamodel/qquickvisualdatamodel.pro +++ b/tests/auto/quick/qquickvisualdatamodel/qquickvisualdatamodel.pro @@ -9,5 +9,5 @@ include (../shared/util.pri) TESTDATA = data/* -QT += core-private gui-private qml-private quick-private testlib +QT += core-private gui-private qml-private quick-private testlib qmlmodels-private qtHaveModule(widgets): QT += widgets diff --git a/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp b/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp index fac8283e2c..fe56cad018 100644 --- a/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp +++ b/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp @@ -39,7 +39,7 @@ #include <QtQuick/qquickview.h> #include <private/qquicklistview_p.h> #include <QtQuick/private/qquicktext_p.h> -#include <QtQml/private/qqmldelegatemodel_p.h> +#include <QtQmlModels/private/qqmldelegatemodel_p.h> #include <private/qqmlvaluetype_p.h> #include <private/qqmlchangeset_p.h> #include <private/qqmlengine_p.h> @@ -432,6 +432,7 @@ private slots: void asynchronousCancel(); void invalidContext(); void externalManagedModel(); + void delegateModelChangeDelegate(); private: template <int N> void groups_verify( @@ -4302,6 +4303,45 @@ void tst_qquickvisualdatamodel::externalManagedModel() QTRY_VERIFY(!object->property("running").toBool()); } +void tst_qquickvisualdatamodel::delegateModelChangeDelegate() +{ + // Verify that QTBUG-63477 is fixed. + // Changing the delegate would not update existing items. + QQmlEngine engine; + QScopedPointer<QQmlContext> context(new QQmlContext(engine.rootContext())); + + QQmlComponent c(&engine); + c.setData("import QtQml.Models 2.2\nDelegateModel {}\n", QUrl()); + QCOMPARE(c.status(), QQmlComponent::Ready); + + QQmlDelegateModel *visualModel = qobject_cast<QQmlDelegateModel*>(c.create(context.data())); + QVERIFY(visualModel); + visualModel->setModel(QVariant(3)); + + QQmlComponent first(&engine); + first.setData("import QtQuick 2.0\nItem { objectName: \"old\" }\n", QUrl()); + QCOMPARE(first.status(), QQmlComponent::Ready); + + // Without delegate, claim to have an item count of 0 + QCOMPARE(visualModel->count(), 0); + + visualModel->setDelegate(&first); + // The first delegate has been set, verify we get it + QObject* old = visualModel->object(0, QQmlIncubator::Synchronous); + QVERIFY(old); + QCOMPARE(visualModel->object(0, QQmlIncubator::Synchronous)->objectName(), QStringLiteral("old")); + QCOMPARE(visualModel->count(), 3); + + QQmlComponent second(&engine); + second.setData("import QtQuick 2.0\nItem { objectName: \"new\" }\n", QUrl()); + QCOMPARE(second.status(), QQmlComponent::Ready); + + visualModel->setDelegate(&second); + // After changing the delegate, expect the existing item to have the new delegate + QCOMPARE(visualModel->object(0, QQmlIncubator::Synchronous)->objectName(), QStringLiteral("new")); + QCOMPARE(visualModel->count(), 3); +} + QTEST_MAIN(tst_qquickvisualdatamodel) #include "tst_qquickvisualdatamodel.moc" diff --git a/tests/auto/quick/qquickwindow/BLACKLIST b/tests/auto/quick/qquickwindow/BLACKLIST index 1282a9d5ec..b4b7d2d761 100644 --- a/tests/auto/quick/qquickwindow/BLACKLIST +++ b/tests/auto/quick/qquickwindow/BLACKLIST @@ -1,6 +1,3 @@ [openglContextCreatedSignal] opensuse-42.3 opensuse-leap -# QTBUG-62177 -[attachedProperty] -osx diff --git a/tests/auto/quick/qquickwindow/data/shortcut.qml b/tests/auto/quick/qquickwindow/data/shortcut.qml new file mode 100644 index 0000000000..2632e27859 --- /dev/null +++ b/tests/auto/quick/qquickwindow/data/shortcut.qml @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.9 +import QtQuick.Window 2.2 + +Window { + id: root + visible: true + width: 200 + height: 200 + property bool received: false + Item { + focus: true + Shortcut { + sequence: "B" + onActivated: { + root.received = true + } + } + } +} + diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index f08b9207d1..7faa621e86 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -482,6 +482,10 @@ private slots: void testChildMouseEventFilter_data(); void cleanupGrabsOnRelease(); +#if QT_CONFIG(shortcut) + void testShortCut(); +#endif + private: QTouchDevice *touchDevice; QTouchDevice *touchDeviceWithVelocity; @@ -3579,6 +3583,30 @@ void tst_qquickwindow::cleanupGrabsOnRelease() QCOMPARE(parent->mouseUngrabEventCount, 1); } +#if QT_CONFIG(shortcut) +void tst_qquickwindow::testShortCut() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.loadUrl(testFileUrl("shortcut.qml")); + + QObject *created = component.create(); + QScopedPointer<QObject> cleanup(created); + QVERIFY(created); + + QQuickWindow *window = qobject_cast<QQuickWindow *>(created); + QVERIFY(QTest::qWaitForWindowActive(window)); + + EventFilter eventFilter; + window->activeFocusItem()->installEventFilter(&eventFilter); + //Send non-spontaneous key press event + QKeyEvent keyEvent(QEvent::KeyPress, Qt::Key_B, Qt::NoModifier); + QCoreApplication::sendEvent(window, &keyEvent); + QVERIFY(eventFilter.events.contains(int(QEvent::ShortcutOverride))); + QVERIFY(window->property("received").value<bool>()); +} +#endif + QTEST_MAIN(tst_qquickwindow) #include "tst_qquickwindow.moc" diff --git a/tests/auto/quick/quick.pro b/tests/auto/quick/quick.pro index 7257a99d2a..b6ffdc0f7e 100644 --- a/tests/auto/quick/quick.pro +++ b/tests/auto/quick/quick.pro @@ -29,6 +29,7 @@ PRIVATETESTS += \ qquickanimations \ qquickapplication \ qquickbehaviors \ + qquickboundaryrule \ qquickfontloader \ qquickfontloader_static \ qquickfontmetrics \ diff --git a/tests/auto/quick/rendernode/tst_rendernode.cpp b/tests/auto/quick/rendernode/tst_rendernode.cpp index 0e06ee6f50..6ca5231343 100644 --- a/tests/auto/quick/rendernode/tst_rendernode.cpp +++ b/tests/auto/quick/rendernode/tst_rendernode.cpp @@ -49,8 +49,7 @@ public: view.setResizeMode(QQuickView::SizeViewToRootObject); view.setSource(testFileUrl(fileName)); view.setVisible(true); - QTest::qWaitForWindowExposed(&view); - return view.grabWindow(); + return QTest::qWaitForWindowExposed(&view) ? view.grabWindow() : QImage(); } //It is important for platforms that only are able to show fullscreen windows @@ -61,6 +60,9 @@ private slots: void renderOrder(); void messUpState(); void matrix(); + +private: + bool isRunningOnRhi() const; }; class ClearNode : public QSGRenderNode @@ -218,7 +220,11 @@ void tst_rendernode::renderOrder() || (QGuiApplication::platformName() == QLatin1String("minimal"))) QSKIP("Skipping due to grabWindow not functional on offscreen/minimimal platforms"); + if (isRunningOnRhi()) + QSKIP("Render nodes not yet supported with QRhi"); + QImage fb = runTest("RenderOrder.qml"); + QVERIFY(!fb.isNull()); const qreal scaleFactor = QGuiApplication::primaryScreen()->devicePixelRatio(); QCOMPARE(fb.width(), qRound(200 * scaleFactor)); @@ -247,7 +253,11 @@ void tst_rendernode::messUpState() || (QGuiApplication::platformName() == QLatin1String("minimal"))) QSKIP("Skipping due to grabWindow not functional on offscreen/minimimal platforms"); + if (isRunningOnRhi()) + QSKIP("Render nodes not yet supported with QRhi"); + QImage fb = runTest("MessUpState.qml"); + QVERIFY(!fb.isNull()); int x1 = 0; int x2 = fb.width() / 2; int x3 = fb.width() - 1; @@ -304,9 +314,12 @@ void tst_rendernode::matrix() || (QGuiApplication::platformName() == QLatin1String("minimal"))) QSKIP("Skipping due to grabWindow not functional on offscreen/minimimal platforms"); + if (isRunningOnRhi()) + QSKIP("Render nodes not yet supported with QRhi"); + qmlRegisterType<StateRecordingRenderNodeItem>("RenderNode", 1, 0, "StateRecorder"); StateRecordingRenderNode::matrices.clear(); - runTest("matrix.qml"); + QVERIFY(!runTest("matrix.qml").isNull()); QMatrix4x4 noRotateOffset; noRotateOffset.translate(20, 20); @@ -351,6 +364,22 @@ void tst_rendernode::matrix() } } +bool tst_rendernode::isRunningOnRhi() const +{ + static bool retval = false; + static bool decided = false; + if (!decided) { + decided = true; + QQuickView dummy; + dummy.show(); + if (QTest::qWaitForWindowExposed(&dummy)) { + QSGRendererInterface::GraphicsApi api = dummy.rendererInterface()->graphicsApi(); + retval = QSGRendererInterface::isApiRhiBased(api); + } + dummy.hide(); + } + return retval; +} QTEST_MAIN(tst_rendernode) diff --git a/tests/auto/quick/scenegraph/data/render_bug37422.frag b/tests/auto/quick/scenegraph/data/render_bug37422.frag new file mode 100644 index 0000000000..da157232ac --- /dev/null +++ b/tests/auto/quick/scenegraph/data/render_bug37422.frag @@ -0,0 +1,9 @@ +#version 440 + +layout(location = 0) in vec2 qt_TexCoord0; +layout(location = 0) out vec4 fragColor; + +void main() +{ + fragColor = vec4(1, 0, 0, 1); +} diff --git a/tests/auto/quick/scenegraph/data/render_bug37422.frag.qsb b/tests/auto/quick/scenegraph/data/render_bug37422.frag.qsb Binary files differnew file mode 100644 index 0000000000..1233ccfbca --- /dev/null +++ b/tests/auto/quick/scenegraph/data/render_bug37422.frag.qsb diff --git a/tests/auto/quick/scenegraph/data/render_bug37422.qml b/tests/auto/quick/scenegraph/data/render_bug37422.qml index c1b47eddb8..09661d8ccb 100644 --- a/tests/auto/quick/scenegraph/data/render_bug37422.qml +++ b/tests/auto/quick/scenegraph/data/render_bug37422.qml @@ -26,7 +26,7 @@ ** ****************************************************************************/ -import QtQuick 2.2 +import QtQuick 2.12 /* The test verifies that batching does not interfere with overlapping @@ -71,7 +71,9 @@ RenderTestBase width: 100 height: 9 y: 10 - fragmentShader: "varying highp vec2 qt_TexCoord0; void main() { gl_FragColor = vec4(1, 0, 0, 1); }" + fragmentShader: GraphicsInfo.shaderType === GraphicsInfo.GLSL + ? "varying highp vec2 qt_TexCoord0; void main() { gl_FragColor = vec4(1, 0, 0, 1); }" + : "file:data/render_bug37422.frag.qsb" Rectangle { width: 5 diff --git a/tests/auto/quick/scenegraph/tst_scenegraph.cpp b/tests/auto/quick/scenegraph/tst_scenegraph.cpp index 063358c795..3f605348c3 100644 --- a/tests/auto/quick/scenegraph/tst_scenegraph.cpp +++ b/tests/auto/quick/scenegraph/tst_scenegraph.cpp @@ -70,16 +70,17 @@ public: QColor color() const { return m_color; } - QSGNode *updatePaintNode(QSGNode *old, UpdatePaintNodeData *) + QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *) { - if (old) - delete old; - - QSGNode *node = new QSGNode(); - - for (int y=0; y<height(); ++y) { - for (int x=0; x<width(); ++x) { - QSGSimpleRectNode *rn = new QSGSimpleRectNode(); + delete node; + node = new QSGNode; + + const int w = int(width()); + const int h = int(height()); + QQuickWindow *win = window(); + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; ++x) { + QSGRectangleNode *rn = win->createRectangleNode(); rn->setRect(x, y, 1, 1); rn->setColor(m_color); node->appendChildNode(rn); @@ -90,7 +91,7 @@ public: } Q_SIGNALS: - void colorChanged(const QColor &c ); + void colorChanged(const QColor &c); private: QColor m_color; @@ -117,7 +118,8 @@ private slots: private: bool m_brokenMipmapSupport; QQuickView *createView(const QString &file, QWindow *parent = nullptr, int x = -1, int y = -1, int w = -1, int h = -1); - bool isRunningOnOpenGL(); + bool isRunningOnOpenGLDirectly(); + bool isRunningOnRhi(); }; template <typename T> class ScopedList : public QList<T> { @@ -402,7 +404,7 @@ void tst_SceneGraph::render_data() << "render_bug37422.qml" << "render_OpacityThroughBatchRoot.qml"; if (!m_brokenMipmapSupport) - files << "render_Mipmap.qml"; + files << "render_Mipmap.qml"; QRegExp sampleCount("#samples: *(\\d+)"); // X:int Y:int R:float G:float B:float Error:float @@ -444,8 +446,8 @@ void tst_SceneGraph::render_data() void tst_SceneGraph::render() { - if (!isRunningOnOpenGL()) - QSKIP("Skipping complex rendering tests due to not running with OpenGL"); + if (!isRunningOnOpenGLDirectly() && !isRunningOnRhi()) + QSKIP("Skipping complex rendering tests due to not running with OpenGL or QRhi"); QFETCH(QString, file); QFETCH(QList<Sample>, baseStage); @@ -495,7 +497,7 @@ void tst_SceneGraph::render() // current on the other window. void tst_SceneGraph::hideWithOtherContext() { - if (!isRunningOnOpenGL()) + if (!isRunningOnOpenGLDirectly()) QSKIP("Skipping OpenGL context test due to not running with OpenGL"); QWindow window; @@ -559,15 +561,37 @@ void tst_SceneGraph::createTextureFromImage() QCOMPARE(texture->hasAlphaChannel(), expectedAlpha); } -bool tst_SceneGraph::isRunningOnOpenGL() +bool tst_SceneGraph::isRunningOnOpenGLDirectly() { - bool retval = false; - QQuickView dummy; - dummy.show(); - QTest::qWaitForWindowExposed(&dummy); - if (dummy.rendererInterface()->graphicsApi() == QSGRendererInterface::OpenGL) - retval = true; - dummy.hide(); + static bool retval = false; + static bool decided = false; + if (!decided) { + decided = true; + QQuickView dummy; + dummy.show(); + if (QTest::qWaitForWindowExposed(&dummy)) + retval = dummy.rendererInterface()->graphicsApi() == QSGRendererInterface::OpenGL; + dummy.hide(); + } + return retval; +} + +bool tst_SceneGraph::isRunningOnRhi() +{ + static bool retval = false; + static bool decided = false; + if (!decided) { + decided = true; + QQuickView dummy; + dummy.show(); + if (!QTest::qWaitForWindowExposed(&dummy)) { + [](){ QFAIL("Could not show a QQuickView"); }(); + return false; + } + QSGRendererInterface::GraphicsApi api = dummy.rendererInterface()->graphicsApi(); + retval = QSGRendererInterface::isApiRhiBased(api); + dummy.hide(); + } return retval; } diff --git a/tests/auto/quick/shared/viewtestutil.cpp b/tests/auto/quick/shared/viewtestutil.cpp index 5631ffe047..1680e850f7 100644 --- a/tests/auto/quick/shared/viewtestutil.cpp +++ b/tests/auto/quick/shared/viewtestutil.cpp @@ -328,7 +328,8 @@ QQuickViewTestUtil::ListRange QQuickViewTestUtil::ListRange::operator+(const Lis bool QQuickViewTestUtil::ListRange::operator==(const ListRange &other) const { - return indexes.toSet() == other.indexes.toSet(); + return QSet<int>(indexes.cbegin(), indexes.cend()) + == QSet<int>(other.indexes.cbegin(), other.indexes.cend()); } bool QQuickViewTestUtil::ListRange::operator!=(const ListRange &other) const |