|
| 1 | +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. |
| 2 | +// |
| 3 | +// SPDX-License-Identifier: GPL-3.0-or-later |
| 4 | + |
| 5 | +#include "cliapplicationhelper.h" |
| 6 | + |
| 7 | +#include <gtest/gtest.h> |
| 8 | + |
| 9 | +#include <QApplication> |
| 10 | +#include <QLocalServer> |
| 11 | +#include <QLocalSocket> |
| 12 | +#include <QTest> |
| 13 | + |
| 14 | +// 单实例辅助类单元测试 |
| 15 | +// 覆盖目标:application/cliapplicationhelper.cpp 中的全部可测函数 |
| 16 | +// - CliApplicationHelper::instance() |
| 17 | +// - CliApplicationHelper::CliApplicationHelper() / ~CliApplicationHelper() |
| 18 | +// - CliApplicationHelper::setSingleInstance()(三种 SingleScope) |
| 19 | +// - CliApplicationHelper::staticCleanApplication() |
| 20 | +// - _CliApplicationHelper::helper() / clear() / defaultCreator()(通过公开接口间接覆盖) |
| 21 | + |
| 22 | +// instance() 应返回非空指针,且重复调用返回同一实例(单例语义) |
| 23 | +TEST(CliApplicationHelper_Instance_UT, Instance_ReturnsNonNull) |
| 24 | +{ |
| 25 | + CliApplicationHelper *p1 = CliApplicationHelper::instance(); |
| 26 | + ASSERT_NE(p1, nullptr); |
| 27 | + |
| 28 | + CliApplicationHelper *p2 = CliApplicationHelper::instance(); |
| 29 | + EXPECT_EQ(p1, p2); |
| 30 | +} |
| 31 | + |
| 32 | +// staticCleanApplication() 清理后再次 instance() 仍可正常获取实例, |
| 33 | +// 间接覆盖 clear()、~CliApplicationHelper()、defaultCreator()、helper() |
| 34 | +TEST(CliApplicationHelper_StaticCleanApplication_UT, StaticCleanApplication_ClearsAndRecreates) |
| 35 | +{ |
| 36 | + CliApplicationHelper *p1 = CliApplicationHelper::instance(); |
| 37 | + ASSERT_NE(p1, nullptr); |
| 38 | + |
| 39 | + CliApplicationHelper::staticCleanApplication(); |
| 40 | + |
| 41 | + // 清理后再次获取,应得到新的有效实例 |
| 42 | + CliApplicationHelper *p2 = CliApplicationHelper::instance(); |
| 43 | + EXPECT_NE(p2, nullptr); |
| 44 | +} |
| 45 | + |
| 46 | +// staticCleanApplication() 在未创建实例时也不应崩溃 |
| 47 | +TEST(CliApplicationHelper_StaticCleanApplication_UT, StaticCleanApplication_NoCrashWhenEmpty) |
| 48 | +{ |
| 49 | + // 先清理确保无残留 |
| 50 | + CliApplicationHelper::staticCleanApplication(); |
| 51 | + // 再次清理不应崩溃 |
| 52 | + CliApplicationHelper::staticCleanApplication(); |
| 53 | + EXPECT_TRUE(true); |
| 54 | +} |
| 55 | + |
| 56 | +// 直接构造/析构对象,覆盖构造函数(qAddPostRoutine)与析构函数(m_helper = nullptr) |
| 57 | +TEST(CliApplicationHelper_Constructor_UT, Constructor_CreatesValidObject) |
| 58 | +{ |
| 59 | + CliApplicationHelper *p = new CliApplicationHelper(); |
| 60 | + EXPECT_NE(p, nullptr); |
| 61 | + delete p; |
| 62 | +} |
| 63 | + |
| 64 | +// setSingleInstance —— UserScope 首次调用应成功(获取锁 + 监听) |
| 65 | +TEST(CliApplicationHelper_SetSingleInstance_UT, SetSingleInstance_UserScope_ReturnsTrue) |
| 66 | +{ |
| 67 | + bool ret = CliApplicationHelper::setSingleInstance( |
| 68 | + QStringLiteral("ut_cli_helper_user"), CliApplicationHelper::UserScope); |
| 69 | + EXPECT_TRUE(ret); |
| 70 | +} |
| 71 | + |
| 72 | +// setSingleInstance —— GroupScope |
| 73 | +TEST(CliApplicationHelper_SetSingleInstance_UT, SetSingleInstance_GroupScope_ReturnsTrue) |
| 74 | +{ |
| 75 | + bool ret = CliApplicationHelper::setSingleInstance( |
| 76 | + QStringLiteral("ut_cli_helper_group"), CliApplicationHelper::GroupScope); |
| 77 | + EXPECT_TRUE(ret); |
| 78 | +} |
| 79 | + |
| 80 | +// setSingleInstance —— WorldScope |
| 81 | +TEST(CliApplicationHelper_SetSingleInstance_UT, SetSingleInstance_WorldScope_ReturnsTrue) |
| 82 | +{ |
| 83 | + bool ret = CliApplicationHelper::setSingleInstance( |
| 84 | + QStringLiteral("ut_cli_helper_world"), CliApplicationHelper::WorldScope); |
| 85 | + EXPECT_TRUE(ret); |
| 86 | +} |
| 87 | + |
| 88 | +// setSingleInstance —— 同一 key 再次调用(同进程内复用锁),应返回 true |
| 89 | +TEST(CliApplicationHelper_SetSingleInstance_UT, SetSingleInstance_SameKeyTwice_ReturnsTrue) |
| 90 | +{ |
| 91 | + QString key = QStringLiteral("ut_cli_helper_samekey"); |
| 92 | + bool first = CliApplicationHelper::setSingleInstance(key, CliApplicationHelper::UserScope); |
| 93 | + EXPECT_TRUE(first); |
| 94 | + |
| 95 | + bool second = CliApplicationHelper::setSingleInstance(key, CliApplicationHelper::UserScope); |
| 96 | + EXPECT_TRUE(second); |
| 97 | +} |
| 98 | + |
| 99 | +// waitTime 静态成员可读写,验证默认值合理性 |
| 100 | +TEST(CliApplicationHelper_Misc_UT, WaitTime_IsPositive) |
| 101 | +{ |
| 102 | + EXPECT_GT(CliApplicationHelper::waitTime, 0); |
| 103 | +} |
| 104 | + |
| 105 | +#include <QDataStream> |
| 106 | +#include <QIODevice> |
| 107 | + |
| 108 | +// 触发 setSingleInstance 中 newConnection/readyRead 两个 lambda(line 159/168): |
| 109 | +// 使用事件循环驱动(QTest::qWait 轮询),而非阻塞式 waitForReadyRead, |
| 110 | +// 因为 server 端 QLocalSocket 的 flush() 需要事件循环才能将数据写入 socket。 |
| 111 | +TEST(CliApplicationHelper_SetSingleInstance_UT, SetSingleInstance_NewConnectionHandlerFires) |
| 112 | +{ |
| 113 | + QString key = QStringLiteral("ut_lambda_trigger"); |
| 114 | + bool ret = CliApplicationHelper::setSingleInstance(key, CliApplicationHelper::UserScope); |
| 115 | + EXPECT_TRUE(ret); |
| 116 | + |
| 117 | + // UserScope on Linux: prefix + "<uid>_" + key |
| 118 | + QString socketKey = QStringLiteral("_d_deepin_logger_single_instance_%1_%2") |
| 119 | + .arg(::getuid()).arg(key); |
| 120 | + |
| 121 | + QLocalSocket client; |
| 122 | + client.connectToServer(socketKey); |
| 123 | + ASSERT_TRUE(client.waitForConnected(3000)); |
| 124 | + |
| 125 | + // 事件循环驱动:等待 server 端 newConnection lambda 触发并发送数据 |
| 126 | + // QTest::qWait 运行事件循环,使 server 端 socket flush 生效 |
| 127 | + for (int i = 0; i < 30 && client.bytesAvailable() < (int)sizeof(qint64); ++i) { |
| 128 | + QTest::qWait(100); |
| 129 | + } |
| 130 | + ASSERT_GT(client.bytesAvailable(), 0); |
| 131 | + |
| 132 | + QDataStream in(&client); |
| 133 | + qint8 version = 0; |
| 134 | + qint64 pid = -1; |
| 135 | + QStringList arguments; |
| 136 | + in >> version >> pid >> arguments; |
| 137 | + EXPECT_GE(pid, 0); |
| 138 | + |
| 139 | + // 回写数据触发 server 端 readyRead lambda(line 168) |
| 140 | + { |
| 141 | + QByteArray block; |
| 142 | + QDataStream out(&block, QIODevice::WriteOnly); |
| 143 | + out << (qint8)1 << (qint64)QCoreApplication::applicationPid() |
| 144 | + << QStringList{QStringLiteral("ut_lambda_arg")}; |
| 145 | + client.write(block); |
| 146 | + client.flush(); |
| 147 | + } |
| 148 | + |
| 149 | + // 事件循环驱动:等待 server 端 readyRead lambda 触发 |
| 150 | + QTest::qWait(1000); |
| 151 | + |
| 152 | + client.disconnectFromServer(); |
| 153 | + if (client.state() != QLocalSocket::UnconnectedState) { |
| 154 | + client.waitForDisconnected(1000); |
| 155 | + } |
| 156 | + SUCCEED(); |
| 157 | +} |
0 commit comments