1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28#include <QCoreApplication>
29#include <QDebug>
30#include <QElapsedTimer>
31#include <QtTest/QtTest>
32
33#include <QtConcurrent>
34#include <private/qfutureinterface_p.h>
35
36using namespace QtConcurrent;
37
38#include <QtTest/QtTest>
39
40//#define PRINT
41
42class tst_QFutureWatcher: public QObject
43{
44 Q_OBJECT
45private slots:
46 void startFinish();
47 void progressValueChanged();
48 void canceled();
49 void resultAt();
50 void resultReadyAt();
51 void futureSignals();
52 void watchFinishedFuture();
53 void watchCanceledFuture();
54 void disconnectRunningFuture();
55 void tooMuchProgress();
56 void progressText();
57 void sharedFutureInterface();
58 void changeFuture();
59 void cancelEvents();
60 void pauseEvents();
61 void finishedState();
62 void throttling();
63 void incrementalMapResults();
64 void incrementalFilterResults();
65 void qfutureSynchronizer();
66 void warnRace();
67 void matchFlags();
68};
69
70void sleeper()
71{
72 QTest::qSleep(100);
73}
74
75void tst_QFutureWatcher::startFinish()
76{
77 QFutureWatcher<void> futureWatcher;
78
79 QSignalSpy startedSpy(&futureWatcher, &QFutureWatcher<void>::started);
80 QSignalSpy finishedSpy(&futureWatcher, &QFutureWatcher<void>::finished);
81
82 QVERIFY(startedSpy.isValid());
83 QVERIFY(finishedSpy.isValid());
84
85 futureWatcher.setFuture(QtConcurrent::run(sleeper));
86 QVERIFY(startedSpy.wait());
87 QCOMPARE(startedSpy.count(), 1);
88 QCOMPARE(finishedSpy.count(), 0);
89 futureWatcher.future().waitForFinished();
90 QVERIFY(finishedSpy.wait());
91 QCOMPARE(startedSpy.count(), 1);
92 QCOMPARE(finishedSpy.count(), 1);
93}
94
95void mapSleeper(int &)
96{
97 QTest::qSleep(100);
98}
99
100QSet<int> progressValues;
101QSet<QString> progressTexts;
102QMutex mutex;
103class ProgressObject : public QObject
104{
105Q_OBJECT
106public slots:
107 void printProgress(int);
108 void printText(const QString &text);
109 void registerProgress(int);
110 void registerText(const QString &text);
111};
112
113void ProgressObject::printProgress(int progress)
114{
115 qDebug() << "thread" << QThread::currentThread() << "reports progress" << progress;
116}
117
118void ProgressObject::printText(const QString &text)
119{
120 qDebug() << "thread" << QThread::currentThread() << "reports progress text" << text;
121}
122
123void ProgressObject::registerProgress(int progress)
124{
125 QTest::qSleep(1);
126 progressValues.insert(progress);
127}
128
129void ProgressObject::registerText(const QString &text)
130{
131 QTest::qSleep(1);
132 progressTexts.insert(text);
133}
134
135
136QList<int> createList(int listSize)
137{
138 QList<int> list;
139 for (int i = 0; i < listSize; ++i) {
140 list.append(i);
141 }
142 return list;
143}
144
145void tst_QFutureWatcher::progressValueChanged()
146{
147#ifdef PRINT
148 qDebug() << "main thread" << QThread::currentThread();
149#endif
150
151 progressValues.clear();
152 const int listSize = 20;
153 QList<int> list = createList(listSize);
154
155 QFutureWatcher<void> futureWatcher;
156 ProgressObject progressObject;
157 QObject::connect(&futureWatcher, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
158#ifdef PRINT
159 QObject::connect(&futureWatcher, SIGNAL(progressValueChanged(int)), &progressObject, SLOT(printProgress(int)), Qt::DirectConnection );
160#endif
161 QObject::connect(&futureWatcher, SIGNAL(progressValueChanged(int)), &progressObject, SLOT(registerProgress(int)));
162
163 futureWatcher.setFuture(QtConcurrent::map(list, mapSleeper));
164
165 QTestEventLoop::instance().enterLoop(5);
166 QVERIFY(!QTestEventLoop::instance().timeout());
167 futureWatcher.disconnect();
168 QVERIFY(progressValues.contains(0));
169 QVERIFY(progressValues.contains(listSize));
170}
171
172class CancelObject : public QObject
173{
174Q_OBJECT
175public:
176 bool wasCanceled;
177 CancelObject() : wasCanceled(false) {};
178public slots:
179 void cancel();
180};
181
182void CancelObject::cancel()
183{
184#ifdef PRINT
185 qDebug() << "thread" << QThread::currentThread() << "reports canceled";
186#endif
187 wasCanceled = true;
188}
189
190void tst_QFutureWatcher::canceled()
191{
192 const int listSize = 20;
193 QList<int> list = createList(listSize);
194
195 QFutureWatcher<void> futureWatcher;
196 QFuture<void> future;
197 CancelObject cancelObject;
198
199 QObject::connect(&futureWatcher, SIGNAL(canceled()), &cancelObject, SLOT(cancel()));
200 QObject::connect(&futureWatcher, SIGNAL(canceled()),
201 &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection);
202
203 future = QtConcurrent::map(list, mapSleeper);
204 futureWatcher.setFuture(future);
205 futureWatcher.cancel();
206 QTestEventLoop::instance().enterLoop(5);
207 QVERIFY(!QTestEventLoop::instance().timeout());
208
209 QVERIFY(future.isCanceled());
210 QVERIFY(cancelObject.wasCanceled);
211 futureWatcher.disconnect();
212 future.waitForFinished();
213}
214
215class IntTask : public RunFunctionTask<int>
216{
217public:
218 void runFunctor()
219 {
220 result = 10;
221 }
222};
223
224void tst_QFutureWatcher::resultAt()
225{
226 QFutureWatcher<int> futureWatcher;
227 futureWatcher.setFuture((new IntTask())->start());
228 futureWatcher.waitForFinished();
229 QCOMPARE(futureWatcher.result(), 10);
230 QCOMPARE(futureWatcher.resultAt(0), 10);
231}
232
233void tst_QFutureWatcher::resultReadyAt()
234{
235 QFutureWatcher<int> futureWatcher;
236 QSignalSpy resultSpy(&futureWatcher, &QFutureWatcher<int>::resultReadyAt);
237
238 QFuture<int> future = (new IntTask())->start();
239 futureWatcher.setFuture(future);
240
241 QVERIFY(resultSpy.wait());
242
243 // Setting the future again should give us another signal.
244 // (this is to prevent the race where the task associated
245 // with the future finishes before setFuture is called.)
246 futureWatcher.setFuture(QFuture<int>());
247 futureWatcher.setFuture(future);
248
249 QVERIFY(resultSpy.wait());
250}
251
252class SignalSlotObject : public QObject
253{
254Q_OBJECT
255
256signals:
257 void cancel();
258
259public slots:
260 void started()
261 {
262 qDebug() << "started called";
263 }
264
265 void finished()
266 {
267 qDebug() << "finished called";
268 }
269
270 void canceled()
271 {
272 qDebug() << "canceled called";
273 }
274
275#ifdef PRINT
276 void resultReadyAt(int index)
277 {
278 qDebug() << "result" << index << "ready";
279 }
280#else
281 void resultReadyAt(int) { }
282#endif
283 void progressValueChanged(int progress)
284 {
285 qDebug() << "progress" << progress;
286 }
287
288 void progressRangeChanged(int min, int max)
289 {
290 qDebug() << "progress range" << min << max;
291 }
292
293};
294
295void tst_QFutureWatcher::futureSignals()
296{
297 {
298 QFutureInterface<int> a;
299 QFutureWatcher<int> f;
300
301 SignalSlotObject object;
302#ifdef PRINT
303 connect(&f, SIGNAL(finished()), &object, SLOT(finished()));
304 connect(&f, SIGNAL(progressValueChanged(int)), &object, SLOT(progressValueChanged(int)));
305#endif
306 // must connect to resultReadyAt so that the watcher can detect the connection
307 // (QSignalSpy does not trigger it.)
308 connect(&f, SIGNAL(resultReadyAt(int)), &object, SLOT(resultReadyAt(int)));
309 a.reportStarted();
310
311 QSignalSpy progressSpy(&f, &QFutureWatcher<void>::progressValueChanged);
312 QSignalSpy finishedSpy(&f, &QFutureWatcher<void>::finished);
313 QSignalSpy resultReadySpy(&f, &QFutureWatcher<void>::resultReadyAt);
314
315 QVERIFY(progressSpy.isValid());
316 QVERIFY(finishedSpy.isValid());
317 QVERIFY(resultReadySpy.isValid());
318 f.setFuture(a.future());
319
320 const int progress = 1;
321 a.setProgressValue(progress);
322 QTRY_COMPARE(progressSpy.count(), 2);
323 QCOMPARE(progressSpy.takeFirst().at(0).toInt(), 0);
324 QCOMPARE(progressSpy.takeFirst().at(0).toInt(), 1);
325
326 const int result = 10;
327 a.reportResult(&result);
328 QVERIFY(resultReadySpy.wait());
329 QCOMPARE(resultReadySpy.count(), 1);
330 a.reportFinished(&result);
331
332 QTRY_COMPARE(resultReadySpy.count(), 2);
333 QCOMPARE(resultReadySpy.takeFirst().at(0).toInt(), 0); // check the index
334 QCOMPARE(resultReadySpy.takeFirst().at(0).toInt(), 1);
335
336 QCOMPARE(finishedSpy.count(), 1);
337 }
338}
339
340void tst_QFutureWatcher::watchFinishedFuture()
341{
342 QFutureInterface<int> iface;
343 iface.reportStarted();
344
345 QFuture<int> f = iface.future();
346
347 int value = 100;
348 iface.reportFinished(&value);
349
350 QFutureWatcher<int> watcher;
351
352 SignalSlotObject object;
353#ifdef PRINT
354 connect(&watcher, SIGNAL(started()), &object, SLOT(started()));
355 connect(&watcher, SIGNAL(canceled()), &object, SLOT(canceled()));
356 connect(&watcher, SIGNAL(finished()), &object, SLOT(finished()));
357 connect(&watcher, SIGNAL(progressValueChanged(int)), &object, SLOT(progressValueChanged(int)));
358 connect(&watcher, SIGNAL(progressRangeChanged(int,int)), &object, SLOT(progressRangeChanged(int,int)));
359#endif
360 connect(&watcher, SIGNAL(resultReadyAt(int)), &object, SLOT(resultReadyAt(int)));
361
362 QSignalSpy startedSpy(&watcher, &QFutureWatcher<int>::started);
363 QSignalSpy finishedSpy(&watcher, &QFutureWatcher<int>::finished);
364 QSignalSpy resultReadySpy(&watcher, &QFutureWatcher<int>::resultReadyAt);
365 QSignalSpy canceledSpy(&watcher, &QFutureWatcher<int>::canceled);
366
367 QVERIFY(startedSpy.isValid());
368 QVERIFY(finishedSpy.isValid());
369 QVERIFY(resultReadySpy.isValid());
370 QVERIFY(canceledSpy.isValid());
371
372 watcher.setFuture(f);
373 QVERIFY(finishedSpy.wait());
374
375 QCOMPARE(startedSpy.count(), 1);
376 QCOMPARE(finishedSpy.count(), 1);
377 QCOMPARE(resultReadySpy.count(), 1);
378 QCOMPARE(canceledSpy.count(), 0);
379}
380
381void tst_QFutureWatcher::watchCanceledFuture()
382{
383 QFuture<int> f;
384 QFutureWatcher<int> watcher;
385
386 SignalSlotObject object;
387#ifdef PRINT
388 connect(&watcher, SIGNAL(started()), &object, SLOT(started()));
389 connect(&watcher, SIGNAL(canceled()), &object, SLOT(canceled()));
390 connect(&watcher, SIGNAL(finished()), &object, SLOT(finished()));
391 connect(&watcher, SIGNAL(progressValueChanged(int)), &object, SLOT(progressValueChanged(int)));
392 connect(&watcher, SIGNAL(progressRangeChanged(int,int)), &object, SLOT(progressRangeChanged(int,int)));
393#endif
394 connect(&watcher, SIGNAL(resultReadyAt(int)), &object, SLOT(resultReadyAt(int)));
395
396 QSignalSpy startedSpy(&watcher, &QFutureWatcher<int>::started);
397 QSignalSpy finishedSpy(&watcher, &QFutureWatcher<int>::finished);
398 QSignalSpy resultReadySpy(&watcher, &QFutureWatcher<int>::resultReadyAt);
399 QSignalSpy canceledSpy(&watcher, &QFutureWatcher<int>::canceled);
400
401 QVERIFY(startedSpy.isValid());
402 QVERIFY(finishedSpy.isValid());
403 QVERIFY(resultReadySpy.isValid());
404 QVERIFY(canceledSpy.isValid());
405
406 watcher.setFuture(f);
407 QVERIFY(finishedSpy.wait());
408
409 QCOMPARE(startedSpy.count(), 1);
410 QCOMPARE(finishedSpy.count(), 1);
411 QCOMPARE(resultReadySpy.count(), 0);
412 QCOMPARE(canceledSpy.count(), 1);
413}
414
415void tst_QFutureWatcher::disconnectRunningFuture()
416{
417 QFutureInterface<int> a;
418 a.reportStarted();
419
420 QFuture<int> f = a.future();
421 QFutureWatcher<int> *watcher = new QFutureWatcher<int>();
422 QSignalSpy finishedSpy(watcher, &QFutureWatcher<int>::finished);
423 QSignalSpy resultReadySpy(watcher, &QFutureWatcher<int>::resultReadyAt);
424
425 QVERIFY(finishedSpy.isValid());
426 QVERIFY(resultReadySpy.isValid());
427 watcher->setFuture(f);
428
429 SignalSlotObject object;
430 connect(watcher, SIGNAL(resultReadyAt(int)), &object, SLOT(resultReadyAt(int)));
431
432 const int result = 10;
433 a.reportResult(&result);
434 QVERIFY(resultReadySpy.wait());
435 QCOMPARE(resultReadySpy.count(), 1);
436
437 delete watcher;
438
439 a.reportResult(&result);
440 QTest::qWait(10);
441 QCOMPARE(resultReadySpy.count(), 1);
442
443 a.reportFinished(&result);
444 QTest::qWait(10);
445 QCOMPARE(finishedSpy.count(), 0);
446}
447
448const int maxProgress = 100000;
449class ProgressEmitterTask : public RunFunctionTask<void>
450{
451public:
452 void runFunctor()
453 {
454 setProgressRange(0, maxProgress);
455 for (int p = 0; p <= maxProgress; ++p)
456 setProgressValue(p);
457 }
458};
459
460void tst_QFutureWatcher::tooMuchProgress()
461{
462 progressValues.clear();
463 ProgressObject o;
464
465 QFutureWatcher<void> f;
466 QObject::connect(&f, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
467#ifdef PRINT
468 QObject::connect(&f, SIGNAL(progressValueChanged(int)), &o, SLOT(printProgress(int)));
469#endif
470 QObject::connect(&f, SIGNAL(progressValueChanged(int)), &o, SLOT(registerProgress(int)));
471 f.setFuture((new ProgressEmitterTask())->start());
472
473 QTestEventLoop::instance().enterLoop(5);
474 QVERIFY(!QTestEventLoop::instance().timeout());
475 QVERIFY(progressValues.contains(maxProgress));
476}
477
478template <typename T>
479class ProgressTextTask : public RunFunctionTask<T>
480{
481public:
482 void runFunctor()
483 {
484 this->setProgressValueAndText(1, QLatin1String("Foo 1"));
485
486 while (this->isProgressUpdateNeeded() == false)
487 QTest::qSleep(1);
488 this->setProgressValueAndText(2, QLatin1String("Foo 2"));
489
490 while (this->isProgressUpdateNeeded() == false)
491 QTest::qSleep(1);
492 this->setProgressValueAndText(3, QLatin1String("Foo 3"));
493
494 while (this->isProgressUpdateNeeded() == false)
495 QTest::qSleep(1);
496 this->setProgressValueAndText(4, QLatin1String("Foo 4"));
497 }
498};
499
500void tst_QFutureWatcher::progressText()
501{
502 { // instantiate API for T=int and T=void.
503 ProgressTextTask<int> a;
504 ProgressTextTask<void> b;
505 }
506 {
507 progressValues.clear();
508 progressTexts.clear();
509 QFuture<int> f = ((new ProgressTextTask<int>())->start());
510 QFutureWatcher<int> watcher;
511 ProgressObject o;
512 QObject::connect(&watcher, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
513#ifdef PRINT
514 QObject::connect(&watcher, SIGNAL(progressValueChanged(int)), &o, SLOT(printProgress(int)));
515 QObject::connect(&watcher, SIGNAL(progressTextChanged(QString)), &o, SLOT(printText(QString)));
516#endif
517 QObject::connect(&watcher, SIGNAL(progressValueChanged(int)), &o, SLOT(registerProgress(int)));
518 QObject::connect(&watcher, SIGNAL(progressTextChanged(QString)), &o, SLOT(registerText(QString)));
519
520 watcher.setFuture(f);
521 QTestEventLoop::instance().enterLoop(5);
522 QVERIFY(!QTestEventLoop::instance().timeout());
523
524 QCOMPARE(f.progressText(), QLatin1String("Foo 4"));
525 QCOMPARE(f.progressValue(), 4);
526 QVERIFY(progressValues.contains(1));
527 QVERIFY(progressValues.contains(2));
528 QVERIFY(progressValues.contains(3));
529 QVERIFY(progressValues.contains(4));
530 QVERIFY(progressTexts.contains(QLatin1String("Foo 1")));
531 QVERIFY(progressTexts.contains(QLatin1String("Foo 2")));
532 QVERIFY(progressTexts.contains(QLatin1String("Foo 3")));
533 QVERIFY(progressTexts.contains(QLatin1String("Foo 4")));
534 }
535}
536
537template <typename T>
538void callInterface(T &obj)
539{
540 obj.progressValue();
541 obj.progressMinimum();
542 obj.progressMaximum();
543 obj.progressText();
544
545 obj.isStarted();
546 obj.isFinished();
547 obj.isRunning();
548 obj.isCanceled();
549 obj.isPaused();
550
551 obj.cancel();
552 obj.pause();
553 obj.resume();
554 obj.togglePaused();
555 obj.waitForFinished();
556
557 const T& objConst = obj;
558 objConst.progressValue();
559 objConst.progressMinimum();
560 objConst.progressMaximum();
561 objConst.progressText();
562
563 objConst.isStarted();
564 objConst.isFinished();
565 objConst.isRunning();
566 objConst.isCanceled();
567 objConst.isPaused();
568}
569
570template <typename T>
571void callInterface(const T &obj)
572{
573 obj.result();
574 obj.resultAt(0);
575}
576
577
578// QFutureWatcher and QFuture has a similar interface. Test
579// that the functions we want ot have in both are actually
580// there.
581void tst_QFutureWatcher::sharedFutureInterface()
582{
583 QFutureInterface<int> iface;
584 iface.reportStarted();
585
586 QFuture<int> intFuture = iface.future();
587
588 int value = 0;
589 iface.reportFinished(&value);
590
591 QFuture<void> voidFuture;
592 QFutureWatcher<int> intWatcher;
593 intWatcher.setFuture(intFuture);
594 QFutureWatcher<void> voidWatcher;
595
596 callInterface(intFuture);
597 callInterface(voidFuture);
598 callInterface(intWatcher);
599 callInterface(voidWatcher);
600
601 callInterface(intFuture);
602 callInterface(intWatcher);
603}
604
605void tst_QFutureWatcher::changeFuture()
606{
607 QFutureInterface<int> iface;
608 iface.reportStarted();
609
610 QFuture<int> a = iface.future();
611
612 int value = 0;
613 iface.reportFinished(&value);
614
615 QFuture<int> b;
616
617 QFutureWatcher<int> watcher;
618
619 SignalSlotObject object;
620 connect(&watcher, SIGNAL(resultReadyAt(int)), &object, SLOT(resultReadyAt(int)));
621 QSignalSpy resultReadySpy(&watcher, &QFutureWatcher<int>::resultReadyAt);
622 QVERIFY(resultReadySpy.isValid());
623
624 watcher.setFuture(a); // Watch 'a' which will generate a resultReady event.
625 watcher.setFuture(b); // But oh no! we're switching to another future
626 QTest::qWait(10); // before the event gets delivered.
627
628 QCOMPARE(resultReadySpy.count(), 0);
629
630 watcher.setFuture(a);
631 watcher.setFuture(b);
632 watcher.setFuture(a); // setting it back gets us one event, not two.
633 QVERIFY(resultReadySpy.wait());
634
635 QCOMPARE(resultReadySpy.count(), 1);
636}
637
638// Test that events aren't delivered from canceled futures
639void tst_QFutureWatcher::cancelEvents()
640{
641 QFutureInterface<int> iface;
642 iface.reportStarted();
643
644 QFuture<int> a = iface.future();
645
646 int value = 0;
647 iface.reportFinished(&value);
648
649 QFutureWatcher<int> watcher;
650
651 SignalSlotObject object;
652 connect(&watcher, SIGNAL(resultReadyAt(int)), &object, SLOT(resultReadyAt(int)));
653 QSignalSpy finishedSpy(&watcher, &QFutureWatcher<int>::finished);
654 QSignalSpy resultReadySpy(&watcher, &QFutureWatcher<int>::resultReadyAt);
655 QVERIFY(finishedSpy.isValid());
656 QVERIFY(resultReadySpy.isValid());
657
658 watcher.setFuture(a);
659 watcher.cancel();
660
661 QVERIFY(finishedSpy.wait());
662
663 QCOMPARE(resultReadySpy.count(), 0);
664}
665
666// Tests that events from paused futures are saved and
667// delivered on resume.
668void tst_QFutureWatcher::pauseEvents()
669{
670 {
671 QFutureInterface<int> iface;
672 iface.reportStarted();
673
674 QFutureWatcher<int> watcher;
675
676 SignalSlotObject object;
677 connect(&watcher, SIGNAL(resultReadyAt(int)), &object, SLOT(resultReadyAt(int)));
678 QSignalSpy resultReadySpy(&watcher, &QFutureWatcher<int>::resultReadyAt);
679 QVERIFY(resultReadySpy.isValid());
680
681 watcher.setFuture(iface.future());
682 watcher.pause();
683
684 int value = 0;
685 iface.reportFinished(&value);
686
687 QTest::qWait(10);
688 QCOMPARE(resultReadySpy.count(), 0);
689
690 watcher.resume();
691 QTRY_VERIFY2(!resultReadySpy.isEmpty(), "Result didn't arrive");
692 QCOMPARE(resultReadySpy.count(), 1);
693 }
694 {
695 QFutureInterface<int> iface;
696 iface.reportStarted();
697
698 QFuture<int> a = iface.future();
699
700 QFutureWatcher<int> watcher;
701
702 SignalSlotObject object;
703 connect(&watcher, SIGNAL(resultReadyAt(int)), &object, SLOT(resultReadyAt(int)));
704 QSignalSpy resultReadySpy(&watcher, &QFutureWatcher<int>::resultReadyAt);
705 QVERIFY(resultReadySpy.isValid());
706
707 watcher.setFuture(a);
708 a.pause();
709
710 int value = 0;
711 iface.reportFinished(&value);
712
713 QFuture<int> b;
714 watcher.setFuture(b); // If we watch b instead, resuming a
715 a.resume(); // should give us no results.
716
717 QTest::qWait(10);
718 QCOMPARE(resultReadySpy.count(), 0);
719 }
720}
721
722// Test that the finished state for the watcher gets
723// set when the finished event is delivered.
724// This means it will lag the finished state for the future,
725// but makes it more useful.
726void tst_QFutureWatcher::finishedState()
727{
728 QFutureInterface<int> iface;
729 iface.reportStarted();
730 QFuture<int> future = iface.future();
731 QFutureWatcher<int> watcher;
732 QSignalSpy startedSpy(&watcher, &QFutureWatcher<int>::started);
733 QSignalSpy finishedSpy(&watcher, &QFutureWatcher<int>::finished);
734
735 watcher.setFuture(future);
736 QVERIFY(startedSpy.wait());
737
738 iface.reportFinished();
739 QVERIFY(future.isFinished());
740 QVERIFY(!watcher.isFinished());
741
742 QVERIFY(finishedSpy.wait());
743 QVERIFY(watcher.isFinished());
744}
745
746/*
747 Verify that throttling kicks in if you report a lot of results,
748 and that it clears when the result events are processed.
749*/
750void tst_QFutureWatcher::throttling()
751{
752 QFutureInterface<int> iface;
753 iface.reportStarted();
754 QFuture<int> future = iface.future();
755 QFutureWatcher<int> watcher;
756 QSignalSpy resultSpy(&watcher, &QFutureWatcher<int>::resultReadyAt);
757 watcher.setFuture(future);
758
759 QVERIFY(!iface.isThrottled());
760
761 const int resultCount = 1000;
762 for (int i = 0; i < resultCount; ++i) {
763 int result = 0;
764 iface.reportResult(result);
765 }
766
767 QVERIFY(iface.isThrottled());
768
769 QTRY_COMPARE(resultSpy.count(), resultCount); // Process the results
770
771 QVERIFY(!iface.isThrottled());
772
773 iface.reportFinished();
774}
775
776int mapper(const int &i)
777{
778 return i;
779}
780
781class ResultReadyTester : public QObject
782{
783Q_OBJECT
784public:
785 ResultReadyTester(QFutureWatcher<int> *watcher)
786 :m_watcher(watcher), filter(false), ok(true), count(0)
787 {
788
789 }
790public slots:
791 void resultReadyAt(int index)
792 {
793 ++count;
794 if (m_watcher->future().isResultReadyAt(index) == false)
795 ok = false;
796 if (!filter && m_watcher->future().resultAt(index) != index)
797 ok = false;
798 if (filter && m_watcher->future().resultAt(index) != index * 2 + 1)
799 ok = false;
800 }
801public:
802 QFutureWatcher<int> *m_watcher;
803 bool filter;
804 bool ok;
805 int count;
806};
807
808void tst_QFutureWatcher::incrementalMapResults()
809{
810 QFutureWatcher<int> watcher;
811
812 SignalSlotObject object;
813#ifdef PRINT
814 connect(&watcher, SIGNAL(finished()), &object, SLOT(finished()));
815 connect(&watcher, SIGNAL(progressValueChanged(int)), &object, SLOT(progressValueChanged(int)));
816 connect(&watcher, SIGNAL(resultReadyAt(int)), &object, SLOT(resultReadyAt(int)));
817#endif
818
819 QObject::connect(&watcher, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
820
821 ResultReadyTester resultReadyTester(&watcher);
822 connect(&watcher, SIGNAL(resultReadyAt(int)), &resultReadyTester, SLOT(resultReadyAt(int)));
823
824 const int count = 10000;
825 QList<int> ints;
826 for (int i = 0; i < count; ++i)
827 ints << i;
828
829 QFuture<int> future = QtConcurrent::mapped(ints, mapper);
830 watcher.setFuture(future);
831
832 QTestEventLoop::instance().enterLoop(10);
833 QVERIFY(!QTestEventLoop::instance().timeout());
834 QCOMPARE(resultReadyTester.count, count);
835 QVERIFY(resultReadyTester.ok);
836 QVERIFY(watcher.isFinished());
837 future.waitForFinished();
838}
839
840bool filterer(int i)
841{
842 return (i % 2);
843}
844
845void tst_QFutureWatcher::incrementalFilterResults()
846{
847 QFutureWatcher<int> watcher;
848
849 SignalSlotObject object;
850#ifdef PRINT
851 connect(&watcher, SIGNAL(finished()), &object, SLOT(finished()));
852 connect(&watcher, SIGNAL(progressValueChanged(int)), &object, SLOT(progressValueChanged(int)));
853 connect(&watcher, SIGNAL(resultReadyAt(int)), &object, SLOT(resultReadyAt(int)));
854#endif
855
856 QObject::connect(&watcher, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
857
858
859 ResultReadyTester resultReadyTester(&watcher);
860 resultReadyTester.filter = true;
861 connect(&watcher, SIGNAL(resultReadyAt(int)), &resultReadyTester, SLOT(resultReadyAt(int)));
862
863 const int count = 10000;
864 QList<int> ints;
865 for (int i = 0; i < count; ++i)
866 ints << i;
867
868 QFuture<int> future = QtConcurrent::filtered(ints, filterer);
869 watcher.setFuture(future);
870
871 QTestEventLoop::instance().enterLoop(10);
872 QVERIFY(!QTestEventLoop::instance().timeout());
873 QCOMPARE(resultReadyTester.count, count / 2);
874 QVERIFY(resultReadyTester.ok);
875 QVERIFY(watcher.isFinished());
876 future.waitForFinished();
877}
878
879void tst_QFutureWatcher::qfutureSynchronizer()
880{
881 int taskCount = 1000;
882 QElapsedTimer t;
883 t.start();
884
885 {
886 QFutureSynchronizer<void> sync;
887
888 sync.setCancelOnWait(true);
889 for (int i = 0; i < taskCount; ++i) {
890 sync.addFuture(run(sleeper));
891 }
892 }
893
894 // Test that we're not running each task.
895 QVERIFY(t.elapsed() < taskCount * 10);
896}
897
898class DummyObject : public QObject {
899 Q_OBJECT
900public slots:
901 void dummySlot() {}
902public:
903 static void function(QMutex *m)
904 {
905 QMutexLocker lock(m);
906 }
907};
908
909void tst_QFutureWatcher::warnRace()
910{
911#ifndef Q_OS_MAC //I don't know why it is not working on mac
912#ifndef QT_NO_DEBUG
913 QTest::ignoreMessage(QtWarningMsg, "QFutureWatcher::connect: connecting after calling setFuture() is likely to produce race");
914#endif
915#endif
916 QFutureWatcher<void> watcher;
917 DummyObject object;
918 QMutex mutex;
919 mutex.lock();
920
921 QFuture<void> future = QtConcurrent::run(DummyObject::function, &mutex);
922 watcher.setFuture(future);
923 QTRY_VERIFY(future.isStarted());
924 connect(&watcher, SIGNAL(finished()), &object, SLOT(dummySlot()));
925 mutex.unlock();
926 future.waitForFinished();
927}
928
929void tst_QFutureWatcher::matchFlags()
930{
931 /* Regression test: expect a default watcher to be in the same state as a
932 * default future. */
933 QFutureWatcher<int> watcher;
934 QFuture<int> future;
935 QCOMPARE(watcher.isStarted(), future.isStarted());
936 QCOMPARE(watcher.isCanceled(), future.isCanceled());
937 QCOMPARE(watcher.isFinished(), future.isFinished());
938}
939
940
941QTEST_MAIN(tst_QFutureWatcher)
942#include "tst_qfuturewatcher.moc"
943