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"