Skip to content

Commit 2be3e8e

Browse files
committed
fix(dock): fix memory leak in RoleGroupModel and add comprehensive tests
Added destructor to RoleGroupModel class to properly release resources stored in m_map using qDeleteAll() to prevent memory leaks when the model is destroyed Added comprehensive memory leak test cases for RoleGroupModel: - DestructorMemoryLeakTest: verify proper cleanup on destruction - RebuildTreeSourceMemoryTest: test memory stability during tree rebuilds - SetSourceModelMemoryTest: verify memory handling when switching source models - EmptyModelMemoryTest: test boundary conditions with empty models - LargeDataMemoryStabilityTest: stress test with large datasets - SetDeduplicationRoleMemoryTest: verify cleanup when changing roles - RowsInsertedMemoryTest: test memory handling on row insertion - RowsRemovedWithEmptyGroupMemoryTest: verify cleanup when removing rows - ModelResetMemoryTest: test memory stability during model reset Log: Fix memory leak in RoleGroupModel with comprehensive test coverage Influence: 1. Verify dock taskmanager functionality remains intact 2. Run memory leak tests with AddressSanitizer enabled 3. Test RoleGroupModel lifecycle scenarios 4. Validate memory cleanup in all test cases fix(dock): 修复 RoleGroupModel 内存泄漏并添加全面测试 为 RoleGroupModel 类添加析构函数,使用 qDeleteAll() 正确释放 m_map 中存储的资源,防止模型销毁时的内存泄漏问题 添加全面的 RoleGroupModel 内存泄漏测试用例: - DestructorMemoryLeakTest: 验证析构时的正确清理 - RebuildTreeSourceMemoryTest: 测试树重建时的内存稳定性 - SetSourceModelMemoryTest: 验证切换源模型时的内存处理 - EmptyModelMemoryTest: 测试空模型的边界条件 - LargeDataMemoryStabilityTest: 大数据集压力测试 - SetDeduplicationRoleMemoryTest: 验证角色切换时的清理 - RowsInsertedMemoryTest: 测试插入行时的内存处理 - RowsRemovedWithEmptyGroupMemoryTest: 验证删除行时的清理 - ModelResetMemoryTest: 测试模型重置时的内存稳定性 Log: 修复 RoleGroupModel 内存泄漏并添加全面测试覆盖 Influence: 1. 验证 dock 任务管理器功能保持正常 2. 启用 AddressSanitizer 运行内存泄漏测试 3. 测试 RoleGroupModel 生命周期场景 4. 验证所有测试用例的内存清理
1 parent a303597 commit 2be3e8e

3 files changed

Lines changed: 296 additions & 1 deletion

File tree

panels/dock/taskmanager/rolegroupmodel.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ RoleGroupModel::RoleGroupModel(QAbstractItemModel *sourceModel, int role, QObjec
1313
RoleGroupModel::setSourceModel(sourceModel);
1414
}
1515

16+
RoleGroupModel::~RoleGroupModel()
17+
{
18+
qDeleteAll(m_map);
19+
}
20+
1621
void RoleGroupModel::setDeduplicationRole(const int &role)
1722
{
1823
if (role != m_roleForDeduplication) {

panels/dock/taskmanager/rolegroupmodel.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class RoleGroupModel : public QAbstractProxyModel
1212

1313
public:
1414
explicit RoleGroupModel(QAbstractItemModel *sourceModel, int role, QObject *parent = nullptr);
15+
~RoleGroupModel() override;
1516
void setSourceModel(QAbstractItemModel *sourceModel) override;
1617

1718
void setDeduplicationRole(const int &role);

tests/panels/dock/taskmanager/rolegroupmodeltests.cpp

Lines changed: 290 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
1+
// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd.
22
//
33
// SPDX-License-Identifier: GPL-3.0-or-later
44

@@ -286,6 +286,295 @@ TEST(RoleGroupModel, HasChildrenTest)
286286
EXPECT_EQ(groupModel.rowCount(), 0);
287287
}
288288

289+
// 测试析构函数是否正确释放内存(内存泄漏检测)
290+
TEST(RoleGroupModel, DestructorMemoryLeakTest)
291+
{
292+
// 这个测试验证当 RoleGroupModel 被销毁时,所有内部分配的 QList 对象都被正确释放
293+
294+
auto role = Qt::UserRole + 1;
295+
296+
// 创建源模型并添加数据
297+
QStandardItemModel *sourceModel = new QStandardItemModel();
298+
for (int i = 0; i < 5; ++i) {
299+
QStandardItem *item = new QStandardItem;
300+
item->setData(QString("group%1").arg(i % 2), role);
301+
sourceModel->appendRow(item);
302+
}
303+
304+
// 创建 RoleGroupModel 并验证数据
305+
RoleGroupModel *groupModel = new RoleGroupModel(sourceModel, role);
306+
EXPECT_EQ(groupModel->rowCount(), 2); // 应该有2个分组
307+
308+
// 销毁 RoleGroupModel - 应该释放所有内部 QList 对象
309+
EXPECT_NO_THROW({
310+
delete groupModel;
311+
groupModel = nullptr;
312+
});
313+
314+
// 销毁源模型
315+
delete sourceModel;
316+
317+
// 如果存在内存泄漏,ASan 会在这里报告
318+
EXPECT_EQ(groupModel, nullptr);
319+
}
320+
321+
// 测试多次重建数据源时的内存管理
322+
TEST(RoleGroupModel, RebuildTreeSourceMemoryTest)
323+
{
324+
QStandardItemModel model;
325+
auto role = Qt::UserRole + 1;
326+
RoleGroupModel groupModel(&model, role);
327+
328+
// 第一次添加数据
329+
for (int i = 0; i < 3; ++i) {
330+
QStandardItem *item = new QStandardItem;
331+
item->setData(QString("group1"), role);
332+
model.appendRow(item);
333+
}
334+
EXPECT_EQ(groupModel.rowCount(), 1);
335+
336+
// 修改数据触发重建(通过 dataChanged 信号)
337+
for (int i = 0; i < model.rowCount(); ++i) {
338+
model.setData(model.index(i, 0), QString("group%1").arg(i), role);
339+
}
340+
341+
// 重建后应该有3个分组
342+
EXPECT_EQ(groupModel.rowCount(), 3);
343+
344+
// 再次修改数据触发重建
345+
for (int i = 0; i < model.rowCount(); ++i) {
346+
model.setData(model.index(i, 0), QString("newgroup"), role);
347+
}
348+
349+
// 重建后应该只有1个分组
350+
EXPECT_EQ(groupModel.rowCount(), 1);
351+
352+
}
353+
354+
// 测试设置不同的 sourceModel 时的内存管理
355+
TEST(RoleGroupModel, SetSourceModelMemoryTest)
356+
{
357+
auto role = Qt::UserRole + 1;
358+
359+
// 创建第一个源模型
360+
QStandardItemModel model1;
361+
for (int i = 0; i < 3; ++i) {
362+
QStandardItem *item = new QStandardItem;
363+
item->setData(QString("group1"), role);
364+
model1.appendRow(item);
365+
}
366+
367+
// 创建 RoleGroupModel
368+
RoleGroupModel groupModel(&model1, role);
369+
EXPECT_EQ(groupModel.rowCount(), 1);
370+
371+
// 创建第二个源模型
372+
QStandardItemModel model2;
373+
for (int i = 0; i < 5; ++i) {
374+
QStandardItem *item = new QStandardItem;
375+
item->setData(QString("group%1").arg(i), role);
376+
model2.appendRow(item);
377+
}
378+
379+
// 切换到新的源模型
380+
groupModel.setSourceModel(&model2);
381+
EXPECT_EQ(groupModel.rowCount(), 5);
382+
383+
// 设置空源模型
384+
groupModel.setSourceModel(nullptr);
385+
EXPECT_EQ(groupModel.rowCount(), 0);
386+
387+
// 再次设置源模型
388+
groupModel.setSourceModel(&model1);
389+
EXPECT_EQ(groupModel.rowCount(), 1);
390+
391+
}
392+
393+
// 测试空模型和边界情况的内存管理
394+
TEST(RoleGroupModel, EmptyModelMemoryTest)
395+
{
396+
QStandardItemModel model;
397+
auto role = Qt::UserRole + 1;
398+
399+
// 创建空的 RoleGroupModel
400+
RoleGroupModel groupModel(&model, role);
401+
EXPECT_EQ(groupModel.rowCount(), 0);
402+
403+
// 添加一些数据
404+
QStandardItem *item1 = new QStandardItem;
405+
item1->setData(QString("group1"), role);
406+
model.appendRow(item1);
407+
EXPECT_EQ(groupModel.rowCount(), 1);
408+
409+
// 清空模型
410+
model.clear();
411+
EXPECT_EQ(groupModel.rowCount(), 0);
412+
413+
// 再次添加数据
414+
QStandardItem *item2 = new QStandardItem;
415+
item2->setData(QString("group2"), role);
416+
model.appendRow(item2);
417+
EXPECT_EQ(groupModel.rowCount(), 1);
418+
419+
// 再次清空
420+
model.clear();
421+
EXPECT_EQ(groupModel.rowCount(), 0);
422+
423+
}
424+
425+
// 测试大量数据操作的内存稳定性
426+
TEST(RoleGroupModel, LargeDataMemoryStabilityTest)
427+
{
428+
QStandardItemModel model;
429+
auto role = Qt::UserRole + 1;
430+
RoleGroupModel groupModel(&model, role);
431+
432+
// 添加大量数据
433+
const int itemCount = 100;
434+
for (int i = 0; i < itemCount; ++i) {
435+
QStandardItem *item = new QStandardItem;
436+
item->setData(QString("group%1").arg(i % 10), role);
437+
model.appendRow(item);
438+
}
439+
440+
EXPECT_EQ(groupModel.rowCount(), 10);
441+
442+
// 删除一半数据
443+
model.removeRows(0, itemCount / 2);
444+
EXPECT_EQ(groupModel.rowCount(), 10); // 分组可能还在,但子项减少
445+
446+
// 再次添加数据
447+
for (int i = 0; i < itemCount / 2; ++i) {
448+
QStandardItem *item = new QStandardItem;
449+
item->setData(QString("newgroup%1").arg(i % 5), role);
450+
model.appendRow(item);
451+
}
452+
453+
}
454+
455+
// 测试 setDeduplicationRole 改变时的内存管理
456+
TEST(RoleGroupModel, SetDeduplicationRoleMemoryTest)
457+
{
458+
QStandardItemModel model;
459+
auto role1 = Qt::UserRole + 1;
460+
auto role2 = Qt::UserRole + 2;
461+
462+
// 设置两个角色的数据
463+
for (int i = 0; i < 5; ++i) {
464+
QStandardItem *item = new QStandardItem;
465+
item->setData(QString("groupA"), role1);
466+
item->setData(QString("group%1").arg(i % 3), role2);
467+
model.appendRow(item);
468+
}
469+
470+
RoleGroupModel groupModel(&model, role1);
471+
EXPECT_EQ(groupModel.rowCount(), 1); // role1: 1个分组
472+
473+
// 切换到 role2
474+
groupModel.setDeduplicationRole(role2);
475+
EXPECT_EQ(groupModel.rowCount(), 3); // role2: 3个分组
476+
477+
// 切换回 role1
478+
groupModel.setDeduplicationRole(role1);
479+
EXPECT_EQ(groupModel.rowCount(), 1); // role1: 1个分组
480+
481+
}
482+
483+
// 测试 rowsInserted 信号处理时的内存管理
484+
TEST(RoleGroupModel, RowsInsertedMemoryTest)
485+
{
486+
QStandardItemModel model;
487+
auto role = Qt::UserRole + 1;
488+
RoleGroupModel groupModel(&model, role);
489+
490+
// 初始添加数据
491+
for (int i = 0; i < 3; ++i) {
492+
QStandardItem *item = new QStandardItem;
493+
item->setData(QString("group1"), role);
494+
model.appendRow(item);
495+
}
496+
EXPECT_EQ(groupModel.rowCount(), 1);
497+
EXPECT_EQ(groupModel.rowCount(groupModel.index(0, 0)), 3);
498+
499+
// 插入新行到现有分组
500+
QStandardItem *item1 = new QStandardItem;
501+
item1->setData(QString("group1"), role);
502+
model.insertRow(1, item1);
503+
EXPECT_EQ(groupModel.rowCount(groupModel.index(0, 0)), 4);
504+
505+
// 插入新行创建新分组
506+
QStandardItem *item2 = new QStandardItem;
507+
item2->setData(QString("group2"), role);
508+
model.appendRow(item2);
509+
EXPECT_EQ(groupModel.rowCount(), 2);
510+
511+
}
512+
513+
// 测试 rowsRemoved 信号处理时的内存管理(包括删除空分组)
514+
TEST(RoleGroupModel, RowsRemovedWithEmptyGroupMemoryTest)
515+
{
516+
QStandardItemModel model;
517+
auto role = Qt::UserRole + 1;
518+
RoleGroupModel groupModel(&model, role);
519+
520+
// 添加数据到分组
521+
QStandardItem *item1 = new QStandardItem;
522+
item1->setData(QString("group1"), role);
523+
model.appendRow(item1);
524+
525+
QStandardItem *item2 = new QStandardItem;
526+
item2->setData(QString("group1"), role);
527+
model.appendRow(item2);
528+
529+
QStandardItem *item3 = new QStandardItem;
530+
item3->setData(QString("group2"), role);
531+
model.appendRow(item3);
532+
533+
EXPECT_EQ(groupModel.rowCount(), 2);
534+
535+
// 删除 group1 的所有项目,触发分组删除
536+
model.removeRow(0);
537+
model.removeRow(0); // 注意:删除第一行后,原来的第二行变成了第一行
538+
539+
// 现在应该只有 group2
540+
EXPECT_EQ(groupModel.rowCount(), 1);
541+
542+
// 删除最后一个项目
543+
model.removeRow(0);
544+
EXPECT_EQ(groupModel.rowCount(), 0);
545+
546+
}
547+
548+
549+
// 测试 modelReset 信号处理时的内存管理
550+
TEST(RoleGroupModel, ModelResetMemoryTest)
551+
{
552+
QStandardItemModel model;
553+
auto role = Qt::UserRole + 1;
554+
RoleGroupModel groupModel(&model, role);
555+
556+
// 添加数据
557+
for (int i = 0; i < 5; ++i) {
558+
QStandardItem *item = new QStandardItem;
559+
item->setData(QString("group%1").arg(i % 2), role);
560+
model.appendRow(item);
561+
}
562+
EXPECT_EQ(groupModel.rowCount(), 2);
563+
564+
// 使用 clear() 方法会触发 modelReset 信号
565+
model.clear();
566+
567+
// 重新添加不同的数据
568+
for (int i = 0; i < 3; ++i) {
569+
QStandardItem *item = new QStandardItem;
570+
item->setData(QString("newgroup"), role);
571+
model.appendRow(item);
572+
}
573+
574+
EXPECT_EQ(groupModel.rowCount(), 1);
575+
576+
}
577+
289578
// 测试大量索引访问的边界情况(模拟滚动场景)
290579
TEST(RoleGroupModel, ScrollingBoundaryTest)
291580
{

0 commit comments

Comments
 (0)