diff options
Diffstat (limited to 'tests/auto/corelib/thread/qfuturewatcher/tst_qfuturewatcher.cpp')
-rw-r--r-- | tests/auto/corelib/thread/qfuturewatcher/tst_qfuturewatcher.cpp | 275 |
1 files changed, 169 insertions, 106 deletions
diff --git a/tests/auto/corelib/thread/qfuturewatcher/tst_qfuturewatcher.cpp b/tests/auto/corelib/thread/qfuturewatcher/tst_qfuturewatcher.cpp index 0d759eebd6..40aa89ded4 100644 --- a/tests/auto/corelib/thread/qfuturewatcher/tst_qfuturewatcher.cpp +++ b/tests/auto/corelib/thread/qfuturewatcher/tst_qfuturewatcher.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QCoreApplication> #include <QDebug> #include <QElapsedTimer> @@ -34,6 +9,7 @@ #include <private/qfutureinterface_p.h> using namespace QtConcurrent; +using namespace std::chrono_literals; #include <QTest> @@ -46,8 +22,11 @@ private slots: void startFinish(); void progressValueChanged(); void canceled(); + void cancelAndFinish_data(); + void cancelAndFinish(); void resultAt(); void resultReadyAt(); + void orderedResultReadyAt(); void futureSignals(); void watchFinishedFuture(); void watchCanceledFuture(); @@ -64,13 +43,13 @@ private slots: void suspendEvents(); void suspended(); void suspendedEventsOrder(); - void finishedState(); void throttling(); void incrementalMapResults(); void incrementalFilterResults(); void qfutureSynchronizer(); void warnRace(); void matchFlags(); + void checkStateConsistency(); }; void sleeper() @@ -229,12 +208,48 @@ void tst_QFutureWatcher::canceled() future.waitForFinished(); } -class IntTask : public RunFunctionTask<int> +void tst_QFutureWatcher::cancelAndFinish_data() +{ + QTest::addColumn<bool>("isCanceled"); + QTest::addColumn<bool>("isFinished"); + + QTest::addRow("running") << false << false; + QTest::addRow("canceled") << true << false; + QTest::addRow("finished") << false << true; + QTest::addRow("canceledAndFinished") << true << true; +} + +void tst_QFutureWatcher::cancelAndFinish() +{ + QFETCH(bool, isCanceled); + QFETCH(bool, isFinished); + + QFutureInterface<void> fi; + QFutureWatcher<void> futureWatcher; + QSignalSpy finishedSpy(&futureWatcher, &QFutureWatcher<void>::finished); + QSignalSpy canceledSpy(&futureWatcher, &QFutureWatcher<void>::canceled); + futureWatcher.setFuture(fi.future()); + + fi.reportStarted(); + + if (isCanceled) + fi.cancel(); + if (isFinished) + fi.reportFinished(); + + fi.cancelAndFinish(); + + // The signals should be emitted only once + QTRY_COMPARE(canceledSpy.size(), 1); + QTRY_COMPARE(finishedSpy.size(), 1); +} + +class IntTask : public RunFunctionTaskBase<int> { public: void runFunctor() override { - result = 10; + promise.reportResult(10); } }; @@ -266,6 +281,28 @@ void tst_QFutureWatcher::resultReadyAt() QVERIFY(resultSpy.wait()); } +void tst_QFutureWatcher::orderedResultReadyAt() +{ + for (int i = 0; i < 1000; ++i) { + QObject context; + QFuture<QString> f = run([](QPromise<QString> &fi) { + fi.addResult("First"); + fi.addResult("Second"); + }); + QList<int> actualIndices; + + QFutureWatcher<QString> watcher; + connect(&watcher, &QFutureWatcherBase::resultReadyAt, &context, + [&actualIndices](int index) { actualIndices.append(index); }); + watcher.setFuture(f); + f.waitForFinished(); + QCoreApplication::processEvents(); + const QList<int> expectedIndices{0, 1}; + QCOMPARE(actualIndices.size(), expectedIndices.size()); + QCOMPARE(actualIndices, expectedIndices); + } +} + class SignalSlotObject : public QObject { Q_OBJECT @@ -336,21 +373,21 @@ void tst_QFutureWatcher::futureSignals() const int progress = 1; a.setProgressValue(progress); - QTRY_COMPARE(progressSpy.count(), 2); + QTRY_COMPARE(progressSpy.size(), 2); QCOMPARE(progressSpy.takeFirst().at(0).toInt(), 0); QCOMPARE(progressSpy.takeFirst().at(0).toInt(), 1); const int result = 10; a.reportResult(&result); QVERIFY(resultReadySpy.wait()); - QCOMPARE(resultReadySpy.count(), 1); + QCOMPARE(resultReadySpy.size(), 1); a.reportFinished(&result); - QTRY_COMPARE(resultReadySpy.count(), 2); + QTRY_COMPARE(resultReadySpy.size(), 2); QCOMPARE(resultReadySpy.takeFirst().at(0).toInt(), 0); // check the index QCOMPARE(resultReadySpy.takeFirst().at(0).toInt(), 1); - QCOMPARE(finishedSpy.count(), 1); + QCOMPARE(finishedSpy.size(), 1); } } @@ -389,10 +426,10 @@ void tst_QFutureWatcher::watchFinishedFuture() watcher.setFuture(f); QVERIFY(finishedSpy.wait()); - QCOMPARE(startedSpy.count(), 1); - QCOMPARE(finishedSpy.count(), 1); - QCOMPARE(resultReadySpy.count(), 1); - QCOMPARE(canceledSpy.count(), 0); + QCOMPARE(startedSpy.size(), 1); + QCOMPARE(finishedSpy.size(), 1); + QCOMPARE(resultReadySpy.size(), 1); + QCOMPARE(canceledSpy.size(), 0); } void tst_QFutureWatcher::watchCanceledFuture() @@ -423,10 +460,10 @@ void tst_QFutureWatcher::watchCanceledFuture() watcher.setFuture(f); QVERIFY(finishedSpy.wait()); - QCOMPARE(startedSpy.count(), 1); - QCOMPARE(finishedSpy.count(), 1); - QCOMPARE(resultReadySpy.count(), 0); - QCOMPARE(canceledSpy.count(), 1); + QCOMPARE(startedSpy.size(), 1); + QCOMPARE(finishedSpy.size(), 1); + QCOMPARE(resultReadySpy.size(), 0); + QCOMPARE(canceledSpy.size(), 1); } void tst_QFutureWatcher::disconnectRunningFuture() @@ -449,21 +486,21 @@ void tst_QFutureWatcher::disconnectRunningFuture() const int result = 10; a.reportResult(&result); QVERIFY(resultReadySpy.wait()); - QCOMPARE(resultReadySpy.count(), 1); + QCOMPARE(resultReadySpy.size(), 1); delete watcher; a.reportResult(&result); QTest::qWait(10); - QCOMPARE(resultReadySpy.count(), 1); + QCOMPARE(resultReadySpy.size(), 1); a.reportFinished(&result); QTest::qWait(10); - QCOMPARE(finishedSpy.count(), 0); + QCOMPARE(finishedSpy.size(), 0); } const int maxProgress = 100000; -class ProgressEmitterTask : public RunFunctionTask<void> +class ProgressEmitterTask : public RunFunctionTaskBase<void> { public: void runFunctor() override @@ -487,13 +524,14 @@ void tst_QFutureWatcher::tooMuchProgress() QObject::connect(&f, SIGNAL(progressValueChanged(int)), &o, SLOT(registerProgress(int))); f.setFuture((new ProgressEmitterTask())->start()); - QTestEventLoop::instance().enterLoop(5); + // Android reports ca. 10k progressValueChanged per second + QTestEventLoop::instance().enterLoop(15); QVERIFY(!QTestEventLoop::instance().timeout()); QVERIFY(progressValues.contains(maxProgress)); } template <typename T> -class ProgressTextTask : public RunFunctionTask<T> +class ProgressTextTask : public RunFunctionTaskBase<T> { public: void runFunctor() override @@ -658,14 +696,14 @@ void tst_QFutureWatcher::changeFuture() watcher.setFuture(b); // But oh no! we're switching to another future QTest::qWait(10); // before the event gets delivered. - QCOMPARE(resultReadySpy.count(), 0); + QCOMPARE(resultReadySpy.size(), 0); watcher.setFuture(a); watcher.setFuture(b); watcher.setFuture(a); // setting it back gets us one event, not two. QVERIFY(resultReadySpy.wait()); - QCOMPARE(resultReadySpy.count(), 1); + QCOMPARE(resultReadySpy.size(), 1); } // Test that events aren't delivered from canceled futures @@ -693,7 +731,7 @@ void tst_QFutureWatcher::cancelEvents() QVERIFY(finishedSpy.wait()); - QCOMPARE(resultReadySpy.count(), 0); + QCOMPARE(resultReadySpy.size(), 0); } #if QT_DEPRECATED_SINCE(6, 0) @@ -720,14 +758,14 @@ void tst_QFutureWatcher::pauseEvents() watcher.setFuture(iface.future()); watcher.pause(); - QTRY_COMPARE(pauseSpy.count(), 1); + QTRY_COMPARE(pauseSpy.size(), 1); int value = 0; iface.reportFinished(&value); // A result is reported, although the watcher is paused. // The corresponding event should be also reported. - QTRY_COMPARE(resultReadySpy.count(), 1); + QTRY_COMPARE(resultReadySpy.size(), 1); watcher.resume(); } @@ -755,7 +793,7 @@ void tst_QFutureWatcher::pauseEvents() a.resume(); // should give us no results. QTest::qWait(10); - QCOMPARE(resultReadySpy.count(), 0); + QCOMPARE(resultReadySpy.size(), 0); } } @@ -775,23 +813,23 @@ void tst_QFutureWatcher::pausedSuspendedOrder() bool pausedBeforeSuspended = false; bool notSuspendedBeforePaused = false; connect(&watcher, &QFutureWatcher<void>::paused, - [&] { notSuspendedBeforePaused = (suspendedSpy.count() == 0); }); + [&] { notSuspendedBeforePaused = (suspendedSpy.size() == 0); }); connect(&watcher, &QFutureWatcher<void>::suspended, - [&] { pausedBeforeSuspended = (pausedSpy.count() == 1); }); + [&] { pausedBeforeSuspended = (pausedSpy.size() == 1); }); watcher.setFuture(iface.future()); iface.reportSuspended(); // Make sure reportPaused() is ignored if the state is not paused pausedSpy.wait(100); - QCOMPARE(pausedSpy.count(), 0); - QCOMPARE(suspendedSpy.count(), 0); + QCOMPARE(pausedSpy.size(), 0); + QCOMPARE(suspendedSpy.size(), 0); iface.setPaused(true); iface.reportSuspended(); - QTRY_COMPARE(suspendedSpy.count(), 1); - QCOMPARE(pausedSpy.count(), 1); + QTRY_COMPARE(suspendedSpy.size(), 1); + QCOMPARE(pausedSpy.size(), 1); QVERIFY(notSuspendedBeforePaused); QVERIFY(pausedBeforeSuspended); @@ -822,14 +860,14 @@ void tst_QFutureWatcher::suspendEvents() watcher.setFuture(iface.future()); watcher.suspend(); - QTRY_COMPARE(suspendingSpy.count(), 1); + QTRY_COMPARE(suspendingSpy.size(), 1); int value = 0; iface.reportFinished(&value); // A result is reported, although the watcher is paused. // The corresponding event should be also reported. - QTRY_COMPARE(resultReadySpy.count(), 1); + QTRY_COMPARE(resultReadySpy.size(), 1); watcher.resume(); } @@ -858,7 +896,7 @@ void tst_QFutureWatcher::suspendEvents() a.resume(); // should give us no results. QTest::qWait(10); - QCOMPARE(resultReadySpy.count(), 0); + QCOMPARE(resultReadySpy.size(), 0); } } @@ -886,39 +924,39 @@ QT_WARNING_POP QFuture<int> future = QtConcurrent::mapped(&pool, values, [&](int value) { ++count; // Sleep, to make sure not all threads will start at once. - QThread::msleep(50); + QThread::sleep(50ms); return value; }); watcher.setFuture(future); // Allow some threads to start before suspending. - QThread::msleep(200); + QThread::sleep(200ms); watcher.suspend(); watcher.suspend(); - QTRY_COMPARE(suspendedSpy.count(), 1); // suspended() should be emitted only once - QCOMPARE(suspendingSpy.count(), 2); // suspending() is emitted as many times as requested + QTRY_COMPARE(suspendedSpy.size(), 1); // suspended() should be emitted only once + QCOMPARE(suspendingSpy.size(), 2); // suspending() is emitted as many times as requested #if QT_DEPRECATED_SINCE(6, 0) - QCOMPARE(pausedSpy.count(), 2); // paused() is emitted as many times as requested + QCOMPARE(pausedSpy.size(), 2); // paused() is emitted as many times as requested #endif // Make sure QFutureWatcher::resultReadyAt() is emitted only for already started threads. - const auto resultReadyAfterPaused = resultReadySpy.count(); + const auto resultReadyAfterPaused = resultReadySpy.size(); QCOMPARE(resultReadyAfterPaused, count); // Make sure no more results are reported before resuming. - QThread::msleep(200); - QCOMPARE(resultReadyAfterPaused, resultReadySpy.count()); + QThread::sleep(200ms); + QCOMPARE(resultReadyAfterPaused, resultReadySpy.size()); resultReadySpy.clear(); watcher.resume(); - QTRY_COMPARE(finishedSpy.count(), 1); + QTRY_COMPARE(finishedSpy.size(), 1); // Make sure that no more suspended() signals have been emitted. - QCOMPARE(suspendedSpy.count(), 1); + QCOMPARE(suspendedSpy.size(), 1); // Make sure the rest of results were reported after resume. - QCOMPARE(resultReadySpy.count(), numValues - resultReadyAfterPaused); + QCOMPARE(resultReadySpy.size(), numValues - resultReadyAfterPaused); } void tst_QFutureWatcher::suspendedEventsOrder() @@ -937,53 +975,29 @@ void tst_QFutureWatcher::suspendedEventsOrder() bool suspendingBeforeSuspended = false; bool notSuspendedBeforeSuspending = false; connect(&watcher, &QFutureWatcher<void>::suspending, - [&] { notSuspendedBeforeSuspending = (suspendedSpy.count() == 0); }); + [&] { notSuspendedBeforeSuspending = (suspendedSpy.size() == 0); }); connect(&watcher, &QFutureWatcher<void>::suspended, - [&] { suspendingBeforeSuspended = (suspendingSpy.count() == 1); }); + [&] { suspendingBeforeSuspended = (suspendingSpy.size() == 1); }); watcher.setFuture(iface.future()); iface.reportSuspended(); // Make sure reportPaused() is ignored if the state is not paused suspendingSpy.wait(100); - QCOMPARE(suspendingSpy.count(), 0); - QCOMPARE(suspendedSpy.count(), 0); + QCOMPARE(suspendingSpy.size(), 0); + QCOMPARE(suspendedSpy.size(), 0); iface.setSuspended(true); iface.reportSuspended(); - QTRY_COMPARE(suspendedSpy.count(), 1); - QCOMPARE(suspendingSpy.count(), 1); + QTRY_COMPARE(suspendedSpy.size(), 1); + QCOMPARE(suspendingSpy.size(), 1); QVERIFY(notSuspendedBeforeSuspending); QVERIFY(suspendingBeforeSuspended); iface.reportFinished(); } -// Test that the finished state for the watcher gets -// set when the finished event is delivered. -// This means it will lag the finished state for the future, -// but makes it more useful. -void tst_QFutureWatcher::finishedState() -{ - QFutureInterface<int> iface; - iface.reportStarted(); - QFuture<int> future = iface.future(); - QFutureWatcher<int> watcher; - QSignalSpy startedSpy(&watcher, &QFutureWatcher<int>::started); - QSignalSpy finishedSpy(&watcher, &QFutureWatcher<int>::finished); - - watcher.setFuture(future); - QVERIFY(startedSpy.wait()); - - iface.reportFinished(); - QVERIFY(future.isFinished()); - QVERIFY(!watcher.isFinished()); - - QVERIFY(finishedSpy.wait()); - QVERIFY(watcher.isFinished()); -} - /* Verify that throttling kicks in if you report a lot of results, and that it clears when the result events are processed. @@ -1007,7 +1021,7 @@ void tst_QFutureWatcher::throttling() QVERIFY(iface.isThrottled()); - QTRY_COMPARE(resultSpy.count(), resultCount); // Process the results + QTRY_COMPARE(resultSpy.size(), resultCount); // Process the results QVERIFY(!iface.isThrottled()); @@ -1149,7 +1163,7 @@ public: void tst_QFutureWatcher::warnRace() { -#ifndef Q_OS_MAC //I don't know why it is not working on mac +#ifndef Q_OS_DARWIN // I don't know why it is not working on mac #ifndef QT_NO_DEBUG QTest::ignoreMessage(QtWarningMsg, "QFutureWatcher::connect: connecting after calling setFuture() is likely to produce race"); #endif @@ -1178,6 +1192,55 @@ void tst_QFutureWatcher::matchFlags() QCOMPARE(watcher.isFinished(), future.isFinished()); } +void tst_QFutureWatcher::checkStateConsistency() +{ +#define CHECK_FAIL(state) \ + do { \ + if (QTest::currentTestFailed()) \ + QFAIL("checkState() failed, QFutureWatcher has inconistent state after " state "!"); \ + } while (false) + + QFutureWatcher<void> futureWatcher; + + auto checkState = [&futureWatcher] { + QCOMPARE(futureWatcher.isStarted(), futureWatcher.future().isStarted()); + QCOMPARE(futureWatcher.isRunning(), futureWatcher.future().isRunning()); + QCOMPARE(futureWatcher.isCanceled(), futureWatcher.future().isCanceled()); + QCOMPARE(futureWatcher.isSuspended(), futureWatcher.future().isSuspended()); + QCOMPARE(futureWatcher.isSuspending(), futureWatcher.future().isSuspending()); + QCOMPARE(futureWatcher.isFinished(), futureWatcher.future().isFinished()); + }; + + checkState(); + CHECK_FAIL("default-constructing"); + + QFutureInterface<void> fi; + futureWatcher.setFuture(fi.future()); + checkState(); + CHECK_FAIL("setting future"); + + fi.reportStarted(); + checkState(); + CHECK_FAIL("starting"); + + fi.future().suspend(); + checkState(); + CHECK_FAIL("suspending"); + + fi.reportSuspended(); + checkState(); + CHECK_FAIL("suspended"); + + fi.reportCanceled(); + checkState(); + CHECK_FAIL("canceling"); + + fi.reportFinished(); + checkState(); + CHECK_FAIL("finishing"); + +#undef CHECK_FAIL +} QTEST_MAIN(tst_QFutureWatcher) #include "tst_qfuturewatcher.moc" |