diff --git a/tests/app/ut_dbusobject.cpp b/tests/app/ut_dbusobject.cpp index 18a2e15aa..cc7efa328 100644 --- a/tests/app/ut_dbusobject.cpp +++ b/tests/app/ut_dbusobject.cpp @@ -8,6 +8,7 @@ #include "stub.h" #include +#include #include @@ -164,3 +165,66 @@ TEST_F(TestDBusObject, UT_DBusObject_handleFiles_002) delete g_mainWindow; } + +/* ========== PMS 回归用例(sev1/2 bug 补强,批次1) ========== */ + +// PMS: https://pms.uniontech.com/bug-view-312013.html commit: c44cc7a0 +static QString g_lastAddFilePath; +// 成员函数桩经 ut-stub jmp 补丁以成员 ABI (this, path) 被调用,需双参接收 +void addFileCapture_stub(MainWindow *self, const QString &path) +{ + Q_UNUSED(self) + g_funcName = __FUNCTION__; + g_lastAddFilePath = path; +} + +// PMS: https://pms.uniontech.com/bug-view-312013.html commit: c44cc7a0 +TEST_F(TestDBusObject, BUG312013_handleFiles_urlConvertedToLocal) +{ + // 双击 pdf 无法查看修复(DBus 支持 URL 入参):file:// URL 必须转换为本地路径后打开 + Stub s; + s.set(ADDR(MainWindow, addFile), addFileCapture_stub); + s.set(ADDR(QWidget, show), show_stub); + s.set(static_cast(ADDR(MainWindow, createWindow)), createWindow_stub); + + m_tester->m_isBlockShutdown = true; + QString localPath = UTSOURCEDIR; + localPath += "/files/normal.pdf"; + QStringList filePathList = {QUrl::fromLocalFile(localPath).toString()}; + m_tester->handleFiles(filePathList); + EXPECT_EQ(g_lastAddFilePath, localPath); + delete g_mainWindow; +} + +// PMS: https://pms.uniontech.com/bug-view-312013.html commit: c44cc7a0 +TEST_F(TestDBusObject, BUG312013_handleFiles_alreadyOpenNoReadd) +{ + // 已打开文件(activateSheetIfExist 命中)不得重复入窗 + Stub s; + s.set(ADDR(MainWindow, addFile), addFileCapture_stub); + s.set(ADDR(QWidget, show), show_stub); + s.set(static_cast(ADDR(MainWindow, createWindow)), createWindow_stub); + s.set(ADDR(MainWindow, activateSheetIfExist), registerService_stub); // 任意返回 true 的 bool(const QString&) + + m_tester->m_isBlockShutdown = true; + QString localPath = UTSOURCEDIR; + localPath += "/files/normal.pdf"; + QStringList filePathList = {QUrl::fromLocalFile(localPath).toString()}; + g_lastAddFilePath.clear(); + m_tester->handleFiles(filePathList); + EXPECT_FALSE(g_funcName == QString("addFileCapture_stub")); + delete g_mainWindow; +} + +// PMS: https://pms.uniontech.com/bug-view-312013.html commit: c44cc7a0 +TEST_F(TestDBusObject, BUG312013_unBlockShutdown_repeatNoCrash) +{ + // DBus 入口修复涉及 unBlockShutdown:重复解除关机阻断不得崩溃 + Stub s; + s.set(ADDR(QDBusAbstractInterface, callWithArgumentList), callWithArgumentList_stub); + m_tester->blockShutdown(); + m_tester->unBlockShutdown(); + EXPECT_FALSE(m_tester->m_isBlockShutdown); + m_tester->unBlockShutdown(); + EXPECT_FALSE(m_tester->m_isBlockShutdown); +} diff --git a/tests/app/ut_utils.cpp b/tests/app/ut_utils.cpp index 5a64fa674..261589c8a 100644 --- a/tests/app/ut_utils.cpp +++ b/tests/app/ut_utils.cpp @@ -176,3 +176,27 @@ TEST_F(TestUtils, UT_Utils_setCurrentFilePath_001) EXPECT_TRUE(Utils::m_currenFilePath == "/test/path/file.pdf"); Utils::setCurrentFilePath(""); } + +/* ========== PMS 回归用例(sev1/2 bug 补强,批次1) ========== */ + +// PMS: https://pms.uniontech.com/bug-view-218701.html commit: bb8f3e92 +TEST_F(TestUtils, BUG218701_roundQPixmap_nullInputGuard) +{ + // 复制文本修复涉及 roundQPixmap:空图输入必须返回空图不崩溃 + QPixmap in; + EXPECT_TRUE(Utils::roundQPixmap(in, 10).isNull()); +} + +// PMS: https://pms.uniontech.com/bug-view-218701.html commit: bb8f3e92 +TEST_F(TestUtils, BUG218701_roundQPixmap_validInputKeepsSize) +{ + // 有效图输入圆角处理:输出非空且保持原始尺寸,零半径退化场景不崩溃 + QPixmap in(100, 80); + QPixmap out = Utils::roundQPixmap(in, 12); + EXPECT_FALSE(out.isNull()); + EXPECT_EQ(out.size(), in.size()); + + QPixmap out0 = Utils::roundQPixmap(in, 0); + EXPECT_FALSE(out0.isNull()); + EXPECT_EQ(out0.size(), in.size()); +} diff --git a/tests/document/ut_model.cpp b/tests/document/ut_model.cpp index 73478dd9b..52ad73a8d 100644 --- a/tests/document/ut_model.cpp +++ b/tests/document/ut_model.cpp @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include using namespace deepin_reader; @@ -305,3 +307,116 @@ TEST(UT_DocumentBase, UT_Annotation_destructor_001) delete annot; delete dAnnot; } + +/* ========== PMS 回归用例(sev1/2 bug 补强,批次1) ========== */ + +/* 说明:calculateTimeout / getHtmlToPdfPath 为 Model.cpp 文件内 static 自由函数, + * 无法从测试侧直接调用,经公开入口 DocumentFactory::getDocument 的 DOCX 转换管线 + * 间接驱动(管线内 calculateTimeout×3、getHtmlToPdfPath×1)。 + */ + +// PMS: https://pms.uniontech.com/bug-view-332133.html commit: b1746b32 +TEST(UT_DocumentFactory_getDocument, BUG332133_docxTimeout_dynamicTimeoutNoHang) +{ + // 大文件 docx 转换超时修复:动态超时(按 MB 计算)替代固定超时, + // 常规文件取基准超时且各阶段失败时优雅返回 ConvertFailed,不得挂起 + Stub s; + s.set(static_cast(ADDR(QFile, copy)), copy_stub); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + s.set(static_cast(ADDR(QProcess, start)), start_stub_qt6); +#else + s.set(static_cast(ADDR(QProcess, start)), start_stub); +#endif + Stub s1; + s1.set(ADDR(QProcess, waitForStarted), waitForStarted_true_stub); + s1.set(ADDR(QProcess, waitForFinished), waitForFinished_true_stub); + + int fileType = Dr::DOCX; + QString filePath = UTSOURCEDIR; + filePath += "/files/normal.docx"; + QString convertedFileDir = QCoreApplication::applicationDirPath(); + QString password; + QProcess p; + QProcess *process = &p; + Document::Error error = Document::NoError; + + Document *pdocument = DocumentFactory::getDocument(fileType, filePath, convertedFileDir, password, &process, error); + EXPECT_EQ(pdocument, nullptr); + EXPECT_EQ(error, Document::ConvertFailed); // word/ 目录未生成(unzip 桩不落盘) + EXPECT_EQ(process, nullptr); +} + +// PMS: https://pms.uniontech.com/bug-view-332133.html commit: b1746b32 +TEST(UT_DocumentFactory_getDocument, BUG332133_docxTimeout_overflowGuard) +{ + // 溢出保护:超大文件 sizeInMB 超过安全上限时超时取 MAX_TIMEOUT_MS, + // 不得整型溢出/崩溃(300MB 稀疏文件 > 各阶段 maxSafeSize:unzip 285/pandoc 108/htmltopdf 48) + QString bigPath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/ut_big_sparse.docx"; + QFile bigFile(bigPath); + if (bigFile.exists()) + bigFile.remove(); + ASSERT_TRUE(bigFile.open(QIODevice::WriteOnly)); + ASSERT_TRUE(bigFile.resize(300LL * 1024 * 1024)); // 300MB 稀疏文件,瞬间创建 + bigFile.close(); + + Stub s; + s.set(static_cast(ADDR(QFile, copy)), copy_stub); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + s.set(static_cast(ADDR(QProcess, start)), start_stub_qt6); +#else + s.set(static_cast(ADDR(QProcess, start)), start_stub); +#endif + Stub s1; + s1.set(ADDR(QProcess, waitForStarted), waitForStarted_true_stub); + s1.set(ADDR(QProcess, waitForFinished), waitForFinished_true_stub); + + int fileType = Dr::DOCX; + QString convertedFileDir = QCoreApplication::applicationDirPath(); + QString password; + QProcess p; + QProcess *process = &p; + Document::Error error = Document::NoError; + + Document *pdocument = DocumentFactory::getDocument(fileType, bigPath, convertedFileDir, password, &process, error); + EXPECT_EQ(pdocument, nullptr); + EXPECT_EQ(error, Document::ConvertFailed); + EXPECT_EQ(process, nullptr); + + bigFile.remove(); +} + +// PMS: https://pms.uniontech.com/bug-view-304083.html commit: f260a63f +TEST(UT_DocumentFactory_getDocument, BUG304083_docxPipeline_gracefulFailNoCrash) +{ + // docx 打不开修复(getHtmlToPdfPath 多级路径查找):完整转换管线(unzip->pandoc->htmltopdf) + // 各阶段失败必须优雅返回不崩溃,getHtmlToPdfPath 真实路径查找正常执行 + Stub s; + s.set(static_cast(ADDR(QFile, copy)), copy_stub); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + s.set(static_cast(ADDR(QProcess, start)), start_stub_qt6); +#else + s.set(static_cast(ADDR(QProcess, start)), start_stub); +#endif + Stub s1; + s1.set(ADDR(QProcess, waitForStarted), waitForStarted_true_stub); + s1.set(ADDR(QProcess, waitForFinished), waitForFinished_true_stub); + Stub s2; + s2.set(static_cast(ADDR(QDir, exists)), exists_stub); + s2.set(static_cast(ADDR(QFile, exists)), exists_stub); + Stub s3; + s3.set(ADDR(PDFDocument, loadDocument), loadpdfDocument_stub); + + int fileType = Dr::DOCX; + QString filePath = UTSOURCEDIR; + filePath += "/files/normal.docx"; + QString convertedFileDir = QCoreApplication::applicationDirPath(); + QString password; + QProcess p; + QProcess *process = &p; + Document::Error error = Document::NoError; + + Document *pdocument = DocumentFactory::getDocument(fileType, filePath, convertedFileDir, password, &process, error); + EXPECT_EQ(pdocument, nullptr); + EXPECT_EQ(error, Document::FileError); // 转换产物加载失败(stub FileError) + EXPECT_EQ(process, nullptr); +} diff --git a/tests/document/ut_xpsmodel.cpp b/tests/document/ut_xpsmodel.cpp index 579948a4d..f98887635 100644 --- a/tests/document/ut_xpsmodel.cpp +++ b/tests/document/ut_xpsmodel.cpp @@ -492,3 +492,31 @@ TEST_F(TestXpsTextExtractor, parseGlyphsMissingOrigin) // Position is default-constructed QPointF. EXPECT_TRUE(info.position.isNull()); } + +/* ========== PMS 回归用例(sev1/2 bug 补强,批次1) ========== */ + +// PMS: https://pms.uniontech.com/bug-view-343541.html commit: a3cb6cff +TEST_F(TestXpsDocumentAdapter, BUG343541_renderPage_highDpiScaling) +{ + // XPS 渲染慢修复(cairo_matrix 高DPI 缩放):目标尺寸大于逻辑页尺寸时 + // 按 A4@300dpi 渲染不崩溃且尺寸精确 + ASSERT_GT(m_doc->pageCount(), 0); + QImage img = m_doc->renderPage(0, 2480, 3508, QRect()); + EXPECT_FALSE(img.isNull()); + EXPECT_EQ(img.size(), QSize(2480, 3508)); +} + +// PMS: https://pms.uniontech.com/bug-view-343541.html commit: a3cb6cff +TEST_F(TestXpsDocumentAdapter, BUG343541_renderPage_fixedSizeAndGuard) +{ + // 固定尺寸渲染与守卫补充:单维负值/负页码返回空图且不崩溃 + QImage img = m_doc->renderPage(0, 100, 100, QRect()); + EXPECT_FALSE(img.isNull()); + EXPECT_EQ(img.size(), QSize(100, 100)); + + // 单维负值(既有用例仅覆盖双负值与零值) + EXPECT_TRUE(m_doc->renderPage(0, -1, 100, QRect()).isNull()); + EXPECT_TRUE(m_doc->renderPage(0, 100, -1, QRect()).isNull()); + // 负页码(既有用例仅覆盖越界正页码) + EXPECT_TRUE(m_doc->renderPage(-1, 100, 100, QRect()).isNull()); +} diff --git a/tests/sidebar/ut_sidebarimageviewmodel.cpp b/tests/sidebar/ut_sidebarimageviewmodel.cpp index 1ce5ac713..7a3d28e94 100644 --- a/tests/sidebar/ut_sidebarimageviewmodel.cpp +++ b/tests/sidebar/ut_sidebarimageviewmodel.cpp @@ -7,11 +7,14 @@ #include "DocSheet.h" #include "SideBarImageViewModel.h" #include "PageRenderThread.h" +#include "PDFModel.h" +#include "dpdfannot.h" #include "stub.h" #include #include +#include #include class TestImagePageInfo_t : public ::testing::Test @@ -233,3 +236,143 @@ TEST_F(TestSideBarImageViewModel, testonBatchUpdateTimer) m_tester->onBatchUpdateTimer(); SUCCEED(); } + +/* ========== PMS 回归用例(sev1/2 bug 补强,批次1) ========== */ + +// PMS: https://pms.uniontech.com/bug-view-343541.html commit: a3cb6cff +TEST_F(TestSideBarImageViewModel, BUG343541_ctor_batchUpdateTimerConfigured) +{ + // XPS 渲染慢修复引入批量更新去抖:构造后批量刷新 timer 必须为 100ms 单次触发且未激活 + EXPECT_FALSE(m_tester->m_batchUpdateTimer->isActive()); + EXPECT_TRUE(m_tester->m_batchUpdateTimer->isSingleShot()); + EXPECT_EQ(m_tester->m_batchUpdateTimer->interval(), 100); +} + +// PMS: https://pms.uniontech.com/bug-view-343541.html commit: a3cb6cff +TEST_F(TestSideBarImageViewModel, BUG343541_ctor_nullSheetNoCrash) +{ + // 构造入参 sheet 允许为空(内部日志有指针守卫),不得崩溃 + SideBarImageViewModel model(nullptr); + EXPECT_TRUE(model.m_pagelst.isEmpty()); + EXPECT_EQ(model.rowCount(), 0); +} + +// PMS: https://pms.uniontech.com/bug-view-343541.html commit: a3cb6cff +TEST_F(TestSideBarImageViewModel, BUG343541_handleRenderThumbnail_batchDebounce) +{ + // 缩略图渲染去抖:连续渲染仅启动一次批量刷新 timer, + // timer 触发后发出 dataChanged 且待更新集合清空 + m_tester->initModelLst(QList() << ImagePageInfo_t(0) << ImagePageInfo_t(1), true); + m_tester->handleRenderThumbnail(0, QPixmap(10, 10), QVector() << QRectF(0, 0, 10, 10)); + m_tester->handleRenderThumbnail(1, QPixmap(10, 10), QVector() << QRectF(0, 0, 10, 10)); + EXPECT_TRUE(m_tester->m_pendingUpdatePages.contains(0)); + EXPECT_TRUE(m_tester->m_pendingUpdatePages.contains(1)); + EXPECT_TRUE(m_tester->m_batchUpdateTimer->isActive()); + + QSignalSpy spy(m_tester, &SideBarImageViewModel::dataChanged); + QTest::qWait(200); // timer 100ms 单次触发 onBatchUpdateTimer + EXPECT_GE(spy.count(), 1); + EXPECT_TRUE(m_tester->m_pendingUpdatePages.isEmpty()); + EXPECT_FALSE(m_tester->m_batchUpdateTimer->isActive()); +} + +// PMS: https://pms.uniontech.com/bug-view-343541.html commit: a3cb6cff +TEST_F(TestSideBarImageViewModel, BUG343541_onBatchUpdateTimer_emptyPendingNoCrash) +{ + // 空待更新集合直接批量刷新:提前返回,不崩溃且不发出 dataChanged + QSignalSpy spy(m_tester, &SideBarImageViewModel::dataChanged); + m_tester->onBatchUpdateTimer(); + EXPECT_EQ(spy.count(), 0); + EXPECT_TRUE(m_tester->m_pendingUpdatePages.isEmpty()); +} + +// PMS: https://pms.uniontech.com/bug-view-343541.html commit: a3cb6cff +TEST_F(TestSideBarImageViewModel, BUG343541_onBatchUpdateTimer_emitsDataChangedAndClears) +{ + // 批量刷新:待更新页映射为模型索引后按行序发出 dataChanged,集合清空 + m_tester->initModelLst(QList() << ImagePageInfo_t(2) << ImagePageInfo_t(0), true); + m_tester->m_pendingUpdatePages.insert(0); + m_tester->m_pendingUpdatePages.insert(2); + QSignalSpy spy(m_tester, &SideBarImageViewModel::dataChanged); + m_tester->onBatchUpdateTimer(); + EXPECT_GE(spy.count(), 2); + EXPECT_TRUE(m_tester->m_pendingUpdatePages.isEmpty()); +} + +// PMS: https://pms.uniontech.com/bug-view-335473.html commit: b8625f70 +TEST_F(TestSideBarImageViewModel, BUG335473_findItemForAnno_boundaryLookup) +{ + // 注释拖动崩溃修复(移除不安全的 QObject* 日志):注释查找必须返回边界值不崩溃 + // 列表内空注释命中索引 0;不存在的注释返回 -1;空列表返回 -1 + ImagePageInfo_t info; + info.pageIndex = 0; + info.annotation = nullptr; + m_tester->initModelLst(QList() << info, true); + EXPECT_EQ(m_tester->findItemForAnno(nullptr), 0); + + DPdfTextAnnot dAnnot; + deepin_reader::PDFAnnotation annot(&dAnnot); + EXPECT_EQ(m_tester->findItemForAnno(&annot), -1); + + m_tester->resetData(); + EXPECT_EQ(m_tester->findItemForAnno(nullptr), -1); +} + +// PMS: https://pms.uniontech.com/bug-view-335473.html commit: b8625f70 +TEST_F(TestSideBarImageViewModel, BUG335473_removeItemForAnno_idempotentNoCrash) +{ + // 注释拖动崩溃修复:移除存在的注释生效,重复移除(索引 -1 守卫)不崩溃 + DPdfTextAnnot dAnnot; + deepin_reader::PDFAnnotation annot(&dAnnot); + ImagePageInfo_t info; + info.pageIndex = 0; + info.annotation = &annot; + m_tester->initModelLst(QList() << info, true); + EXPECT_EQ(m_tester->m_pagelst.count(), 1); + + m_tester->removeItemForAnno(&annot); + EXPECT_TRUE(m_tester->m_pagelst.isEmpty()); + + m_tester->removeItemForAnno(&annot); + EXPECT_TRUE(m_tester->m_pagelst.isEmpty()); + + m_tester->removeItemForAnno(nullptr); + EXPECT_TRUE(m_tester->m_pagelst.isEmpty()); +} + +// PMS: https://pms.uniontech.com/bug-view-335473.html commit: b8625f70 +TEST_F(TestSideBarImageViewModel, BUG335473_removePageIndex_idempotentNoCrash) +{ + // 页码移除:存在的页移除生效,不存在的页仅告警,重复移除不崩溃 + m_tester->initModelLst(QList() << ImagePageInfo_t(0) << ImagePageInfo_t(1), true); + EXPECT_EQ(m_tester->m_pagelst.count(), 2); + + m_tester->removePageIndex(0); + EXPECT_EQ(m_tester->m_pagelst.count(), 1); + + m_tester->removePageIndex(99); + EXPECT_EQ(m_tester->m_pagelst.count(), 1); + + m_tester->removePageIndex(1); + EXPECT_TRUE(m_tester->m_pagelst.isEmpty()); +} + +// PMS: https://pms.uniontech.com/bug-view-335473.html commit: b8625f70 +TEST_F(TestSideBarImageViewModel, BUG335473_getModelIndexImageInfo_boundaryGuard) +{ + // 模型索引信息获取:有效索引填充数据,越界索引守卫不修改不崩溃 + m_tester->initModelLst(QList() << ImagePageInfo_t(3), true); + + ImagePageInfo_t info; + info.pageIndex = -1; + m_tester->getModelIndexImageInfo(0, info); + EXPECT_EQ(info.pageIndex, 3); + + ImagePageInfo_t untouched; + untouched.pageIndex = 42; + m_tester->getModelIndexImageInfo(-1, untouched); + EXPECT_EQ(untouched.pageIndex, 42); + + m_tester->getModelIndexImageInfo(m_tester->m_pagelst.size(), untouched); + EXPECT_EQ(untouched.pageIndex, 42); +} diff --git a/tests/uiframe/ut_central.cpp b/tests/uiframe/ut_central.cpp index de047c1be..4f638eb01 100644 --- a/tests/uiframe/ut_central.cpp +++ b/tests/uiframe/ut_central.cpp @@ -649,3 +649,27 @@ TEST_F(TestCentral, UT_Central_closeRestoreTip_lambda_001) emit tip->sigCloseRestoreTip(); SUCCEED(); } + +/* ========== PMS 回归用例(sev1/2 bug 补强,批次1) ========== */ + +// PMS: https://pms.uniontech.com/bug-view-97761.html commit: f1be9fc9 +TEST_F(TestCentral, BUG97761_docPage_lazyCreationStable) +{ + // wayland 选择界面外菜单点击无效修复涉及 docPage:懒创建必须幂等, + // 二次调用返回同实例(不得重复创建导致菜单信号失联) + CentralDocPage *first = m_tester->docPage(); + ASSERT_NE(first, nullptr); + EXPECT_EQ(m_tester->docPage(), first); + EXPECT_EQ(m_tester->docPage(), first); +} + +// PMS: https://pms.uniontech.com/bug-view-97761.html commit: f1be9fc9 +TEST_F(TestCentral, BUG97761_docPage_signalsWiredNoCrash) +{ + // docPage 创建后信号槽接线完成:发出页数变化信号不得崩溃 + CentralDocPage *page = m_tester->docPage(); + ASSERT_NE(page, nullptr); + emit page->sigSheetCountChanged(0); + emit page->sigSheetCountChanged(1); + SUCCEED(); +} diff --git a/tests/uiframe/ut_docsheet.cpp b/tests/uiframe/ut_docsheet.cpp index 5a9370a78..dbe721c0d 100644 --- a/tests/uiframe/ut_docsheet.cpp +++ b/tests/uiframe/ut_docsheet.cpp @@ -1780,3 +1780,74 @@ TEST_F(TestDocSheet, UT_DocSheet_onPrintRequested_singleArg_lambda) m_tester->m_fileType = Dr::PDF; SUCCEED(); } + +/* ========== PMS 回归用例(sev1/2 bug 补强,批次1) ========== */ + +// PMS: https://pms.uniontech.com/bug-view-348017.html commit: da254c72 +TEST_F(TestDocSheet, BUG348017_setAlive_registerUnregisterUuid) +{ + // 打印乱码/空白修复涉及 setAlive 状态机:反复激活/注销时 uuid 注册必须一致, + // 激活后可查询到 uuid,注销后 uuid 清空,重复切换不崩溃 + m_tester->setAlive(true); + EXPECT_FALSE(DocSheet::getUuid(m_tester).isNull()); + + m_tester->setAlive(false); + EXPECT_TRUE(DocSheet::getUuid(m_tester).isNull()); + + // 重复激活(setAlive(true) 内部先注销旧 uuid 再注册新 uuid) + m_tester->setAlive(true); + EXPECT_FALSE(DocSheet::getUuid(m_tester).isNull()); + + m_tester->setAlive(false); + EXPECT_TRUE(DocSheet::getUuid(m_tester).isNull()); +} + +// PMS: https://pms.uniontech.com/bug-view-106171.html commit: 17dbeb2d +TEST_F(TestDocSheet, BUG106171_scaleFactorList_containsDefaultAscending) +{ + // 打印预览空白修复涉及 scaleFactorList:列表非空、升序、含默认 1.0, + // 且所有因子不超过文档最大缩放比例 + QList list = m_tester->scaleFactorList(); + EXPECT_FALSE(list.isEmpty()); + EXPECT_TRUE(list.contains(1.0)); + for (int i = 1; i < list.size(); ++i) { + EXPECT_TRUE(list.at(i) > list.at(i - 1)) << "factor list must be ascending"; + } + EXPECT_TRUE(list.last() <= m_tester->maxScaleFactor() + 0.0001); + EXPECT_TRUE(list.first() > 0); +} + +// PMS: https://pms.uniontech.com/bug-view-164325.html commit: 894b5c56 +TEST_F(TestDocSheet, BUG164325_setSidebarVisible_notifyToggleNoCrash) +{ + // 大量注释 ctrl+s 保存崩溃修复涉及 setSidebarVisible:notify=true 双向显隐切换不得崩溃 + // (isFullScreen 需 3 级父链,测试环境无完整层级,同 UT_002 桩掉) + Stub s; + s.set(ADDR(DocSheet, isFullScreen), isFullScreen_stub); + m_tester->setSidebarVisible(true, true); + m_tester->setSidebarVisible(false, true); + m_tester->setSidebarVisible(true, true); + m_tester->setSidebarVisible(false, true); + SUCCEED(); +} + +// PMS: https://pms.uniontech.com/bug-view-164325.html commit: 894b5c56 +TEST_F(TestDocSheet, BUG164325_setSidebarVisible_animationToggleNoCrash) +{ + // notify=false 走 QPropertyAnimation 动画路径:重复触发动画不得崩溃 + // (Running 且目标一致时直接返回,目标变化时重启动画) + Stub s; + s.set(ADDR(DocSheet, isFullScreen), isFullScreen_stub); + m_tester->setSidebarVisible(true, false); + m_tester->setSidebarVisible(false, false); + m_tester->setSidebarVisible(false, false); + m_tester->setSidebarVisible(true, false); + SUCCEED(); +} + +// PMS: https://pms.uniontech.com/bug-view-106171.html commit: 17dbeb2d +TEST_F(TestDocSheet, BUG106171_removeAllAnnotation_emptySheetSucceeds) +{ + // 打印预览空白修复涉及 removeAllAnnotation:无注释文档移除仍返回成功且不崩溃 + EXPECT_TRUE(m_tester->removeAllAnnotation()); +}