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:
Tor Arne Vestbø 2017-02-07 14:09:04 +01:00
parent 0dbede2b17
commit 292cb12e02
4 changed files with 66 additions and 30 deletions

View File

@ -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]
}

View File

@ -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; \

View File

@ -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

View File

@ -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