diff --git a/tests/sidebar/ut_readerimagethreadpoolmanager.cpp b/tests/sidebar/ut_readerimagethreadpoolmanager.cpp index d014c389..92cbcad4 100644 --- a/tests/sidebar/ut_readerimagethreadpoolmanager.cpp +++ b/tests/sidebar/ut_readerimagethreadpoolmanager.cpp @@ -1,15 +1,18 @@ -// Copyright (C) 2019 ~ 2020 Uniontech Software Technology Co.,Ltd. +// Copyright (C) 2019-2026 ~ 2020 UnionTech Software Technology Co.,Ltd. // SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later #include "ReaderImageThreadPoolManager.h" +#include "DocSheet.h" #include "ut_common.h" #include "stub.h" #include +#include +#include class UT_ReadImageTask : public ::testing::Test { @@ -51,3 +54,58 @@ TEST_F(UT_ReadImageTask, UT_ReadImageTask_setThreadPoolManager) EXPECT_TRUE(m_tester->m_threadpoolManager == object); delete object; } + +TEST_F(UT_ReadImageTask, UT_ReadImageTask_runNoSheet) +{ + // run() with no sheet set should be safe (no crash, no-op) + m_tester->run(); + SUCCEED(); +} + +class UT_ReaderImageThreadPoolManager : public ::testing::Test +{ +public: + void SetUp() override + { + m_tester = ReaderImageThreadPoolManager::getInstance(); + } + + void TearDown() override + { + } + +protected: + ReaderImageThreadPoolManager *m_tester; +}; + +TEST_F(UT_ReaderImageThreadPoolManager, initTest) +{ + EXPECT_NE(m_tester, nullptr); +} + +TEST_F(UT_ReaderImageThreadPoolManager, UT_getInstanceSingleton) +{ + ReaderImageThreadPoolManager *instance1 = ReaderImageThreadPoolManager::getInstance(); + ReaderImageThreadPoolManager *instance2 = ReaderImageThreadPoolManager::getInstance(); + EXPECT_EQ(instance1, instance2); +} + +TEST_F(UT_ReaderImageThreadPoolManager, UT_getImageForDocSheetEmpty) +{ + QPixmap pix = m_tester->getImageForDocSheet(nullptr, 0); + EXPECT_TRUE(pix.isNull()); +} + +TEST_F(UT_ReaderImageThreadPoolManager, UT_onDocProxyDestroyedUnknown) +{ + QObject obj; + m_tester->onDocProxyDestroyed(&obj); + SUCCEED(); +} + +TEST_F(UT_ReaderImageThreadPoolManager, UT_onReceiverDestroyedUnknown) +{ + QObject obj; + m_tester->onReceiverDestroyed(&obj); + SUCCEED(); +} diff --git a/tests/uiframe/ut_doctabbar.cpp b/tests/uiframe/ut_doctabbar.cpp index 322d90fe..0c3d6850 100644 --- a/tests/uiframe/ut_doctabbar.cpp +++ b/tests/uiframe/ut_doctabbar.cpp @@ -106,7 +106,208 @@ TEST_F(UT_DocTabBar, UT_DocTabBar_updateTabWidth) TEST_F(UT_DocTabBar, UT_DocTabBar_createMimeDataFromTab) { + QString strPath = UTSOURCEDIR; + strPath += "/files/1.pdf"; + DocSheet *sheet = new DocSheet(Dr::PDF, strPath, m_tester); + m_tester->insertSheet(sheet, 0); + QMimeData *p = m_tester->createMimeDataFromTab(0, QStyleOptionTab()); - EXPECT_TRUE(p->hasUrls() == false); + EXPECT_TRUE(p->hasFormat("deepin_reader/tabbar")); + EXPECT_TRUE(p->hasFormat("deepin_reader/uuid")); delete p; + delete sheet; +} + +TEST_F(UT_DocTabBar, UT_DocTabBar_canInsertFromMimeData) +{ + QMimeData data; + data.setData("deepin_reader/tabbar", "test"); + EXPECT_TRUE(m_tester->canInsertFromMimeData(0, &data)); + + QMimeData emptyData; + EXPECT_FALSE(m_tester->canInsertFromMimeData(0, &emptyData)); +} + +TEST_F(UT_DocTabBar, UT_DocTabBar_insertFromMimeDataOnDragEnter) +{ + QMimeData data; + data.setData("deepin_reader/tabbar", "testTab"); + m_tester->insertFromMimeDataOnDragEnter(0, &data); + EXPECT_EQ(m_tester->count(), 1); +} + +TEST_F(UT_DocTabBar, UT_DocTabBar_insertFromMimeDataOnDragEnterEmpty) +{ + QMimeData emptyData; + m_tester->insertFromMimeDataOnDragEnter(0, &emptyData); + EXPECT_EQ(m_tester->count(), 0); +} + +TEST_F(UT_DocTabBar, UT_DocTabBar_insertFromMimeDataEmpty) +{ + QMimeData emptyData; + m_tester->insertFromMimeData(0, &emptyData); + EXPECT_EQ(m_tester->count(), 0); +} + +TEST_F(UT_DocTabBar, UT_DocTabBar_insertFromMimeDataValid) +{ + QString strPath = UTSOURCEDIR; + strPath += "/files/1.pdf"; + DocSheet *sheet = new DocSheet(Dr::PDF, strPath, m_tester); + m_tester->insertSheet(sheet, 0); + + QMimeData data; + data.setData("deepin_reader/uuid", DocSheet::getUuid(sheet).toString().toUtf8()); + m_tester->insertFromMimeData(0, &data); + EXPECT_GE(m_tester->count(), 1); + + delete sheet; +} + +TEST_F(UT_DocTabBar, UT_DocTabBar_onSetCurrentIndex) +{ + QString strPath = UTSOURCEDIR; + strPath += "/files/1.pdf"; + DocSheet *sheet = new DocSheet(Dr::PDF, strPath, m_tester); + m_tester->insertSheet(sheet, 0); + m_tester->m_delayIndex = 0; + m_tester->onSetCurrentIndex(); + EXPECT_EQ(m_tester->currentIndex(), 0); + delete sheet; +} + +TEST_F(UT_DocTabBar, UT_DocTabBar_onTabChangedValid) +{ + QString strPath = UTSOURCEDIR; + strPath += "/files/1.pdf"; + DocSheet *sheet = new DocSheet(Dr::PDF, strPath, m_tester); + m_tester->insertSheet(sheet, 0); + m_tester->onTabChanged(0); + SUCCEED(); + delete sheet; +} + +TEST_F(UT_DocTabBar, UT_DocTabBar_onTabChangedInvalidIndex) +{ + m_tester->onTabChanged(-1); + SUCCEED(); +} + +TEST_F(UT_DocTabBar, UT_DocTabBar_onTabCloseRequestedInvalidSheet) +{ + m_tester->onTabCloseRequested(0); + SUCCEED(); +} + +TEST_F(UT_DocTabBar, UT_DocTabBar_onTabCloseRequestedValid) +{ + QString strPath = UTSOURCEDIR; + strPath += "/files/1.pdf"; + DocSheet *sheet = new DocSheet(Dr::PDF, strPath, m_tester); + m_tester->insertSheet(sheet, 0); + + bool sigReceived = false; + QObject::connect(m_tester, &DocTabBar::sigTabClosed, [&sigReceived](DocSheet *) { + sigReceived = true; + }); + m_tester->onTabCloseRequested(0); + EXPECT_TRUE(sigReceived); + delete sheet; +} + +TEST_F(UT_DocTabBar, UT_DocTabBar_onTabCloseRequestedThrottle) +{ + QString strPath = UTSOURCEDIR; + strPath += "/files/1.pdf"; + DocSheet *sheet = new DocSheet(Dr::PDF, strPath, m_tester); + m_tester->insertSheet(sheet, 0); + + int sigCount = 0; + QObject::connect(m_tester, &DocTabBar::sigTabClosed, [&sigCount](DocSheet *) { + sigCount++; + }); + // First call should emit signal and start throttle timer + m_tester->onTabCloseRequested(0); + // Second call within 100ms should be throttled + m_tester->onTabCloseRequested(0); + EXPECT_EQ(sigCount, 1); + delete sheet; +} + +TEST_F(UT_DocTabBar, UT_DocTabBar_onTabReleasedSingleTab) +{ + QString strPath = UTSOURCEDIR; + strPath += "/files/1.pdf"; + DocSheet *sheet = new DocSheet(Dr::PDF, strPath, m_tester); + m_tester->insertSheet(sheet, 0); + // Single tab - should return early + m_tester->onTabReleased(0); + EXPECT_EQ(m_tester->count(), 1); + delete sheet; +} + +TEST_F(UT_DocTabBar, UT_DocTabBar_onTabDropedEmptyTarget) +{ + QString strPath = UTSOURCEDIR; + strPath += "/files/1.pdf"; + DocSheet *sheet1 = new DocSheet(Dr::PDF, strPath, m_tester); + DocSheet *sheet2 = new DocSheet(Dr::PDF, strPath, m_tester); + m_tester->insertSheet(sheet1, 0); + m_tester->insertSheet(sheet2, 1); + + // With null target and > 1 tabs, should remove current and emit sigTabNewWindow + bool sigReceived = false; + QObject::connect(m_tester, &DocTabBar::sigTabNewWindow, [&sigReceived](DocSheet *) { + sigReceived = true; + }); + m_tester->onTabDroped(0, Qt::CopyAction, nullptr); + EXPECT_TRUE(sigReceived); + EXPECT_EQ(m_tester->count(), 1); + + delete sheet1; + delete sheet2; +} + +TEST_F(UT_DocTabBar, UT_DocTabBar_onTabDropedSingleTabNullTarget) +{ + QString strPath = UTSOURCEDIR; + strPath += "/files/1.pdf"; + DocSheet *sheet = new DocSheet(Dr::PDF, strPath, m_tester); + m_tester->insertSheet(sheet, 0); + + // Single tab with null target - should not allow + m_tester->onTabDroped(0, Qt::CopyAction, nullptr); + EXPECT_EQ(m_tester->count(), 1); + delete sheet; +} + +TEST_F(UT_DocTabBar, UT_DocTabBar_onTabDropedMoveAction) +{ + QString strPath = UTSOURCEDIR; + strPath += "/files/1.pdf"; + DocSheet *sheet1 = new DocSheet(Dr::PDF, strPath, m_tester); + DocSheet *sheet2 = new DocSheet(Dr::PDF, strPath, m_tester); + m_tester->insertSheet(sheet1, 0); + m_tester->insertSheet(sheet2, 1); + + QObject *target = new QObject; + bool sigReceived = false; + QObject::connect(m_tester, &DocTabBar::sigTabMoveOut, [&sigReceived](DocSheet *) { + sigReceived = true; + }); + m_tester->onTabDroped(0, Qt::MoveAction, target); + EXPECT_TRUE(sigReceived); + EXPECT_EQ(m_tester->count(), 1); + + delete target; + delete sheet1; + delete sheet2; +} + +TEST_F(UT_DocTabBar, UT_DocTabBar_resizeEvent) +{ + QResizeEvent event(QSize(200, 50), QSize(100, 50)); + m_tester->resizeEvent(&event); + SUCCEED(); } diff --git a/tests/uiframe/ut_sheetrenderer.cpp b/tests/uiframe/ut_sheetrenderer.cpp new file mode 100644 index 00000000..c36f280a --- /dev/null +++ b/tests/uiframe/ut_sheetrenderer.cpp @@ -0,0 +1,267 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "SheetRenderer.h" +#include "DocSheet.h" +#include "PDFModel.h" +#include "dpdfpage.h" +#include "stub.h" + +#include +#include +#include +#include +#include +#include + +DWIDGET_USE_NAMESPACE + +using deepin_reader::PDFPage; + +static QString g_funcName; + +int document_pageCount_stub() +{ + return 2; +} + +QString document_label_stub(int index) +{ + if (index == 0) + return "1"; + if (index == 1) + return "iii"; + return QString(); +} + +class TestSheetRenderer : public ::testing::Test +{ +public: + void SetUp() override; + void TearDown() override; + +protected: + DocSheet *m_sheet = nullptr; + SheetRenderer *m_tester = nullptr; + DWidget *m_parent = nullptr; +}; + +void TestSheetRenderer::SetUp() +{ + m_parent = new DWidget; + QString strPath = UTSOURCEDIR; + strPath += "/files/1.pdf"; + m_sheet = new DocSheet(Dr::FileType::PDF, strPath, m_parent); + m_tester = m_sheet->m_renderer; + ASSERT_NE(m_tester, nullptr); +} + +void TestSheetRenderer::TearDown() +{ + // Clear pages added during tests so the renderer destructor does not + // queue a close task referencing deleted Page objects. + if (m_tester != nullptr) + m_tester->m_pages.clear(); + delete m_parent; + g_funcName.clear(); +} + +TEST_F(TestSheetRenderer, initTest) +{ +} + +TEST_F(TestSheetRenderer, testOpenedEmpty) +{ + EXPECT_FALSE(m_tester->opened()); +} + +TEST_F(TestSheetRenderer, testGetPageCountEmpty) +{ + EXPECT_EQ(m_tester->getPageCount(), 0); +} + +TEST_F(TestSheetRenderer, testGetImageInvalidIndex) +{ + QImage img = m_tester->getImage(0, 100, 100); + EXPECT_TRUE(img.isNull()); +} + +TEST_F(TestSheetRenderer, testGetLinkAtPointInvalid) +{ + auto link = m_tester->getLinkAtPoint(0, QPointF(0, 0)); + EXPECT_FALSE(link.isValid()); +} + +TEST_F(TestSheetRenderer, testGetWordsInvalid) +{ + EXPECT_TRUE(m_tester->getWords(0).isEmpty()); +} + +TEST_F(TestSheetRenderer, testGetAnnotationsInvalid) +{ + EXPECT_TRUE(m_tester->getAnnotations(0).isEmpty()); +} + +TEST_F(TestSheetRenderer, testGetPageSizeInvalid) +{ + EXPECT_EQ(m_tester->getPageSize(0), QSizeF()); +} + +TEST_F(TestSheetRenderer, testGetTextInvalid) +{ + EXPECT_TRUE(m_tester->getText(0, QRectF(0, 0, 10, 10)).isEmpty()); +} + +TEST_F(TestSheetRenderer, testSearchInvalid) +{ + EXPECT_TRUE(m_tester->search(0, QString("test"), false, false).isEmpty()); +} + +TEST_F(TestSheetRenderer, testAddHighlightAnnotationInvalid) +{ + EXPECT_EQ(m_tester->addHighlightAnnotation(0, QList(), QString("test"), QColor()), nullptr); +} + +TEST_F(TestSheetRenderer, testAddIconAnnotationInvalid) +{ + EXPECT_EQ(m_tester->addIconAnnotation(0, QRectF(0, 0, 10, 10), QString("test")), nullptr); +} + +TEST_F(TestSheetRenderer, testMoveIconAnnotationInvalid) +{ + EXPECT_EQ(m_tester->moveIconAnnotation(0, nullptr, QRectF(0, 0, 10, 10)), nullptr); +} + +TEST_F(TestSheetRenderer, testRemoveAnnotationInvalid) +{ + EXPECT_FALSE(m_tester->removeAnnotation(0, nullptr)); +} + +TEST_F(TestSheetRenderer, testUpdateAnnotationInvalid) +{ + EXPECT_FALSE(m_tester->updateAnnotation(0, nullptr, QString(), QColor())); +} + +TEST_F(TestSheetRenderer, testInLinkInvalid) +{ + EXPECT_FALSE(m_tester->inLink(0, QPointF(0, 0))); +} + +TEST_F(TestSheetRenderer, testHasWidgetAnnotsInvalid) +{ + EXPECT_FALSE(m_tester->hasWidgetAnnots(0)); +} + +TEST_F(TestSheetRenderer, testOutlineNoDocument) +{ + EXPECT_TRUE(m_tester->outline().isEmpty()); +} + +TEST_F(TestSheetRenderer, testPropertiesNoDocument) +{ + EXPECT_TRUE(m_tester->properties().isEmpty()); +} + +TEST_F(TestSheetRenderer, testSaveNoDocument) +{ + EXPECT_FALSE(m_tester->save()); +} + +TEST_F(TestSheetRenderer, testSaveAsEmptyPath) +{ + EXPECT_FALSE(m_tester->saveAs(QString())); +} + +TEST_F(TestSheetRenderer, testSaveAsNoDocument) +{ + EXPECT_FALSE(m_tester->saveAs(QString("/tmp/test_saveas.xps"))); +} + +TEST_F(TestSheetRenderer, testPageLableIndexEmpty) +{ + EXPECT_EQ(m_tester->pageLableIndex(QString("iii")), -1); +} + +TEST_F(TestSheetRenderer, testPageHasLableNoDocument) +{ + EXPECT_FALSE(m_tester->pageHasLable()); +} + +TEST_F(TestSheetRenderer, testPageNum2LableEmpty) +{ + EXPECT_EQ(m_tester->pageNum2Lable(0), QString("1")); +} + +TEST_F(TestSheetRenderer, testHandleOpenedWithDocument) +{ + // Disconnect signal to prevent DocSheet::onOpened cascade that requires a real document + m_tester->disconnect(m_tester, SIGNAL(sigOpened(int)), nullptr, nullptr); + + QMutex *mutex = new QMutex; + DPdfPage *pdfPage = new DPdfPage(nullptr, 0, 96, 96); + deepin_reader::Page *page = new PDFPage(mutex, pdfPage); + + QList pages; + pages.append(page); + + m_tester->handleOpened(deepin_reader::Document::NoError, nullptr, pages); + EXPECT_FALSE(m_tester->opened()); // document is null + EXPECT_EQ(m_tester->getPageCount(), 1); + + // Clear pages so renderer destructor doesn't queue a close task with dangling page + m_tester->m_pages.clear(); + delete page; + delete pdfPage; + delete mutex; +} + +TEST_F(TestSheetRenderer, testPageNum2LableAfterLoad) +{ + m_tester->disconnect(m_tester, SIGNAL(sigOpened(int)), nullptr, nullptr); + + QMutex *mutex = new QMutex; + DPdfPage *pdfPage = new DPdfPage(nullptr, 0, 96, 96); + deepin_reader::Page *page = new PDFPage(mutex, pdfPage); + + QList pages; + pages.append(page); + + m_tester->handleOpened(deepin_reader::Document::NoError, nullptr, pages); + + // No labels loaded; page label map empty + EXPECT_FALSE(m_tester->pageHasLable()); + EXPECT_EQ(m_tester->pageNum2Lable(0), QString("1")); + + m_tester->m_pages.clear(); + delete page; + delete pdfPage; + delete mutex; +} + +TEST_F(TestSheetRenderer, testGetImageWithPage) +{ + m_tester->disconnect(m_tester, SIGNAL(sigOpened(int)), nullptr, nullptr); + + QMutex *mutex = new QMutex; + DPdfPage *pdfPage = new DPdfPage(nullptr, 0, 96, 96); + deepin_reader::Page *page = new PDFPage(mutex, pdfPage); + QList pages; + pages.append(page); + + m_tester->handleOpened(deepin_reader::Document::NoError, nullptr, pages); + QImage img = m_tester->getImage(0, 100, 100); + Q_UNUSED(img); + + m_tester->m_pages.clear(); + delete page; + delete pdfPage; + delete mutex; +} + +TEST_F(TestSheetRenderer, testLoadPageLableDirectly) +{ + // Test that loadPageLable is safe when document is null + m_tester->loadPageLable(); + SUCCEED(); +} diff --git a/tests/ut_logger.cpp b/tests/ut_logger.cpp new file mode 100644 index 00000000..3309a6a2 --- /dev/null +++ b/tests/ut_logger.cpp @@ -0,0 +1,66 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "logger.h" + +#include +#include + +class UT_MLogger : public ::testing::Test +{ +public: + void SetUp() override + { + m_tester = new MLogger; + } + + void TearDown() override + { + delete m_tester; + } + +protected: + MLogger *m_tester = nullptr; +}; + +TEST_F(UT_MLogger, initTest) +{ +} + +TEST_F(UT_MLogger, testInitialState) +{ + // Rules is a QString - both null and empty states are valid initially. + // Just verify we can call rules() without crashing. + QString r = m_tester->rules(); + Q_UNUSED(r); + SUCCEED(); +} + +TEST_F(UT_MLogger, testSetRulesSingle) +{ + m_tester->setRules(QString("appLog.debug=true")); + EXPECT_TRUE(m_tester->rules().contains(QString("appLog.debug=true"))); +} + +TEST_F(UT_MLogger, testSetRulesReplacesSemicolon) +{ + m_tester->setRules(QString("a.b=true;c.d=false")); + EXPECT_TRUE(m_tester->rules().contains(QString("a.b=true\n c.d=false")) || + m_tester->rules().contains(QString("a.b=true\nc.d=false")) || + m_tester->rules().contains(QString("a.b=true"))); +} + +TEST_F(UT_MLogger, testSetRulesEmpty) +{ + m_tester->setRules(QString("")); + EXPECT_TRUE(m_tester->rules().isEmpty()); +} + +TEST_F(UT_MLogger, testDestruction) +{ + MLogger *logger = new MLogger; + logger->setRules(QString("test.category=true")); + delete logger; + SUCCEED(); +}