diff options
author | Shawn Rutledge <shawn.rutledge@qt.io> | 2021-04-29 16:47:23 +0200 |
---|---|---|
committer | Shawn Rutledge <shawn.rutledge@qt.io> | 2021-05-06 00:40:09 +0200 |
commit | 152e12dc22cc0fd07cf90bcd35ae0e05b8b46fa0 (patch) | |
tree | 86c0baf106c945805848bb33e40b385900a7ace6 /tests | |
parent | 1dc4bdbabfed1eb73b563a77810ceb00a8fd6fca (diff) |
Begin testing QQDeliveryAgent subscenes
The first test is passiveGrabberOrder(), to verify that if the user
interacts with handlers in a subscene and the root scene simultaneously,
the passive grab order and the order of the signals from the handlers
is deterministic: the subscene goes first because it's conceptually
"on top".
Change-Id: Id00ab1497fbd3c9d7afa02f8098699bd569ded70
Reviewed-by: Andy Nichols <andy.nichols@qt.io>
Diffstat (limited to 'tests')
-rw-r--r-- | tests/auto/quick/CMakeLists.txt | 1 | ||||
-rw-r--r-- | tests/auto/quick/qquickdeliveryagent/CMakeLists.txt | 42 | ||||
-rw-r--r-- | tests/auto/quick/qquickdeliveryagent/data/tapHandler.qml | 14 | ||||
-rw-r--r-- | tests/auto/quick/qquickdeliveryagent/tst_qquickdeliveryagent.cpp | 186 | ||||
-rw-r--r-- | tests/auto/quick/shared/visualtestutil.h | 27 |
5 files changed, 270 insertions, 0 deletions
diff --git a/tests/auto/quick/CMakeLists.txt b/tests/auto/quick/CMakeLists.txt index f75201515d..b4e34734c4 100644 --- a/tests/auto/quick/CMakeLists.txt +++ b/tests/auto/quick/CMakeLists.txt @@ -30,6 +30,7 @@ if(QT_FEATURE_private_tests) add_subdirectory(qquickdynamicpropertyanimation) add_subdirectory(qquickborderimage) add_subdirectory(qquickwindow) + add_subdirectory(qquickdeliveryagent) add_subdirectory(qquickdrag) add_subdirectory(qquickdroparea) add_subdirectory(qquickflickable) diff --git a/tests/auto/quick/qquickdeliveryagent/CMakeLists.txt b/tests/auto/quick/qquickdeliveryagent/CMakeLists.txt new file mode 100644 index 0000000000..1a5490effa --- /dev/null +++ b/tests/auto/quick/qquickdeliveryagent/CMakeLists.txt @@ -0,0 +1,42 @@ +##################################################################### +## tst_qquickdeliveryagent Test: +##################################################################### + +# Collect test data +file(GLOB_RECURSE test_data_glob + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + data/*) +list(APPEND test_data ${test_data_glob}) + +qt_internal_add_test(tst_qquickdeliveryagent + SOURCES + ../../shared/util.cpp ../../shared/util.h + ../shared/geometrytestutil.cpp ../shared/geometrytestutil.h + ../shared/viewtestutil.cpp ../shared/viewtestutil.h + ../shared/visualtestutil.cpp ../shared/visualtestutil.h + tst_qquickdeliveryagent.cpp + DEFINES + QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\" + INCLUDE_DIRECTORIES + ../../shared + PUBLIC_LIBRARIES + Qt::CorePrivate + Qt::Gui + Qt::GuiPrivate + Qt::QmlPrivate + Qt::QuickPrivate + TESTDATA ${test_data} +) + +## Scopes: +##################################################################### + +qt_internal_extend_target(tst_qquickdeliveryagent CONDITION ANDROID OR IOS + DEFINES + QT_QMLTEST_DATADIR=\\\":/data\\\" +) + +qt_internal_extend_target(tst_qquickdeliveryagent CONDITION NOT ANDROID AND NOT IOS + DEFINES + QT_QMLTEST_DATADIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/data\\\" +) diff --git a/tests/auto/quick/qquickdeliveryagent/data/tapHandler.qml b/tests/auto/quick/qquickdeliveryagent/data/tapHandler.qml new file mode 100644 index 0000000000..e2ba88dcde --- /dev/null +++ b/tests/auto/quick/qquickdeliveryagent/data/tapHandler.qml @@ -0,0 +1,14 @@ +import QtQuick + +Rectangle { + id: root + objectName: "root" + color: th.pressed ? "goldenrod" : "khaki" + border.color: "black" + width: 100; height: 100 + TapHandler { + id: th + objectName: root.objectName + "Tap" + onTapped: console.log(this, objectName) + } +} diff --git a/tests/auto/quick/qquickdeliveryagent/tst_qquickdeliveryagent.cpp b/tests/auto/quick/qquickdeliveryagent/tst_qquickdeliveryagent.cpp new file mode 100644 index 0000000000..324ad9cd33 --- /dev/null +++ b/tests/auto/quick/qquickdeliveryagent/tst_qquickdeliveryagent.cpp @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** Copyright (C) 2021 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 <qtest.h> +#include <QSignalSpy> +#include <QDebug> +#include <QtQml/QQmlEngine> +#include <QtQml/QQmlComponent> +#include <QtQuick/QQuickItem> +#include <QtQuick/QQuickView> +#include <QtQuick/QQuickWindow> +#include <QtQuick/private/qquickshadereffectsource_p.h> +#include <QtQuick/private/qquicktaphandler_p.h> +#include <QtQuick/private/qquickwindow_p.h> +#include "../../shared/util.h" +#include "../shared/visualtestutil.h" +#include "../shared/viewtestutil.h" + +Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests") + +struct ViewportTransformHelper : public QQuickDeliveryAgent::Transform +{ + QPointF offset = QPointF(50, 50); + + // Transforms window coordinates to subscene coordinates. + QPointF map(const QPointF &viewportPoint) override { + qCDebug(lcTests) << viewportPoint << "->" << viewportPoint - offset; + return viewportPoint - offset; + } +}; + +// A QQuick3DViewport simulator +class SubsceneRootItem : public QQuickShaderEffectSource +{ +public: + SubsceneRootItem(QQuickItem *source, QRectF bounds, QQuickItem *parent = nullptr) + : QQuickShaderEffectSource(parent), + deliveryAgent(QQuickItemPrivate::get(source)->ensureSubsceneDeliveryAgent()) + { + setAcceptedMouseButtons(Qt::AllButtons); + setAcceptTouchEvents(true); + setAcceptHoverEvents(true); + setSourceItem(source); + setSize(bounds.size()); + setPosition(bounds.topLeft()); + setOpacity(0.5); + deliveryAgent->setObjectName("subscene"); + } + + QQuickDeliveryAgent *deliveryAgent = nullptr; + +protected: + bool event(QEvent *e) override { + if (e->isPointerEvent()) { + bool ret = false; + auto pe = static_cast<QPointerEvent *>(e); + + QVarLengthArray<QPointF, 16> originalScenePositions; + originalScenePositions.resize(pe->pointCount()); + for (int pointIndex = 0; pointIndex < pe->pointCount(); ++pointIndex) + originalScenePositions[pointIndex] = pe->point(pointIndex).scenePosition(); + + for (int pointIndex = 0; pointIndex < pe->pointCount(); ++pointIndex) { + QMutableEventPoint &mut = QMutableEventPoint::from(pe->point(pointIndex)); + mut.setScenePosition(vxh->map(mut.scenePosition())); + mut.setPosition(mut.position()); + } + + qCDebug(lcTests) << "forwarding to subscene DA" << pe; + if (deliveryAgent->event(pe)) { + ret = true; + if (QQuickDeliveryAgentPrivate::anyPointGrabbed(pe)) + deliveryAgent->setSceneTransform(vxh); // takes ownership + } + + // restore original scene positions + for (int pointIndex = 0; pointIndex < pe->pointCount(); ++pointIndex) + QMutableEventPoint::from(pe->point(pointIndex)).setScenePosition(originalScenePositions.at(pointIndex)); + + pe->setAccepted(false); // reject implicit grab and let it keep propagating + qCDebug(lcTests) << e << "returning" << ret; + return ret; + } else { + return QQuickShaderEffectSource::event(e); + } + } + + ViewportTransformHelper *vxh = new ViewportTransformHelper; +}; + +class tst_qquickdeliveryagent : public QQmlDataTest +{ + Q_OBJECT +public: + tst_qquickdeliveryagent() + { + } + +private slots: + void passiveGrabberOrder(); + +private: + QScopedPointer<QPointingDevice> touchDevice = QScopedPointer<QPointingDevice>(QTest::createTouchDevice()); +}; + +void tst_qquickdeliveryagent::passiveGrabberOrder() +{ + QQuickView view; + QQmlComponent component(view.engine()); + component.loadUrl(testFileUrl("tapHandler.qml")); + view.setContent(QUrl(), &component, component.create()); + view.resize(160, 160); + QQuickItem *root = qobject_cast<QQuickItem*>(view.rootObject()); + QVERIFY(root); + QQuickTapHandler *rootTap = root->findChild<QQuickTapHandler *>(); + QVERIFY(rootTap); + + QScopedPointer<QQuickItem> subsceneRect(qobject_cast<QQuickItem *>(component.createWithInitialProperties({{"objectName", "child"}}))); + QVERIFY(subsceneRect); + QQuickTapHandler *subsceneTap = subsceneRect->findChild<QQuickTapHandler *>(); + QVERIFY(subsceneTap); + + SubsceneRootItem subscene(subsceneRect.data(), {50, 50, 100, 100}, view.rootObject()); + QCOMPARE(subsceneRect->parentItem(), nullptr); + QQuickDeliveryAgent *windowAgent = QQuickWindowPrivate::get(&view)->deliveryAgent; + windowAgent->setObjectName("window"); + QVERIFY(subscene.deliveryAgent); + QVERIFY(subscene.deliveryAgent != windowAgent); + QQuickVisualTestUtil::SignalMultiSpy spy; + QVERIFY(spy.connectToSignal(rootTap, &QQuickTapHandler::tapped)); + QVERIFY(spy.connectToSignal(subsceneTap, &QQuickTapHandler::tapped)); + + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QPoint pos(75, 75); + QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, pos); + QTest::qWait(1000); + auto devPriv = QPointingDevicePrivate::get(QPointingDevice::primaryPointingDevice()); + const auto &persistentPoint = devPriv->activePoints.values().first(); + qCDebug(lcTests) << "passive grabbers" << persistentPoint.passiveGrabbers << "contexts" << persistentPoint.passiveGrabbersContext; + QCOMPARE(persistentPoint.passiveGrabbers.count(), 2); + QCOMPARE(persistentPoint.passiveGrabbers.first(), subsceneTap); + QCOMPARE(persistentPoint.passiveGrabbersContext.first(), subscene.deliveryAgent); + QCOMPARE(persistentPoint.passiveGrabbers.last(), rootTap); + + QTest::mouseRelease(&view, Qt::LeftButton); + QTest::qWait(100); + // QQuickWindow::event() has failsafe: clear all grabbers after release + QCOMPARE(persistentPoint.passiveGrabbers.count(), 0); + + qCDebug(lcTests) << "TapHandlers emitted tapped in this order:" << spy.senders; + QCOMPARE(spy.senders.count(), 2); + // passive grabbers are visited in order, and emit tapped() at that time + QCOMPARE(spy.senders.first(), subsceneTap); + QCOMPARE(spy.senders.last(), rootTap); +} + +QTEST_MAIN(tst_qquickdeliveryagent) + +#include "tst_qquickdeliveryagent.moc" diff --git a/tests/auto/quick/shared/visualtestutil.h b/tests/auto/quick/shared/visualtestutil.h index e623e3e225..c07cecfa47 100644 --- a/tests/auto/quick/shared/visualtestutil.h +++ b/tests/auto/quick/shared/visualtestutil.h @@ -98,6 +98,33 @@ namespace QQuickVisualTestUtil } bool compareImages(const QImage &ia, const QImage &ib, QString *errorMessage); + + struct SignalMultiSpy : public QObject + { + Q_OBJECT + public: + QList<QObject *> senders; + QList<QByteArray> signalNames; + + template <typename Func1> + QMetaObject::Connection connectToSignal(const typename QtPrivate::FunctionPointer<Func1>::Object *obj, Func1 signal, + Qt::ConnectionType type = Qt::AutoConnection) + { + return connect(obj, signal, this, &SignalMultiSpy::receive, type); + } + + void clear() { + senders.clear(); + signalNames.clear(); + } + + public slots: + void receive() { + QMetaMethod m = sender()->metaObject()->method(senderSignalIndex()); + senders << sender(); + signalNames << m.name(); + } + }; } #endif // QQUICKVISUALTESTUTIL_H |