Revision control

++ // Copyright (C) 2025 Jarek Kobus
++ // Copyright (C) 2025 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++
++ #include <QtTaskTree/qthreadfunctiontask.h>
++
++ #include <QTest>
++
++ using namespace QtTaskTree;
++
++ using namespace std::chrono;
++ using namespace std::chrono_literals;
++
++ class MyObject
++ {
++ public:
++ static void staticMember(QPromise<double> &promise, int n)
++ {
++ for (int i = 0; i < n; ++i)
++ promise.addResult(0);
++ }
++
++ void member(QPromise<double> &promise, int n) const
++ {
++ for (int i = 0; i < n; ++i)
++ promise.addResult(0);
++ }
++ };
++
++ class tst_QThreadFunction : public QObject
++ {
++ Q_OBJECT
++
++ private Q_SLOTS:
++ void taskTree_data();
++ void taskTree();
++ void futureWatcher();
++
++ private:
++ QThreadPool m_threadPool;
++ MyObject m_myObject;
++ };
++
++ struct TestData
++ {
++ Storage<bool> storage;
++ Group root;
++ };
++
++ void report3(QPromise<int> &promise)
++ {
++ promise.addResult(0);
++ promise.addResult(2);
++ promise.addResult(1);
++ }
++
++ static void staticReport3(QPromise<int> &promise)
++ {
++ promise.addResult(0);
++ promise.addResult(2);
++ promise.addResult(1);
++ }
++
++ void reportN(QPromise<double> &promise, int n)
++ {
++ for (int i = 0; i < n; ++i)
++ promise.addResult(0);
++ }
++
++ static void staticReportN(QPromise<double> &promise, int n)
++ {
++ for (int i = 0; i < n; ++i)
++ promise.addResult(0);
++ }
++
++ class Functor {
++ public:
++ void operator()(QPromise<double> &promise, int n) const
++ {
++ for (int i = 0; i < n; ++i)
++ promise.addResult(0);
++ }
++ };
++
++ void multiplyBy2(QPromise<int> &promise, int input) { promise.addResult(input * 2); }
++
++ template <typename...>
++ struct FutureArgType;
++
++ template <typename Arg>
++ struct FutureArgType<QFuture<Arg>>
++ {
++ using Type = Arg;
++ };
++
++ template <typename...>
++ struct FunctionResultType;
++
++ template<typename Function, typename ...Args>
++ struct FunctionResultType<Function, Args...>
++ {
++ using Type = typename FutureArgType<decltype(QtConcurrent::run(
++ std::declval<Function>(), std::declval<Args>()...))>::Type;
++ };
++
++ template <typename Function, typename ...Args,
++ typename ResultType = typename FunctionResultType<Function, Args...>::Type>
++ TestData createTestData(const QList<ResultType> &expectedResults, Function &&function, Args &&...args)
++ {
++ Storage<bool> storage;
++
++ const auto onSetup = [=](QThreadFunction<ResultType> &task) {
++ task.setThreadFunctionData(function, args...);
++ };
++ const auto onDone = [storage, expectedResults](const QThreadFunction<ResultType> &task) {
++ *storage = task.results() == expectedResults;
++ };
++
++ const Group root {
++ storage,
++ QThreadFunctionTask<ResultType>(onSetup, onDone)
++ };
++
++ return TestData{storage, root};
++ }
++
++ void tst_QThreadFunction::taskTree_data()
++ {
++ QTest::addColumn<TestData>("testData");
++
++ const QList<int> report3Result{0, 2, 1};
++ const QList<double> reportNResult{0, 0};
++
++ auto lambda = [](QPromise<double> &promise, int n) {
++ for (int i = 0; i < n; ++i)
++ promise.addResult(0);
++ };
++ const std::function<void(QPromise<double> &, int)> fun = [](QPromise<double> &promise, int n) {
++ for (int i = 0; i < n; ++i)
++ promise.addResult(0);
++ };
++
++ QTest::newRow("RefGlobalNoArgs")
++ << createTestData(report3Result, &report3);
++ QTest::newRow("GlobalNoArgs")
++ << createTestData(report3Result, report3);
++ QTest::newRow("RefStaticNoArgs")
++ << createTestData(report3Result, &staticReport3);
++ QTest::newRow("StaticNoArgs")
++ << createTestData(report3Result, staticReport3);
++ QTest::newRow("RefGlobalIntArg")
++ << createTestData(reportNResult, &reportN, 2);
++ QTest::newRow("GlobalIntArg")
++ << createTestData(reportNResult, reportN, 2);
++ QTest::newRow("RefStaticIntArg")
++ << createTestData(reportNResult, &staticReportN, 2);
++ QTest::newRow("StaticIntArg")
++ << createTestData(reportNResult, staticReportN, 2);
++ QTest::newRow("Lambda")
++ << createTestData(reportNResult, lambda, 2);
++ QTest::newRow("Function")
++ << createTestData(reportNResult, fun, 2);
++ QTest::newRow("Functor")
++ << createTestData(reportNResult, Functor(), 2);
++ QTest::newRow("StaticMemberFunction")
++ << createTestData(reportNResult, &MyObject::staticMember, 2);
++ QTest::newRow("MemberFunction")
++ << createTestData(reportNResult, &MyObject::member, &m_myObject, 2);
++
++ {
++ Storage<bool> storage;
++ Storage<int> internalStorage;
++
++ const auto onSetup = [internalStorage](QThreadFunction<int> &task) {
++ task.setThreadFunctionData(multiplyBy2, *internalStorage);
++ };
++ const auto onDone = [internalStorage](const QThreadFunction<int> &task) {
++ *internalStorage = task.result();
++ };
++
++ const Group root {
++ storage,
++ internalStorage,
++ onGroupSetup([internalStorage] { *internalStorage = 1; }),
++ QThreadFunctionTask<int>(onSetup, onDone, CallDoneFlag::OnSuccess),
++ QThreadFunctionTask<int>(onSetup, onDone, CallDoneFlag::OnSuccess),
++ QThreadFunctionTask<int>(onSetup, onDone, CallDoneFlag::OnSuccess),
++ QThreadFunctionTask<int>(onSetup, onDone, CallDoneFlag::OnSuccess),
++ onGroupDone([storage, internalStorage] { *storage = *internalStorage == 16; })
++ };
++
++ QTest::newRow("Sequential") << TestData{storage, root};
++ }
++ }
++
++ void tst_QThreadFunction::taskTree()
++ {
++ QFETCH(TestData, testData);
++
++ QTaskTree taskTree({testData.root.withTimeout(1000ms)});
++ bool actualResult = false;
++ const auto collectResult = [&actualResult](const bool &storage) {
++ actualResult = storage;
++ };
++ taskTree.onStorageDone(testData.storage, collectResult);
++ const DoneWith result = taskTree.runBlocking();
++ QCOMPARE(taskTree.isRunning(), false);
++ QCOMPARE(result, DoneWith::Success);
++ QVERIFY(actualResult);
++ }
++
++ void tst_QThreadFunction::futureWatcher()
++ {
++ QThreadFunction<int> task;
++ QObject::connect(task.futureWatcher(), &QFutureWatcherBase::progressValueChanged,
++ qApp, [](int progress) { qDebug() << "progress" << progress; });
++ }
++
++ QTEST_GUILESS_MAIN(tst_QThreadFunction)
++
++ #include "tst_qthreadfunction.moc"