forked from qt-creator/qt-creator
TaskTree: Add tests for ConcurrentCall
Change-Id: I493ab170656a61a841614dff87495fa324336c45 Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
@@ -31,6 +31,10 @@ public:
|
||||
{
|
||||
return m_future.resultCount() ? m_future.result() : ResultType();
|
||||
}
|
||||
QList<ResultType> results() const
|
||||
{
|
||||
return m_future.results();
|
||||
}
|
||||
QFuture<ResultType> future() const { return m_future; }
|
||||
|
||||
private:
|
||||
@@ -38,9 +42,8 @@ private:
|
||||
void wrapConcurrent(Function &&function, Args &&...args)
|
||||
{
|
||||
m_startHandler = [=] {
|
||||
if (m_threadPool)
|
||||
return QtConcurrent::run(m_threadPool, function, args...);
|
||||
return QtConcurrent::run(function, args...);
|
||||
QThreadPool *threadPool = m_threadPool ? m_threadPool : QThreadPool::globalInstance();
|
||||
return QtConcurrent::run(threadPool, function, args...);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -48,11 +51,9 @@ private:
|
||||
void wrapConcurrent(std::reference_wrapper<const Function> &&wrapper, Args &&...args)
|
||||
{
|
||||
m_startHandler = [=] {
|
||||
if (m_threadPool) {
|
||||
return QtConcurrent::run(m_threadPool,
|
||||
std::forward<const Function>(wrapper.get()), args...);
|
||||
}
|
||||
return QtConcurrent::run(std::forward<const Function>(wrapper.get()), args...);
|
||||
QThreadPool *threadPool = m_threadPool ? m_threadPool : QThreadPool::globalInstance();
|
||||
return QtConcurrent::run(threadPool, std::forward<const Function>(wrapper.get()),
|
||||
args...);
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -1,2 +1,3 @@
|
||||
add_subdirectory(concurrentcall)
|
||||
add_subdirectory(qprocesstask)
|
||||
add_subdirectory(tasking)
|
||||
|
4
tests/auto/solutions/concurrentcall/CMakeLists.txt
Normal file
4
tests/auto/solutions/concurrentcall/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
add_qtc_test(tst_concurrentcall
|
||||
DEPENDS Tasking Qt::Concurrent Qt::Network
|
||||
SOURCES tst_concurrentcall.cpp
|
||||
)
|
8
tests/auto/solutions/concurrentcall/concurrentcall.qbs
Normal file
8
tests/auto/solutions/concurrentcall/concurrentcall.qbs
Normal file
@@ -0,0 +1,8 @@
|
||||
QtcAutotest {
|
||||
name: "ConcurrentCall autotest"
|
||||
|
||||
Depends { name: "Qt"; submodules: ["concurrent", "network"] }
|
||||
Depends { name: "Tasking" }
|
||||
|
||||
files: "tst_concurrentcall.cpp"
|
||||
}
|
243
tests/auto/solutions/concurrentcall/tst_concurrentcall.cpp
Normal file
243
tests/auto/solutions/concurrentcall/tst_concurrentcall.cpp
Normal file
@@ -0,0 +1,243 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <tasking/concurrentcall.h>
|
||||
|
||||
#include <QtTest>
|
||||
|
||||
using namespace Tasking;
|
||||
|
||||
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_ConcurrentCall : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
// void futureSynchonizer();
|
||||
void taskTree_data();
|
||||
void taskTree();
|
||||
|
||||
private:
|
||||
QThreadPool m_threadPool;
|
||||
MyObject m_myObject;
|
||||
};
|
||||
|
||||
struct TestData
|
||||
{
|
||||
TreeStorage<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 tst_ConcurrentCall::futureSynchonizer()
|
||||
// {
|
||||
// auto lambda = [](QPromise<int> &promise) {
|
||||
// while (true) {
|
||||
// if (promise.isCanceled()) {
|
||||
// promise.future().cancel();
|
||||
// promise.finish();
|
||||
// return;
|
||||
// }
|
||||
// QThread::msleep(100);
|
||||
// }
|
||||
// };
|
||||
|
||||
// FutureSynchronizer synchronizer;
|
||||
// synchronizer.setCancelOnWait(false);
|
||||
// {
|
||||
// Async<int> task;
|
||||
// task.setConcurrentCallData(lambda);
|
||||
// task.setFutureSynchronizer(&synchronizer);
|
||||
// task.start();
|
||||
// QThread::msleep(10);
|
||||
// // We assume here that worker thread will still work for about 90 ms.
|
||||
// QVERIFY(!task.isCanceled());
|
||||
// QVERIFY(!task.isDone());
|
||||
// }
|
||||
// synchronizer.flushFinishedFutures();
|
||||
// QVERIFY(!synchronizer.isEmpty());
|
||||
// // The destructor of synchronizer should wait for about 90 ms for worker thread to be canceled
|
||||
// }
|
||||
|
||||
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 ConcurrentResultType;
|
||||
|
||||
template<typename Function, typename ...Args>
|
||||
struct ConcurrentResultType<Function, Args...>
|
||||
{
|
||||
using Type = typename FutureArgType<decltype(QtConcurrent::run(
|
||||
std::declval<Function>(), std::declval<Args>()...))>::Type;
|
||||
};
|
||||
|
||||
template <typename Function, typename ...Args,
|
||||
typename ResultType = typename ConcurrentResultType<Function, Args...>::Type>
|
||||
TestData createTestData(const QList<ResultType> &expectedResults, Function &&function, Args &&...args)
|
||||
{
|
||||
TreeStorage<bool> storage;
|
||||
|
||||
const auto onSetup = [=](ConcurrentCall<ResultType> &task) {
|
||||
task.setConcurrentCallData(function, args...);
|
||||
};
|
||||
const auto onDone = [storage, expectedResults](const ConcurrentCall<ResultType> &task) {
|
||||
*storage = task.results() == expectedResults;
|
||||
};
|
||||
|
||||
const Group root {
|
||||
Storage(storage),
|
||||
ConcurrentCallTask<ResultType>(onSetup, onDone)
|
||||
};
|
||||
|
||||
return TestData{storage, root};
|
||||
}
|
||||
|
||||
void tst_ConcurrentCall::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);
|
||||
|
||||
{
|
||||
TreeStorage<bool> storage;
|
||||
TreeStorage<int> internalStorage;
|
||||
|
||||
const auto onSetup = [internalStorage](ConcurrentCall<int> &task) {
|
||||
task.setConcurrentCallData(multiplyBy2, *internalStorage);
|
||||
};
|
||||
const auto onDone = [internalStorage](const ConcurrentCall<int> &task) {
|
||||
*internalStorage = task.result();
|
||||
};
|
||||
|
||||
const Group root {
|
||||
Storage(storage),
|
||||
Storage(internalStorage),
|
||||
onGroupSetup([internalStorage] { *internalStorage = 1; }),
|
||||
ConcurrentCallTask<int>(onSetup, onDone, CallDoneIf::Success),
|
||||
ConcurrentCallTask<int>(onSetup, onDone, CallDoneIf::Success),
|
||||
ConcurrentCallTask<int>(onSetup, onDone, CallDoneIf::Success),
|
||||
ConcurrentCallTask<int>(onSetup, onDone, CallDoneIf::Success),
|
||||
onGroupDone([storage, internalStorage] { *storage = *internalStorage == 16; })
|
||||
};
|
||||
|
||||
QTest::newRow("Sequential") << TestData{storage, root};
|
||||
}
|
||||
}
|
||||
|
||||
void tst_ConcurrentCall::taskTree()
|
||||
{
|
||||
QFETCH(TestData, testData);
|
||||
|
||||
TaskTree 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);
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(tst_ConcurrentCall)
|
||||
|
||||
#include "tst_concurrentcall.moc"
|
@@ -3,6 +3,7 @@ import qbs
|
||||
Project {
|
||||
name: "Solutions autotests"
|
||||
references: [
|
||||
"concurrentcall/concurrentcall.qbs",
|
||||
"qprocesstask/qprocesstask.qbs",
|
||||
"tasking/tasking.qbs",
|
||||
]
|
||||
|
@@ -62,7 +62,7 @@ void reportString2(QPromise<QString> &promise, QString s)
|
||||
promise.addResult(s);
|
||||
}
|
||||
|
||||
class Callable {
|
||||
class Functor {
|
||||
public:
|
||||
void operator()(QPromise<double> &promise, int n) const
|
||||
{
|
||||
@@ -262,11 +262,11 @@ void tst_Async::runAsync()
|
||||
QList<double>({0, 0}));
|
||||
|
||||
// operator()
|
||||
QCOMPARE(createAsyncTask(Callable(), 3)->results(),
|
||||
QCOMPARE(createAsyncTask(Functor(), 3)->results(),
|
||||
QList<double>({0, 0, 0}));
|
||||
QCOMPARE(Utils::asyncRun(Callable(), 3).results(),
|
||||
QCOMPARE(Utils::asyncRun(Functor(), 3).results(),
|
||||
QList<double>({0, 0, 0}));
|
||||
const Callable c{};
|
||||
const Functor c{};
|
||||
QCOMPARE(createAsyncTask(c, 2)->results(),
|
||||
QList<double>({0, 0}));
|
||||
QCOMPARE(Utils::asyncRun(c, 2).results(),
|
||||
@@ -361,7 +361,7 @@ void tst_Async::crefFunction()
|
||||
QList<double>({0, 0}));
|
||||
|
||||
// callable with promise
|
||||
const Callable c{};
|
||||
const Functor c{};
|
||||
QCOMPARE(createAsyncTask(std::cref(c), 2)->results(),
|
||||
QList<double>({0, 0}));
|
||||
QCOMPARE(Utils::asyncRun(std::cref(c), 2).results(),
|
||||
|
Reference in New Issue
Block a user