summaryrefslogtreecommitdiffstats
path: root/tests/auto/corelib/concurrent/qfuturewatcher/tst_qfuturewatcher.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/corelib/concurrent/qfuturewatcher/tst_qfuturewatcher.cpp')
-rw-r--r--tests/auto/corelib/concurrent/qfuturewatcher/tst_qfuturewatcher.cpp930
1 files changed, 930 insertions, 0 deletions
diff --git a/tests/auto/corelib/concurrent/qfuturewatcher/tst_qfuturewatcher.cpp b/tests/auto/corelib/concurrent/qfuturewatcher/tst_qfuturewatcher.cpp
new file mode 100644
index 0000000000..d8fa2230f3
--- /dev/null
+++ b/tests/auto/corelib/concurrent/qfuturewatcher/tst_qfuturewatcher.cpp
@@ -0,0 +1,930 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QCoreApplication>
+#include <QDebug>
+#include <QtTest/QtTest>
+
+#include <qfuture.h>
+#include "../qfuture/versioncheck.h"
+#include <qfuturewatcher.h>
+#include <qtconcurrentrun.h>
+#include <qtconcurrentmap.h>
+#include "../../../../shared/util.h"
+
+#ifndef QT_NO_CONCURRENT_TEST
+#include <private/qfutureinterface_p.h>
+
+using namespace QtConcurrent;
+
+#include <QtTest/QtTest>
+
+//#define PRINT
+
+class tst_QFutureWatcher: public QObject
+{
+ Q_OBJECT
+private slots:
+ void startFinish();
+ void progressValueChanged();
+ void canceled();
+ void resultAt();
+ void resultReadyAt();
+ void futureSignals();
+ void watchFinishedFuture();
+ void watchCanceledFuture();
+ void disconnectRunningFuture();
+ void toMuchProgress();
+ void progressText();
+ void sharedFutureInterface();
+ void changeFuture();
+ void cancelEvents();
+ void pauseEvents();
+ void finishedState();
+ void throttling();
+ void incrementalMapResults();
+ void incrementalFilterResults();
+ void qfutureSynchornizer();
+ void warnRace();
+};
+
+QTEST_MAIN(tst_QFutureWatcher)
+
+void sleeper()
+{
+ QTest::qSleep(100);
+}
+
+void tst_QFutureWatcher::startFinish()
+{
+ QFutureWatcher<void> futureWatcher;
+
+ QSignalSpy started(&futureWatcher, SIGNAL(started()));
+ QSignalSpy finished(&futureWatcher, SIGNAL(finished()));
+
+ futureWatcher.setFuture(QtConcurrent::run(sleeper));
+ QTest::qWait(10); // spin the event loop to deliver queued signals.
+ QCOMPARE(started.count(), 1);
+ QCOMPARE(finished.count(), 0);
+ futureWatcher.future().waitForFinished();
+ QTest::qWait(10);
+ QCOMPARE(started.count(), 1);
+ QCOMPARE(finished.count(), 1);
+}
+
+void mapSleeper(int &)
+{
+ QTest::qSleep(100);
+}
+
+QSet<int> progressValues;
+QSet<QString> progressTexts;
+QMutex mutex;
+class ProgressObject : public QObject
+{
+Q_OBJECT
+public slots:
+ void printProgress(int);
+ void printText(const QString &text);
+ void registerProgress(int);
+ void registerText(const QString &text);
+};
+
+void ProgressObject::printProgress(int progress)
+{
+ qDebug() << "thread" << QThread::currentThread() << "reports progress" << progress;
+}
+
+void ProgressObject::printText(const QString &text)
+{
+ qDebug() << "thread" << QThread::currentThread() << "reports progress text" << text;
+}
+
+void ProgressObject::registerProgress(int progress)
+{
+ QTest::qSleep(1);
+ progressValues.insert(progress);
+}
+
+void ProgressObject::registerText(const QString &text)
+{
+ QTest::qSleep(1);
+ progressTexts.insert(text);
+}
+
+
+QList<int> createList(int listSize)
+{
+ QList<int> list;
+ for (int i = 0; i < listSize; ++i) {
+ list.append(i);
+ }
+ return list;
+}
+
+void tst_QFutureWatcher::progressValueChanged()
+{
+#ifdef PRINT
+ qDebug() << "main thread" << QThread::currentThread();
+#endif
+
+ progressValues.clear();
+ const int listSize = 20;
+ QList<int> list = createList(listSize);
+
+ QFutureWatcher<void> futureWatcher;
+ ProgressObject progressObject;
+ QObject::connect(&futureWatcher, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
+#ifdef PRINT
+ QObject::connect(&futureWatcher, SIGNAL(progressValueChanged(int)), &progressObject, SLOT(printProgress(int)), Qt::DirectConnection );
+#endif
+ QObject::connect(&futureWatcher, SIGNAL(progressValueChanged(int)), &progressObject, SLOT(registerProgress(int)));
+
+ futureWatcher.setFuture(QtConcurrent::map(list, mapSleeper));
+
+ QTestEventLoop::instance().enterLoop(5);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+ futureWatcher.disconnect();
+ QVERIFY(progressValues.contains(0));
+ QVERIFY(progressValues.contains(listSize));
+}
+
+class CancelObject : public QObject
+{
+Q_OBJECT
+public:
+ bool wasCanceled;
+ CancelObject() : wasCanceled(false) {};
+public slots:
+ void cancel();
+};
+
+void CancelObject::cancel()
+{
+#ifdef PRINT
+ qDebug() << "thread" << QThread::currentThread() << "reports canceled";
+#endif
+ wasCanceled = true;
+}
+
+void tst_QFutureWatcher::canceled()
+{
+ const int listSize = 20;
+ QList<int> list = createList(listSize);
+
+ QFutureWatcher<void> futureWatcher;
+ QFuture<void> future;
+ CancelObject cancelObject;
+
+ QObject::connect(&futureWatcher, SIGNAL(canceled()), &cancelObject, SLOT(cancel()));
+ QObject::connect(&futureWatcher, SIGNAL(canceled()),
+ &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection);
+
+ future = QtConcurrent::map(list, mapSleeper);
+ futureWatcher.setFuture(future);
+ futureWatcher.cancel();
+ QTestEventLoop::instance().enterLoop(5);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+
+ QVERIFY(future.isCanceled());
+ QVERIFY(cancelObject.wasCanceled);
+ futureWatcher.disconnect();
+ future.waitForFinished();
+}
+
+class IntTask : public RunFunctionTask<int>
+{
+public:
+ void runFunctor()
+ {
+ result = 10;
+ }
+};
+
+void tst_QFutureWatcher::resultAt()
+{
+ QFutureWatcher<int> futureWatcher;
+ futureWatcher.setFuture((new IntTask())->start());
+ futureWatcher.waitForFinished();
+ QCOMPARE(futureWatcher.result(), 10);
+ QCOMPARE(futureWatcher.resultAt(0), 10);
+}
+
+void tst_QFutureWatcher::resultReadyAt()
+{
+ QFutureWatcher<int> futureWatcher;
+ QObject::connect(&futureWatcher, SIGNAL(resultReadyAt(int)), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection);
+
+ QFuture<int> future = (new IntTask())->start();
+ futureWatcher.setFuture(future);
+
+ QTestEventLoop::instance().enterLoop(1);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+
+ // Setting the future again should give us another signal.
+ // (this is to prevent the race where the task associated
+ // with the future finishes before setFuture is called.)
+ futureWatcher.setFuture(QFuture<int>());
+ futureWatcher.setFuture(future);
+
+ QTestEventLoop::instance().enterLoop(1);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+}
+
+class SignalSlotObject : public QObject
+{
+Q_OBJECT
+
+signals:
+ void cancel();
+
+public slots:
+ void started()
+ {
+ qDebug() << "started called";
+ }
+
+ void finished()
+ {
+ qDebug() << "finished called";
+ }
+
+ void canceled()
+ {
+ qDebug() << "canceled called";
+ }
+
+#ifdef PRINT
+ void resultReadyAt(int index)
+ {
+ qDebug() << "result" << index << "ready";
+ }
+#else
+ void resultReadyAt(int) { }
+#endif
+ void progressValueChanged(int progress)
+ {
+ qDebug() << "progress" << progress;
+ }
+
+ void progressRangeChanged(int min, int max)
+ {
+ qDebug() << "progress range" << min << max;
+ }
+
+};
+
+void tst_QFutureWatcher::futureSignals()
+{
+ {
+ QFutureInterface<int> a;
+ QFutureWatcher<int> f;
+
+ SignalSlotObject object;
+#ifdef PRINT
+ connect(&f, SIGNAL(finished()), &object, SLOT(finished()));
+ connect(&f, SIGNAL(progressValueChanged(int)), &object, SLOT(progressValueChanged(int)));
+#endif
+ // must connect to resultReadyAt so that the watcher can detect the connection
+ // (QSignalSpy does not trigger it.)
+ connect(&f, SIGNAL(resultReadyAt(int)), &object, SLOT(resultReadyAt(int)));
+ a.reportStarted();
+ f.setFuture(a.future());
+
+ QSignalSpy progressSpy(&f, SIGNAL(progressValueChanged(int)));
+ const int progress = 1;
+ a.setProgressValue(progress);
+ QTest::qWait(10);
+ QCOMPARE(progressSpy.count(), 2);
+ QCOMPARE(progressSpy.takeFirst().at(0).toInt(), 0);
+ QCOMPARE(progressSpy.takeFirst().at(0).toInt(), 1);
+
+ QSignalSpy finishedSpy(&f, SIGNAL(finished()));
+ QSignalSpy resultReadySpy(&f, SIGNAL(resultReadyAt(int)));
+
+ const int result = 10;
+ a.reportResult(&result);
+ QTest::qWait(10);
+ QCOMPARE(resultReadySpy.count(), 1);
+ a.reportFinished(&result);
+ QTest::qWait(10);
+
+ QCOMPARE(resultReadySpy.count(), 2);
+ QCOMPARE(resultReadySpy.takeFirst().at(0).toInt(), 0); // check the index
+ QCOMPARE(resultReadySpy.takeFirst().at(0).toInt(), 1);
+
+ QCOMPARE(finishedSpy.count(), 1);
+ }
+}
+
+void tst_QFutureWatcher::watchFinishedFuture()
+{
+ QFutureInterface<int> iface;
+ iface.reportStarted();
+
+ QFuture<int> f = iface.future();
+
+ int value = 100;
+ iface.reportFinished(&value);
+
+ QFutureWatcher<int> watcher;
+
+ SignalSlotObject object;
+#ifdef PRINT
+ connect(&watcher, SIGNAL(started()), &object, SLOT(started()));
+ connect(&watcher, SIGNAL(canceled()), &object, SLOT(canceled()));
+ connect(&watcher, SIGNAL(finished()), &object, SLOT(finished()));
+ connect(&watcher, SIGNAL(progressValueChanged(int)), &object, SLOT(progressValueChanged(int)));
+ connect(&watcher, SIGNAL(progressRangeChanged(int, int)), &object, SLOT(progressRangeChanged(int, int)));
+#endif
+ connect(&watcher, SIGNAL(resultReadyAt(int)), &object, SLOT(resultReadyAt(int)));
+
+ QSignalSpy startedSpy(&watcher, SIGNAL(started()));
+ QSignalSpy finishedSpy(&watcher, SIGNAL(finished()));
+ QSignalSpy resultReadySpy(&watcher, SIGNAL(resultReadyAt(int)));
+ QSignalSpy canceledSpy(&watcher, SIGNAL(canceled()));
+
+ watcher.setFuture(f);
+ QTest::qWait(10);
+
+ QCOMPARE(startedSpy.count(), 1);
+ QCOMPARE(finishedSpy.count(), 1);
+ QCOMPARE(resultReadySpy.count(), 1);
+ QCOMPARE(canceledSpy.count(), 0);
+}
+
+void tst_QFutureWatcher::watchCanceledFuture()
+{
+ QFuture<int> f;
+ QFutureWatcher<int> watcher;
+
+ SignalSlotObject object;
+#ifdef PRINT
+ connect(&watcher, SIGNAL(started()), &object, SLOT(started()));
+ connect(&watcher, SIGNAL(canceled()), &object, SLOT(canceled()));
+ connect(&watcher, SIGNAL(finished()), &object, SLOT(finished()));
+ connect(&watcher, SIGNAL(progressValueChanged(int)), &object, SLOT(progressValueChanged(int)));
+ connect(&watcher, SIGNAL(progressRangeChanged(int, int)), &object, SLOT(progressRangeChanged(int, int)));
+#endif
+ connect(&watcher, SIGNAL(resultReadyAt(int)), &object, SLOT(resultReadyAt(int)));
+
+ QSignalSpy startedSpy(&watcher, SIGNAL(started()));
+ QSignalSpy finishedSpy(&watcher, SIGNAL(finished()));
+ QSignalSpy resultReadySpy(&watcher, SIGNAL(resultReadyAt(int)));
+ QSignalSpy canceledSpy(&watcher, SIGNAL(canceled()));
+
+ watcher.setFuture(f);
+ QTest::qWait(10);
+
+ QCOMPARE(startedSpy.count(), 1);
+ QCOMPARE(finishedSpy.count(), 1);
+ QCOMPARE(resultReadySpy.count(), 0);
+ QCOMPARE(canceledSpy.count(), 1);
+}
+
+void tst_QFutureWatcher::disconnectRunningFuture()
+{
+ QFutureInterface<int> a;
+ a.reportStarted();
+
+ QFuture<int> f = a.future();
+ QFutureWatcher<int> *watcher = new QFutureWatcher<int>();
+ watcher->setFuture(f);
+
+ SignalSlotObject object;
+ connect(watcher, SIGNAL(resultReadyAt(int)), &object, SLOT(resultReadyAt(int)));
+
+ QSignalSpy finishedSpy(watcher, SIGNAL(finished()));
+ QSignalSpy resultReadySpy(watcher, SIGNAL(resultReadyAt(int)));
+
+ const int result = 10;
+ a.reportResult(&result);
+ QTest::qWait(10);
+ QCOMPARE(resultReadySpy.count(), 1);
+
+ delete watcher;
+
+ a.reportResult(&result);
+ QTest::qWait(10);
+ QCOMPARE(resultReadySpy.count(), 1);
+
+ a.reportFinished(&result);
+ QTest::qWait(10);
+ QCOMPARE(finishedSpy.count(), 0);
+}
+
+const int maxProgress = 100000;
+class ProgressEmitterTask : public RunFunctionTask<void>
+{
+public:
+ void runFunctor()
+ {
+ setProgressRange(0, maxProgress);
+ for (int p = 0; p <= maxProgress; ++p)
+ setProgressValue(p);
+ }
+};
+
+void tst_QFutureWatcher::toMuchProgress()
+{
+ progressValues.clear();
+ ProgressObject o;
+
+ QFutureWatcher<void> f;
+ QObject::connect(&f, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
+#ifdef PRINT
+ QObject::connect(&f, SIGNAL(progressValueChanged(int)), &o, SLOT(printProgress(int)));
+#endif
+ QObject::connect(&f, SIGNAL(progressValueChanged(int)), &o, SLOT(registerProgress(int)));
+ f.setFuture((new ProgressEmitterTask())->start());
+
+ QTestEventLoop::instance().enterLoop(5);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+ QVERIFY(progressValues.contains(maxProgress));
+}
+
+template <typename T>
+class ProgressTextTask : public RunFunctionTask<T>
+{
+public:
+ void runFunctor()
+ {
+ this->setProgressValueAndText(1, QLatin1String("Foo 1"));
+
+ while (this->isProgressUpdateNeeded() == false)
+ QTest::qSleep(1);
+ this->setProgressValueAndText(2, QLatin1String("Foo 2"));
+
+ while (this->isProgressUpdateNeeded() == false)
+ QTest::qSleep(1);
+ this->setProgressValueAndText(3, QLatin1String("Foo 3"));
+
+ while (this->isProgressUpdateNeeded() == false)
+ QTest::qSleep(1);
+ this->setProgressValueAndText(4, QLatin1String("Foo 4"));
+ }
+};
+
+void tst_QFutureWatcher::progressText()
+{
+ { // instantiate API for T=int and T=void.
+ ProgressTextTask<int> a;
+ ProgressTextTask<void> b;
+ }
+ {
+ progressValues.clear();
+ progressTexts.clear();
+ QFuture<int> f = ((new ProgressTextTask<int>())->start());
+ QFutureWatcher<int> watcher;
+ ProgressObject o;
+ QObject::connect(&watcher, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
+#ifdef PRINT
+ QObject::connect(&watcher, SIGNAL(progressValueChanged(int)), &o, SLOT(printProgress(int)));
+ QObject::connect(&watcher, SIGNAL(progressTextChanged(const QString &)), &o, SLOT(printText(const QString &)));
+#endif
+ QObject::connect(&watcher, SIGNAL(progressValueChanged(int)), &o, SLOT(registerProgress(int)));
+ QObject::connect(&watcher, SIGNAL(progressTextChanged(const QString &)), &o, SLOT(registerText(const QString &)));
+
+ watcher.setFuture(f);
+ QTestEventLoop::instance().enterLoop(5);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+
+ QCOMPARE(f.progressText(), QLatin1String("Foo 4"));
+ QCOMPARE(f.progressValue(), 4);
+ QVERIFY(progressValues.contains(1));
+ QVERIFY(progressValues.contains(2));
+ QVERIFY(progressValues.contains(3));
+ QVERIFY(progressValues.contains(4));
+ QVERIFY(progressTexts.contains(QLatin1String("Foo 1")));
+ QVERIFY(progressTexts.contains(QLatin1String("Foo 2")));
+ QVERIFY(progressTexts.contains(QLatin1String("Foo 3")));
+ QVERIFY(progressTexts.contains(QLatin1String("Foo 4")));
+ }
+}
+
+template <typename T>
+void callInterface(T &obj)
+{
+ obj.progressValue();
+ obj.progressMinimum();
+ obj.progressMaximum();
+ obj.progressText();
+
+ obj.isStarted();
+ obj.isFinished();
+ obj.isRunning();
+ obj.isCanceled();
+ obj.isPaused();
+
+ obj.cancel();
+ obj.pause();
+ obj.resume();
+ obj.togglePaused();
+ obj.waitForFinished();
+
+ const T& objConst = obj;
+ objConst.progressValue();
+ objConst.progressMinimum();
+ objConst.progressMaximum();
+ objConst.progressText();
+
+ objConst.isStarted();
+ objConst.isFinished();
+ objConst.isRunning();
+ objConst.isCanceled();
+ objConst.isPaused();
+}
+
+template <typename T>
+void callInterface(const T &obj)
+{
+ obj.result();
+ obj.resultAt(0);
+}
+
+
+// QFutureWatcher and QFuture has a similar interface. Test
+// that the functions we want ot have in both are actually
+// there.
+void tst_QFutureWatcher::sharedFutureInterface()
+{
+ QFutureInterface<int> iface;
+ iface.reportStarted();
+
+ QFuture<int> intFuture = iface.future();
+
+ int value = 0;
+ iface.reportFinished(&value);
+
+ QFuture<void> voidFuture;
+ QFutureWatcher<int> intWatcher;
+ intWatcher.setFuture(intFuture);
+ QFutureWatcher<void> voidWatcher;
+
+ callInterface(intFuture);
+ callInterface(voidFuture);
+ callInterface(intWatcher);
+ callInterface(voidWatcher);
+
+ callInterface(intFuture);
+ callInterface(intWatcher);
+}
+
+void tst_QFutureWatcher::changeFuture()
+{
+ QFutureInterface<int> iface;
+ iface.reportStarted();
+
+ QFuture<int> a = iface.future();
+
+ int value = 0;
+ iface.reportFinished(&value);
+
+ QFuture<int> b;
+
+ QFutureWatcher<int> watcher;
+
+ SignalSlotObject object;
+ connect(&watcher, SIGNAL(resultReadyAt(int)), &object, SLOT(resultReadyAt(int)));
+ QSignalSpy resultReadySpy(&watcher, SIGNAL(resultReadyAt(int)));
+
+ watcher.setFuture(a); // Watch 'a' which will genere a resultReady event.
+ watcher.setFuture(b); // But oh no! we're switching to another future
+ QTest::qWait(10); // before the event gets delivered.
+
+ QCOMPARE(resultReadySpy.count(), 0);
+
+ watcher.setFuture(a);
+ watcher.setFuture(b);
+ watcher.setFuture(a); // setting it back gets us one event, not two.
+ QTest::qWait(10);
+
+ QCOMPARE(resultReadySpy.count(), 1);
+}
+
+// Test that events aren't delivered from canceled futures
+void tst_QFutureWatcher::cancelEvents()
+{
+ QFutureInterface<int> iface;
+ iface.reportStarted();
+
+ QFuture<int> a = iface.future();
+
+ int value = 0;
+ iface.reportFinished(&value);
+
+ QFutureWatcher<int> watcher;
+
+ SignalSlotObject object;
+ connect(&watcher, SIGNAL(resultReadyAt(int)), &object, SLOT(resultReadyAt(int)));
+ QSignalSpy resultReadySpy(&watcher, SIGNAL(resultReadyAt(int)));
+
+ watcher.setFuture(a);
+ watcher.cancel();
+
+ QTest::qWait(10);
+
+ QCOMPARE(resultReadySpy.count(), 0);
+}
+
+// Tests that events from paused futures are saved and
+// delivered on resume.
+void tst_QFutureWatcher::pauseEvents()
+{
+ {
+ QFutureInterface<int> iface;
+ iface.reportStarted();
+
+ QFuture<int> a = iface.future();
+
+ int value = 0;
+ iface.reportFinished(&value);
+
+ QFutureWatcher<int> watcher;
+
+ SignalSlotObject object;
+ connect(&watcher, SIGNAL(resultReadyAt(int)), &object, SLOT(resultReadyAt(int)));
+ QSignalSpy resultReadySpy(&watcher, SIGNAL(resultReadyAt(int)));
+
+ watcher.setFuture(a);
+ watcher.pause();
+
+ QTest::qWait(10);
+ QCOMPARE(resultReadySpy.count(), 0);
+
+ watcher.resume();
+ QTest::qWait(10);
+ QCOMPARE(resultReadySpy.count(), 1);
+ }
+ {
+ QFutureInterface<int> iface;
+ iface.reportStarted();
+
+ QFuture<int> a = iface.future();
+
+ int value = 0;
+ iface.reportFinished(&value);
+
+ QFutureWatcher<int> watcher;
+
+ SignalSlotObject object;
+ connect(&watcher, SIGNAL(resultReadyAt(int)), &object, SLOT(resultReadyAt(int)));
+ QSignalSpy resultReadySpy(&watcher, SIGNAL(resultReadyAt(int)));
+
+ watcher.setFuture(a);
+ a.pause();
+
+ QFuture<int> b;
+ watcher.setFuture(b); // If we watch b instead, resuming a
+ a.resume(); // should give us no results.
+
+ QTest::qWait(10);
+ QCOMPARE(resultReadySpy.count(), 0);
+ }
+}
+
+// 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;
+
+ watcher.setFuture(future);
+ QTest::qWait(10);
+
+ iface.reportFinished();
+ QVERIFY(future.isFinished());
+ QVERIFY(watcher.isFinished() == false);
+
+ QTest::qWait(10);
+ 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.
+*/
+void tst_QFutureWatcher::throttling()
+{
+ QFutureInterface<int> iface;
+ iface.reportStarted();
+ QFuture<int> future = iface.future();
+ QFutureWatcher<int> watcher;
+ watcher.setFuture(future);
+
+ QVERIFY(iface.isThrottled() == false);
+
+ for (int i = 0; i < 1000; ++i) {
+ int result = 0;
+ iface.reportResult(result);
+ }
+
+ QVERIFY(iface.isThrottled() == true);
+
+ QTest::qWait(100); // process events.
+
+ QVERIFY(iface.isThrottled() == false);
+
+ iface.reportFinished();
+}
+
+int mapper(const int &i)
+{
+ return i;
+}
+
+class ResultReadyTester : public QObject
+{
+Q_OBJECT
+public:
+ ResultReadyTester(QFutureWatcher<int> *watcher)
+ :m_watcher(watcher), filter(false), ok(true), count(0)
+ {
+
+ }
+public slots:
+ void resultReadyAt(int index)
+ {
+ ++count;
+ if (m_watcher->future().isResultReadyAt(index) == false)
+ ok = false;
+ if (!filter && m_watcher->future().resultAt(index) != index)
+ ok = false;
+ if (filter && m_watcher->future().resultAt(index) != index * 2 + 1)
+ ok = false;
+ }
+public:
+ QFutureWatcher<int> *m_watcher;
+ bool filter;
+ bool ok;
+ int count;
+};
+
+void tst_QFutureWatcher::incrementalMapResults()
+{
+ QFutureWatcher<int> watcher;
+
+ SignalSlotObject object;
+#ifdef PRINT
+ connect(&watcher, SIGNAL(finished()), &object, SLOT(finished()));
+ connect(&watcher, SIGNAL(progressValueChanged(int)), &object, SLOT(progressValueChanged(int)));
+ connect(&watcher, SIGNAL(resultReadyAt(int)), &object, SLOT(resultReadyAt(int)));
+#endif
+
+ QObject::connect(&watcher, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
+
+ ResultReadyTester resultReadyTester(&watcher);
+ connect(&watcher, SIGNAL(resultReadyAt(int)), &resultReadyTester, SLOT(resultReadyAt(int)));
+
+ const int count = 10000;
+ QList<int> ints;
+ for (int i = 0; i < count; ++i)
+ ints << i;
+
+ QFuture<int> future = QtConcurrent::mapped(ints, mapper);
+ watcher.setFuture(future);
+
+ QTestEventLoop::instance().enterLoop(10);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+ QCOMPARE(resultReadyTester.count, count);
+ QVERIFY(resultReadyTester.ok);
+ QVERIFY(watcher.isFinished());
+ future.waitForFinished();
+}
+
+bool filterer(int i)
+{
+ return (i % 2);
+}
+
+void tst_QFutureWatcher::incrementalFilterResults()
+{
+ QFutureWatcher<int> watcher;
+
+ SignalSlotObject object;
+#ifdef PRINT
+ connect(&watcher, SIGNAL(finished()), &object, SLOT(finished()));
+ connect(&watcher, SIGNAL(progressValueChanged(int)), &object, SLOT(progressValueChanged(int)));
+ connect(&watcher, SIGNAL(resultReadyAt(int)), &object, SLOT(resultReadyAt(int)));
+#endif
+
+ QObject::connect(&watcher, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
+
+
+ ResultReadyTester resultReadyTester(&watcher);
+ resultReadyTester.filter = true;
+ connect(&watcher, SIGNAL(resultReadyAt(int)), &resultReadyTester, SLOT(resultReadyAt(int)));
+
+ const int count = 10000;
+ QList<int> ints;
+ for (int i = 0; i < count; ++i)
+ ints << i;
+
+ QFuture<int> future = QtConcurrent::filtered(ints, filterer);
+ watcher.setFuture(future);
+
+ QTestEventLoop::instance().enterLoop(10);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+ QCOMPARE(resultReadyTester.count, count / 2);
+ QVERIFY(resultReadyTester.ok);
+ QVERIFY(watcher.isFinished());
+ future.waitForFinished();
+}
+
+void tst_QFutureWatcher::qfutureSynchornizer()
+{
+ int taskCount = 1000;
+ QTime t;
+ t.start();
+
+ {
+ QFutureSynchronizer<void> sync;
+
+ sync.setCancelOnWait(true);
+ for (int i = 0; i < taskCount; ++i) {
+ sync.addFuture(run(sleeper));
+ }
+ }
+
+ // Test that we're not running each task.
+ QVERIFY(t.elapsed() < taskCount * 10);
+}
+
+class DummyObject : public QObject {
+ Q_OBJECT
+public slots:
+ void dummySlot() {}
+public:
+ static void function(QMutex *m)
+ {
+ QMutexLocker lock(m);
+ }
+};
+
+void tst_QFutureWatcher::warnRace()
+{
+#ifndef Q_OS_MAC //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
+#endif
+ QFutureWatcher<void> watcher;
+ DummyObject object;
+ QMutex mutex;
+ mutex.lock();
+
+ QFuture<void> future = QtConcurrent::run(DummyObject::function, &mutex);
+ watcher.setFuture(future);
+ QTRY_VERIFY(future.isStarted());
+ connect(&watcher, SIGNAL(finished()), &object, SLOT(dummySlot()));
+ mutex.unlock();
+ future.waitForFinished();
+}
+
+#include "tst_qfuturewatcher.moc"
+
+#else
+QTEST_NOOP_MAIN
+#endif