File indexing completed on 2024-12-08 12:44:37
0001 /* 0002 SPDX-FileCopyrightText: 2014 Daniel Vrátil <dvratil@redhat.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 // Krazy mistakes job.exec() for QDialog::exec() and urges us to use QPointer 0008 //krazy:excludeall=crashy 0009 0010 #include "../src/async.h" 0011 0012 #include <QObject> 0013 #include <QString> 0014 #include <QTimer> 0015 #include <QtTest/QTest> 0016 #include <QDebug> 0017 0018 #include <functional> 0019 0020 #define COMPARERET(actual, expected, retval) \ 0021 do {\ 0022 if (!QTest::qCompare(actual, expected, #actual, #expected, __FILE__, __LINE__))\ 0023 return retval;\ 0024 } while (0) 0025 0026 #define VERIFYRET(statement, retval) \ 0027 do {\ 0028 if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__))\ 0029 return retval;\ 0030 } while (0) 0031 0032 class AsyncTest : public QObject 0033 { 0034 Q_OBJECT 0035 0036 public: 0037 AsyncTest() 0038 {} 0039 0040 ~AsyncTest() 0041 {} 0042 0043 private Q_SLOTS: 0044 void testStart2(); 0045 void testSyncPromises(); 0046 void testErrorHandling(); 0047 void testContext(); 0048 void testGuard(); 0049 void testDoWhile(); 0050 void testDoWhileWithJob(); 0051 void testNestedDoWhile(); 0052 void testAsyncPromises(); 0053 void testNestedAsync(); 0054 void testVoidNestedJob(); 0055 void testAsyncEach(); 0056 void testAsyncSerialEach(); 0057 void noTemplateArguments(); 0058 void testValueJob(); 0059 0060 void benchmarkSyncThenExecutor(); 0061 void benchmarkFutureThenExecutor(); 0062 void benchmarkThenExecutor(); 0063 0064 private: 0065 template<typename T> 0066 class AsyncSimulator { 0067 public: 0068 AsyncSimulator(KAsync::Future<T> &future, const T &result) 0069 : mFuture(future) 0070 , mResult(result) 0071 { 0072 QObject::connect(&mTimer, &QTimer::timeout, 0073 [this]() { 0074 mFuture.setValue(mResult); 0075 mFuture.setFinished(); 0076 }); 0077 QObject::connect(&mTimer, &QTimer::timeout, 0078 [this]() { 0079 delete this; 0080 }); 0081 mTimer.setSingleShot(true); 0082 mTimer.start(200); 0083 } 0084 0085 AsyncSimulator(KAsync::Future<T> &future, std::function<void(KAsync::Future<T>&)> callback) 0086 : mFuture(future) 0087 , mCallback(callback) 0088 { 0089 QObject::connect(&mTimer, &QTimer::timeout, 0090 [this]() { 0091 mCallback(mFuture); 0092 }); 0093 QObject::connect(&mTimer, &QTimer::timeout, 0094 [this]() { 0095 delete this; 0096 }); 0097 mTimer.setSingleShot(true); 0098 mTimer.start(200); 0099 } 0100 0101 private: 0102 KAsync::Future<T> mFuture; 0103 std::function<void(KAsync::Future<T>&)> mCallback; 0104 T mResult; 0105 QTimer mTimer; 0106 }; 0107 0108 class MemberTest 0109 { 0110 public: 0111 MemberTest() 0112 : mFoo(-1) 0113 { 0114 } 0115 0116 void syncFoo(int foo) 0117 { 0118 mFoo = foo; 0119 } 0120 0121 int syncFooRet(int foo) 0122 { 0123 return ++foo; 0124 } 0125 0126 void asyncFoo(int foo, KAsync::Future<int> &future) 0127 { 0128 new AsyncSimulator<int>(future, ++foo); 0129 } 0130 0131 int mFoo; 0132 }; 0133 }; 0134 0135 template<> 0136 class AsyncTest::AsyncSimulator<void> { 0137 public: 0138 AsyncSimulator(KAsync::Future<void> &future) 0139 : mFuture(future) 0140 { 0141 QObject::connect(&mTimer, &QTimer::timeout, 0142 [this]() { 0143 mFuture.setFinished(); 0144 }); 0145 QObject::connect(&mTimer, &QTimer::timeout, 0146 [this]() { 0147 delete this; 0148 }); 0149 mTimer.setSingleShot(true); 0150 mTimer.start(200); 0151 } 0152 0153 private: 0154 KAsync::Future<void> mFuture; 0155 QTimer mTimer; 0156 }; 0157 0158 void AsyncTest::testStart2() 0159 { 0160 { 0161 auto future = KAsync::start<int>( 0162 []() { 0163 return 42; 0164 }).exec(); 0165 QVERIFY(future.isFinished()); 0166 QCOMPARE(future.value(), 42); 0167 } 0168 { 0169 auto future = KAsync::start<int, int>( 0170 [](int i) { 0171 return i; 0172 }).exec(42); 0173 QVERIFY(future.isFinished()); 0174 QCOMPARE(future.value(), 42); 0175 } 0176 { 0177 bool called = false; 0178 auto future = KAsync::start<void>( 0179 [&]() { 0180 called = true; 0181 }).exec(); 0182 QVERIFY(future.isFinished()); 0183 QVERIFY(called); 0184 } 0185 { 0186 auto future = KAsync::start<int>( 0187 []() { 0188 return KAsync::value(42); 0189 }).exec(); 0190 QVERIFY(future.isFinished()); 0191 QCOMPARE(future.value(), 42); 0192 } 0193 { 0194 auto future = KAsync::start<int, int>( 0195 [](int i) { 0196 return KAsync::value(i); 0197 }).exec(42); 0198 QVERIFY(future.isFinished()); 0199 QCOMPARE(future.value(), 42); 0200 } 0201 { 0202 auto future = KAsync::start<int>( 0203 [](KAsync::Future<int> &f) { 0204 f.setResult(42); 0205 }).exec(); 0206 QVERIFY(future.isFinished()); 0207 QCOMPARE(future.value(), 42); 0208 } 0209 0210 } 0211 0212 0213 void AsyncTest::testSyncPromises() 0214 { 0215 { 0216 auto future = KAsync::start<int>( 0217 []() { 0218 return 42; 0219 }).exec(); 0220 QVERIFY(future.isFinished()); 0221 QCOMPARE(future.value(), 42); 0222 } 0223 0224 { 0225 auto future = KAsync::start<int>( 0226 [](KAsync::Future<int> &f) { 0227 f.setResult(42); 0228 }).exec(); 0229 QVERIFY(future.isFinished()); 0230 QCOMPARE(future.value(), 42); 0231 } 0232 0233 //Sync start 0234 { 0235 auto future = KAsync::start<int>([] { 0236 return KAsync::value<int>(42); 0237 }).exec(); 0238 QVERIFY(future.isFinished()); 0239 QCOMPARE(future.value(), 42); 0240 } 0241 0242 //Sync start 0243 { 0244 bool called = false; 0245 auto future = KAsync::start<void>( 0246 [&called] { 0247 called = true; 0248 return KAsync::null<void>(); 0249 }).exec(); 0250 QVERIFY(future.isFinished()); 0251 QVERIFY(called); 0252 } 0253 //void 0254 { 0255 auto future = KAsync::start<void>( 0256 []() { 0257 return KAsync::null<void>(); 0258 }).exec(); 0259 QVERIFY(future.isFinished()); 0260 } 0261 0262 //value 0263 { 0264 auto future = KAsync::value<int>(42).exec(); 0265 QVERIFY(future.isFinished()); 0266 QCOMPARE(future.value(), 42); 0267 } 0268 0269 //Sync then 0270 { 0271 auto job = KAsync::value<int>(42); 0272 auto future = job.then<int, int>([](int value) { 0273 return KAsync::value<int>(value); 0274 }).exec(); 0275 QVERIFY(future.isFinished()); 0276 QCOMPARE(future.value(), 42); 0277 } 0278 0279 //Job then 0280 { 0281 auto job = KAsync::value<int>(42); 0282 auto future = job.then<QString, int>([](int value) { 0283 return KAsync::value<QString>(QString::number(value)); 0284 }).exec(); 0285 QVERIFY(future.isFinished()); 0286 QCOMPARE(future.value(), QString::number(42)); 0287 } 0288 0289 //void Job then 0290 { 0291 bool continuationCalled = false; 0292 auto job = KAsync::null<void>(); 0293 auto future = job.then<void>([&continuationCalled] { 0294 return KAsync::start<void>([&continuationCalled] { 0295 continuationCalled = true; 0296 return KAsync::null<void>(); 0297 }); 0298 }).exec(); 0299 QVERIFY(future.isFinished()); 0300 QVERIFY(continuationCalled); 0301 } 0302 0303 //Nested job then 0304 { 0305 auto job = KAsync::value<int>(42); 0306 auto future = job.then<QString, int>( 0307 KAsync::start<QString, int>([](int i) { 0308 return KAsync::value<QString>(QString::number(i)); 0309 }) 0310 ).exec(); 0311 QVERIFY(future.isFinished()); 0312 QCOMPARE(future.value(), QString::number(42)); 0313 } 0314 0315 //Convert to void 0316 { 0317 KAsync::Job<void> job = KAsync::start<int>( 0318 [] { 0319 return KAsync::value<int>(42); 0320 }).then<int, int>([](int i) { 0321 return KAsync::value<int>(i); 0322 }); 0323 KAsync::Future<void> future = job.exec(); 0324 QVERIFY(future.isFinished()); 0325 } 0326 0327 //Job then types 0328 { 0329 KAsync::Job<int, double> job1 = KAsync::start<int, double>( 0330 [](double i) { 0331 return KAsync::value<int>(i); 0332 }); 0333 0334 KAsync::Job<QString, double> job2 = job1.then<QString, int>([](int value) { 0335 return KAsync::start<QString>([value]() { 0336 return KAsync::value<QString>(QString::number(value)); 0337 }); 0338 }); 0339 double input = 42; 0340 KAsync::Future<QString> future = job2.exec(input); 0341 QVERIFY(future.isFinished()); 0342 QCOMPARE(future.value(), QString::number(42)); 0343 } 0344 0345 //This is useful to be able to spawn different subjobs depending on the initial input value that the continuation gets. 0346 { 0347 auto future = KAsync::start<int, bool>( 0348 [] (bool i) { 0349 if (i) { 0350 return KAsync::value(42); 0351 } else { 0352 return KAsync::error<int>(KAsync::Error("foo")); 0353 } 0354 }).exec(true); 0355 QVERIFY(future.isFinished()); 0356 QCOMPARE(future.value(), 42); 0357 } 0358 0359 { 0360 auto baseJob = KAsync::value<int>(42) 0361 .then<QString, int>( 0362 [](int v, KAsync::Future<QString> &f) { 0363 f.setValue(QLatin1String("Result is ") + QString::number(v)); 0364 f.setFinished(); 0365 }); 0366 0367 auto job = baseJob.then<QString, QString>( 0368 [](const QString &v, KAsync::Future<QString> &f) { 0369 f.setValue(v.toUpper()); 0370 f.setFinished(); 0371 }); 0372 KAsync::Future<QString> future = job.exec(); 0373 QVERIFY(future.isFinished()); 0374 QCOMPARE(future.value(), QString::fromLatin1("RESULT IS 42")); 0375 } 0376 } 0377 0378 KAsync::Job<void> doStuff() 0379 { 0380 return KAsync::wait(1); 0381 } 0382 0383 void AsyncTest::testErrorHandling() 0384 { 0385 //Failing job 0386 { 0387 auto future = KAsync::start<int>( 0388 [](KAsync::Future<int> &f) { 0389 f.setError({1, "error"}); 0390 }).exec(); 0391 QVERIFY(future.isFinished()); 0392 QCOMPARE(future.errorCode(), 1); 0393 QCOMPARE(future.errorMessage().toUtf8(), QByteArray("error")); 0394 } 0395 0396 //Call error handler 0397 { 0398 bool errorHandlerCalled = false; 0399 auto future = KAsync::error<int>({1, "error"}) 0400 .then<int, int>([&errorHandlerCalled](const KAsync::Error &error, int) { 0401 errorHandlerCalled = true; 0402 COMPARERET(error.errorCode, 1, KAsync::error<int>(error)); 0403 return KAsync::error<int>(error); 0404 }).exec(); 0405 QVERIFY(future.isFinished()); 0406 QVERIFY(errorHandlerCalled); 0407 QCOMPARE(future.errors().first(), KAsync::Error(1, "error")); 0408 } 0409 0410 //Propagate error 0411 { 0412 bool errorHandlerCalled = false; 0413 auto future = KAsync::error<int>({1, "error"}) 0414 .then<int, int>( 0415 [](int) { 0416 VERIFYRET(false, KAsync::null<int>()); 0417 return KAsync::null<int>(); 0418 }) 0419 .then<void, int>([&errorHandlerCalled](const KAsync::Error &error, int) { 0420 errorHandlerCalled = true; 0421 COMPARERET(error.errorCode, 1, KAsync::error<void>(error)); 0422 return KAsync::error<void>(error); 0423 }) 0424 .exec(); 0425 0426 QVERIFY(future.isFinished()); 0427 QVERIFY(errorHandlerCalled); 0428 QCOMPARE(future.errors().first(), KAsync::Error(1, "error")); 0429 } 0430 0431 //Propagate error 0432 { 0433 bool errorHandlerCalled1 = false; 0434 bool errorHandlerCalled2 = false; 0435 auto future = KAsync::error<int>({1, "error"}) 0436 .then<int, int>( 0437 [&errorHandlerCalled1](const KAsync::Error &error, int) { 0438 errorHandlerCalled1 = true; 0439 COMPARERET(error.errorCode, 1, KAsync::error<int>(error)); 0440 return KAsync::error<int>(error); 0441 }) 0442 .then<void, int>([&errorHandlerCalled2](const KAsync::Error &error, int) { 0443 errorHandlerCalled2 = true; 0444 COMPARERET(error.errorCode, 1, KAsync::error<void>(error)); 0445 return KAsync::error<void>(error); 0446 }) 0447 .exec(); 0448 0449 QVERIFY(future.isFinished()); 0450 QVERIFY(errorHandlerCalled1); 0451 QVERIFY(errorHandlerCalled2); 0452 QCOMPARE(future.errors().first(), KAsync::Error(1, "error")); 0453 } 0454 0455 //Reconcile error 0456 { 0457 bool errorHandlerCalled1 = false; 0458 bool errorHandlerCalled2 = false; 0459 auto future = KAsync::error<int>({1, "error"}) 0460 .then<int, int>( 0461 [&errorHandlerCalled1](const KAsync::Error &error, int) { 0462 errorHandlerCalled1 = true; 0463 COMPARERET(error, KAsync::Error(1, "error"), KAsync::null<int>()); 0464 return KAsync::null<int>(); 0465 }) 0466 .then<void, int>([&errorHandlerCalled2](const KAsync::Error &error, int) { 0467 VERIFYRET(!error, KAsync::null<void>()); 0468 errorHandlerCalled2 = true; 0469 return KAsync::null<void>(); 0470 }) 0471 .exec(); 0472 0473 QVERIFY(errorHandlerCalled1); 0474 QVERIFY(errorHandlerCalled2); 0475 QVERIFY(future.isFinished()); 0476 QVERIFY(!future.hasError()); 0477 } 0478 0479 //Propagate value on error 0480 { 0481 KAsync::Future<int> future = KAsync::value<int>(1) 0482 .onError([](const KAsync::Error &error) { 0483 Q_UNUSED(error); 0484 QVERIFY(false); 0485 }) 0486 .exec(); 0487 0488 QVERIFY(future.isFinished()); 0489 QCOMPARE(future.value(), 1); 0490 } 0491 0492 //Ensure an error continuation is called and can clear the error 0493 { 0494 bool errorHandlerCalled1 = false; 0495 auto job = KAsync::null() 0496 .then(KAsync::error({1, "error"})); 0497 0498 auto future = job.then([&](const KAsync::Error &error) { 0499 errorHandlerCalled1 = true; 0500 COMPARERET(error, KAsync::Error(1, "error"), KAsync::null()); 0501 return KAsync::null(); 0502 }) 0503 .exec(); 0504 QVERIFY(future.isFinished()); 0505 QVERIFY(errorHandlerCalled1); 0506 QCOMPARE(future.errorCode(), 0); 0507 } 0508 //Ensure an error continuation is called and can clear the error in a nested job 0509 { 0510 bool errorHandlerCalled1 = false; 0511 bool continuationCalled = false; 0512 auto error = KAsync::Error(1, "error"); 0513 auto job = KAsync::error<void>(error) 0514 .then([&] (const KAsync::Error &e){ 0515 return doStuff() 0516 .then([] { 0517 }) 0518 .then([e, &continuationCalled] { 0519 continuationCalled = true; 0520 return KAsync::error(e); 0521 }); 0522 }); 0523 0524 auto job2 = job.then([&](const KAsync::Error &e) { 0525 errorHandlerCalled1 = true; 0526 COMPARERET(e, error, KAsync::null()); 0527 return KAsync::null(); 0528 }); 0529 auto future = job2.exec(); 0530 future.waitForFinished(); 0531 QVERIFY(future.isFinished()); 0532 QVERIFY(errorHandlerCalled1); 0533 QVERIFY(continuationCalled); 0534 QCOMPARE(future.errorCode(), 0); 0535 } 0536 } 0537 0538 void AsyncTest::testContext() 0539 { 0540 0541 QWeakPointer<QObject> refToObj; 0542 { 0543 KAsync::Job<int> job = KAsync::null<int>(); 0544 { 0545 auto contextObject = QSharedPointer<QObject>::create(); 0546 refToObj = contextObject.toWeakRef(); 0547 QVERIFY(refToObj); 0548 job = KAsync::start<int>( 0549 [](KAsync::Future<int> &future) { 0550 new AsyncSimulator<int>(future, 42); 0551 }); 0552 job.addToContext(contextObject); 0553 0554 //Ensure the context survives for the whole duration of the job 0555 job = job.then<int>([](KAsync::Future<int> &future) { 0556 new AsyncSimulator<int>(future, 42); 0557 }); 0558 } 0559 0560 QVERIFY(refToObj); 0561 0562 { 0563 //Ensure the context survives copies 0564 auto job2 = job; 0565 job = KAsync::null<int>(); 0566 KAsync::Future<int> future = job2.exec(); 0567 QVERIFY(refToObj); 0568 future.waitForFinished(); 0569 } 0570 } 0571 QVERIFY(!refToObj); 0572 } 0573 0574 void AsyncTest::testGuard() 0575 { 0576 //Guard before 0577 { 0578 auto guard = new QObject; 0579 bool continuationCalled = false; 0580 auto job = KAsync::null<void>(); 0581 job = KAsync::start<void>( 0582 [](KAsync::Future<void> &future) { 0583 new AsyncSimulator<void>(future); 0584 }); 0585 job.guard(guard); 0586 job = job 0587 .then([] {}) 0588 .then([&] { 0589 continuationCalled = true; 0590 qWarning() << "Continuation called"; 0591 }); 0592 auto future = job.exec(); 0593 qWarning() << "deleting guard"; 0594 delete guard; 0595 QVERIFY(!continuationCalled); 0596 future.waitForFinished(); 0597 QVERIFY(!continuationCalled); 0598 } 0599 //Guard after 0600 { 0601 auto guard = new QObject; 0602 bool continuationCalled = false; 0603 auto job = KAsync::null<void>(); 0604 job = KAsync::start<void>( 0605 [](KAsync::Future<void> &future) { 0606 new AsyncSimulator<void>(future); 0607 }); 0608 job = job.then([&] { 0609 continuationCalled = true; 0610 qWarning() << "Continuation called"; 0611 }) 0612 .then([] {}) 0613 .guard(guard); 0614 auto future = job.exec(); 0615 qWarning() << "deleting guard"; 0616 delete guard; 0617 QVERIFY(!continuationCalled); 0618 future.waitForFinished(); 0619 QVERIFY(!continuationCalled); 0620 } 0621 0622 0623 { 0624 auto guard = new QObject; 0625 bool continuationCalled = false; 0626 auto job = KAsync::null<void>(); 0627 job = KAsync::start<void>( 0628 [](KAsync::Future<void> &future) { 0629 new AsyncSimulator<void>(future); 0630 }); 0631 //Ensure the continuation is never called. 0632 job = job.then([&] { 0633 continuationCalled = true; 0634 }).guard(guard); 0635 //Ensure the guard survives copies 0636 auto job2 = job; 0637 job = KAsync::null<void>(); 0638 auto future = job2.exec(); 0639 delete guard; 0640 QVERIFY(!continuationCalled); 0641 future.waitForFinished(); 0642 QVERIFY(!continuationCalled); 0643 } 0644 } 0645 0646 void AsyncTest::testDoWhile() 0647 { 0648 int i = 0; 0649 auto future = KAsync::doWhile([&i]() { 0650 i++; 0651 if (i < 5) { 0652 return KAsync::value(KAsync::Continue); 0653 } 0654 return KAsync::value(KAsync::Break); 0655 }) 0656 .exec(); 0657 future.waitForFinished(); 0658 QVERIFY(future.isFinished()); 0659 QCOMPARE(i, 5); 0660 } 0661 0662 void AsyncTest::testDoWhileWithJob() 0663 { 0664 int i = 0; 0665 auto future = KAsync::doWhile(KAsync::start<KAsync::ControlFlowFlag>([&i]() { 0666 i++; 0667 if (i < 5) { 0668 return KAsync::Continue; 0669 } 0670 return KAsync::Break; 0671 })) 0672 .exec(); 0673 future.waitForFinished(); 0674 QVERIFY(future.isFinished()); 0675 QCOMPARE(i, 5); 0676 } 0677 0678 void AsyncTest::testNestedDoWhile() 0679 { 0680 int outer = 0; 0681 int inner = 0; 0682 int total = 0; 0683 auto future = KAsync::doWhile( 0684 KAsync::start([&] { 0685 outer++; 0686 inner = 0; 0687 //Safety net to avoid infinite recursion 0688 Q_ASSERT(outer < 3); 0689 }).then(KAsync::doWhile(KAsync::start<KAsync::ControlFlowFlag>([&] { 0690 total++; 0691 inner++; 0692 //This is the abort condition of the outer loop 0693 if (inner < 2) { 0694 return KAsync::Continue; 0695 } 0696 return KAsync::Break; 0697 }) 0698 )) 0699 .then([&] { 0700 //This is the abort condition of the outer loop 0701 if (outer < 2) { 0702 return KAsync::Continue; 0703 } 0704 return KAsync::Break; 0705 }) 0706 ) 0707 .exec(); 0708 future.waitForFinished(); 0709 QVERIFY(future.isFinished()); 0710 QCOMPARE(outer, 2); 0711 QCOMPARE(total, 4); 0712 } 0713 0714 void AsyncTest::testAsyncPromises() 0715 { 0716 auto job = KAsync::start<int>( 0717 [](KAsync::Future<int> &future) { 0718 new AsyncSimulator<int>(future, 42); 0719 }); 0720 0721 KAsync::Future<int> future = job.exec(); 0722 0723 future.waitForFinished(); 0724 QCOMPARE(future.value(), 42); 0725 } 0726 0727 void AsyncTest::testNestedAsync() 0728 { 0729 bool done = false; 0730 0731 auto job = KAsync::start<int>( 0732 [](KAsync::Future<int> &future) { 0733 auto innerJob = KAsync::start<int>([](KAsync::Future<int> &innerFuture) { 0734 new AsyncSimulator<int>(innerFuture, 42); 0735 }).then<void>([&future](KAsync::Future<void> &innerThenFuture) { 0736 future.setFinished(); 0737 innerThenFuture.setFinished(); 0738 }); 0739 innerJob.exec().waitForFinished(); 0740 } 0741 ).then<int, int>([&done](int result, KAsync::Future<int> &future) { 0742 done = true; 0743 future.setValue(result); 0744 future.setFinished(); 0745 }); 0746 job.exec(); 0747 0748 QTRY_VERIFY(done); 0749 } 0750 0751 void AsyncTest::testVoidNestedJob() 0752 { 0753 bool innerDone1 = false; 0754 bool innerDone2 = false; 0755 bool innerDone3 = false; 0756 auto job = KAsync::start<void>( 0757 [&innerDone1]() -> KAsync::Job<void> { 0758 return KAsync::start<void>([&innerDone1]() { 0759 innerDone1 = true; 0760 return KAsync::null<void>(); 0761 }); 0762 } 0763 ) 0764 .then<void>([&innerDone2, &innerDone3]() -> KAsync::Job<void> { 0765 return KAsync::start<void>([&innerDone2]() { 0766 innerDone2 = true; 0767 return KAsync::null<void>(); 0768 }) 0769 .then<void>([&innerDone3]() { 0770 innerDone3 = true; 0771 return KAsync::null<void>(); 0772 }); 0773 }); 0774 auto future = job.exec(); 0775 future.waitForFinished(); 0776 QCOMPARE(future.errorCode(), 0); 0777 QVERIFY(innerDone1); 0778 QVERIFY(innerDone2); 0779 QVERIFY(innerDone3); 0780 } 0781 0782 void AsyncTest::testAsyncEach() 0783 { 0784 { 0785 auto job = KAsync::value<std::vector<int>>({1}); 0786 auto future = job.each<void>([](int i) { 0787 Q_UNUSED(i); 0788 return KAsync::null<void>(); 0789 }).exec(); 0790 QVERIFY(future.isFinished()); 0791 } 0792 0793 const QList<int> expected({1, 2, 3}); 0794 0795 auto job = KAsync::value<QList<int>>({1, 2, 3}); 0796 { 0797 QList<int> result; 0798 //This is the all manual version 0799 auto subjob = KAsync::forEach<QList<int>>( 0800 KAsync::start<void, int>([&result](int i) { 0801 result << i; 0802 return KAsync::null<void>(); 0803 }) 0804 ); 0805 auto future = job.then<void, QList<int>>( 0806 subjob 0807 ).exec(); 0808 future.waitForFinished(); 0809 QVERIFY(future.isFinished()); 0810 QCOMPARE(result, expected); 0811 0812 } 0813 { 0814 QList<int> result; 0815 //An this is the convenience wrapper 0816 auto future = job.each([&result](int i) { 0817 result << i; 0818 return KAsync::null<void>(); 0819 }).exec(); 0820 future.waitForFinished(); 0821 QVERIFY(future.isFinished()); 0822 QCOMPARE(result, expected); 0823 } 0824 } 0825 0826 void AsyncTest::testAsyncSerialEach() 0827 { 0828 { 0829 auto job = KAsync::value<std::vector<int>>({1}); 0830 auto future = job.serialEach<void>([](int i) { 0831 Q_UNUSED(i); 0832 return KAsync::null<void>(); 0833 }).exec(); 0834 0835 } 0836 0837 const QList<int> expected({1, 2, 3}); 0838 0839 auto job = KAsync::value<QList<int>>({1, 2, 3}); 0840 { 0841 QList<int> result; 0842 auto subjob = KAsync::serialForEach<QList<int>>( 0843 KAsync::start<void, int>([&](int i) { 0844 result << i; 0845 return KAsync::null<void>(); 0846 }) 0847 ); 0848 auto future = job.then<void, QList<int>>(subjob).exec(); 0849 future.waitForFinished(); 0850 QVERIFY(future.isFinished()); 0851 QCOMPARE(result, expected); 0852 } 0853 { 0854 QList<int> result; 0855 //An this is the convenience wrapper 0856 auto future = job.serialEach([&result](int i) { 0857 result << i; 0858 return KAsync::null<void>(); 0859 }).exec(); 0860 future.waitForFinished(); 0861 QVERIFY(future.isFinished()); 0862 QCOMPARE(result, expected); 0863 } 0864 } 0865 0866 void AsyncTest::benchmarkSyncThenExecutor() 0867 { 0868 auto job = KAsync::start<int>( 0869 []() { 0870 return 1; 0871 }); 0872 0873 QBENCHMARK { 0874 job.exec(); 0875 } 0876 } 0877 0878 void AsyncTest::benchmarkFutureThenExecutor() 0879 { 0880 auto job = KAsync::start<int>( 0881 [](KAsync::Future<int> &f) { 0882 f.setResult(1); 0883 }); 0884 0885 QBENCHMARK { 0886 job.exec(); 0887 } 0888 } 0889 0890 void AsyncTest::benchmarkThenExecutor() 0891 { 0892 // auto job = KAsync::start<int>( 0893 // []() { 0894 // return KAsync::value(1); 0895 // }); 0896 0897 //This is exactly the same as the future version (due to it's implementation). 0898 auto job = KAsync::value(1); 0899 0900 QBENCHMARK { 0901 job.exec(); 0902 } 0903 } 0904 0905 //Ensure we don't have to define the template arguments 0906 void AsyncTest::noTemplateArguments() 0907 { 0908 double input = 42; 0909 auto job1 = KAsync::start<int, double>( 0910 [](double i) { 0911 return KAsync::value<int>(i); 0912 }); 0913 0914 //Async continuation 0915 { 0916 auto job2 = job1.then([](int value) { 0917 return KAsync::value(QString::number(value)); 0918 }); 0919 static_assert(std::is_same<decltype(job2), KAsync::Job<QString, double>>::value, "Not the same type"); 0920 auto future = job2.exec(input); 0921 QVERIFY(future.isFinished()); 0922 QCOMPARE(future.value(), QString::number(42)); 0923 } 0924 0925 //void async continuation 0926 { 0927 auto job2 = job1.then([](int) { 0928 return KAsync::null<void>(); 0929 }); 0930 // using foo = decltype(job2)::foo; 0931 static_assert(std::is_same<decltype(job2), KAsync::Job<void, double>>::value, "Not the same type"); 0932 auto future = job2.exec(input); 0933 QVERIFY(future.isFinished()); 0934 // QCOMPARE(future.value(), QString::number(42)); 0935 } 0936 0937 //Job continuation 0938 { 0939 int value = input; 0940 auto job2 = job1.then(KAsync::value(QString::number(value))); 0941 static_assert(std::is_same<decltype(job2), KAsync::Job<QString, double>>::value, "Not the same type"); 0942 auto future = job2.exec(input); 0943 QVERIFY(future.isFinished()); 0944 QCOMPARE(future.value(), QString::number(42)); 0945 } 0946 0947 //Sync continuation 0948 { 0949 auto job2 = job1.then([](int value) { 0950 return QString::number(value); 0951 }); 0952 static_assert(std::is_same<decltype(job2), KAsync::Job<QString, double>>::value, "Not the same type"); 0953 auto future = job2.exec(input); 0954 QVERIFY(future.isFinished()); 0955 QCOMPARE(future.value(), QString::number(42)); 0956 } 0957 0958 //void sync continuation 0959 { 0960 auto job2 = job1.then([](int) { 0961 }); 0962 static_assert(std::is_same<decltype(job2), KAsync::Job<void, double>>::value, "Not the same type"); 0963 auto future = job2.exec(input); 0964 QVERIFY(future.isFinished()); 0965 } 0966 0967 //The following examples should result in a compile time error 0968 { 0969 //Should fail due to no argument 0970 // auto job3 = job1.then([]() { 0971 // return KAsync::value(QString::number(42)); 0972 // }); 0973 0974 //Should fail due to wrong argument 0975 // auto job3 = job1.then([](QByteArray foo) { 0976 // eturn KAsync::value<QString>(QString::number(42)); 0977 // }); 0978 } 0979 } 0980 0981 void AsyncTest::testValueJob() 0982 { 0983 QList<QByteArray> list; 0984 list << "foo"; 0985 list << "foo2"; 0986 auto job1 = KAsync::null(); 0987 { 0988 auto job = job1.then(KAsync::value(list)); 0989 static_assert(std::is_same<decltype(job), KAsync::Job<QList<QByteArray>>>::value, "Not the same type"); 0990 auto future = job.exec(); 0991 QCOMPARE(future.value(), list); 0992 } 0993 { 0994 auto job = job1.then([&] { return KAsync::value(list); }); 0995 static_assert(std::is_same<decltype(job), KAsync::Job<QList<QByteArray>>>::value, "Not the same type"); 0996 auto future = job.exec(); 0997 QCOMPARE(future.value(), list); 0998 } 0999 { 1000 auto job = job1.then([&] { return list; }); 1001 static_assert(std::is_same<decltype(job), KAsync::Job<QList<QByteArray>>>::value, "Not the same type"); 1002 auto future = job.exec(); 1003 QCOMPARE(future.value(), list); 1004 } 1005 } 1006 1007 QTEST_MAIN(AsyncTest) 1008 1009 #include "asynctest.moc"