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 | |
31 | #define QFUTURE_TEST |
32 | |
33 | #include <QtTest/QtTest> |
34 | #include <qfuture.h> |
35 | #include <qfuturewatcher.h> |
36 | #include <qresultstore.h> |
37 | #include <qthreadpool.h> |
38 | #include <qexception.h> |
39 | #include <qrandom.h> |
40 | #include <private/qfutureinterface_p.h> |
41 | |
42 | // COM interface macro. |
43 | #if defined(Q_OS_WIN) && defined(interface) |
44 | # undef interface |
45 | #endif |
46 | |
47 | struct ResultStoreInt : QtPrivate::ResultStoreBase |
48 | { |
49 | ~ResultStoreInt() { clear<int>(); } |
50 | }; |
51 | |
52 | class LambdaThread : public QThread |
53 | { |
54 | public: |
55 | LambdaThread(std::function<void ()> fn) |
56 | :m_fn(fn) |
57 | { |
58 | |
59 | } |
60 | |
61 | void run() override |
62 | { |
63 | m_fn(); |
64 | } |
65 | |
66 | private: |
67 | std::function<void ()> m_fn; |
68 | }; |
69 | |
70 | class tst_QFuture: public QObject |
71 | { |
72 | Q_OBJECT |
73 | private slots: |
74 | void resultStore(); |
75 | void future(); |
76 | void futureInterface(); |
77 | void refcounting(); |
78 | void cancel(); |
79 | void statePropagation(); |
80 | void multipleResults(); |
81 | void indexedResults(); |
82 | void progress(); |
83 | void progressText(); |
84 | void resultsAfterFinished(); |
85 | void resultsAsList(); |
86 | void implicitConversions(); |
87 | void iterators(); |
88 | void iteratorsThread(); |
89 | void pause(); |
90 | void throttling(); |
91 | void voidConversions(); |
92 | #ifndef QT_NO_EXCEPTIONS |
93 | void exceptions(); |
94 | void nestedExceptions(); |
95 | #endif |
96 | void nonGlobalThreadPool(); |
97 | void resultsReadyAt(); |
98 | }; |
99 | |
100 | void tst_QFuture::resultStore() |
101 | { |
102 | int int0 = 0; |
103 | int int1 = 1; |
104 | int int2 = 2; |
105 | |
106 | { |
107 | ResultStoreInt store; |
108 | QCOMPARE(store.begin(), store.end()); |
109 | QCOMPARE(store.resultAt(0), store.end()); |
110 | QCOMPARE(store.resultAt(1), store.end()); |
111 | } |
112 | |
113 | |
114 | { |
115 | ResultStoreInt store; |
116 | store.addResult(index: -1, result: &int0); |
117 | store.addResult(index: 1, result: &int1); |
118 | QtPrivate::ResultIteratorBase it = store.begin(); |
119 | QCOMPARE(it.resultIndex(), 0); |
120 | QVERIFY(it == store.begin()); |
121 | QVERIFY(it != store.end()); |
122 | |
123 | ++it; |
124 | QCOMPARE(it.resultIndex(), 1); |
125 | QVERIFY(it != store.begin()); |
126 | QVERIFY(it != store.end()); |
127 | |
128 | ++it; |
129 | QVERIFY(it != store.begin()); |
130 | QVERIFY(it == store.end()); |
131 | } |
132 | |
133 | QVector<int> vec0 = QVector<int>() << 2 << 3; |
134 | QVector<int> vec1 = QVector<int>() << 4 << 5; |
135 | |
136 | { |
137 | ResultStoreInt store; |
138 | store.addResults(index: -1, results: &vec0, totalCount: 2); |
139 | store.addResults(index: -1, results: &vec1, totalCount: 2); |
140 | QtPrivate::ResultIteratorBase it = store.begin(); |
141 | QCOMPARE(it.resultIndex(), 0); |
142 | QCOMPARE(it, store.begin()); |
143 | QVERIFY(it != store.end()); |
144 | |
145 | ++it; |
146 | QCOMPARE(it.resultIndex(), 1); |
147 | QVERIFY(it != store.begin()); |
148 | QVERIFY(it != store.end()); |
149 | |
150 | ++it; |
151 | QCOMPARE(it.resultIndex(), 2); |
152 | |
153 | ++it; |
154 | QCOMPARE(it.resultIndex(), 3); |
155 | |
156 | ++it; |
157 | QCOMPARE(it, store.end()); |
158 | } |
159 | { |
160 | ResultStoreInt store; |
161 | store.addResult(index: -1, result: &int0); |
162 | store.addResults(index: -1, results: &vec1, totalCount: 2); |
163 | store.addResult(index: -1, result: &int1); |
164 | |
165 | QtPrivate::ResultIteratorBase it = store.begin(); |
166 | QCOMPARE(it.resultIndex(), 0); |
167 | QVERIFY(it == store.begin()); |
168 | QVERIFY(it != store.end()); |
169 | |
170 | ++it; |
171 | QCOMPARE(it.resultIndex(), 1); |
172 | QVERIFY(it != store.begin()); |
173 | QVERIFY(it != store.end()); |
174 | |
175 | ++it; |
176 | QCOMPARE(it.resultIndex(), 2); |
177 | QVERIFY(it != store.end()); |
178 | ++it; |
179 | QCOMPARE(it.resultIndex(), 3); |
180 | QVERIFY(it != store.end()); |
181 | ++it; |
182 | QVERIFY(it == store.end()); |
183 | |
184 | QCOMPARE(store.resultAt(0).resultIndex(), 0); |
185 | QCOMPARE(store.resultAt(1).resultIndex(), 1); |
186 | QCOMPARE(store.resultAt(2).resultIndex(), 2); |
187 | QCOMPARE(store.resultAt(3).resultIndex(), 3); |
188 | QCOMPARE(store.resultAt(4), store.end()); |
189 | } |
190 | { |
191 | ResultStoreInt store; |
192 | store.addResult(index: -1, result: &int0); |
193 | store.addResults(index: -1, results: &vec0); |
194 | store.addResult(index: -1, result: &int1); |
195 | |
196 | QtPrivate::ResultIteratorBase it = store.begin(); |
197 | QCOMPARE(it.resultIndex(), 0); |
198 | QVERIFY(it == store.begin()); |
199 | QVERIFY(it != store.end()); |
200 | |
201 | ++it; |
202 | QCOMPARE(it.resultIndex(), 1); |
203 | QVERIFY(it != store.begin()); |
204 | QVERIFY(it != store.end()); |
205 | |
206 | ++it; |
207 | QCOMPARE(it.resultIndex(), 2); |
208 | QVERIFY(it != store.end()); |
209 | ++it; |
210 | QCOMPARE(it.resultIndex(), 3); |
211 | QVERIFY(it != store.end()); |
212 | ++it; |
213 | QVERIFY(it == store.end()); |
214 | |
215 | QCOMPARE(store.resultAt(0).value<int>(), int0); |
216 | QCOMPARE(store.resultAt(1).value<int>(), vec0[0]); |
217 | QCOMPARE(store.resultAt(2).value<int>(), vec0[1]); |
218 | QCOMPARE(store.resultAt(3).value<int>(), int1); |
219 | } |
220 | { |
221 | ResultStoreInt store; |
222 | store.addResult(index: -1, result: &int0); |
223 | store.addResults(index: -1, results: &vec0); |
224 | store.addResult(index: 200, result: &int1); |
225 | |
226 | QCOMPARE(store.resultAt(0).value<int>(), int0); |
227 | QCOMPARE(store.resultAt(1).value<int>(), vec0[0]); |
228 | QCOMPARE(store.resultAt(2).value<int>(), vec0[1]); |
229 | QCOMPARE(store.resultAt(200).value<int>(), int1); |
230 | } |
231 | |
232 | { |
233 | ResultStoreInt store; |
234 | store.addResult(index: 1, result: &int1); |
235 | store.addResult(index: 0, result: &int0); |
236 | store.addResult(index: -1, result: &int2); |
237 | |
238 | QCOMPARE(store.resultAt(0).value<int>(), int0); |
239 | QCOMPARE(store.resultAt(1).value<int>(), int1); |
240 | QCOMPARE(store.resultAt(2).value<int>(), int2); |
241 | } |
242 | |
243 | { |
244 | ResultStoreInt store; |
245 | QCOMPARE(store.contains(0), false); |
246 | QCOMPARE(store.contains(1), false); |
247 | QCOMPARE(store.contains(INT_MAX), false); |
248 | } |
249 | |
250 | { |
251 | // Test filter mode, where "gaps" in the result array aren't allowed. |
252 | ResultStoreInt store; |
253 | store.setFilterMode(true); |
254 | |
255 | store.addResult(index: 0, result: &int0); |
256 | QCOMPARE(store.contains(0), true); |
257 | |
258 | store.addResult(index: 2, result: &int2); // add result at index 2 |
259 | QCOMPARE(store.contains(2), false); // but 1 is missing, so this 2 won't be reported yet. |
260 | |
261 | store.addResult(index: 1, result: &int1); |
262 | QCOMPARE(store.contains(1), true); |
263 | QCOMPARE(store.contains(2), true); // 2 should be visible now. |
264 | |
265 | store.addResult(index: 4, result: &int0); |
266 | store.addResult(index: 5, result: &int0); |
267 | store.addResult(index: 7, result: &int0); |
268 | QCOMPARE(store.contains(4), false); |
269 | QCOMPARE(store.contains(5), false); |
270 | QCOMPARE(store.contains(7), false); |
271 | |
272 | store.addResult(index: 3, result: &int0); // adding 3 makes 4 and 5 visible |
273 | QCOMPARE(store.contains(4), true); |
274 | QCOMPARE(store.contains(5), true); |
275 | QCOMPARE(store.contains(7), false); |
276 | |
277 | store.addResult(index: 6, result: &int0); // adding 6 makes 7 visible |
278 | |
279 | QCOMPARE(store.contains(6), true); |
280 | QCOMPARE(store.contains(7), true); |
281 | QCOMPARE(store.contains(8), false); |
282 | } |
283 | |
284 | { |
285 | // test canceled results |
286 | ResultStoreInt store; |
287 | store.setFilterMode(true); |
288 | |
289 | store.addResult(index: 0, result: &int0); |
290 | QCOMPARE(store.contains(0), true); |
291 | |
292 | store.addResult(index: 2, result: &int0); |
293 | QCOMPARE(store.contains(2), false); |
294 | |
295 | store.addCanceledResult(index: 1); // report no result at 1 |
296 | |
297 | QCOMPARE(store.contains(0), true); |
298 | QCOMPARE(store.contains(1), true); // 2 gets renamed to 1 |
299 | QCOMPARE(store.contains(2), false); |
300 | |
301 | store.addResult(index: 3, result: &int0); |
302 | QCOMPARE(store.contains(2), true); //3 gets renamed to 2 |
303 | |
304 | store.addResult(index: 6, result: &int0); |
305 | store.addResult(index: 7, result: &int0); |
306 | QCOMPARE(store.contains(3), false); |
307 | |
308 | store.addCanceledResult(index: 4); |
309 | store.addCanceledResult(index: 5); |
310 | |
311 | QCOMPARE(store.contains(3), true); //6 gets renamed to 3 |
312 | QCOMPARE(store.contains(4), true); //7 gets renamed to 4 |
313 | |
314 | store.addResult(index: 8, result: &int0); |
315 | QCOMPARE(store.contains(5), true); //8 gets renamed to 4 |
316 | |
317 | QCOMPARE(store.contains(6), false); |
318 | QCOMPARE(store.contains(7), false); |
319 | } |
320 | |
321 | { |
322 | // test addResult return value |
323 | ResultStoreInt store; |
324 | store.setFilterMode(true); |
325 | |
326 | store.addResult(index: 0, result: &int0); |
327 | QCOMPARE(store.count(), 1); // result 0 becomes available |
328 | QCOMPARE(store.contains(0), true); |
329 | |
330 | store.addResult(index: 2, result: &int0); |
331 | QCOMPARE(store.count(), 1); |
332 | QCOMPARE(store.contains(2), false); |
333 | |
334 | store.addCanceledResult(index: 1); |
335 | QCOMPARE(store.count(), 2); // result 2 is renamed to 1 and becomes available |
336 | |
337 | QCOMPARE(store.contains(0), true); |
338 | QCOMPARE(store.contains(1), true); |
339 | QCOMPARE(store.contains(2), false); |
340 | |
341 | store.addResult(index: 3, result: &int0); |
342 | QCOMPARE(store.count(), 3); |
343 | QCOMPARE(store.contains(2), true); |
344 | |
345 | store.addResult(index: 6, result: &int0); |
346 | QCOMPARE(store.count(), 3); |
347 | store.addResult(index: 7, result: &int0); |
348 | QCOMPARE(store.count(), 3); |
349 | QCOMPARE(store.contains(3), false); |
350 | |
351 | store.addCanceledResult(index: 4); |
352 | store.addCanceledResult(index: 5); |
353 | QCOMPARE(store.count(), 5); // 6 and 7 is renamed to 3 and 4 and becomes available |
354 | |
355 | QCOMPARE(store.contains(3), true); |
356 | QCOMPARE(store.contains(4), true); |
357 | |
358 | store.addResult(index: 8, result: &int0); |
359 | QCOMPARE(store.contains(5), true); |
360 | QCOMPARE(store.count(), 6); |
361 | |
362 | QCOMPARE(store.contains(6), false); |
363 | QCOMPARE(store.contains(7), false); |
364 | } |
365 | |
366 | { |
367 | // test resultCount in non-filtered mode. It should always be possible |
368 | // to iterate through the results 0 to resultCount. |
369 | ResultStoreInt store; |
370 | store.addResult(index: 0, result: &int0); |
371 | |
372 | QCOMPARE(store.count(), 1); |
373 | |
374 | store.addResult(index: 2, result: &int0); |
375 | |
376 | QCOMPARE(store.count(), 1); |
377 | |
378 | store.addResult(index: 1, result: &int0); |
379 | QCOMPARE(store.count(), 3); |
380 | } |
381 | |
382 | { |
383 | ResultStoreInt store; |
384 | store.addResult(index: 2, result: &int0); |
385 | QCOMPARE(store.count(), 0); |
386 | |
387 | store.addResult(index: 1, result: &int0); |
388 | QCOMPARE(store.count(), 0); |
389 | |
390 | store.addResult(index: 0, result: &int0); |
391 | QCOMPARE(store.count(), 3); |
392 | } |
393 | |
394 | { |
395 | ResultStoreInt store; |
396 | store.addResults(index: 2, results: &vec1); |
397 | QCOMPARE(store.count(), 0); |
398 | |
399 | store.addResult(index: 1, result: &int0); |
400 | QCOMPARE(store.count(), 0); |
401 | |
402 | store.addResult(index: 0, result: &int0); |
403 | QCOMPARE(store.count(), 4); |
404 | } |
405 | |
406 | { |
407 | ResultStoreInt store; |
408 | store.addResults(index: 2, results: &vec1); |
409 | QCOMPARE(store.count(), 0); |
410 | |
411 | store.addResults(index: 0, results: &vec0); |
412 | QCOMPARE(store.count(), 4); |
413 | } |
414 | { |
415 | ResultStoreInt store; |
416 | store.addResults(index: 3, results: &vec1); |
417 | QCOMPARE(store.count(), 0); |
418 | |
419 | store.addResults(index: 0, results: &vec0); |
420 | QCOMPARE(store.count(), 2); |
421 | |
422 | store.addResult(index: 2, result: &int0); |
423 | QCOMPARE(store.count(), 5); |
424 | } |
425 | |
426 | { |
427 | ResultStoreInt store; |
428 | store.setFilterMode(true); |
429 | store.addResults(index: 3, results: &vec1); |
430 | QCOMPARE(store.count(), 0); |
431 | |
432 | store.addResults(index: 0, results: &vec0); |
433 | QCOMPARE(store.count(), 2); |
434 | |
435 | store.addCanceledResult(index: 2); |
436 | QCOMPARE(store.count(), 4); |
437 | } |
438 | |
439 | { |
440 | ResultStoreInt store; |
441 | store.setFilterMode(true); |
442 | store.addResults(index: 3, results: &vec1); |
443 | QCOMPARE(store.count(), 0); |
444 | |
445 | store.addCanceledResults<int>(index: 0, count: 3); |
446 | QCOMPARE(store.count(), 2); |
447 | } |
448 | |
449 | { |
450 | ResultStoreInt store; |
451 | store.setFilterMode(true); |
452 | store.addResults(index: 3, results: &vec1); |
453 | QCOMPARE(store.count(), 0); |
454 | |
455 | store.addCanceledResults<int>(index: 0, count: 3); |
456 | QCOMPARE(store.count(), 2); // results at 3 and 4 become available at index 0, 1 |
457 | |
458 | store.addResult(index: 5, result: &int0); |
459 | QCOMPARE(store.count(), 3);// result 5 becomes available at index 2 |
460 | } |
461 | |
462 | { |
463 | ResultStoreInt store; |
464 | store.addResult(index: 1, result: &int0); |
465 | store.addResult(index: 3, result: &int0); |
466 | store.addResults(index: 6, results: &vec0); |
467 | QCOMPARE(store.contains(0), false); |
468 | QCOMPARE(store.contains(1), true); |
469 | QCOMPARE(store.contains(2), false); |
470 | QCOMPARE(store.contains(3), true); |
471 | QCOMPARE(store.contains(4), false); |
472 | QCOMPARE(store.contains(5), false); |
473 | QCOMPARE(store.contains(6), true); |
474 | QCOMPARE(store.contains(7), true); |
475 | } |
476 | |
477 | { |
478 | ResultStoreInt store; |
479 | store.setFilterMode(true); |
480 | store.addResult(index: 1, result: &int0); |
481 | store.addResult(index: 3, result: &int0); |
482 | store.addResults(index: 6, results: &vec0); |
483 | QCOMPARE(store.contains(0), false); |
484 | QCOMPARE(store.contains(1), false); |
485 | QCOMPARE(store.contains(2), false); |
486 | QCOMPARE(store.contains(3), false); |
487 | QCOMPARE(store.contains(4), false); |
488 | QCOMPARE(store.contains(5), false); |
489 | QCOMPARE(store.contains(6), false); |
490 | QCOMPARE(store.contains(7), false); |
491 | |
492 | store.addCanceledResult(index: 0); |
493 | store.addCanceledResult(index: 2); |
494 | store.addCanceledResults<int>(index: 4, count: 2); |
495 | |
496 | QCOMPARE(store.contains(0), true); |
497 | QCOMPARE(store.contains(1), true); |
498 | QCOMPARE(store.contains(2), true); |
499 | QCOMPARE(store.contains(3), true); |
500 | QCOMPARE(store.contains(4), false); |
501 | QCOMPARE(store.contains(5), false); |
502 | QCOMPARE(store.contains(6), false); |
503 | QCOMPARE(store.contains(7), false); |
504 | } |
505 | { |
506 | ResultStoreInt store; |
507 | store.setFilterMode(true); |
508 | store.addCanceledResult(index: 0); |
509 | QCOMPARE(store.contains(0), false); |
510 | |
511 | store.addResult(index: 1, result: &int0); |
512 | QCOMPARE(store.contains(0), true); |
513 | QCOMPARE(store.contains(1), false); |
514 | } |
515 | } |
516 | |
517 | void tst_QFuture::future() |
518 | { |
519 | // default constructors |
520 | QFuture<int> intFuture; |
521 | intFuture.waitForFinished(); |
522 | QFuture<QString> stringFuture; |
523 | stringFuture.waitForFinished(); |
524 | QFuture<void> voidFuture; |
525 | voidFuture.waitForFinished(); |
526 | QFuture<void> defaultVoidFuture; |
527 | defaultVoidFuture.waitForFinished(); |
528 | |
529 | // copy constructor |
530 | QFuture<int> intFuture2(intFuture); |
531 | QFuture<void> voidFuture2(defaultVoidFuture); |
532 | |
533 | // assigmnent operator |
534 | intFuture2 = QFuture<int>(); |
535 | voidFuture2 = QFuture<void>(); |
536 | |
537 | // state |
538 | QCOMPARE(intFuture2.isStarted(), true); |
539 | QCOMPARE(intFuture2.isFinished(), true); |
540 | } |
541 | |
542 | class IntResult : public QFutureInterface<int> |
543 | { |
544 | public: |
545 | QFuture<int> run() |
546 | { |
547 | this->reportStarted(); |
548 | QFuture<int> future = QFuture<int>(this); |
549 | |
550 | int res = 10; |
551 | reportFinished(result: &res); |
552 | return future; |
553 | } |
554 | }; |
555 | |
556 | int value = 10; |
557 | |
558 | class VoidResult : public QFutureInterfaceBase |
559 | { |
560 | public: |
561 | QFuture<void> run() |
562 | { |
563 | this->reportStarted(); |
564 | QFuture<void> future = QFuture<void>(this); |
565 | reportFinished(); |
566 | return future; |
567 | } |
568 | }; |
569 | |
570 | void tst_QFuture::futureInterface() |
571 | { |
572 | { |
573 | QFuture<void> future; |
574 | { |
575 | QFutureInterface<void> i; |
576 | i.reportStarted(); |
577 | future = i.future(); |
578 | i.reportFinished(); |
579 | } |
580 | } |
581 | { |
582 | QFuture<int> future; |
583 | { |
584 | QFutureInterface<int> i; |
585 | i.reportStarted(); |
586 | i.reportResult(result: 10); |
587 | future = i.future(); |
588 | i.reportFinished(); |
589 | } |
590 | QCOMPARE(future.resultAt(0), 10); |
591 | } |
592 | |
593 | { |
594 | QFuture<int> intFuture; |
595 | |
596 | QCOMPARE(intFuture.isStarted(), true); |
597 | QCOMPARE(intFuture.isFinished(), true); |
598 | |
599 | IntResult result; |
600 | |
601 | result.reportStarted(); |
602 | intFuture = result.future(); |
603 | |
604 | QCOMPARE(intFuture.isStarted(), true); |
605 | QCOMPARE(intFuture.isFinished(), false); |
606 | |
607 | result.reportFinished(result: &value); |
608 | |
609 | QCOMPARE(intFuture.isStarted(), true); |
610 | QCOMPARE(intFuture.isFinished(), true); |
611 | |
612 | int e = intFuture.result(); |
613 | |
614 | QCOMPARE(intFuture.isStarted(), true); |
615 | QCOMPARE(intFuture.isFinished(), true); |
616 | QCOMPARE(intFuture.isCanceled(), false); |
617 | |
618 | QCOMPARE(e, value); |
619 | intFuture.waitForFinished(); |
620 | |
621 | IntResult intAlgo; |
622 | intFuture = intAlgo.run(); |
623 | QFuture<int> intFuture2(intFuture); |
624 | QCOMPARE(intFuture.result(), value); |
625 | QCOMPARE(intFuture2.result(), value); |
626 | intFuture.waitForFinished(); |
627 | |
628 | VoidResult a; |
629 | a.run().waitForFinished(); |
630 | } |
631 | |
632 | { |
633 | QFutureInterface<int> fi; |
634 | fi.reportStarted(); |
635 | fi.reportResults(results: QVector<int> {}); |
636 | fi.reportFinished(); |
637 | |
638 | QVERIFY(fi.results().empty()); |
639 | } |
640 | |
641 | { |
642 | QFutureInterface<int> fi; |
643 | fi.reportStarted(); |
644 | QVector<int> values = { 1, 2, 3 }; |
645 | fi.reportResults(results: values); |
646 | fi.reportResults(results: QVector<int> {}); |
647 | fi.reportFinished(); |
648 | |
649 | QCOMPARE(fi.results(), values.toList()); |
650 | } |
651 | } |
652 | |
653 | template <typename T> |
654 | void testRefCounting() |
655 | { |
656 | QFutureInterface<T> interface; |
657 | QCOMPARE(interface.d->refCount.load(), 1); |
658 | |
659 | { |
660 | interface.reportStarted(); |
661 | |
662 | QFuture<T> f = interface.future(); |
663 | QCOMPARE(interface.d->refCount.load(), 2); |
664 | |
665 | QFuture<T> f2(f); |
666 | QCOMPARE(interface.d->refCount.load(), 3); |
667 | |
668 | QFuture<T> f3; |
669 | f3 = f2; |
670 | QCOMPARE(interface.d->refCount.load(), 4); |
671 | |
672 | interface.reportFinished(0); |
673 | QCOMPARE(interface.d->refCount.load(), 4); |
674 | } |
675 | |
676 | QCOMPARE(interface.d->refCount.load(), 1); |
677 | } |
678 | |
679 | void tst_QFuture::refcounting() |
680 | { |
681 | testRefCounting<int>(); |
682 | } |
683 | |
684 | void tst_QFuture::cancel() |
685 | { |
686 | { |
687 | QFuture<void> f; |
688 | QFutureInterface<void> result; |
689 | |
690 | result.reportStarted(); |
691 | f = result.future(); |
692 | QVERIFY(!f.isCanceled()); |
693 | result.reportCanceled(); |
694 | QVERIFY(f.isCanceled()); |
695 | result.reportFinished(); |
696 | QVERIFY(f.isCanceled()); |
697 | f.waitForFinished(); |
698 | QVERIFY(f.isCanceled()); |
699 | } |
700 | |
701 | // Cancel from the QFuture side and test if the result |
702 | // interface detects it. |
703 | { |
704 | QFutureInterface<void> result; |
705 | |
706 | QFuture<void> f; |
707 | QVERIFY(f.isStarted()); |
708 | |
709 | result.reportStarted(); |
710 | f = result.future(); |
711 | |
712 | QVERIFY(f.isStarted()); |
713 | |
714 | QVERIFY(!result.isCanceled()); |
715 | f.cancel(); |
716 | |
717 | QVERIFY(result.isCanceled()); |
718 | |
719 | result.reportFinished(); |
720 | } |
721 | |
722 | // Test that finished futures can be canceled. |
723 | { |
724 | QFutureInterface<void> result; |
725 | |
726 | QFuture<void> f; |
727 | QVERIFY(f.isStarted()); |
728 | |
729 | result.reportStarted(); |
730 | f = result.future(); |
731 | |
732 | QVERIFY(f.isStarted()); |
733 | |
734 | result.reportFinished(); |
735 | |
736 | f.cancel(); |
737 | |
738 | QVERIFY(result.isCanceled()); |
739 | QVERIFY(f.isCanceled()); |
740 | } |
741 | |
742 | // Results reported after canceled is called should not be propagated. |
743 | { |
744 | |
745 | QFutureInterface<int> futureInterface; |
746 | futureInterface.reportStarted(); |
747 | QFuture<int> f = futureInterface.future(); |
748 | |
749 | int result = 0; |
750 | futureInterface.reportResult(result: &result); |
751 | result = 1; |
752 | futureInterface.reportResult(result: &result); |
753 | f.cancel(); |
754 | result = 2; |
755 | futureInterface.reportResult(result: &result); |
756 | result = 3; |
757 | futureInterface.reportResult(result: &result); |
758 | futureInterface.reportFinished(); |
759 | QVERIFY(f.results().isEmpty()); |
760 | } |
761 | } |
762 | |
763 | void tst_QFuture::statePropagation() |
764 | { |
765 | QFuture<void> f1; |
766 | QFuture<void> f2; |
767 | |
768 | QCOMPARE(f1.isStarted(), true); |
769 | |
770 | QFutureInterface<void> result; |
771 | result.reportStarted(); |
772 | f1 = result.future(); |
773 | |
774 | f2 = f1; |
775 | |
776 | QCOMPARE(f2.isStarted(), true); |
777 | |
778 | result.reportCanceled(); |
779 | |
780 | QCOMPARE(f2.isStarted(), true); |
781 | QCOMPARE(f2.isCanceled(), true); |
782 | |
783 | QFuture<void> f3 = f2; |
784 | |
785 | QCOMPARE(f3.isStarted(), true); |
786 | QCOMPARE(f3.isCanceled(), true); |
787 | |
788 | result.reportFinished(); |
789 | |
790 | QCOMPARE(f2.isStarted(), true); |
791 | QCOMPARE(f2.isCanceled(), true); |
792 | |
793 | QCOMPARE(f3.isStarted(), true); |
794 | QCOMPARE(f3.isCanceled(), true); |
795 | } |
796 | |
797 | /* |
798 | Tests that a QFuture can return multiple results. |
799 | */ |
800 | void tst_QFuture::multipleResults() |
801 | { |
802 | IntResult a; |
803 | a.reportStarted(); |
804 | QFuture<int> f = a.future(); |
805 | |
806 | QFuture<int> copy = f; |
807 | int result; |
808 | |
809 | result = 1; |
810 | a.reportResult(result: &result); |
811 | QCOMPARE(f.resultAt(0), 1); |
812 | |
813 | result = 2; |
814 | a.reportResult(result: &result); |
815 | QCOMPARE(f.resultAt(1), 2); |
816 | |
817 | result = 3; |
818 | a.reportResult(result: &result); |
819 | |
820 | result = 4; |
821 | a.reportFinished(result: &result); |
822 | |
823 | QCOMPARE(f.results(), QList<int>() << 1 << 2 << 3 << 4); |
824 | |
825 | // test foreach |
826 | QList<int> fasit = QList<int>() << 1 << 2 << 3 << 4; |
827 | { |
828 | QList<int> results; |
829 | foreach(int result, f) |
830 | results.append(t: result); |
831 | QCOMPARE(results, fasit); |
832 | } |
833 | { |
834 | QList<int> results; |
835 | foreach(int result, copy) |
836 | results.append(t: result); |
837 | QCOMPARE(results, fasit); |
838 | } |
839 | } |
840 | |
841 | /* |
842 | Test out-of-order result reporting using indexes |
843 | */ |
844 | void tst_QFuture::indexedResults() |
845 | { |
846 | { |
847 | QFutureInterface<QChar> Interface; |
848 | QFuture<QChar> f; |
849 | QVERIFY(f.isStarted()); |
850 | |
851 | Interface.reportStarted(); |
852 | f = Interface.future(); |
853 | |
854 | QVERIFY(f.isStarted()); |
855 | |
856 | QChar result; |
857 | |
858 | result = 'B'; |
859 | Interface.reportResult(result: &result, index: 1); |
860 | |
861 | QCOMPARE(f.resultAt(1), result); |
862 | |
863 | result = 'A'; |
864 | Interface.reportResult(result: &result, index: 0); |
865 | QCOMPARE(f.resultAt(0), result); |
866 | |
867 | result = 'C'; |
868 | Interface.reportResult(result: &result); // no index |
869 | QCOMPARE(f.resultAt(2), result); |
870 | |
871 | Interface.reportFinished(); |
872 | |
873 | QCOMPARE(f.results(), QList<QChar>() << 'A' << 'B' << 'C'); |
874 | } |
875 | |
876 | { |
877 | // Test result reporting with a missing result in the middle |
878 | QFutureInterface<int> Interface; |
879 | Interface.reportStarted(); |
880 | QFuture<int> f = Interface.future(); |
881 | int result; |
882 | |
883 | result = 0; |
884 | Interface.reportResult(result: &result, index: 0); |
885 | QVERIFY(f.isResultReadyAt(0)); |
886 | QCOMPARE(f.resultAt(0), 0); |
887 | |
888 | result = 3; |
889 | Interface.reportResult(result: &result, index: 3); |
890 | QVERIFY(f.isResultReadyAt(3)); |
891 | QCOMPARE(f.resultAt(3), 3); |
892 | |
893 | result = 2; |
894 | Interface.reportResult(result: &result, index: 2); |
895 | QVERIFY(f.isResultReadyAt(2)); |
896 | QCOMPARE(f.resultAt(2), 2); |
897 | |
898 | result = 4; |
899 | Interface.reportResult(result: &result); // no index |
900 | QVERIFY(f.isResultReadyAt(4)); |
901 | QCOMPARE(f.resultAt(4), 4); |
902 | |
903 | Interface.reportFinished(); |
904 | |
905 | QCOMPARE(f.results(), QList<int>() << 0 << 2 << 3 << 4); |
906 | } |
907 | } |
908 | |
909 | void tst_QFuture::progress() |
910 | { |
911 | QFutureInterface<QChar> result; |
912 | QFuture<QChar> f; |
913 | |
914 | QCOMPARE (f.progressValue(), 0); |
915 | |
916 | result.reportStarted(); |
917 | f = result.future(); |
918 | |
919 | QCOMPARE (f.progressValue(), 0); |
920 | |
921 | result.setProgressValue(50); |
922 | |
923 | QCOMPARE (f.progressValue(), 50); |
924 | |
925 | result.reportFinished(); |
926 | |
927 | QCOMPARE (f.progressValue(), 50); |
928 | } |
929 | |
930 | void tst_QFuture::progressText() |
931 | { |
932 | QFutureInterface<void> i; |
933 | i.reportStarted(); |
934 | QFuture<void> f = i.future(); |
935 | |
936 | QCOMPARE(f.progressText(), QLatin1String("" )); |
937 | i.setProgressValueAndText(progressValue: 1, progressText: QLatin1String("foo" )); |
938 | QCOMPARE(f.progressText(), QLatin1String("foo" )); |
939 | i.reportFinished(); |
940 | } |
941 | |
942 | /* |
943 | Test that results reported after finished are ignored. |
944 | */ |
945 | void tst_QFuture::resultsAfterFinished() |
946 | { |
947 | { |
948 | IntResult a; |
949 | a.reportStarted(); |
950 | QFuture<int> f = a.future(); |
951 | int result; |
952 | |
953 | QCOMPARE(f.resultCount(), 0); |
954 | |
955 | result = 1; |
956 | a.reportResult(result: &result); |
957 | QCOMPARE(f.resultAt(0), 1); |
958 | |
959 | a.reportFinished(); |
960 | |
961 | QCOMPARE(f.resultAt(0), 1); |
962 | QCOMPARE(f.resultCount(), 1); |
963 | result = 2; |
964 | a.reportResult(result: &result); |
965 | QCOMPARE(f.resultCount(), 1); |
966 | } |
967 | // cancel it |
968 | { |
969 | IntResult a; |
970 | a.reportStarted(); |
971 | QFuture<int> f = a.future(); |
972 | int result; |
973 | |
974 | QCOMPARE(f.resultCount(), 0); |
975 | |
976 | result = 1; |
977 | a.reportResult(result: &result); |
978 | QCOMPARE(f.resultAt(0), 1); |
979 | QCOMPARE(f.resultCount(), 1); |
980 | |
981 | a.reportCanceled(); |
982 | |
983 | QCOMPARE(f.resultAt(0), 1); |
984 | QCOMPARE(f.resultCount(), 1); |
985 | |
986 | result = 2; |
987 | a.reportResult(result: &result); |
988 | a.reportFinished(); |
989 | } |
990 | } |
991 | |
992 | void tst_QFuture::resultsAsList() |
993 | { |
994 | IntResult a; |
995 | a.reportStarted(); |
996 | QFuture<int> f = a.future(); |
997 | |
998 | int result; |
999 | result = 1; |
1000 | a.reportResult(result: &result); |
1001 | result = 2; |
1002 | a.reportResult(result: &result); |
1003 | |
1004 | a.reportFinished(); |
1005 | |
1006 | QList<int> results = f.results(); |
1007 | QCOMPARE(results, QList<int>() << 1 << 2); |
1008 | } |
1009 | |
1010 | /* |
1011 | Test that QFuture<T> can be implicitly converted to T |
1012 | */ |
1013 | void tst_QFuture::implicitConversions() |
1014 | { |
1015 | QFutureInterface<QString> iface; |
1016 | iface.reportStarted(); |
1017 | |
1018 | QFuture<QString> f(&iface); |
1019 | |
1020 | const QString input("FooBar 2000" ); |
1021 | iface.reportFinished(result: &input); |
1022 | |
1023 | const QString result = f; |
1024 | QCOMPARE(result, input); |
1025 | QCOMPARE(QString(f), input); |
1026 | QCOMPARE(static_cast<QString>(f), input); |
1027 | } |
1028 | |
1029 | void tst_QFuture::iterators() |
1030 | { |
1031 | { |
1032 | QFutureInterface<int> e; |
1033 | e.reportStarted(); |
1034 | QFuture<int> f = e.future(); |
1035 | |
1036 | int result; |
1037 | result = 1; |
1038 | e.reportResult(result: &result); |
1039 | result = 2; |
1040 | e.reportResult(result: &result); |
1041 | result = 3; |
1042 | e.reportResult(result: &result); |
1043 | e.reportFinished(); |
1044 | |
1045 | QList<int> results; |
1046 | QFutureIterator<int> i(f); |
1047 | while (i.hasNext()) { |
1048 | results.append(t: i.next()); |
1049 | } |
1050 | |
1051 | QCOMPARE(results, f.results()); |
1052 | |
1053 | QFuture<int>::const_iterator i1 = f.begin(), i2 = i1 + 1; |
1054 | QFuture<int>::const_iterator c1 = i1, c2 = c1 + 1; |
1055 | |
1056 | QCOMPARE(i1, i1); |
1057 | QCOMPARE(i1, c1); |
1058 | QCOMPARE(c1, i1); |
1059 | QCOMPARE(c1, c1); |
1060 | QCOMPARE(i2, i2); |
1061 | QCOMPARE(i2, c2); |
1062 | QCOMPARE(c2, i2); |
1063 | QCOMPARE(c2, c2); |
1064 | QCOMPARE(1 + i1, i1 + 1); |
1065 | QCOMPARE(1 + c1, c1 + 1); |
1066 | |
1067 | QVERIFY(i1 != i2); |
1068 | QVERIFY(i1 != c2); |
1069 | QVERIFY(c1 != i2); |
1070 | QVERIFY(c1 != c2); |
1071 | QVERIFY(i2 != i1); |
1072 | QVERIFY(i2 != c1); |
1073 | QVERIFY(c2 != i1); |
1074 | QVERIFY(c2 != c1); |
1075 | |
1076 | int x1 = *i1; |
1077 | Q_UNUSED(x1); |
1078 | int x2 = *i2; |
1079 | Q_UNUSED(x2); |
1080 | int y1 = *c1; |
1081 | Q_UNUSED(y1); |
1082 | int y2 = *c2; |
1083 | Q_UNUSED(y2); |
1084 | } |
1085 | |
1086 | { |
1087 | QFutureInterface<QString> e; |
1088 | e.reportStarted(); |
1089 | QFuture<QString> f = e.future(); |
1090 | |
1091 | e.reportResult(result: QString("one" )); |
1092 | e.reportResult(result: QString("two" )); |
1093 | e.reportResult(result: QString("three" )); |
1094 | e.reportFinished(); |
1095 | |
1096 | QList<QString> results; |
1097 | QFutureIterator<QString> i(f); |
1098 | while (i.hasNext()) { |
1099 | results.append(t: i.next()); |
1100 | } |
1101 | |
1102 | QCOMPARE(results, f.results()); |
1103 | |
1104 | QFuture<QString>::const_iterator i1 = f.begin(), i2 = i1 + 1; |
1105 | QFuture<QString>::const_iterator c1 = i1, c2 = c1 + 1; |
1106 | |
1107 | QCOMPARE(i1, i1); |
1108 | QCOMPARE(i1, c1); |
1109 | QCOMPARE(c1, i1); |
1110 | QCOMPARE(c1, c1); |
1111 | QCOMPARE(i2, i2); |
1112 | QCOMPARE(i2, c2); |
1113 | QCOMPARE(c2, i2); |
1114 | QCOMPARE(c2, c2); |
1115 | QCOMPARE(1 + i1, i1 + 1); |
1116 | QCOMPARE(1 + c1, c1 + 1); |
1117 | |
1118 | QVERIFY(i1 != i2); |
1119 | QVERIFY(i1 != c2); |
1120 | QVERIFY(c1 != i2); |
1121 | QVERIFY(c1 != c2); |
1122 | QVERIFY(i2 != i1); |
1123 | QVERIFY(i2 != c1); |
1124 | QVERIFY(c2 != i1); |
1125 | QVERIFY(c2 != c1); |
1126 | |
1127 | QString x1 = *i1; |
1128 | QString x2 = *i2; |
1129 | QString y1 = *c1; |
1130 | QString y2 = *c2; |
1131 | |
1132 | QCOMPARE(x1, y1); |
1133 | QCOMPARE(x2, y2); |
1134 | |
1135 | int i1Size = i1->size(); |
1136 | int i2Size = i2->size(); |
1137 | int c1Size = c1->size(); |
1138 | int c2Size = c2->size(); |
1139 | |
1140 | QCOMPARE(i1Size, c1Size); |
1141 | QCOMPARE(i2Size, c2Size); |
1142 | } |
1143 | |
1144 | { |
1145 | const int resultCount = 20; |
1146 | |
1147 | QFutureInterface<int> e; |
1148 | e.reportStarted(); |
1149 | QFuture<int> f = e.future(); |
1150 | |
1151 | for (int i = 0; i < resultCount; ++i) { |
1152 | e.reportResult(result: i); |
1153 | } |
1154 | |
1155 | e.reportFinished(); |
1156 | |
1157 | { |
1158 | QFutureIterator<int> it(f); |
1159 | QFutureIterator<int> it2(it); |
1160 | } |
1161 | |
1162 | { |
1163 | QFutureIterator<int> it(f); |
1164 | |
1165 | for (int i = 0; i < resultCount - 1; ++i) { |
1166 | QVERIFY(it.hasNext()); |
1167 | QCOMPARE(it.peekNext(), i); |
1168 | QCOMPARE(it.next(), i); |
1169 | } |
1170 | |
1171 | QVERIFY(it.hasNext()); |
1172 | QCOMPARE(it.peekNext(), resultCount - 1); |
1173 | QCOMPARE(it.next(), resultCount - 1); |
1174 | QVERIFY(!it.hasNext()); |
1175 | } |
1176 | |
1177 | { |
1178 | QFutureIterator<int> it(f); |
1179 | QVERIFY(it.hasNext()); |
1180 | it.toBack(); |
1181 | QVERIFY(!it.hasNext()); |
1182 | it.toFront(); |
1183 | QVERIFY(it.hasNext()); |
1184 | } |
1185 | } |
1186 | } |
1187 | void tst_QFuture::iteratorsThread() |
1188 | { |
1189 | const int expectedResultCount = 10; |
1190 | const int delay = 10; |
1191 | QFutureInterface<int> futureInterface; |
1192 | |
1193 | // Create result producer thread. The results are |
1194 | // produced with delays in order to make the consumer |
1195 | // wait. |
1196 | QSemaphore sem; |
1197 | LambdaThread thread = {[=, &futureInterface, &sem](){ |
1198 | for (int i = 1; i <= expectedResultCount; i += 2) { |
1199 | int result = i; |
1200 | futureInterface.reportResult(result: &result); |
1201 | result = i + 1; |
1202 | futureInterface.reportResult(result: &result); |
1203 | } |
1204 | |
1205 | sem.acquire(n: 2); |
1206 | futureInterface.reportFinished(); |
1207 | }}; |
1208 | |
1209 | futureInterface.reportStarted(); |
1210 | QFuture<int> future = futureInterface.future(); |
1211 | |
1212 | // Iterate over results while the thread is producing them. |
1213 | thread.start(); |
1214 | int resultCount = 0; |
1215 | int resultSum = 0; |
1216 | for (int result : future) { |
1217 | sem.release(); |
1218 | ++resultCount; |
1219 | resultSum += result; |
1220 | } |
1221 | thread.wait(); |
1222 | |
1223 | QCOMPARE(resultCount, expectedResultCount); |
1224 | QCOMPARE(resultSum, expectedResultCount * (expectedResultCount + 1) / 2); |
1225 | |
1226 | // Reverse iterate |
1227 | resultSum = 0; |
1228 | QFutureIterator<int> it(future); |
1229 | it.toBack(); |
1230 | while (it.hasPrevious()) |
1231 | resultSum += it.previous(); |
1232 | |
1233 | QCOMPARE(resultSum, expectedResultCount * (expectedResultCount + 1) / 2); |
1234 | } |
1235 | |
1236 | class SignalSlotObject : public QObject |
1237 | { |
1238 | Q_OBJECT |
1239 | public: |
1240 | SignalSlotObject() |
1241 | : finishedCalled(false), |
1242 | canceledCalled(false), |
1243 | rangeBegin(0), |
1244 | rangeEnd(0) { } |
1245 | |
1246 | public slots: |
1247 | void finished() |
1248 | { |
1249 | finishedCalled = true; |
1250 | } |
1251 | |
1252 | void canceled() |
1253 | { |
1254 | canceledCalled = true; |
1255 | } |
1256 | |
1257 | void resultReady(int index) |
1258 | { |
1259 | results.insert(value: index); |
1260 | } |
1261 | |
1262 | void progressRange(int begin, int end) |
1263 | { |
1264 | rangeBegin = begin; |
1265 | rangeEnd = end; |
1266 | } |
1267 | |
1268 | void progress(int progress) |
1269 | { |
1270 | reportedProgress.insert(value: progress); |
1271 | } |
1272 | public: |
1273 | bool finishedCalled; |
1274 | bool canceledCalled; |
1275 | QSet<int> results; |
1276 | int rangeBegin; |
1277 | int rangeEnd; |
1278 | QSet<int> reportedProgress; |
1279 | }; |
1280 | |
1281 | void tst_QFuture::pause() |
1282 | { |
1283 | QFutureInterface<void> Interface; |
1284 | |
1285 | Interface.reportStarted(); |
1286 | QFuture<void> f = Interface.future(); |
1287 | |
1288 | QVERIFY(!Interface.isPaused()); |
1289 | f.pause(); |
1290 | QVERIFY(Interface.isPaused()); |
1291 | f.resume(); |
1292 | QVERIFY(!Interface.isPaused()); |
1293 | f.togglePaused(); |
1294 | QVERIFY(Interface.isPaused()); |
1295 | f.togglePaused(); |
1296 | QVERIFY(!Interface.isPaused()); |
1297 | |
1298 | Interface.reportFinished(); |
1299 | } |
1300 | |
1301 | class ResultObject : public QObject |
1302 | { |
1303 | Q_OBJECT |
1304 | public slots: |
1305 | void resultReady(int) |
1306 | { |
1307 | |
1308 | } |
1309 | public: |
1310 | }; |
1311 | |
1312 | // Test that that the isPaused() on future result interface returns true |
1313 | // if we report a lot of results that are not handled. |
1314 | void tst_QFuture::throttling() |
1315 | { |
1316 | { |
1317 | QFutureInterface<void> i; |
1318 | |
1319 | i.reportStarted(); |
1320 | QFuture<void> f = i.future(); |
1321 | |
1322 | QVERIFY(!i.isThrottled()); |
1323 | |
1324 | i.setThrottled(true); |
1325 | QVERIFY(i.isThrottled()); |
1326 | |
1327 | i.setThrottled(false); |
1328 | QVERIFY(!i.isThrottled()); |
1329 | |
1330 | i.setThrottled(true); |
1331 | QVERIFY(i.isThrottled()); |
1332 | |
1333 | i.reportFinished(); |
1334 | } |
1335 | } |
1336 | |
1337 | void tst_QFuture::voidConversions() |
1338 | { |
1339 | { |
1340 | QFutureInterface<int> iface; |
1341 | iface.reportStarted(); |
1342 | |
1343 | QFuture<int> intFuture(&iface); |
1344 | int value = 10; |
1345 | iface.reportFinished(result: &value); |
1346 | |
1347 | QFuture<void> voidFuture(intFuture); |
1348 | voidFuture = intFuture; |
1349 | |
1350 | QVERIFY(voidFuture == intFuture); |
1351 | } |
1352 | |
1353 | { |
1354 | QFuture<void> voidFuture; |
1355 | { |
1356 | QFutureInterface<QList<int> > iface; |
1357 | iface.reportStarted(); |
1358 | |
1359 | QFuture<QList<int> > listFuture(&iface); |
1360 | iface.reportResult(result: QList<int>() << 1 << 2 << 3); |
1361 | voidFuture = listFuture; |
1362 | } |
1363 | QCOMPARE(voidFuture.resultCount(), 0); |
1364 | } |
1365 | } |
1366 | |
1367 | |
1368 | #ifndef QT_NO_EXCEPTIONS |
1369 | |
1370 | QFuture<void> createExceptionFuture() |
1371 | { |
1372 | QFutureInterface<void> i; |
1373 | i.reportStarted(); |
1374 | QFuture<void> f = i.future(); |
1375 | |
1376 | QException e; |
1377 | i.reportException(e); |
1378 | i.reportFinished(); |
1379 | return f; |
1380 | } |
1381 | |
1382 | QFuture<int> createExceptionResultFuture() |
1383 | { |
1384 | QFutureInterface<int> i; |
1385 | i.reportStarted(); |
1386 | QFuture<int> f = i.future(); |
1387 | int r = 0; |
1388 | i.reportResult(result: r); |
1389 | |
1390 | QException e; |
1391 | i.reportException(e); |
1392 | i.reportFinished(); |
1393 | return f; |
1394 | } |
1395 | |
1396 | class DerivedException : public QException |
1397 | { |
1398 | public: |
1399 | void raise() const override { throw *this; } |
1400 | DerivedException *clone() const override { return new DerivedException(*this); } |
1401 | }; |
1402 | |
1403 | QFuture<void> createDerivedExceptionFuture() |
1404 | { |
1405 | QFutureInterface<void> i; |
1406 | i.reportStarted(); |
1407 | QFuture<void> f = i.future(); |
1408 | |
1409 | DerivedException e; |
1410 | i.reportException(e); |
1411 | i.reportFinished(); |
1412 | return f; |
1413 | } |
1414 | |
1415 | void tst_QFuture::exceptions() |
1416 | { |
1417 | // test throwing from waitForFinished |
1418 | { |
1419 | QFuture<void> f = createExceptionFuture(); |
1420 | bool caught = false; |
1421 | try { |
1422 | f.waitForFinished(); |
1423 | } catch (QException &) { |
1424 | caught = true; |
1425 | } |
1426 | QVERIFY(caught); |
1427 | } |
1428 | |
1429 | // test result() |
1430 | { |
1431 | QFuture<int> f = createExceptionResultFuture(); |
1432 | bool caught = false; |
1433 | try { |
1434 | f.result(); |
1435 | } catch (QException &) { |
1436 | caught = true; |
1437 | } |
1438 | QVERIFY(caught); |
1439 | } |
1440 | |
1441 | // test result() and destroy |
1442 | { |
1443 | bool caught = false; |
1444 | try { |
1445 | createExceptionResultFuture().result(); |
1446 | } catch (QException &) { |
1447 | caught = true; |
1448 | } |
1449 | QVERIFY(caught); |
1450 | } |
1451 | |
1452 | // test results() |
1453 | { |
1454 | QFuture<int> f = createExceptionResultFuture(); |
1455 | bool caught = false; |
1456 | try { |
1457 | f.results(); |
1458 | } catch (QException &) { |
1459 | caught = true; |
1460 | } |
1461 | QVERIFY(caught); |
1462 | } |
1463 | |
1464 | // test foreach |
1465 | { |
1466 | QFuture<int> f = createExceptionResultFuture(); |
1467 | bool caught = false; |
1468 | try { |
1469 | foreach (int e, f.results()) { |
1470 | Q_UNUSED(e); |
1471 | QFAIL("did not get exception" ); |
1472 | } |
1473 | } catch (QException &) { |
1474 | caught = true; |
1475 | } |
1476 | QVERIFY(caught); |
1477 | } |
1478 | |
1479 | // catch derived exceptions |
1480 | { |
1481 | bool caught = false; |
1482 | try { |
1483 | createDerivedExceptionFuture().waitForFinished(); |
1484 | } catch (QException &) { |
1485 | caught = true; |
1486 | } |
1487 | QVERIFY(caught); |
1488 | } |
1489 | |
1490 | { |
1491 | bool caught = false; |
1492 | try { |
1493 | createDerivedExceptionFuture().waitForFinished(); |
1494 | } catch (DerivedException &) { |
1495 | caught = true; |
1496 | } |
1497 | QVERIFY(caught); |
1498 | } |
1499 | } |
1500 | |
1501 | class MyClass |
1502 | { |
1503 | public: |
1504 | ~MyClass() |
1505 | { |
1506 | QFuture<void> f = createExceptionFuture(); |
1507 | try { |
1508 | f.waitForFinished(); |
1509 | } catch (QException &) { |
1510 | caught = true; |
1511 | } |
1512 | } |
1513 | static bool caught; |
1514 | }; |
1515 | |
1516 | bool MyClass::caught = false; |
1517 | |
1518 | // This is a regression test for QTBUG-18149. where QFuture did not throw |
1519 | // exceptions if called from destructors when the stack was already unwinding |
1520 | // due to an exception having been thrown. |
1521 | void tst_QFuture::nestedExceptions() |
1522 | { |
1523 | try { |
1524 | MyClass m; |
1525 | Q_UNUSED(m); |
1526 | throw 0; |
1527 | } catch (int) {} |
1528 | |
1529 | QVERIFY(MyClass::caught); |
1530 | } |
1531 | |
1532 | #endif // QT_NO_EXCEPTIONS |
1533 | |
1534 | void tst_QFuture::nonGlobalThreadPool() |
1535 | { |
1536 | static Q_CONSTEXPR int Answer = 42; |
1537 | |
1538 | struct UselessTask : QRunnable, QFutureInterface<int> |
1539 | { |
1540 | QFuture<int> start(QThreadPool *pool) |
1541 | { |
1542 | setRunnable(this); |
1543 | setThreadPool(pool); |
1544 | reportStarted(); |
1545 | QFuture<int> f = future(); |
1546 | pool->start(runnable: this); |
1547 | return f; |
1548 | } |
1549 | |
1550 | void run() override |
1551 | { |
1552 | const int ms = 100 + (QRandomGenerator::global()->bounded(highest: 100) - 100/2); |
1553 | QThread::msleep(ms); |
1554 | reportResult(result: Answer); |
1555 | reportFinished(); |
1556 | } |
1557 | }; |
1558 | |
1559 | QThreadPool pool; |
1560 | |
1561 | const int numTasks = QThread::idealThreadCount(); |
1562 | |
1563 | QVector<QFuture<int> > futures; |
1564 | futures.reserve(size: numTasks); |
1565 | |
1566 | for (int i = 0; i < numTasks; ++i) |
1567 | futures.push_back(t: (new UselessTask)->start(pool: &pool)); |
1568 | |
1569 | QVERIFY(!pool.waitForDone(0)); // pool is busy (meaning our tasks did end up executing there) |
1570 | |
1571 | QVERIFY(pool.waitForDone(10000)); // max sleep time in UselessTask::run is 150ms, so 10s should be enough |
1572 | // (and the call returns as soon as all tasks finished anyway, so the |
1573 | // maximum wait time only matters when the test fails) |
1574 | |
1575 | Q_FOREACH (const QFuture<int> &future, futures) { |
1576 | QVERIFY(future.isFinished()); |
1577 | QCOMPARE(future.result(), Answer); |
1578 | } |
1579 | } |
1580 | |
1581 | void tst_QFuture::resultsReadyAt() |
1582 | { |
1583 | QFutureInterface<int> iface; |
1584 | QFutureWatcher<int> watcher; |
1585 | watcher.setFuture(iface.future()); |
1586 | |
1587 | QTestEventLoop eventProcessor; |
1588 | connect(sender: &watcher, signal: &QFutureWatcher<int>::finished, receiver: &eventProcessor, slot: &QTestEventLoop::exitLoop); |
1589 | |
1590 | const int nExpectedResults = 4; |
1591 | int reported = 0; |
1592 | int taken = 0; |
1593 | connect(sender: &watcher, signal: &QFutureWatcher<int>::resultsReadyAt, |
1594 | slot: [&iface, &reported, &taken](int begin, int end) |
1595 | { |
1596 | auto future = iface.future(); |
1597 | QVERIFY(end - begin > 0); |
1598 | for (int i = begin; i < end; ++i, ++reported) { |
1599 | QVERIFY(future.isResultReadyAt(i)); |
1600 | taken |= 1 << i; |
1601 | } |
1602 | }); |
1603 | |
1604 | auto report = [&iface](int index) |
1605 | { |
1606 | int dummyResult = 0b101010; |
1607 | iface.reportResult(result: &dummyResult, index); |
1608 | }; |
1609 | |
1610 | const QSignalSpy readyCounter(&watcher, &QFutureWatcher<int>::resultsReadyAt); |
1611 | QTimer::singleShot(interval: 0, slot: [&iface, &report]{ |
1612 | // With filter mode == true, the result may go into the pending results. |
1613 | // Reporting it as ready will allow an application to try and access the |
1614 | // result, crashing on invalid (store.end()) iterator dereferenced. |
1615 | iface.setFilterMode(true); |
1616 | iface.reportStarted(); |
1617 | report(0); |
1618 | report(1); |
1619 | // This one - should not be reported (it goes into pending): |
1620 | report(3); |
1621 | // Let's close the 'gap' and make them all ready: |
1622 | report(-1); |
1623 | iface.reportFinished(); |
1624 | }); |
1625 | |
1626 | // Run event loop, QCoreApplication::postEvent is in use |
1627 | // in QFutureInterface: |
1628 | eventProcessor.enterLoopMSecs(ms: 2000); |
1629 | QVERIFY(!eventProcessor.timeout()); |
1630 | if (QTest::currentTestFailed()) // Failed in our lambda observing 'ready at' |
1631 | return; |
1632 | |
1633 | QCOMPARE(reported, nExpectedResults); |
1634 | QCOMPARE(nExpectedResults, iface.future().resultCount()); |
1635 | QCOMPARE(readyCounter.count(), 3); |
1636 | QCOMPARE(taken, 0b1111); |
1637 | } |
1638 | |
1639 | QTEST_MAIN(tst_QFuture) |
1640 | #include "tst_qfuture.moc" |
1641 | |