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 | |
36 | using namespace QtConcurrent; |
37 | |
38 | #include <QtTest/QtTest> |
39 | |
40 | //#define PRINT |
41 | |
42 | class tst_QFutureWatcher: public QObject |
43 | { |
44 | Q_OBJECT |
45 | private 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 | |
70 | void sleeper() |
71 | { |
72 | QTest::qSleep(ms: 100); |
73 | } |
74 | |
75 | void 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(functionPointer: 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 | |
95 | void mapSleeper(int &) |
96 | { |
97 | QTest::qSleep(ms: 100); |
98 | } |
99 | |
100 | QSet<int> progressValues; |
101 | QSet<QString> progressTexts; |
102 | QMutex mutex; |
103 | class ProgressObject : public QObject |
104 | { |
105 | Q_OBJECT |
106 | public slots: |
107 | void printProgress(int); |
108 | void printText(const QString &text); |
109 | void registerProgress(int); |
110 | void registerText(const QString &text); |
111 | }; |
112 | |
113 | void ProgressObject::printProgress(int progress) |
114 | { |
115 | qDebug() << "thread" << QThread::currentThread() << "reports progress" << progress; |
116 | } |
117 | |
118 | void ProgressObject::printText(const QString &text) |
119 | { |
120 | qDebug() << "thread" << QThread::currentThread() << "reports progress text" << text; |
121 | } |
122 | |
123 | void ProgressObject::registerProgress(int progress) |
124 | { |
125 | QTest::qSleep(ms: 1); |
126 | progressValues.insert(value: progress); |
127 | } |
128 | |
129 | void ProgressObject::registerText(const QString &text) |
130 | { |
131 | QTest::qSleep(ms: 1); |
132 | progressTexts.insert(value: text); |
133 | } |
134 | |
135 | |
136 | QList<int> createList(int listSize) |
137 | { |
138 | QList<int> list; |
139 | for (int i = 0; i < listSize; ++i) { |
140 | list.append(t: i); |
141 | } |
142 | return list; |
143 | } |
144 | |
145 | void 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(sender: &futureWatcher, SIGNAL(finished()), receiver: &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(sender: &futureWatcher, SIGNAL(progressValueChanged(int)), receiver: &progressObject, SLOT(registerProgress(int))); |
162 | |
163 | futureWatcher.setFuture(QtConcurrent::map(sequence&: list, map: mapSleeper)); |
164 | |
165 | QTestEventLoop::instance().enterLoop(secs: 5); |
166 | QVERIFY(!QTestEventLoop::instance().timeout()); |
167 | futureWatcher.disconnect(); |
168 | QVERIFY(progressValues.contains(0)); |
169 | QVERIFY(progressValues.contains(listSize)); |
170 | } |
171 | |
172 | class CancelObject : public QObject |
173 | { |
174 | Q_OBJECT |
175 | public: |
176 | bool wasCanceled; |
177 | CancelObject() : wasCanceled(false) {}; |
178 | public slots: |
179 | void cancel(); |
180 | }; |
181 | |
182 | void CancelObject::cancel() |
183 | { |
184 | #ifdef PRINT |
185 | qDebug() << "thread" << QThread::currentThread() << "reports canceled" ; |
186 | #endif |
187 | wasCanceled = true; |
188 | } |
189 | |
190 | void 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(sender: &futureWatcher, SIGNAL(canceled()), receiver: &cancelObject, SLOT(cancel())); |
200 | QObject::connect(sender: &futureWatcher, SIGNAL(canceled()), |
201 | receiver: &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection); |
202 | |
203 | future = QtConcurrent::map(sequence&: list, map: mapSleeper); |
204 | futureWatcher.setFuture(future); |
205 | futureWatcher.cancel(); |
206 | QTestEventLoop::instance().enterLoop(secs: 5); |
207 | QVERIFY(!QTestEventLoop::instance().timeout()); |
208 | |
209 | QVERIFY(future.isCanceled()); |
210 | QVERIFY(cancelObject.wasCanceled); |
211 | futureWatcher.disconnect(); |
212 | future.waitForFinished(); |
213 | } |
214 | |
215 | class IntTask : public RunFunctionTask<int> |
216 | { |
217 | public: |
218 | void runFunctor() |
219 | { |
220 | result = 10; |
221 | } |
222 | }; |
223 | |
224 | void 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 | |
233 | void 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 | |
252 | class SignalSlotObject : public QObject |
253 | { |
254 | Q_OBJECT |
255 | |
256 | signals: |
257 | void cancel(); |
258 | |
259 | public 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 | |
295 | void 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(sender: &f, SIGNAL(resultReadyAt(int)), receiver: &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: &result); |
328 | QVERIFY(resultReadySpy.wait()); |
329 | QCOMPARE(resultReadySpy.count(), 1); |
330 | a.reportFinished(result: &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 | |
340 | void 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(result: &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(sender: &watcher, SIGNAL(resultReadyAt(int)), receiver: &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 | |
381 | void 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(sender: &watcher, SIGNAL(resultReadyAt(int)), receiver: &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 | |
415 | void 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(sender: watcher, SIGNAL(resultReadyAt(int)), receiver: &object, SLOT(resultReadyAt(int))); |
431 | |
432 | const int result = 10; |
433 | a.reportResult(result: &result); |
434 | QVERIFY(resultReadySpy.wait()); |
435 | QCOMPARE(resultReadySpy.count(), 1); |
436 | |
437 | delete watcher; |
438 | |
439 | a.reportResult(result: &result); |
440 | QTest::qWait(ms: 10); |
441 | QCOMPARE(resultReadySpy.count(), 1); |
442 | |
443 | a.reportFinished(result: &result); |
444 | QTest::qWait(ms: 10); |
445 | QCOMPARE(finishedSpy.count(), 0); |
446 | } |
447 | |
448 | const int maxProgress = 100000; |
449 | class ProgressEmitterTask : public RunFunctionTask<void> |
450 | { |
451 | public: |
452 | void runFunctor() |
453 | { |
454 | setProgressRange(minimum: 0, maximum: maxProgress); |
455 | for (int p = 0; p <= maxProgress; ++p) |
456 | setProgressValue(p); |
457 | } |
458 | }; |
459 | |
460 | void tst_QFutureWatcher::tooMuchProgress() |
461 | { |
462 | progressValues.clear(); |
463 | ProgressObject o; |
464 | |
465 | QFutureWatcher<void> f; |
466 | QObject::connect(sender: &f, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop())); |
467 | #ifdef PRINT |
468 | QObject::connect(&f, SIGNAL(progressValueChanged(int)), &o, SLOT(printProgress(int))); |
469 | #endif |
470 | QObject::connect(sender: &f, SIGNAL(progressValueChanged(int)), receiver: &o, SLOT(registerProgress(int))); |
471 | f.setFuture((new ProgressEmitterTask())->start()); |
472 | |
473 | QTestEventLoop::instance().enterLoop(secs: 5); |
474 | QVERIFY(!QTestEventLoop::instance().timeout()); |
475 | QVERIFY(progressValues.contains(maxProgress)); |
476 | } |
477 | |
478 | template <typename T> |
479 | class ProgressTextTask : public RunFunctionTask<T> |
480 | { |
481 | public: |
482 | void runFunctor() |
483 | { |
484 | this->setProgressValueAndText(1, QLatin1String("Foo 1" )); |
485 | |
486 | while (this->isProgressUpdateNeeded() == false) |
487 | QTest::qSleep(ms: 1); |
488 | this->setProgressValueAndText(2, QLatin1String("Foo 2" )); |
489 | |
490 | while (this->isProgressUpdateNeeded() == false) |
491 | QTest::qSleep(ms: 1); |
492 | this->setProgressValueAndText(3, QLatin1String("Foo 3" )); |
493 | |
494 | while (this->isProgressUpdateNeeded() == false) |
495 | QTest::qSleep(ms: 1); |
496 | this->setProgressValueAndText(4, QLatin1String("Foo 4" )); |
497 | } |
498 | }; |
499 | |
500 | void 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(sender: &watcher, SIGNAL(finished()), receiver: &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(sender: &watcher, SIGNAL(progressValueChanged(int)), receiver: &o, SLOT(registerProgress(int))); |
518 | QObject::connect(sender: &watcher, SIGNAL(progressTextChanged(QString)), receiver: &o, SLOT(registerText(QString))); |
519 | |
520 | watcher.setFuture(f); |
521 | QTestEventLoop::instance().enterLoop(secs: 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 | |
537 | template <typename T> |
538 | void 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 | |
570 | template <typename T> |
571 | void 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. |
581 | void 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(result: &value); |
590 | |
591 | QFuture<void> voidFuture; |
592 | QFutureWatcher<int> intWatcher; |
593 | intWatcher.setFuture(intFuture); |
594 | QFutureWatcher<void> voidWatcher; |
595 | |
596 | callInterface(obj&: intFuture); |
597 | callInterface(obj&: voidFuture); |
598 | callInterface(obj&: intWatcher); |
599 | callInterface(obj&: voidWatcher); |
600 | |
601 | callInterface(obj&: intFuture); |
602 | callInterface(obj&: intWatcher); |
603 | } |
604 | |
605 | void 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(result: &value); |
614 | |
615 | QFuture<int> b; |
616 | |
617 | QFutureWatcher<int> watcher; |
618 | |
619 | SignalSlotObject object; |
620 | connect(sender: &watcher, SIGNAL(resultReadyAt(int)), receiver: &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(ms: 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 |
639 | void 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(result: &value); |
648 | |
649 | QFutureWatcher<int> watcher; |
650 | |
651 | SignalSlotObject object; |
652 | connect(sender: &watcher, SIGNAL(resultReadyAt(int)), receiver: &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. |
668 | void tst_QFutureWatcher::pauseEvents() |
669 | { |
670 | { |
671 | QFutureInterface<int> iface; |
672 | iface.reportStarted(); |
673 | |
674 | QFutureWatcher<int> watcher; |
675 | |
676 | SignalSlotObject object; |
677 | connect(sender: &watcher, SIGNAL(resultReadyAt(int)), receiver: &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(result: &value); |
686 | |
687 | QTest::qWait(ms: 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(sender: &watcher, SIGNAL(resultReadyAt(int)), receiver: &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(result: &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(ms: 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. |
726 | void 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 | */ |
750 | void 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 | |
776 | int mapper(const int &i) |
777 | { |
778 | return i; |
779 | } |
780 | |
781 | class ResultReadyTester : public QObject |
782 | { |
783 | Q_OBJECT |
784 | public: |
785 | ResultReadyTester(QFutureWatcher<int> *watcher) |
786 | :m_watcher(watcher), filter(false), ok(true), count(0) |
787 | { |
788 | |
789 | } |
790 | public slots: |
791 | void resultReadyAt(int index) |
792 | { |
793 | ++count; |
794 | if (m_watcher->future().isResultReadyAt(resultIndex: 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 | } |
801 | public: |
802 | QFutureWatcher<int> *m_watcher; |
803 | bool filter; |
804 | bool ok; |
805 | int count; |
806 | }; |
807 | |
808 | void 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(sender: &watcher, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop())); |
820 | |
821 | ResultReadyTester resultReadyTester(&watcher); |
822 | connect(sender: &watcher, SIGNAL(resultReadyAt(int)), receiver: &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(sequence: ints, map: mapper); |
830 | watcher.setFuture(future); |
831 | |
832 | QTestEventLoop::instance().enterLoop(secs: 10); |
833 | QVERIFY(!QTestEventLoop::instance().timeout()); |
834 | QCOMPARE(resultReadyTester.count, count); |
835 | QVERIFY(resultReadyTester.ok); |
836 | QVERIFY(watcher.isFinished()); |
837 | future.waitForFinished(); |
838 | } |
839 | |
840 | bool filterer(int i) |
841 | { |
842 | return (i % 2); |
843 | } |
844 | |
845 | void 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(sender: &watcher, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop())); |
857 | |
858 | |
859 | ResultReadyTester resultReadyTester(&watcher); |
860 | resultReadyTester.filter = true; |
861 | connect(sender: &watcher, SIGNAL(resultReadyAt(int)), receiver: &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(sequence: ints, keep: filterer); |
869 | watcher.setFuture(future); |
870 | |
871 | QTestEventLoop::instance().enterLoop(secs: 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 | |
879 | void 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(future: run(functionPointer: sleeper)); |
891 | } |
892 | } |
893 | |
894 | // Test that we're not running each task. |
895 | QVERIFY(t.elapsed() < taskCount * 10); |
896 | } |
897 | |
898 | class DummyObject : public QObject { |
899 | Q_OBJECT |
900 | public slots: |
901 | void dummySlot() {} |
902 | public: |
903 | static void function(QMutex *m) |
904 | { |
905 | QMutexLocker lock(m); |
906 | } |
907 | }; |
908 | |
909 | void 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(type: QtWarningMsg, message: "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(functionPointer: DummyObject::function, arg1: &mutex); |
922 | watcher.setFuture(future); |
923 | QTRY_VERIFY(future.isStarted()); |
924 | connect(sender: &watcher, SIGNAL(finished()), receiver: &object, SLOT(dummySlot())); |
925 | mutex.unlock(); |
926 | future.waitForFinished(); |
927 | } |
928 | |
929 | void 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 | |
941 | QTEST_MAIN(tst_QFutureWatcher) |
942 | #include "tst_qfuturewatcher.moc" |
943 | |