testlib: make it possible to test double-clicks with discrete events

The timestamp will no longer be incremented by 500ms after a mouse
release if the delay has been explicitly specified.

The default delay is 1 ms since f5010c49a3
but the running timestamp was unconditionally post-incremented by 500ms
after every mouse release, to prevent double-clicks, which were always
deemed as unintended (because we have a mouseDClick function for that).
Now, we do that 500ms increment only if the user has not provided a
delay value in the function argument at all. We have often found it
useful in our own tests to generate double-clicks "the hard way", by
sending indivdual events, so as to be able to check state in some target
object at each step, as shown in the new snippet.

[ChangeLog][QtTest] QTest::mouseRelease() and mouseClick() can now be
used to test double-clicks, by specifying a realistic timestamp delay.

Fixes: QTBUG-102441
Change-Id: I8e8d242061f79efb4c6e02638645e03661a9cd92
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
bb10
Shawn Rutledge 2022-10-06 16:54:14 +02:00
parent 5caf808750
commit 0f94430a0f
10 changed files with 111 additions and 12 deletions

View File

@ -119,3 +119,16 @@ char *toString(const MyType &t)
return repr;
}
//! [34]
//! [35]
QSignalSpy doubleClickSpy(target, &TargetClass::doubleClicked);
const QPoint p(1, 2);
QTest::mousePress(&myWindow, Qt::LeftButton, Qt::NoModifier, p);
QVERIFY(target.isPressed());
QTest::mouseRelease(&myWindow, Qt::LeftButton, Qt::NoModifier, p, 10);
QCOMPARE(target.isPressed(), false);
QTest::mousePress(&myWindow, Qt::LeftButton, Qt::NoModifier, p, 10);
QCOMPARE(target.pressCount(), 2);
QTest::mouseRelease(&myWindow, Qt::LeftButton, Qt::NoModifier, p, 10);
QCOMPARE(doubleClickSpy.count(), 1);
//! [35]

View File

@ -1260,7 +1260,16 @@
on a \a widget. The position of the release is defined by \a pos;
the default position is the center of the widget. If \a delay is
specified, the test will wait for the specified amount of
milliseconds before releasing the button.
milliseconds before releasing the button; otherwise, it will wait for a
default amount of time (1 ms), which can be overridden via
\l {Testing Options}{command-line arguments}.
\note If you wish to test a double-click by sending events individually,
specify a short delay, greater than the default, on both mouse release events.
The total of the delays for the press, release, press and release must be
less than QStyleHints::mouseDoubleClickInterval(). But if you don't need
to check state between events, it's better to use QTest::mouseDClick().
\snippet code/src_qtestlib_qtestcase_snippet.cpp 35
\sa QTest::mousePress(), QTest::mouseClick()
*/
@ -1273,7 +1282,16 @@
on a \a window. The position of the release is defined by \a pos;
the default position is the center of the window. If \a delay is
specified, the test will wait for the specified amount of
milliseconds before releasing the button.
milliseconds before releasing the button; otherwise, it will wait for a
default amount of time (1 ms), which can be overridden via
\l {Testing Options}{command-line arguments}.
\note If you wish to test a double-click by sending events individually,
specify a short delay, greater than the default, on both mouse release events.
The total of the delays for the press, release, press and release must be
less than QStyleHints::mouseDoubleClickInterval(). But if you don't need
to check state between events, it's better to use QTest::mouseDClick().
\snippet code/src_qtestlib_qtestcase_snippet.cpp 35
\sa QTest::mousePress(), QTest::mouseClick()
*/

View File

@ -57,6 +57,12 @@ namespace QTest
event. We expect all event-handling code to rely on the event
timestamps, not the system clock; therefore tests can be run faster
than real-time.
If \a delay is not given, a default minimum mouse delay is used, and
unintended double-click events are prevented by incrementing the
timestamp by 500ms after each mouse release. Therefore, to test
double-clicks, it's necessary to give a realistic \a delay value (for
example, 10ms).
*/
static void mouseEvent(MouseAction action, QWindow *window, Qt::MouseButton button,
Qt::KeyboardModifiers stateKey, QPoint pos, int delay=-1)
@ -71,9 +77,8 @@ namespace QTest
pos.x(), pos.y(), windowSize.width(), windowSize.height());
}
if (delay == -1 || delay < defaultMouseDelay())
delay = defaultMouseDelay();
lastMouseTimestamp += qMax(1, delay);
int actualDelay = (delay == -1 || delay < defaultMouseDelay()) ? defaultMouseDelay() : delay;
lastMouseTimestamp += qMax(1, actualDelay);
if (pos.isNull())
pos = QPoint(window->width() / 2, window->height() / 2);
@ -108,7 +113,8 @@ namespace QTest
qtestMouseButtons.setFlag(button, false);
qt_handleMouseEvent(w, pos, global, qtestMouseButtons, button, QEvent::MouseButtonRelease,
stateKey, lastMouseTimestamp);
lastMouseTimestamp += mouseDoubleClickInterval; // avoid double clicks being generated
if (delay == -1)
lastMouseTimestamp += mouseDoubleClickInterval; // avoid double clicks being generated
break;
case MouseMove:
qt_handleMouseEvent(w, pos, global, qtestMouseButtons, Qt::NoButton, QEvent::MouseMove,

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<testsuite name="tst_Mouse" timestamp="@TEST_START_TIME@" hostname="@HOSTNAME@" tests="13" failures="0" errors="0" skipped="3" time="@TEST_DURATION@">
<testsuite name="tst_Mouse" timestamp="@TEST_START_TIME@" hostname="@HOSTNAME@" tests="14" failures="0" errors="0" skipped="3" time="@TEST_DURATION@">
<properties>
<property name="QTestVersion" value="@INSERT_QT_VERSION_HERE@"/>
<property name="QtVersion" value="@INSERT_QT_VERSION_HERE@"/>
@ -23,5 +23,6 @@
<testcase name="deterministicEvents(first&#x002D;run&#x002D;false)" classname="tst_Mouse" time="@TEST_DURATION@">
<skipped message="Not implemented!"/>
</testcase>
<testcase name="doubleClick" classname="tst_Mouse" time="@TEST_DURATION@"/>
<testcase name="cleanupTestCase" classname="tst_Mouse" time="@TEST_DURATION@"/>
</testsuite>

View File

@ -54,6 +54,10 @@
</Incident>
<Duration msecs="0"/>
</TestFunction>
<TestFunction name="doubleClick">
<Incident type="pass" file="" line="0" />
<Duration msecs="0"/>
</TestFunction>
<TestFunction name="cleanupTestCase">
<Incident type="pass" file="" line="0" />
<Duration msecs="0"/>

View File

@ -12,8 +12,9 @@ ok 9 - stateHandlingPart1(dummy-2)
ok 10 - stateHandlingPart2() # SKIP Not implemented beyond this point!
ok 11 - deterministicEvents(first-run-true) # SKIP Not implemented!
ok 12 - deterministicEvents(first-run-false) # SKIP Not implemented!
ok 13 - cleanupTestCase()
1..13
# tests 13
# pass 10
ok 13 - doubleClick()
ok 14 - cleanupTestCase()
1..14
# tests 14
# pass 11
# fail 0

View File

@ -26,6 +26,8 @@
##teamcity[testStarted name='deterministicEvents(first-run-false)' flowId='tst_Mouse']
##teamcity[testIgnored name='deterministicEvents(first-run-false)' message='Not implemented! |[Loc: qtbase/tests/auto/testlib/selftests/mouse/tst_mouse.cpp(0)|]' flowId='tst_Mouse']
##teamcity[testFinished name='deterministicEvents(first-run-false)' flowId='tst_Mouse']
##teamcity[testStarted name='doubleClick()' flowId='tst_Mouse']
##teamcity[testFinished name='doubleClick()' flowId='tst_Mouse']
##teamcity[testStarted name='cleanupTestCase()' flowId='tst_Mouse']
##teamcity[testFinished name='cleanupTestCase()' flowId='tst_Mouse']
##teamcity[testSuiteFinished name='tst_Mouse' flowId='tst_Mouse']

View File

@ -15,6 +15,7 @@ SKIP : tst_Mouse::deterministicEvents(first-run-true) Not implemented!
Loc: [qtbase/tests/auto/testlib/selftests/mouse/tst_mouse.cpp(0)]
SKIP : tst_Mouse::deterministicEvents(first-run-false) Not implemented!
Loc: [qtbase/tests/auto/testlib/selftests/mouse/tst_mouse.cpp(0)]
PASS : tst_Mouse::doubleClick()
PASS : tst_Mouse::cleanupTestCase()
Totals: 10 passed, 0 failed, 3 skipped, 0 blacklisted, 0ms
Totals: 11 passed, 0 failed, 3 skipped, 0 blacklisted, 0ms
********* Finished testing of tst_Mouse *********

View File

@ -56,6 +56,10 @@
</Incident>
<Duration msecs="0"/>
</TestFunction>
<TestFunction name="doubleClick">
<Incident type="pass" file="" line="0" />
<Duration msecs="0"/>
</TestFunction>
<TestFunction name="cleanupTestCase">
<Incident type="pass" file="" line="0" />
<Duration msecs="0"/>

View File

@ -24,6 +24,7 @@ private slots:
void stateHandlingPart2();
void deterministicEvents_data();
void deterministicEvents();
void doubleClick();
};
class MouseWindow : public QWindow
@ -251,5 +252,53 @@ void tst_Mouse::deterministicEvents()
QCOMPARE(w.moveCount, 1);
}
void tst_Mouse::doubleClick()
{
MouseWindow w;
w.show();
w.setGeometry(100, 100, 200, 200);
QVERIFY(QTest::qWaitForWindowActive(&w));
// click
QPoint point(10, 10);
QCOMPARE(w.pressCount, 0);
QTest::mousePress(&w, Qt::LeftButton, { }, point);
QCOMPARE(w.pressCount, 1);
// give a delay of 10ms
auto ts = w.lastTimeStamp;
QTest::mouseRelease(&w, Qt::LeftButton, { }, point, 10);
QCOMPARE(w.lastTimeStamp, ts + 10);
QCOMPARE(w.doubleClickCount, 0);
// click again within a short time to generate double-click
QTest::mousePress(&w, Qt::LeftButton, { }, point, 10);
QCOMPARE(w.pressCount, 2);
QCOMPARE(w.lastTimeStamp, ts + 20);
// this time, let some virtual time elapse, because we're going to test double-click again afterwards
QTest::mouseRelease(&w, Qt::LeftButton, { }, point);
QCOMPARE_GT(w.lastTimeStamp, ts + 20);
QCOMPARE(w.doubleClickCount, 1);
// use the mouseClick function to generate another double-click
ts = w.lastTimeStamp;
QTest::mouseClick(&w, Qt::LeftButton, {}, point, 10);
QCOMPARE_GE(w.lastTimeStamp, ts + 500); // because the last release had a default delay
QTest::mouseClick(&w, Qt::LeftButton, {}, point);
QCOMPARE(w.doubleClickCount, 2);
// use the mouseDClick function to generate another double-click
ts = w.lastTimeStamp;
QTest::mouseDClick(&w, Qt::LeftButton, {}, point);
QCOMPARE_GE(w.lastTimeStamp, ts + 500); // because the last release had a default delay
QCOMPARE(w.doubleClickCount, 3);
// use the mouseClick function with default delay to avoid double-click
ts = w.lastTimeStamp;
QTest::mouseClick(&w, Qt::LeftButton, {}, point);
QCOMPARE_GE(w.lastTimeStamp, ts + 500); // because the last release had a default delay
QTest::mouseClick(&w, Qt::LeftButton, {}, point);
QCOMPARE(w.doubleClickCount, 3);
}
QTEST_MAIN(tst_Mouse)
#include "tst_mouse.moc"