diff --git a/src/editorapplication.cpp b/src/editorapplication.cpp index daec65d4..e2067ffd 100644 --- a/src/editorapplication.cpp +++ b/src/editorapplication.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2019 - 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2019-2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -45,13 +45,8 @@ EditorApplication::EditorApplication(int &argc, char *argv[]) : DApplication(arg EditorApplication::~EditorApplication() { qDebug() << "Enter EditorApplication destructor"; - // app结束时,释放 - if (nullptr != StartManager::instance()) { - qDebug() << "Deleting StartManager instance"; - delete StartManager::instance(); - } else { - qDebug() << "StartManager instance is already null"; - } + // StartManager 生命周期由 main() 接管(创建于 create(),事件循环结束后显式释放), + // 析构不得触碰,否则 D-Bus 转发进程会在退出期意外实例化单例(BUG-378901) qDebug() << "Exit EditorApplication destructor"; qInfo() << "Application resources released"; } diff --git a/src/main.cpp b/src/main.cpp index ef06e7e7..4ccf78b6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -133,7 +133,8 @@ int main(int argc, char *argv[]) qDebug() << "DTKWIDGET_CLASS_DSizeMode not defined, skipping size mode handler"; #endif - StartManager *startManager = StartManager::instance(); + // 单例唯一创建点;D-Bus 转发进程走不到这里,不会实例化 StartManager + StartManager *startManager = StartManager::create(); //埋点记录启动数据 QJsonObject objStartEvent{ @@ -169,7 +170,11 @@ int main(int argc, char *argv[]) PerformanceMonitor::initializeAppFinish(); qDebug() << "Entering main event loop"; - return app.exec(); + int ret = app.exec(); + // 事件循环已停止,在此显式释放单例;~EditorApplication 不再管理其生命周期 + qDebug() << "Deleting StartManager instance on exit"; + delete StartManager::instance(); + return ret; } // Just send dbus message to exist editor process. else { diff --git a/src/startmanager.cpp b/src/startmanager.cpp index a5c896be..232dc5d3 100644 --- a/src/startmanager.cpp +++ b/src/startmanager.cpp @@ -32,7 +32,7 @@ static const QString s_bookMarkKey = "advance.editor.bookmark"; StartManager *StartManager::m_instance = nullptr; -StartManager *StartManager::instance() +StartManager *StartManager::create() { if (m_instance == nullptr) { qDebug() << "StartManager instance is null, creating new instance"; @@ -42,6 +42,11 @@ StartManager *StartManager::instance() return m_instance; } +StartManager *StartManager::instance() +{ + return m_instance; +} + StartManager::~StartManager() { qDebug() << "Enter StartManager destructor"; diff --git a/src/startmanager.h b/src/startmanager.h index 12dd2a97..1f6f94ba 100644 --- a/src/startmanager.h +++ b/src/startmanager.h @@ -29,6 +29,8 @@ class StartManager : public QObject int tabIndex; }; + // 唯一创建点:仅 main() 调用;其余场合一律用 instance() 纯查询 + static StartManager *create(); static StartManager *instance(); explicit StartManager(QObject *parent = nullptr); ~StartManager(); diff --git a/tests/app_ut/test_editorapplication.cpp b/tests/app_ut/test_editorapplication.cpp index 17a6adc7..857b6016 100644 --- a/tests/app_ut/test_editorapplication.cpp +++ b/tests/app_ut/test_editorapplication.cpp @@ -13,8 +13,7 @@ // 分支清单 → 用例映射(editorapplication.cpp): // ctor(9) → SetUpTestSuite 真实构造 + Ctor_应用属性就位(Qt>=6 分支) // dtor(45) → TearDownTestSuite delete s_app: -// D2/D0 双记录 + "StartManager::instance() 非空 → delete"分支 -// (else 空分支不可二次构造 QApplication,函数覆盖已达成) +// D2/D0 双记录(StartManager 生命周期已移交 main(),dtor 不再触碰) // handleQuitAction(59) // B1 activeWindow 非空 → close // → HandleQuitAction_ActiveWindow_ClosesIt(closeEvent 计数 + 隐藏断言) @@ -444,14 +443,19 @@ TEST_F(EditorApplicationTest, PressSpace_Button_ReleasesAfter80msAndClicks) // ---------------- dtor 前置 ---------------- -// dtor(45) 前置:保证 TearDownTestSuite delete s_app 时 StartManager::instance() -// 非空,命中“Deleting StartManager instance”分支(D2/D0 双记录在 delete 时落点) -TEST_F(EditorApplicationTest, Dtor_Precondition_StartManagerInstanceAlive) +// dtor(45) 前置:~EditorApplication 已不管理 StartManager 生命周期(BUG-378901 修复: +// 创建点唯一化为 main() 中的 create(),事件循环结束后于 main() 显式释放)。 +// 此处验证 instance() 为纯查询访问器,无创建副作用——TearDownTestSuite delete s_app +// 时不会意外实例化/销毁单例(D2/D0 双记录在 delete 时落点) +TEST_F(EditorApplicationTest, Dtor_Precondition_InstanceIsNonCreating) { - // Arrange / Act:惰性单例真实构造(DBus/Iflytek 已 stub,XDG 已重定向) + // Arrange + StartManager::m_instance = nullptr; + + // Act:纯查询访问器(旧实现此处会惰性创建完整单例) StartManager *instance = StartManager::instance(); - // Assert - EXPECT_NE(instance, nullptr); - EXPECT_EQ(StartManager::m_instance, instance); + // Assert:不创建,析构路径对 StartManager 零接触 + EXPECT_EQ(instance, nullptr); + EXPECT_EQ(StartManager::m_instance, nullptr); } diff --git a/tests/startmanager/test_startmanager.cpp b/tests/startmanager/test_startmanager.cpp index 28d41ff9..6dc09ad9 100644 --- a/tests/startmanager/test_startmanager.cpp +++ b/tests/startmanager/test_startmanager.cpp @@ -548,18 +548,20 @@ QCoreApplication *StartManagerTest::s_app = nullptr; // 构造 / 单例 / 析构 // ============================================================ -TEST_F(StartManagerTest, Instance_FirstCall_CreatesAndReusesSingleton) +TEST_F(StartManagerTest, Create_FirstCall_CreatesAndReusesSingleton) { // Arrange StartManager::m_instance = nullptr; - // Act - StartManager *p1 = StartManager::instance(); - StartManager *p2 = StartManager::instance(); + // Act:create() 为唯一创建点,重复调用幂等;instance() 纯查询同实例 + StartManager *p1 = StartManager::create(); + StartManager *p2 = StartManager::create(); + StartManager *q = StartManager::instance(); // Assert:首建非空且复用同一实例 ASSERT_NE(p1, nullptr); EXPECT_EQ(p1, p2); + EXPECT_EQ(q, p1); // Cleanup:析构置空静态指针(覆盖 ~StartManager B2 正侧) delete p1->m_pTimer; @@ -567,6 +569,16 @@ TEST_F(StartManagerTest, Instance_FirstCall_CreatesAndReusesSingleton) EXPECT_EQ(StartManager::m_instance, nullptr); } +TEST_F(StartManagerTest, Instance_NonCreating_ReturnsNullWhenAbsent) +{ + // Arrange:BUG-378901 修复后 instance() 不再惰性创建 + StartManager::m_instance = nullptr; + + // Act / Assert:单例缺席时纯查询返回空,不得实例化 + EXPECT_EQ(StartManager::instance(), nullptr); + EXPECT_EQ(StartManager::m_instance, nullptr); +} + TEST_F(StartManagerTest, Destructor_ForeignInstance_KeepsStaticPointer) { // Arrange:静态指针指向其他实例 diff --git a/tests/widgets_ut/test_window.cpp b/tests/widgets_ut/test_window.cpp index 8d6af7ef..28e44d2d 100644 --- a/tests/widgets_ut/test_window.cpp +++ b/tests/widgets_ut/test_window.cpp @@ -93,6 +93,11 @@ class WindowTest : public ::testing::Test { QDir().mkpath(xdgData + "/deepin/deepin-editor/blank-files"); QDir().mkpath(xdgData + "/deepin/deepin-editor/backup-files"); QDir().mkpath(xdgData + "/deepin/deepin-editor/autoBackup-files"); + + // BUG-378901 修复后 instance() 为纯查询不创建,window.cpp 各调用点 + // (checkTabbarForReload/removeWrapper 的 delayMallocTrim 等)依赖单例已存在; + // 套件级显式建真实例,与旧懒创建语义等价(生产路径由 main() 中 create() 保证) + StartManager::create(); } static void TearDownTestSuite()