2023-06-15 13:59:23 +08:00
|
|
|
// SPDX-FileCopyrightText: 2020 - 2023 UnionTech Software Technology Co., Ltd.
|
2022-08-11 13:02:07 +08:00
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
2020-08-15 17:38:59 +08:00
|
|
|
|
|
|
|
#include <QObject>
|
2021-02-26 17:23:56 +08:00
|
|
|
#include <gtest/gtest.h>
|
2020-08-15 17:38:59 +08:00
|
|
|
#include <QTest>
|
|
|
|
#include <QtConcurrent>
|
2023-06-15 13:59:23 +08:00
|
|
|
#include <QSignalSpy>
|
2022-08-04 14:43:41 +08:00
|
|
|
#include <DThreadUtils>
|
2023-06-15 13:59:23 +08:00
|
|
|
#include <string>
|
|
|
|
#include <thread>
|
|
|
|
#include <chrono>
|
2020-08-15 17:38:59 +08:00
|
|
|
|
|
|
|
DCORE_USE_NAMESPACE
|
|
|
|
|
2023-06-15 13:59:23 +08:00
|
|
|
#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0)
|
|
|
|
|
2021-02-26 17:23:56 +08:00
|
|
|
class ThreadUtils : public QObject
|
2020-08-15 17:38:59 +08:00
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
|
2021-02-26 17:23:56 +08:00
|
|
|
public Q_SLOTS:
|
2020-08-15 17:38:59 +08:00
|
|
|
void testCallInMainThread();
|
|
|
|
};
|
|
|
|
|
2021-02-26 17:23:56 +08:00
|
|
|
void ThreadUtils::testCallInMainThread()
|
2020-08-15 17:38:59 +08:00
|
|
|
{
|
2021-02-26 17:23:56 +08:00
|
|
|
DThreadUtil::runInMainThread([]() {
|
|
|
|
bool result = QThread::currentThread() == QCoreApplication::instance()->thread();
|
|
|
|
ASSERT_TRUE(result);
|
|
|
|
});
|
2020-08-15 17:38:59 +08:00
|
|
|
|
2021-02-26 17:23:56 +08:00
|
|
|
auto fe = QtConcurrent::run([] {
|
|
|
|
ASSERT_TRUE(DThreadUtil::runInMainThread([](QThread *thread) -> bool {
|
2020-08-15 17:38:59 +08:00
|
|
|
return QThread::currentThread() == QCoreApplication::instance()->thread() && QThread::currentThread() != thread;
|
2021-07-09 11:11:27 +08:00
|
|
|
}, QThread::currentThread()));
|
2020-08-15 17:38:59 +08:00
|
|
|
});
|
|
|
|
|
2021-02-26 17:23:56 +08:00
|
|
|
ASSERT_TRUE(QTest::qWaitFor([&] {
|
|
|
|
return fe.isFinished();
|
2020-08-15 17:38:59 +08:00
|
|
|
}));
|
2021-07-09 11:11:27 +08:00
|
|
|
}
|
2020-08-15 17:38:59 +08:00
|
|
|
|
2021-02-26 17:23:56 +08:00
|
|
|
class ut_DThreadUtils : public testing::Test
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
virtual void SetUp()
|
|
|
|
{
|
|
|
|
m_threadutil = new ThreadUtils();
|
|
|
|
}
|
|
|
|
virtual void TearDown()
|
|
|
|
{
|
|
|
|
delete m_threadutil;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
ThreadUtils *m_threadutil = nullptr;
|
|
|
|
};
|
|
|
|
|
|
|
|
TEST_F(ut_DThreadUtils, CallInMainThread)
|
|
|
|
{
|
|
|
|
ASSERT_TRUE(m_threadutil);
|
|
|
|
m_threadutil->testCallInMainThread();
|
|
|
|
}
|
2020-08-15 17:38:59 +08:00
|
|
|
|
2023-06-15 13:59:23 +08:00
|
|
|
#else
|
|
|
|
|
|
|
|
class ut_DThreadUtils :public testing::Test{
|
|
|
|
public:
|
|
|
|
virtual void SetUp()
|
|
|
|
{
|
|
|
|
t = new QThread();
|
|
|
|
t->start();
|
|
|
|
m_threadutil = new DThreadUtils(t);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void TearDown()
|
|
|
|
{
|
|
|
|
t->exit();
|
|
|
|
t->wait();
|
|
|
|
delete t;
|
|
|
|
delete m_threadutil;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
QThread *t{nullptr};
|
|
|
|
DThreadUtils *m_threadutil{nullptr};
|
|
|
|
};
|
|
|
|
|
2023-07-18 16:36:11 +08:00
|
|
|
class QWorker : public QObject {
|
2023-06-15 13:59:23 +08:00
|
|
|
Q_OBJECT
|
|
|
|
public:
|
|
|
|
explicit QWorker(QObject *parent = nullptr):QObject(parent){}
|
|
|
|
~QWorker() = default;
|
|
|
|
public Q_SLOTS:
|
|
|
|
int testFunc(int i, double j) {
|
2023-07-18 16:36:11 +08:00
|
|
|
int r =qFloor(i + j);
|
|
|
|
wait();
|
2023-06-15 13:59:23 +08:00
|
|
|
emit testFuncTrigger(r);
|
2023-07-18 16:36:11 +08:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
void setWaitForSecs(int sec){
|
|
|
|
m_waitSecs = sec;
|
|
|
|
}
|
|
|
|
void wait() {
|
|
|
|
std::unique_lock<std::mutex> lock(m_mutex);
|
|
|
|
m_cv.wait_for(lock, std::chrono::seconds(m_waitSecs));
|
|
|
|
}
|
|
|
|
|
|
|
|
void notifyOne() {
|
|
|
|
m_cv.notify_one();
|
2023-06-15 13:59:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
Q_SIGNALS:
|
|
|
|
void testFuncTrigger(int v);
|
2023-07-18 16:36:11 +08:00
|
|
|
private:
|
|
|
|
std::mutex m_mutex;
|
|
|
|
std::condition_variable m_cv;
|
|
|
|
int m_waitSecs = 2;
|
2023-06-15 13:59:23 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
class CallableObject{
|
|
|
|
|
|
|
|
public:
|
|
|
|
CallableObject() = default;
|
|
|
|
~CallableObject() = default;
|
|
|
|
|
2023-07-18 16:36:11 +08:00
|
|
|
QString operator()(const QString &str){
|
2023-06-15 13:59:23 +08:00
|
|
|
s += str;
|
2023-07-18 16:36:11 +08:00
|
|
|
std::this_thread::sleep_for(std::chrono::microseconds(100));
|
2023-06-15 13:59:23 +08:00
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2023-07-18 16:36:11 +08:00
|
|
|
QString testFunc(const QString &){
|
2023-06-15 13:59:23 +08:00
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
QString s{"CallableObject: "};
|
|
|
|
};
|
|
|
|
|
2023-07-18 16:36:11 +08:00
|
|
|
TEST_F(ut_DThreadUtils, testThread)
|
2023-06-15 13:59:23 +08:00
|
|
|
{
|
|
|
|
auto tmp = m_threadutil->thread();
|
|
|
|
EXPECT_EQ(tmp, t);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ut_DThreadUtils, testRunWithQObj)
|
|
|
|
{
|
|
|
|
QWorker w;
|
2023-07-18 16:36:11 +08:00
|
|
|
QSignalSpy spy(&w, &QWorker::testFuncTrigger);
|
2023-06-15 13:59:23 +08:00
|
|
|
auto result = m_threadutil->run(&w, &QWorker::testFunc, 10, 24.6);
|
2023-07-18 16:36:11 +08:00
|
|
|
std::this_thread::sleep_for(std::chrono::microseconds(100));
|
2023-06-15 13:59:23 +08:00
|
|
|
EXPECT_TRUE(result.isStarted());
|
|
|
|
EXPECT_TRUE(result.isRunning());
|
2023-07-18 16:36:11 +08:00
|
|
|
w.notifyOne();
|
2023-06-15 13:59:23 +08:00
|
|
|
result.waitForFinished();
|
|
|
|
EXPECT_TRUE(result.isFinished());
|
|
|
|
auto raw = result.result();
|
|
|
|
EXPECT_EQ(raw, 34);
|
|
|
|
EXPECT_EQ(spy.count(), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ut_DThreadUtils,testRunWithLambda)
|
|
|
|
{
|
|
|
|
auto threadId1 = std::this_thread::get_id();
|
2023-07-18 16:36:11 +08:00
|
|
|
auto result = m_threadutil->run([](decltype(threadId1) id){
|
|
|
|
EXPECT_NE(std::this_thread::get_id(), id);
|
2023-06-15 13:59:23 +08:00
|
|
|
return true;
|
2023-07-18 16:36:11 +08:00
|
|
|
}, threadId1);
|
2023-06-15 13:59:23 +08:00
|
|
|
result.waitForFinished();
|
|
|
|
auto raw = result.result();
|
|
|
|
EXPECT_TRUE(raw);
|
|
|
|
}
|
|
|
|
|
2023-07-18 16:36:11 +08:00
|
|
|
TEST_F(ut_DThreadUtils,testRunWithCallableObj)
|
|
|
|
{
|
2023-06-15 13:59:23 +08:00
|
|
|
CallableObject obj;
|
|
|
|
QString tmp{"Hello"};
|
2023-07-18 16:36:11 +08:00
|
|
|
auto result1 = m_threadutil->run(obj, tmp);
|
2023-06-15 13:59:23 +08:00
|
|
|
result1.waitForFinished();
|
|
|
|
auto raw1 = result1.result();
|
|
|
|
EXPECT_EQ(raw1, QString{"CallableObject: Hello"});
|
|
|
|
|
2023-07-18 16:36:11 +08:00
|
|
|
auto result2 = m_threadutil->run(&obj, &CallableObject::testFunc, tmp);
|
2023-06-15 13:59:23 +08:00
|
|
|
result2.waitForFinished();
|
|
|
|
auto raw2 = result2.result();
|
|
|
|
EXPECT_EQ(raw2, QString{"CallableObject: "});
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ut_DThreadUtils, testExecWithQObj)
|
|
|
|
{
|
|
|
|
QWorker w;
|
2023-07-18 16:36:11 +08:00
|
|
|
w.setWaitForSecs(0);
|
2023-06-15 13:59:23 +08:00
|
|
|
QSignalSpy spy(&w, SIGNAL(testFuncTrigger(int)));
|
|
|
|
auto result = m_threadutil->exec(&w, &QWorker::testFunc, 10, 24.6);
|
|
|
|
EXPECT_EQ(result, 34);
|
|
|
|
EXPECT_EQ(spy.count(), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ut_DThreadUtils, testExecWithLambda)
|
|
|
|
{
|
|
|
|
auto threadId1 = std::this_thread::get_id();
|
2023-07-18 16:36:11 +08:00
|
|
|
auto result = m_threadutil->exec (
|
|
|
|
[](decltype(threadId1) id) {
|
2023-06-15 13:59:23 +08:00
|
|
|
EXPECT_NE(std::this_thread::get_id(), id);
|
|
|
|
return true;
|
2023-07-18 16:36:11 +08:00
|
|
|
}, threadId1);
|
2023-06-15 13:59:23 +08:00
|
|
|
EXPECT_TRUE(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ut_DThreadUtils, testExecWithCallableObj)
|
|
|
|
{
|
|
|
|
CallableObject obj;
|
|
|
|
QString tmp{"Hello"};
|
|
|
|
auto result1 = m_threadutil->exec(obj, tmp);
|
|
|
|
EXPECT_EQ(result1, QString{"CallableObject: Hello"});
|
|
|
|
|
|
|
|
auto result2 = m_threadutil->exec(&obj, &CallableObject::testFunc, tmp);
|
|
|
|
EXPECT_EQ(result2, QString{"CallableObject: "});
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ut_DThreadUtils, testDirectlyInvoke)
|
|
|
|
{
|
|
|
|
DThreadUtils tu(QThread::currentThread());
|
|
|
|
QWorker w;
|
2023-07-18 16:36:11 +08:00
|
|
|
w.setWaitForSecs(0);
|
2023-06-15 13:59:23 +08:00
|
|
|
QSignalSpy spy(&w, SIGNAL(testFuncTrigger(int)));
|
|
|
|
auto result = tu.run(&w, &QWorker::testFunc, 10, 24.6);
|
|
|
|
auto raw = result.result(); // no wait
|
|
|
|
EXPECT_EQ(raw, 34);
|
|
|
|
EXPECT_EQ(spy.count(), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ut_DThreadUtils, testCancel)
|
|
|
|
{
|
|
|
|
CallableObject obj;
|
|
|
|
QString tmp{"Hello"};
|
|
|
|
int cancelCounter{0};
|
|
|
|
auto result1 = m_threadutil->run(obj, tmp);
|
|
|
|
auto cancelResult = result1.onCanceled([&cancelCounter]() {
|
|
|
|
cancelCounter += 1;
|
|
|
|
return QString{"failed"};
|
|
|
|
});
|
2023-07-18 16:36:11 +08:00
|
|
|
|
2023-06-15 13:59:23 +08:00
|
|
|
result1.cancel();
|
|
|
|
EXPECT_FALSE(result1.isFinished());
|
|
|
|
EXPECT_FALSE(result1.isValid());
|
|
|
|
EXPECT_TRUE(result1.isCanceled());
|
|
|
|
cancelResult.waitForFinished();
|
|
|
|
EXPECT_EQ(cancelCounter, 1);
|
|
|
|
EXPECT_EQ(cancelResult.result(), QString{"failed"});
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(ut_DThreadUtils, testDestructCancel)
|
|
|
|
{
|
|
|
|
auto w = new QWorker{};
|
|
|
|
auto failedCounter{0};
|
|
|
|
auto result = m_threadutil->run(w, &QWorker::testFunc, 10, 24.6);
|
|
|
|
delete w;
|
|
|
|
auto failedResult = result.onFailed([&failedCounter]() {
|
|
|
|
failedCounter += 1;
|
|
|
|
return -1;
|
|
|
|
});
|
|
|
|
EXPECT_FALSE(result.isValid());
|
|
|
|
failedResult.waitForFinished();
|
|
|
|
EXPECT_EQ(failedCounter, 1);
|
|
|
|
EXPECT_EQ(failedResult.result(), -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2021-02-01 18:07:31 +08:00
|
|
|
#include "ut_dthreadutils.moc"
|