1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351
|
// SPDX-FileCopyrightText: 2021 - 2022 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include <QTest>
#include <QTimer>
#include <gtest/gtest.h>
#include "dasync.h"
#include "dtimedloop.h"
#include "ut_dutil.h"
DCORE_USE_NAMESPACE
#if 0
// 为了方便托管 std::thread 而创建的辅助类
class Thread : public QObject {
std::thread *m_thread = nullptr;
public:
template<typename FUNC>
Thread(FUNC &&func, QObject *parent = nullptr)
: QObject (parent)
, m_thread (new std::thread(func))
{
}
void detach() {
m_thread->detach();
}
void join() {
m_thread->join();
}
virtual ~Thread()
{
if (m_thread) {
delete m_thread;
m_thread = nullptr;
}
}
};
bool gInSubFlag = true;
// 全局内存托管,防止 asan 报错
QObject gRoot;
template <typename FUNC>
void DetachedRun(FUNC &&func) {
Thread *thread = new Thread(func, &gRoot);
if (!gInSubFlag) {
func();
}
thread->detach();
}
class ut_DAsync : public testing::Test, public QObject
{
public:
class Test : public QObject {
public:
Test(int in, QObject *parent = nullptr)
: QObject (parent)
, count (in)
{
}
int count = 0;
};
ut_DAsync() { }
virtual ~ut_DAsync() {}
virtual void SetUp() {
task1 = new DAsync<int, int>(this);
// 测试 task2 在线程内部new能正常工作
task3 = new DAsync<int, QString>(this);
// task4~task7 测试固定的API,功能大同小异,在函数内部创建
task8 = new DAsync<void, QString>(this);
task9 = new DAsync<void, void>(this);
task10 = new DAsync<int, void>(this);
m_loop = new DTimedLoop(this);
m_loop->setTimeDump(true);
}
virtual void TearDown() {
// 释放资源要用 deleteLater 或者托管内存
// 避免线程不同步时直接 delete 导致 asan 偶发性报使用释放掉的堆内存
}
// 首先要保证这些不同类型的模板参数的声明没有编译问题
DAsync<int ,int> *task1 = nullptr;
DAsync<int ,int> *task2 = nullptr;
DAsync<int, QString> *task3 = nullptr;
DAsync<QString, QString> *task4 = nullptr;
DAsync<Test *, Test*> *task5 = nullptr;
DAsync<QString, void> *task6 = nullptr;
// 第一个模板参数是 void 的类型的仅执行一次函数调用
DAsync<void, void> *task7 = nullptr;
DAsync<void, QString> *task8 = nullptr;
DAsync<void, void> *task9 = nullptr;
DAsync<int, void> *task10 = nullptr;
// m_loop 须是 static 的,asan 会有误报
static DTimedLoop *m_loop;
};
DTimedLoop *ut_DAsync::m_loop = nullptr;
TEST_F(ut_DAsync, testRunInCorrectThread)
{
// 测试 post 中的函数一定在非主线程异步调用
// 返回结果传到 then 中的函数在主线程中调用
task1->post([](int arg) {
HAVE_FUN(ASSERT_TRUE(!D_THREAD_IN_MAIN()));
return arg;
})->then([&](int arg) {
ASSERT_EQ(arg, 1);
HAVE_FUN(ASSERT_TRUE(D_THREAD_IN_MAIN()));
m_loop->exit();
})->start();
task1->postData(1);
m_loop->exec("testRunInCorrectThread");
}
TEST_F(ut_DAsync, testRunInSubThread)
{
// 和上面 testRunInCorrectThread 测项类似
// task2, 测试 task 在非主线程中依然能正确创建和运行
bool startedFlag = false;
DetachedRun([&]{
// 这里用托管也可以的,但是会有警告
task2 = new DAsync<int, int>(/*this*/);
task2->post([](int arg) {
HAVE_FUN(ASSERT_TRUE(!D_THREAD_IN_MAIN()));
return arg;
})->then([&](int arg) {
static int i = 0;
ASSERT_EQ(arg, i++);
HAVE_FUN(ASSERT_TRUE(D_THREAD_IN_MAIN()));
if (i > 3) m_loop->exit();
})->start();
startedFlag = true;
});
DetachedRun([&]{
static int i = 0;
while (true) {
// 要自己设置 flag,因为在不同的线程中,
// 到这里 task2 还不一定已经被创建完毕
if (startedFlag) {
task2->postData(i++);
if (i > 3) {
break;
}
}
usleep(100*1000);
}
});
m_loop->exec("testRunInSubThread");
}
TEST_F(ut_DAsync, testMultiThreadSynchronization)
{
// task3, 在子线程中输入 0~999, 在 post 中乘以 2 输出到主线程 then 中
static int n = 1000;
static int result = 0;
task3->post([](int arg) -> QString {
return QString("%1").arg(arg * 2);
})->then([](QString arg) {
static int i = 0;
ASSERT_TRUE(arg == QString("%1").arg(i * 2));
i++;
result = n;
})->start();
DetachedRun([&] {
int i = 0;
while (i < n) {
if (!task3->isFinished()) {
task3->postData(i++);
}
}
task3->cancelAll();
});
DetachedRun([&] {
// 该线程启动后会一直阻塞等待,直到 cancelAll 被调用,
// 说明任务结束了,就可以往下走,判断执行结果
task3->waitForFinished(false);
ASSERT_EQ(result, n);
m_loop->exit();
});
m_loop->exec("testMultiThreadSynchronization");
}
TEST_F(ut_DAsync, testOneTimeTask)
{
// task8, 测试一次性任务,确保两个函数只会进来执行一次
task8->post([] {
static int i = 0;
return QString("testOneTimeTask%1").arg(i++);
})->then([&](const QString &arg) {
ASSERT_TRUE(arg == "testOneTimeTask0");
m_loop->exit();
})->start();
m_loop->exec("test task8");
// task9, 测试一次性任务,确保只有 post 的函数能够被执行到
task9->post([&]{
m_loop->exit();
})->start();
// task9->startUp(); # 或者在合适的时候调用
m_loop->exec("test task9");
// task10, 测试仅有 post 的任务的正确执行
task10->post([&] (int arg) {
static int j = 0;
ASSERT_EQ(arg, j++);
if (j == 2) {
m_loop->exit();
}
});
task10->postData(0);
task10->startUp();
task10->postData(1);
m_loop->exec("test task10");
}
TEST_F(ut_DAsync, testFixedApi)
{
// 测试这些固定的 API 能够正确处理不同的参数类型
// task4
task4 = new DAsync<QString, QString>(this);
static int i = 0;
while (i < 100) {
task4->postData(QString::number(i++));
}
task4->post([](const QString &arg) -> QString {
static int j = 0;
HAVE_FUN(ASSERT_TRUE(arg == QString::number(j++)));
return arg;
})->then([](QString arg) {
static int k = 0;
ASSERT_TRUE(arg == QString::number(k++));
if (k == 100) {
m_loop->exit();
}
})->start();
m_loop->exec("test task4");
// 和上面的不一样的地方就是 postData 在 start 前后都调用了
// task5
i = 0;
task5 = new DAsync<Test *, Test*>(this);
while (i < 50) {
task5->postData(new Test(i++, this));
}
task5->post([](Test *arg) -> Test * {
static int j = 0;
HAVE_FUN(ASSERT_TRUE(arg->count == j++));
return arg;
})->then([](Test *arg) {
static int k = 0;
HAVE_FUN(ASSERT_TRUE(arg->count == k++));
if (k == 100) {
m_loop->exit();
}
})->start();
while (i < 100) {
task5->postData(new Test(i++, this));
}
m_loop->exec("test task5");
// task6
i = 0;
task6 = new DAsync<QString, void>(this);
while (i < 50) {
task6->postData(QString::number(i++));
}
task6->post([](QString arg) {
static int j = 0;
HAVE_FUN(ASSERT_TRUE(arg == QString::number(j++)));
})->then([]() {
static int k = 0;
k++;
if (k == 100) {
m_loop->exit();
}
})->start(false);
DetachedRun([&]{
usleep(100 * 1000);
while (i < 100) {
task6->postData(QString::number(i++));
}
task6->startUp();
});
m_loop->exec("test task6");
// task7
task7 = new DAsync<void, void>(this);
task7->post([]() {
static int j = 0;
HAVE_FUN(ASSERT_TRUE(0 == j++));
})->then([]() {
static int k = 0;
HAVE_FUN(ASSERT_TRUE(0 == k++));
m_loop->exit();
})->start();
m_loop->exec("test task7");
}
#endif
|