Skip to content

Commit

Permalink
Add QQuickTest::pointerPress/Move/Release convenience functions
Browse files Browse the repository at this point in the history
More autotests should test mouse, especially touch and maybe tablet
events, with similar expectations, instead of testing mainly mouse and
mostly neglecting the other device types. This makes it easier to work
with data-driven tests that take specific QPointingDevice instances as
test data.

Started using them in tst_TapHandler::gesturePolicyDragWithinBounds()
which was already data-driven by device type. Clearly, this code is
now easier to read and less repetitious.

In tst_qquickwindow::subclassWithPointerEventVirtualOverrides() the
improvement is somewhat smaller, but this test validates that
tablet events are working.

Change-Id: I74d0fcc2f082af3737a0754c58205fa2b18c1a2d
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
  • Loading branch information
ec1oud committed Oct 20, 2022
1 parent b879180 commit 59d967a
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 54 deletions.
87 changes: 87 additions & 0 deletions src/quicktestutils/quick/viewtestutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <QtQuick/QQuickView>
#include <QtQuick/QQuickView>
#include <QtGui/QScreen>
#include <QtGui/qpa/qwindowsysteminterface.h>

#include <QtTest/QTest>

Expand Down Expand Up @@ -446,6 +447,10 @@ namespace QQuickTouchUtils {

}

namespace QTest {
int Q_TESTLIB_EXPORT defaultMouseDelay();
}

namespace QQuickTest {

/*! \internal
Expand Down Expand Up @@ -503,6 +508,88 @@ namespace QQuickTest {
return false;
return true;
}

// TODO maybe move the generic pointerPress/Move/Release functions to QTestLib later on

static Qt::MouseButton pressedTabletButton = Qt::NoButton;
static Qt::KeyboardModifiers pressedTabletModifiers = Qt::NoModifier;

void pointerPress(const QPointingDevice *dev, QQuickWindow *window, int pointId, const QPoint &p,
Qt::MouseButton button, Qt::KeyboardModifiers modifiers)
{
switch (dev->type()) {
case QPointingDevice::DeviceType::Mouse:
case QPointingDevice::DeviceType::TouchPad:
QTest::mousePress(window, button, modifiers, p);
break;
case QPointingDevice::DeviceType::TouchScreen:
QTest::touchEvent(window, const_cast<QPointingDevice *>(dev)).press(pointId, p, window);
QQuickTouchUtils::flush(window);
break;
case QPointingDevice::DeviceType::Puck:
case QPointingDevice::DeviceType::Stylus:
case QPointingDevice::DeviceType::Airbrush:
QTest::lastMouseTimestamp += QTest::defaultMouseDelay();
pressedTabletButton = button;
pressedTabletModifiers = modifiers;
QWindowSystemInterface::handleTabletEvent(window, QTest::lastMouseTimestamp, dev, p, window->mapToGlobal(p),
button, 0.8, 0, 0, 0, 0, 0, modifiers);
break;
default:
qWarning() << "can't send a press event from" << dev;
break;
}
}

void pointerMove(const QPointingDevice *dev, QQuickWindow *window, int pointId, const QPoint &p)
{
switch (dev->type()) {
case QPointingDevice::DeviceType::Mouse:
case QPointingDevice::DeviceType::TouchPad:
QTest::mouseMove(window, p);
break;
case QPointingDevice::DeviceType::TouchScreen:
QTest::touchEvent(window, const_cast<QPointingDevice *>(dev)).move(pointId, p, window);
QQuickTouchUtils::flush(window);
break;
case QPointingDevice::DeviceType::Puck:
case QPointingDevice::DeviceType::Stylus:
case QPointingDevice::DeviceType::Airbrush:
QTest::lastMouseTimestamp += QTest::defaultMouseDelay();
QWindowSystemInterface::handleTabletEvent(window, QTest::lastMouseTimestamp, dev, p, window->mapToGlobal(p),
pressedTabletButton, 0, 0, 0, 0, 0, 0, pressedTabletModifiers);
break;
default:
qWarning() << "can't send a move event from" << dev;
break;
}
}

void pointerRelease(const QPointingDevice *dev, QQuickWindow *window, int pointId, const QPoint &p,
Qt::MouseButton button, Qt::KeyboardModifiers modifiers)
{
switch (dev->type()) {
case QPointingDevice::DeviceType::Mouse:
case QPointingDevice::DeviceType::TouchPad:
QTest::mouseRelease(window, button, modifiers, p);
break;
case QPointingDevice::DeviceType::TouchScreen:
QTest::touchEvent(window, const_cast<QPointingDevice *>(dev)).release(pointId, p, window);
QQuickTouchUtils::flush(window);
break;
case QPointingDevice::DeviceType::Puck:
case QPointingDevice::DeviceType::Stylus:
case QPointingDevice::DeviceType::Airbrush:
QTest::lastMouseTimestamp += QTest::defaultMouseDelay();
QWindowSystemInterface::handleTabletEvent(window, QTest::lastMouseTimestamp, dev, p, window->mapToGlobal(p),
Qt::NoButton, 0, 0, 0, 0, 0, 0, modifiers);
break;
default:
qWarning() << "can't send a press event from" << dev;
break;
}
}

}

QT_END_NAMESPACE
Expand Down
11 changes: 11 additions & 0 deletions src/quicktestutils/quick/viewtestutils_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,17 @@ namespace QQuickTest {
[[nodiscard]] bool initView(QQuickView &v, const QUrl &url,
bool moveMouseOut = true, QByteArray *errorMessage = nullptr);
[[nodiscard]] bool showView(QQuickView &v, const QUrl &url);

void pointerPress(const QPointingDevice *dev, QQuickWindow *window,
int pointId, const QPoint &p, Qt::MouseButton button = Qt::LeftButton,
Qt::KeyboardModifiers modifiers = Qt::NoModifier);

void pointerMove(const QPointingDevice *dev, QQuickWindow *window, int pointId,
const QPoint &p);

void pointerRelease(const QPointingDevice *dev, QQuickWindow *window, int pointId,
const QPoint &p, Qt::MouseButton button = Qt::LeftButton,
Qt::KeyboardModifiers modifiers = Qt::NoModifier);
}

QT_END_NAMESPACE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -415,22 +415,22 @@ void tst_TapHandler::mouseGesturePolicyReleaseWithinBounds()

void tst_TapHandler::gesturePolicyDragWithinBounds_data()
{
QTest::addColumn<QPointingDevice::DeviceType>("deviceType");
QTest::addColumn<const QPointingDevice *>("device");
QTest::addColumn<QPoint>("dragStart");
QTest::addColumn<QPoint>("dragDistance");
QTest::addColumn<QString>("expectedFeedback");

QTest::newRow("mouse: click") << QPointingDevice::DeviceType::Mouse << QPoint(200, 200) << QPoint(0, 0) << "middle";
QTest::newRow("touch: tap") << QPointingDevice::DeviceType::TouchScreen << QPoint(200, 200) << QPoint(0, 0) << "middle";
QTest::newRow("mouse: drag up") << QPointingDevice::DeviceType::Mouse << QPoint(200, 200) << QPoint(0, -20) << "top";
QTest::newRow("touch: drag up") << QPointingDevice::DeviceType::TouchScreen << QPoint(200, 200) << QPoint(0, -20) << "top";
QTest::newRow("mouse: drag out to cancel") << QPointingDevice::DeviceType::Mouse << QPoint(435, 200) << QPoint(10, 0) << "canceled";
QTest::newRow("touch: drag out to cancel") << QPointingDevice::DeviceType::TouchScreen << QPoint(435, 200) << QPoint(10, 0) << "canceled";
QTest::newRow("mouse: click") << QPointingDevice::primaryPointingDevice() << QPoint(200, 200) << QPoint(0, 0) << "middle";
QTest::newRow("touch: tap") << touchDevice << QPoint(200, 200) << QPoint(0, 0) << "middle";
QTest::newRow("mouse: drag up") << QPointingDevice::primaryPointingDevice() << QPoint(200, 200) << QPoint(0, -20) << "top";
QTest::newRow("touch: drag up") << touchDevice << QPoint(200, 200) << QPoint(0, -20) << "top";
QTest::newRow("mouse: drag out to cancel") << QPointingDevice::primaryPointingDevice() << QPoint(435, 200) << QPoint(10, 0) << "canceled";
QTest::newRow("touch: drag out to cancel") << touchDevice << QPoint(435, 200) << QPoint(10, 0) << "canceled";
}

void tst_TapHandler::gesturePolicyDragWithinBounds()
{
QFETCH(QPointingDevice::DeviceType, deviceType);
QFETCH(const QPointingDevice *, device);
QFETCH(QPoint, dragStart);
QFETCH(QPoint, dragDistance);
QFETCH(QString, expectedFeedback);
Expand All @@ -442,33 +442,14 @@ void tst_TapHandler::gesturePolicyDragWithinBounds()
QVERIFY(tapHandler);
QSignalSpy canceledSpy(tapHandler, &QQuickTapHandler::canceled);

switch (static_cast<QPointingDevice::DeviceType>(deviceType)) {
case QPointingDevice::DeviceType::Mouse:
QTest::mousePress(&window, Qt::LeftButton, Qt::NoModifier, dragStart);
QTRY_VERIFY(tapHandler->isPressed());
QTest::mouseMove(&window, dragStart + dragDistance);
if (expectedCanceled)
QTRY_COMPARE(tapHandler->timeHeld(), -1);
else
QTRY_VERIFY(tapHandler->timeHeld() > 0.1);
QTest::mouseRelease(&window, Qt::LeftButton, Qt::NoModifier, dragStart + dragDistance);
break;
case QPointingDevice::DeviceType::TouchScreen:
QTest::touchEvent(&window, touchDevice).press(0, dragStart, &window);
QQuickTouchUtils::flush(&window);
QTRY_VERIFY(tapHandler->isPressed());
QTest::touchEvent(&window, touchDevice).move(0, dragStart + dragDistance, &window);
QQuickTouchUtils::flush(&window);
if (expectedCanceled)
QTRY_COMPARE(tapHandler->timeHeld(), -1);
else
QTRY_VERIFY(tapHandler->timeHeld() > 0.1);
QTest::touchEvent(&window, touchDevice).release(0, dragStart + dragDistance, &window);
QQuickTouchUtils::flush(&window);
break;
default:
break;
}
QQuickTest::pointerPress(device, &window, 0, dragStart);
QTRY_VERIFY(tapHandler->isPressed());
QQuickTest::pointerMove(device, &window, 0, dragStart + dragDistance);
if (expectedCanceled)
QTRY_COMPARE(tapHandler->timeHeld(), -1);
else
QTRY_VERIFY(tapHandler->timeHeld() > 0.1);
QQuickTest::pointerRelease(device, &window, 0, dragStart + dragDistance);

QCOMPARE(window.rootObject()->property("feedbackText"), expectedFeedback);
if (expectedCanceled)
Expand Down
33 changes: 14 additions & 19 deletions tests/auto/quick/qquickwindow/tst_qquickwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,9 @@ class tst_qquickwindow : public QQmlDataTest
, touchDevice(QTest::createTouchDevice())
, touchDeviceWithVelocity(QTest::createTouchDevice(QInputDevice::DeviceType::TouchScreen,
QInputDevice::Capability::Position | QPointingDevice::Capability::Velocity))
, tabletStylusDevice(QPointingDevicePrivate::tabletDevice(QInputDevice::DeviceType::Stylus,
QPointingDevice::PointerType::Pen,
QPointingDeviceUniqueId::fromNumericId(1234567890)))
{
QQuickWindow::setDefaultAlphaBuffer(true);
}
Expand Down Expand Up @@ -545,6 +548,7 @@ private slots:
private:
QPointingDevice *touchDevice;
QPointingDevice *touchDeviceWithVelocity;
const QPointingDevice *tabletStylusDevice;
};

#if QT_CONFIG(opengl)
Expand Down Expand Up @@ -3629,47 +3633,38 @@ void tst_qquickwindow::cleanupGrabsOnRelease()

void tst_qquickwindow::subclassWithPointerEventVirtualOverrides_data()
{
QTest::addColumn<QPointingDevice::DeviceType>("deviceType");
QTest::addColumn<const QPointingDevice *>("device");

QTest::newRow("mouse click") << QPointingDevice::DeviceType::Mouse;
QTest::newRow("touch tap") << QPointingDevice::DeviceType::TouchScreen;
QTest::newRow("stylus tap") << QPointingDevice::DeviceType::Stylus;
QTest::newRow("mouse click") << QPointingDevice::primaryPointingDevice();
QTest::newRow("touch tap") << touchDevice;
QTest::newRow("stylus tap") << tabletStylusDevice;
}

void tst_qquickwindow::subclassWithPointerEventVirtualOverrides() // QTBUG-97859
{
QFETCH(QPointingDevice::DeviceType, deviceType);
QFETCH(const QPointingDevice *, device);

PointerRecordingWindow window;
window.resize(250, 250);
window.setPosition(100, 100);
window.setTitle(QTest::currentTestFunction());
window.show();
QVERIFY(QTest::qWaitForWindowActive(&window));
const qint64 stylusId = 1234567890;

const QPoint pos(120, 120);
switch (static_cast<QPointingDevice::DeviceType>(deviceType)) {

QQuickTest::pointerPress(device, &window, 0, pos);
QQuickTest::pointerRelease(device, &window, 0, pos);

switch (device->type()) {
case QPointingDevice::DeviceType::Mouse:
QTest::mouseClick(&window, Qt::LeftButton, Qt::NoModifier, pos);
QTRY_COMPARE(window.m_mouseEvents.size(), 3); // separate move before press
QCOMPARE(window.m_events.size(), 3);
break;
case QPointingDevice::DeviceType::TouchScreen:
QTest::touchEvent(&window, touchDevice).press(0, pos, &window);
QTest::touchEvent(&window, touchDevice).release(0, pos, &window);
QTRY_COMPARE(window.m_touchEvents.size(), 2);
QCOMPARE(window.m_events.size(), 2);
break;
case QPointingDevice::DeviceType::Stylus:
// press (pressure is 0.8)
QWindowSystemInterface::handleTabletEvent(&window, pos, window.mapToGlobal(pos),
int(QInputDevice::DeviceType::Stylus), int(QPointingDevice::PointerType::Pen),
Qt::LeftButton, 0.8, 0, 0, 0, 0, 0, stylusId, Qt::NoModifier);
// release (pressure is 0)
QWindowSystemInterface::handleTabletEvent(&window, pos, window.mapToGlobal(pos),
int(QInputDevice::DeviceType::Stylus), int(QPointingDevice::PointerType::Pen),
Qt::NoButton, 0, 0, 0, 0, 0, 0, stylusId, Qt::NoModifier);
QTRY_COMPARE(window.m_tabletEvents.size(), 2);
QVERIFY(window.m_events.size() >= window.m_tabletEvents.size()); // tablet + synth-mouse events
break;
Expand Down

0 comments on commit 59d967a

Please sign in to comment.