testlib: Add qWaitFor to wait for predicate
Reduces duplication of logic and allows other primitives to be built on top. Change-Id: Ia100014cfb0c09ac2f47c3a156d0c76f0fddafa8 Reviewed-by: Gatis Paeglis <gatis.paeglis@qt.io>
This commit is contained in:
parent
0dbede2b17
commit
292cb12e02
@ -306,5 +306,13 @@ QTest::keyClick(myWindow, Qt::Key_Escape);
|
||||
QTest::keyClick(myWindow, Qt::Key_Escape, Qt::ShiftModifier, 200);
|
||||
//! [29]
|
||||
|
||||
//! [30]
|
||||
MyObject obj;
|
||||
obj.startup();
|
||||
QTest::qWaitFor([&]() {
|
||||
return obj.isReady();
|
||||
}, 3000);
|
||||
//! [30]
|
||||
|
||||
}
|
||||
|
||||
|
@ -147,6 +147,8 @@ do {\
|
||||
} \
|
||||
}
|
||||
|
||||
// Ideally we'd use qWaitFor instead of QTRY_LOOP_IMPL, but due
|
||||
// to a compiler bug on MSVC < 2017 we can't (see QTBUG-59096)
|
||||
#define QTRY_IMPL(expr, timeout)\
|
||||
const int qt_test_step = 50; \
|
||||
const int qt_test_timeoutValue = timeout; \
|
||||
|
@ -1075,6 +1075,21 @@
|
||||
\sa QTest::qSleep(), QSignalSpy::wait()
|
||||
*/
|
||||
|
||||
/*! \fn void QTest::qWaitFor(Functor predicate, int timeout)
|
||||
|
||||
Waits for \a timeout milliseconds or until the \a predicate returns true.
|
||||
|
||||
Returns \c true if the \a predicate returned true at any point, otherwise returns \c false.
|
||||
|
||||
Example:
|
||||
\snippet code/src_qtestlib_qtestcase.cpp 30
|
||||
|
||||
The code above will wait for the object to become ready, for a
|
||||
maximum of three seconds.
|
||||
|
||||
\since 5.10
|
||||
*/
|
||||
|
||||
/*! \fn bool QTest::qWaitForWindowExposed(QWindow *window, int timeout)
|
||||
\since 5.0
|
||||
|
||||
|
@ -54,41 +54,60 @@ QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace QTest
|
||||
{
|
||||
template <typename Functor>
|
||||
static Q_REQUIRED_RESULT bool qWaitFor(Functor predicate, int timeout = 5000)
|
||||
{
|
||||
// We should not spint the event loop in case the predicate is already true,
|
||||
// otherwise we might send new events that invalidate the predicate.
|
||||
if (predicate())
|
||||
return true;
|
||||
|
||||
// qWait() is expected to spin the event loop, even when called with a small
|
||||
// timeout like 1ms, so we we can't use a simple while-loop here based on
|
||||
// the deadline timer not having timed out. Use do-while instead.
|
||||
|
||||
int remaining = timeout;
|
||||
QDeadlineTimer deadline(remaining, Qt::PreciseTimer);
|
||||
|
||||
do {
|
||||
QCoreApplication::processEvents(QEventLoop::AllEvents, remaining);
|
||||
QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
|
||||
|
||||
remaining = deadline.remainingTime();
|
||||
if (remaining > 0) {
|
||||
QTest::qSleep(qMin(10, remaining));
|
||||
remaining = deadline.remainingTime();
|
||||
}
|
||||
|
||||
if (predicate())
|
||||
return true;
|
||||
|
||||
remaining = deadline.remainingTime();
|
||||
} while (remaining > 0);
|
||||
|
||||
return predicate(); // Last chance
|
||||
}
|
||||
|
||||
Q_DECL_UNUSED inline static void qWait(int ms)
|
||||
{
|
||||
Q_ASSERT(QCoreApplication::instance());
|
||||
|
||||
QDeadlineTimer timer(ms, Qt::PreciseTimer);
|
||||
int remaining = ms;
|
||||
do {
|
||||
QCoreApplication::processEvents(QEventLoop::AllEvents, remaining);
|
||||
QCoreApplication::sendPostedEvents(Q_NULLPTR, QEvent::DeferredDelete);
|
||||
remaining = timer.remainingTime();
|
||||
if (remaining <= 0)
|
||||
break;
|
||||
QTest::qSleep(qMin(10, remaining));
|
||||
remaining = timer.remainingTime();
|
||||
} while (remaining > 0);
|
||||
auto unconditionalWait = []() { return false; };
|
||||
bool timedOut = !qWaitFor(unconditionalWait, ms);
|
||||
Q_UNUSED(timedOut);
|
||||
}
|
||||
|
||||
#ifdef QT_GUI_LIB
|
||||
inline static bool qWaitForWindowActive(QWindow *window, int timeout = 5000)
|
||||
{
|
||||
QDeadlineTimer timer(timeout, Qt::PreciseTimer);
|
||||
int remaining = timeout;
|
||||
while (!window->isActive() && remaining > 0) {
|
||||
QCoreApplication::processEvents(QEventLoop::AllEvents, remaining);
|
||||
QCoreApplication::sendPostedEvents(Q_NULLPTR, QEvent::DeferredDelete);
|
||||
QTest::qSleep(10);
|
||||
remaining = timer.remainingTime();
|
||||
}
|
||||
bool becameActive = qWaitFor([&]() { return window->isActive(); }, timeout);
|
||||
|
||||
// Try ensuring the platform window receives the real position.
|
||||
// (i.e. that window->pos() reflects reality)
|
||||
// isActive() ( == FocusIn in case of X) does not guarantee this. It seems some WMs randomly
|
||||
// send the final ConfigureNotify (the one with the non-bogus 0,0 position) after the FocusIn.
|
||||
// If we just let things go, every mapTo/FromGlobal call the tests perform directly after
|
||||
// qWaitForWindowShown() will generate bogus results.
|
||||
if (window->isActive()) {
|
||||
if (becameActive) {
|
||||
int waitNo = 0; // 0, 0 might be a valid position after all, so do not wait for ever
|
||||
while (window->position().isNull()) {
|
||||
if (waitNo++ > timeout / 10)
|
||||
@ -101,15 +120,7 @@ namespace QTest
|
||||
|
||||
inline static bool qWaitForWindowExposed(QWindow *window, int timeout = 5000)
|
||||
{
|
||||
QDeadlineTimer timer(timeout, Qt::PreciseTimer);
|
||||
int remaining = timeout;
|
||||
while (!window->isExposed() && remaining > 0) {
|
||||
QCoreApplication::processEvents(QEventLoop::AllEvents, remaining);
|
||||
QCoreApplication::sendPostedEvents(Q_NULLPTR, QEvent::DeferredDelete);
|
||||
QTest::qSleep(10);
|
||||
remaining = timer.remainingTime();
|
||||
}
|
||||
return window->isExposed();
|
||||
return qWaitFor([&]() { return window->isExposed(); }, timeout);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user