Skip to content

Commit 0e2cb8f

Browse files
18202781743zccrs
authored andcommitted
test: add unit tests for dconfig2cpp generated class
Added comprehensive unit tests for the dconfig_org_deepin_dtk_preference class generated by dconfig2cpp tool. The tests focus on crash stability and thread safety of the generated DConfig wrapper class. The test suite includes 8 test cases covering: immediate destruction during initialization, destruction after successful initialization, race conditions during initialization, signal thread affinity verification, property change operations under stress, and concurrent config instance handling. Tests check DBus service availability and skip execution if DConfig service is not available. Added include path for log directory in CMakeLists.txt to support test compilation. Influence: 1. Verify DConfig service is available on test system before running tests 2. Test immediate destruction of config objects during initialization 3. Test property getters and setters functionality 4. Verify signal emission and thread affinity for property changes 5. Test concurrent access with multiple config instances 6. Validate rapid property changes don't cause crashes 7. Check memory management during initialization race conditions test: 为dconfig2cpp生成的类添加单元测试 为dconfig2cpp工具生成的dconfig_org_deepin_dtk_preference类添加了全面的 单元测试。测试重点在于生成的DConfig包装类的崩溃稳定性和线程安全性。测试 套件包含8个测试用例,涵盖:初始化期间的立即销毁、成功初始化后的销毁、初 始化期间的竞争条件、信号线程亲和性验证、压力下的属性更改操作以及并发配置 实例处理。测试会检查DBus服务可用性,如果DConfig服务不可用则跳过执行。在 CMakeLists.txt中添加了日志目录的包含路径以支持测试编译。 Influence: 1. 验证测试系统上DConfig服务是否可用 2. 测试初始化期间配置对象的立即销毁 3. 测试属性getter和setter功能 4. 验证属性更改时的信号发射和线程亲和性 5. 测试多个配置实例的并发访问 6. 验证快速属性更改不会导致崩溃 7. 检查初始化竞争条件下的内存管理
1 parent cd0f07f commit 0e2cb8f

File tree

2 files changed

+303
-0
lines changed

2 files changed

+303
-0
lines changed

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ target_include_directories( ${BIN_NAME} PUBLIC
117117
../include/settings/
118118
../include/filesystem/
119119
../include/
120+
../src/log/
120121
./testso/
121122
)
122123

Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
// Copyright (C) 2026 UnionTech Software Technology Co., Ltd.
2+
// SPDX-License-Identifier: LGPL-3.0-or-later
3+
4+
// Test: dconfig_org_deepin_dtk_preference crash stability
5+
// This test verifies the thread-safe generated DConfig class handles:
6+
// 1. Immediate destruction during initialization
7+
// 2. Destruction after successful initialization
8+
// 3. Destruction during active initialization (race condition)
9+
// 4. Signal thread affinity and correctness
10+
// 5. Property change operations under stress
11+
// 6. Concurrent config instances
12+
13+
#include <gtest/gtest.h>
14+
#include <QDBusConnection>
15+
#include <QDBusConnectionInterface>
16+
#include <QDBusReply>
17+
#include <QEventLoop>
18+
#include <QGuiApplication>
19+
#include <QObject>
20+
#include <QSignalSpy>
21+
#include <QTest>
22+
#include <QThread>
23+
24+
// Include the generated DConfig class
25+
#include "dconfig_org_deepin_dtk_preference.hpp"
26+
27+
class ut_dconfig_org_deepin_dtk_preference : public testing::Test
28+
{
29+
protected:
30+
// Test configuration constants - reduced for faster execution
31+
static constexpr int IMMEDIATE_DESTROY_CYCLES = 10;
32+
static constexpr int POST_INIT_DESTROY_CYCLES = 5;
33+
static constexpr int MID_INIT_DESTROY_CYCLES = 20;
34+
static constexpr int PROPERTY_CHANGE_CYCLES = 5;
35+
static constexpr int RAPID_CHANGE_CYCLES = 3;
36+
static constexpr int CONCURRENT_CONFIG_CYCLES = 3;
37+
38+
static constexpr int INIT_TIMEOUT_MS = 2000; // Total timeout for initialization
39+
40+
static constexpr const char *DSG_CONFIG_SERVICE = "org.desktopspec.ConfigManager";
41+
42+
// Static flag to track if DConfig service is available
43+
static bool s_dConfigAvailable;
44+
45+
// Helper to create config
46+
dconfig_org_deepin_dtk_preference *createConfig()
47+
{
48+
return dconfig_org_deepin_dtk_preference::create(
49+
QStringLiteral("org.deepin.dtk.preference"),
50+
QStringLiteral("/test"));
51+
}
52+
53+
// Check if DConfig service is available using DBus
54+
static bool isDConfigServiceAvailable()
55+
{
56+
// Check if service is registered
57+
if (!QDBusConnection::systemBus().interface()->isServiceRegistered(DSG_CONFIG_SERVICE)) {
58+
return false;
59+
}
60+
61+
// Check if service is activatable (similar to DConfig::isServiceActivatable)
62+
const QDBusReply<QStringList> activatableNames =
63+
QDBusConnection::systemBus().interface()->callWithArgumentList(
64+
QDBus::AutoDetect,
65+
QLatin1String("ListActivatableNames"),
66+
QList<QVariant>());
67+
68+
return activatableNames.isValid() && activatableNames.value().contains(DSG_CONFIG_SERVICE);
69+
}
70+
71+
static void SetUpTestSuite()
72+
{
73+
// Quick check using DBus instead of creating actual config
74+
s_dConfigAvailable = isDConfigServiceAvailable();
75+
}
76+
};
77+
78+
// Static member initialization
79+
bool ut_dconfig_org_deepin_dtk_preference::s_dConfigAvailable = false;
80+
81+
// Test 1: Simple creation and immediate deletion
82+
TEST_F(ut_dconfig_org_deepin_dtk_preference, test_immediate_destroy)
83+
{
84+
if (!s_dConfigAvailable) {
85+
GTEST_SKIP() << "DConfig service not available on this system";
86+
}
87+
88+
for (int cycle = 0; cycle < IMMEDIATE_DESTROY_CYCLES; ++cycle) {
89+
auto config = createConfig();
90+
91+
// Delete immediately without waiting for initialization
92+
delete config;
93+
QCoreApplication::processEvents();
94+
}
95+
}
96+
97+
// Test 2: Destroy after signal connection is established
98+
TEST_F(ut_dconfig_org_deepin_dtk_preference, test_destroy_after_initialization)
99+
{
100+
if (!s_dConfigAvailable) {
101+
GTEST_SKIP() << "DConfig service not available on this system";
102+
}
103+
104+
for (int cycle = 0; cycle < POST_INIT_DESTROY_CYCLES; ++cycle) {
105+
auto config = createConfig();
106+
107+
// Wait for initialization
108+
bool initialized = QTest::qWaitFor([config]() {
109+
return config->isInitializeSucceeded();
110+
}, INIT_TIMEOUT_MS);
111+
112+
if (initialized) {
113+
auto dconfig = config->config();
114+
EXPECT_TRUE(dconfig->isValid()) << "DConfig is not valid in cycle " << cycle;
115+
}
116+
117+
delete config;
118+
}
119+
}
120+
121+
// Test 3: Destroy DURING initialization (mid-initialization race condition)
122+
TEST_F(ut_dconfig_org_deepin_dtk_preference, test_destroy_during_initialization)
123+
{
124+
if (!s_dConfigAvailable) {
125+
GTEST_SKIP() << "DConfig service not available on this system";
126+
}
127+
128+
for (int cycle = 0; cycle < MID_INIT_DESTROY_CYCLES; ++cycle) {
129+
auto config = createConfig();
130+
131+
// Delete after a very short delay to hit the initialization window
132+
int delayMicros = (cycle % 10) * 100; // 0-900 microseconds
133+
if (delayMicros > 0) {
134+
QCoreApplication::processEvents();
135+
QThread::usleep(delayMicros);
136+
}
137+
138+
delete config;
139+
QCoreApplication::processEvents();
140+
}
141+
}
142+
143+
// Test 4: Verify signal thread affinity
144+
TEST_F(ut_dconfig_org_deepin_dtk_preference, test_signal_thread_affinity)
145+
{
146+
if (!s_dConfigAvailable) {
147+
GTEST_SKIP() << "DConfig service not available on this system";
148+
}
149+
150+
auto config = createConfig();
151+
152+
QThread *mainThread = QThread::currentThread();
153+
QThread *signalThread = nullptr;
154+
155+
QSignalSpy spyAutoDisplay(config, &dconfig_org_deepin_dtk_preference::autoDisplayFeatureChanged);
156+
157+
// Connect to verify thread affinity
158+
QObject::connect(config, &dconfig_org_deepin_dtk_preference::autoDisplayFeatureChanged,
159+
qApp, [&]() {
160+
signalThread = QThread::currentThread();
161+
}, Qt::DirectConnection);
162+
163+
// Wait for initialization
164+
ASSERT_TRUE(QTest::qWaitFor([config]() {
165+
return config->isInitializeSucceeded();
166+
}, INIT_TIMEOUT_MS)) << "Config failed to initialize";
167+
168+
auto dconfig = config->config();
169+
ASSERT_TRUE(dconfig->isValid()) << "DConfig is not valid";
170+
171+
// Trigger property change
172+
bool currentValue = config->autoDisplayFeature();
173+
config->setAutoDisplayFeature(!currentValue);
174+
175+
// Wait for signal using QSignalSpy
176+
EXPECT_EQ(spyAutoDisplay.count(), 1) << "autoDisplayFeatureChanged signal not emitted";
177+
EXPECT_EQ(signalThread, mainThread) << "Signal emitted in wrong thread";
178+
179+
delete config;
180+
}
181+
182+
// Test 5: Trigger property change then destroy
183+
TEST_F(ut_dconfig_org_deepin_dtk_preference, test_property_change_then_destroy)
184+
{
185+
if (!s_dConfigAvailable) {
186+
GTEST_SKIP() << "DConfig service not available on this system";
187+
}
188+
189+
for (int cycle = 0; cycle < PROPERTY_CHANGE_CYCLES; ++cycle) {
190+
auto config = createConfig();
191+
192+
if (!QTest::qWaitFor([config]() {
193+
return config->isInitializeSucceeded();
194+
}, INIT_TIMEOUT_MS)) {
195+
delete config;
196+
continue;
197+
}
198+
199+
auto dconfig = config->config();
200+
EXPECT_TRUE(dconfig->isValid()) << "DConfig is not valid in cycle " << cycle;
201+
202+
// Change a property and immediately delete
203+
config->setColorMode(QString("test_theme_%1").arg(cycle));
204+
205+
delete config;
206+
}
207+
}
208+
209+
// Test 6: Rapid property changes then destroy
210+
TEST_F(ut_dconfig_org_deepin_dtk_preference, test_rapid_changes_then_destroy)
211+
{
212+
if (!s_dConfigAvailable) {
213+
GTEST_SKIP() << "DConfig service not available on this system";
214+
}
215+
216+
for (int cycle = 0; cycle < RAPID_CHANGE_CYCLES; ++cycle) {
217+
auto config = createConfig();
218+
219+
if (!QTest::qWaitFor([config]() {
220+
return config->isInitializeSucceeded();
221+
}, INIT_TIMEOUT_MS)) {
222+
delete config;
223+
continue;
224+
}
225+
226+
auto dconfig = config->config();
227+
EXPECT_TRUE(dconfig->isValid()) << "DConfig is not valid in cycle " << cycle;
228+
229+
// Rapidly change multiple properties
230+
for (int j = 0; j < 5; ++j) {
231+
config->setColorMode(QString("theme_%1").arg(j));
232+
config->setDefaultColorMode(QString("color_%1").arg(j));
233+
QTest::qWait(1);
234+
}
235+
236+
delete config;
237+
}
238+
}
239+
240+
// Test 7: Concurrent multiple configs
241+
TEST_F(ut_dconfig_org_deepin_dtk_preference, test_concurrent_configs)
242+
{
243+
if (!s_dConfigAvailable) {
244+
GTEST_SKIP() << "DConfig service not available on this system";
245+
}
246+
247+
for (int cycle = 0; cycle < CONCURRENT_CONFIG_CYCLES; ++cycle) {
248+
QList<dconfig_org_deepin_dtk_preference *> configs;
249+
250+
// Create multiple configs at once
251+
for (int i = 0; i < 3; ++i) {
252+
auto config = createConfig();
253+
configs.append(config);
254+
}
255+
256+
// Wait for all to initialize
257+
for (auto config : configs) {
258+
EXPECT_TRUE(QTest::qWaitFor([config]() {
259+
return config->isInitializeSucceeded();
260+
}, INIT_TIMEOUT_MS));
261+
}
262+
263+
// Clean up all configs
264+
qDeleteAll(configs);
265+
}
266+
}
267+
268+
// Test 8: Verify property getters and setters
269+
TEST_F(ut_dconfig_org_deepin_dtk_preference, test_property_getters_and_setters)
270+
{
271+
if (!s_dConfigAvailable) {
272+
GTEST_SKIP() << "DConfig service not available on this system";
273+
}
274+
275+
auto config = createConfig();
276+
277+
// Wait for initialization
278+
ASSERT_TRUE(QTest::qWaitFor([config]() {
279+
return config->isInitializeSucceeded();
280+
}, INIT_TIMEOUT_MS)) << "Config failed to initialize";
281+
282+
// Test getters - should not crash
283+
bool autoDisplay = config->autoDisplayFeature();
284+
285+
// Test setters
286+
config->setAutoDisplayFeature(!autoDisplay);
287+
288+
// Use QSignalSpy to verify signals are emitted
289+
QSignalSpy spyAutoDisplay(config, &dconfig_org_deepin_dtk_preference::autoDisplayFeatureChanged);
290+
291+
// Trigger changes
292+
config->setAutoDisplayFeature(autoDisplay); // Set back to original
293+
294+
// Wait for signals with timeout
295+
EXPECT_TRUE(QTest::qWaitFor([&spyAutoDisplay] () {
296+
return spyAutoDisplay.count() > 0;
297+
}, 1000)) << "autoDisplayFeatureChanged signal not emitted";
298+
299+
delete config;
300+
}
301+
302+
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ut_dconfig_org_deepin_dtk_preference);

0 commit comments

Comments
 (0)