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();
|
return m_future.resultCount() ? m_future.result() : ResultType();
|
||||||
}
|
}
|
||||||
|
QList<ResultType> results() const
|
||||||
|
{
|
||||||
|
return m_future.results();
|
||||||
|
}
|
||||||
QFuture<ResultType> future() const { return m_future; }
|
QFuture<ResultType> future() const { return m_future; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -38,9 +42,8 @@ private:
|
|||||||
void wrapConcurrent(Function &&function, Args &&...args)
|
void wrapConcurrent(Function &&function, Args &&...args)
|
||||||
{
|
{
|
||||||
m_startHandler = [=] {
|
m_startHandler = [=] {
|
||||||
if (m_threadPool)
|
QThreadPool *threadPool = m_threadPool ? m_threadPool : QThreadPool::globalInstance();
|
||||||
return QtConcurrent::run(m_threadPool, function, args...);
|
return QtConcurrent::run(threadPool, function, args...);
|
||||||
return QtConcurrent::run(function, args...);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,11 +51,9 @@ private:
|
|||||||
void wrapConcurrent(std::reference_wrapper<const Function> &&wrapper, Args &&...args)
|
void wrapConcurrent(std::reference_wrapper<const Function> &&wrapper, Args &&...args)
|
||||||
{
|
{
|
||||||
m_startHandler = [=] {
|
m_startHandler = [=] {
|
||||||
if (m_threadPool) {
|
QThreadPool *threadPool = m_threadPool ? m_threadPool : QThreadPool::globalInstance();
|
||||||
return QtConcurrent::run(m_threadPool,
|
return QtConcurrent::run(threadPool, std::forward<const Function>(wrapper.get()),
|
||||||
std::forward<const Function>(wrapper.get()), args...);
|
args...);
|
||||||
}
|
|
||||||
return QtConcurrent::run(std::forward<const Function>(wrapper.get()), args...);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,2 +1,3 @@
|
|||||||
|
add_subdirectory(concurrentcall)
|
||||||
add_subdirectory(qprocesstask)
|
add_subdirectory(qprocesstask)
|
||||||
add_subdirectory(tasking)
|
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 {
|
Project {
|
||||||
name: "Solutions autotests"
|
name: "Solutions autotests"
|
||||||
references: [
|
references: [
|
||||||
|
"concurrentcall/concurrentcall.qbs",
|
||||||
"qprocesstask/qprocesstask.qbs",
|
"qprocesstask/qprocesstask.qbs",
|
||||||
"tasking/tasking.qbs",
|
"tasking/tasking.qbs",
|
||||||
]
|
]
|
||||||
|
@@ -62,7 +62,7 @@ void reportString2(QPromise<QString> &promise, QString s)
|
|||||||
promise.addResult(s);
|
promise.addResult(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
class Callable {
|
class Functor {
|
||||||
public:
|
public:
|
||||||
void operator()(QPromise<double> &promise, int n) const
|
void operator()(QPromise<double> &promise, int n) const
|
||||||
{
|
{
|
||||||
@@ -262,11 +262,11 @@ void tst_Async::runAsync()
|
|||||||
QList<double>({0, 0}));
|
QList<double>({0, 0}));
|
||||||
|
|
||||||
// operator()
|
// operator()
|
||||||
QCOMPARE(createAsyncTask(Callable(), 3)->results(),
|
QCOMPARE(createAsyncTask(Functor(), 3)->results(),
|
||||||
QList<double>({0, 0, 0}));
|
QList<double>({0, 0, 0}));
|
||||||
QCOMPARE(Utils::asyncRun(Callable(), 3).results(),
|
QCOMPARE(Utils::asyncRun(Functor(), 3).results(),
|
||||||
QList<double>({0, 0, 0}));
|
QList<double>({0, 0, 0}));
|
||||||
const Callable c{};
|
const Functor c{};
|
||||||
QCOMPARE(createAsyncTask(c, 2)->results(),
|
QCOMPARE(createAsyncTask(c, 2)->results(),
|
||||||
QList<double>({0, 0}));
|
QList<double>({0, 0}));
|
||||||
QCOMPARE(Utils::asyncRun(c, 2).results(),
|
QCOMPARE(Utils::asyncRun(c, 2).results(),
|
||||||
@@ -361,7 +361,7 @@ void tst_Async::crefFunction()
|
|||||||
QList<double>({0, 0}));
|
QList<double>({0, 0}));
|
||||||
|
|
||||||
// callable with promise
|
// callable with promise
|
||||||
const Callable c{};
|
const Functor c{};
|
||||||
QCOMPARE(createAsyncTask(std::cref(c), 2)->results(),
|
QCOMPARE(createAsyncTask(std::cref(c), 2)->results(),
|
||||||
QList<double>({0, 0}));
|
QList<double>({0, 0}));
|
||||||
QCOMPARE(Utils::asyncRun(std::cref(c), 2).results(),
|
QCOMPARE(Utils::asyncRun(std::cref(c), 2).results(),
|
||||||
|
Reference in New Issue
Block a user