38be0d1383
This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the Qt Git wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://qt.gitorious.org/qt/pages/GitIntroductionWithQt If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo, Qt master branch, at commit 896db169ea224deb96c59ce8af800d019de63f12
931 lines
25 KiB
C++
931 lines
25 KiB
C++
/****************************************************************************
|
|
**
|
|
** 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$
|
|
** No Commercial Usage
|
|
** This file contains pre-release code and may not be distributed.
|
|
** You may use this file in accordance with the terms and conditions
|
|
** contained in the Technology Preview License Agreement accompanying
|
|
** this package.
|
|
**
|
|
** GNU Lesser General Public License Usage
|
|
** Alternatively, 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.
|
|
**
|
|
** If you have questions regarding the use of this file, please contact
|
|
** Nokia at qt-info@nokia.com.
|
|
**
|
|
**
|
|
**
|
|
**
|
|
**
|
|
**
|
|
**
|
|
**
|
|
** $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
|