QtConcurrent: Provide a test for runWithPromise with handlers

Test runWithPromise with "then" and "onCanceled" handlers.
Test the case when QFuture::cancel() is being called when
the task's thread already started, so that a call to
QPromise::isCanceled() from inside the running thread
returns different values in the same task's run. This nicely
proves that communication between QFuture and QPromise
works between different threads.

Task-number: QTBUG-84868
Change-Id: Icb2e0b1f99e2dcd919d881515f1ccd08e2f25b8c
Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
Reviewed-by: Sona Kurazyan <sona.kurazyan@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
Jarek Kobus 2020-09-29 16:50:01 +02:00
parent cde42e2f76
commit cc0be95ac7

View File

@ -26,8 +26,11 @@
**
****************************************************************************/
#include <qtconcurrentrun.h>
#include <qfuture.h>
#include <QFuture>
#include <QMutex>
#include <QMutexLocker>
#include <QString>
#include <QWaitCondition>
#include <QtTest/QtTest>
using namespace QtConcurrent;
@ -55,6 +58,7 @@ private slots:
void callableObjectWithState();
void withPromise();
void withPromiseInThreadPool();
void withPromiseAndThen();
void moveOnlyType();
void crefFunction();
void customPromise();
@ -1346,6 +1350,111 @@ void tst_QtConcurrentRun::withPromiseInThreadPool()
QList<QString>({QString(QLatin1String("rvalue"))}));
}
void tst_QtConcurrentRun::withPromiseAndThen()
{
bool runExecuted = false;
bool cancelReceivedBeforeSync = false;
bool cancelReceivedAfterSync = false;
bool syncBegin = false;
bool syncEnd = false;
QMutex mutex;
QWaitCondition condition;
auto reset = [&]() {
runExecuted = false;
cancelReceivedBeforeSync = false;
cancelReceivedAfterSync = false;
syncBegin = false;
syncEnd = false;
};
auto setFlag = [&mutex, &condition] (bool &flag) {
QMutexLocker locker(&mutex);
flag = true;
condition.wakeOne();
};
auto waitForFlag = [&mutex, &condition] (const bool &flag) {
QMutexLocker locker(&mutex);
while (!flag)
condition.wait(&mutex);
};
auto report1WithCancel = [&](QPromise<int> &promise) {
runExecuted = true;
cancelReceivedBeforeSync = promise.isCanceled();
setFlag(syncBegin);
waitForFlag(syncEnd);
cancelReceivedAfterSync = promise.isCanceled();
if (cancelReceivedAfterSync)
return;
promise.addResult(1);
};
{
auto future = run(report1WithCancel);
waitForFlag(syncBegin);
future.cancel();
setFlag(syncEnd);
future.waitForFinished();
QCOMPARE(future.results().count(), 0);
QVERIFY(runExecuted);
QVERIFY(!cancelReceivedBeforeSync);
QVERIFY(cancelReceivedAfterSync);
}
reset();
{
bool thenExecuted = false;
bool cancelExecuted = false;
auto future = run(report1WithCancel);
auto resultFuture = future.then(QtFuture::Launch::Async, [&](int) { thenExecuted = true; })
.onCanceled([&]() { cancelExecuted = true; });
waitForFlag(syncBegin);
// no cancel this time
setFlag(syncEnd);
resultFuture.waitForFinished();
QCOMPARE(future.results().count(), 1);
QCOMPARE(future.result(), 1);
QVERIFY(runExecuted);
QVERIFY(thenExecuted);
QVERIFY(!cancelExecuted);
QVERIFY(!cancelReceivedBeforeSync);
QVERIFY(!cancelReceivedAfterSync);
}
reset();
{
bool thenExecuted = false;
bool cancelExecuted = false;
auto future = run(report1WithCancel);
auto resultFuture = future.then(QtFuture::Launch::Async, [&](int) { thenExecuted = true; })
.onCanceled([&]() { cancelExecuted = true; });
waitForFlag(syncBegin);
future.cancel();
setFlag(syncEnd);
resultFuture.waitForFinished();
QCOMPARE(future.results().count(), 0);
QVERIFY(runExecuted);
QVERIFY(!thenExecuted);
QVERIFY(cancelExecuted);
QVERIFY(!cancelReceivedBeforeSync);
QVERIFY(cancelReceivedAfterSync);
}
}
class MoveOnlyType
{
public: