From a7ec9431083cf5dde8a86e59b9af203f3d35073d Mon Sep 17 00:00:00 2001 From: Xisco Fauli Date: Wed, 20 Nov 2024 17:32:59 +0100 Subject: [PATCH] CppunitTest_sw_tiledrendering: factor out common code Change-Id: Icc8958f9f86844149bc66d12d60111bdcac0f8ca Reviewed-on: https://gerrit.libreoffice.org/c/core/+/176867 Tested-by: Jenkins Reviewed-by: Xisco Fauli --- sw/qa/extras/tiledrendering/tiledrendering.cxx | 478 +------------------ sw/qa/extras/tiledrendering/tiledrendering2.cxx | 163 +------ .../tiledrendering/tiledrenderingmodeltestbase.cxx | 510 +++++++++++++++++++++ 3 files changed, 526 insertions(+), 625 deletions(-) create mode 100644 sw/qa/extras/tiledrendering/tiledrenderingmodeltestbase.cxx diff --git a/sw/qa/extras/tiledrendering/tiledrendering.cxx b/sw/qa/extras/tiledrendering/tiledrendering.cxx index a5fb0f88e03a..1846c1b535a2 100644 --- a/sw/qa/extras/tiledrendering/tiledrendering.cxx +++ b/sw/qa/extras/tiledrendering/tiledrendering.cxx @@ -7,15 +7,11 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include +#include "tiledrenderingmodeltestbase.cxx" #include #include -#include - -#include -#include #include #include #include @@ -24,11 +20,8 @@ #include #include -#include #include #include -#include -#include #include #include #include @@ -44,7 +37,6 @@ #include #include #include -#include #include #include #include @@ -53,11 +45,9 @@ #include #include #include -#include #include #include -#include #include #include #include @@ -66,14 +56,10 @@ #include #include #include -#include -#include #include #include #include #include -#include -#include static std::ostream& operator<<(std::ostream& os, ViewShellId id) { @@ -81,240 +67,6 @@ static std::ostream& operator<<(std::ostream& os, ViewShellId id) return os; } -/// Testsuite for the SwXTextDocument methods implementing the vcl::ITiledRenderable interface. -class SwTiledRenderingTest : public SwModelTestBase -{ -public: - SwTiledRenderingTest(); - virtual void setUp() override; - virtual void tearDown() override; - -protected: - SwXTextDocument* createDoc(const char* pName = nullptr); - void setupLibreOfficeKitViewCallback(SfxViewShell* pViewShell); - static void callback(int nType, const char* pPayload, void* pData); - void callbackImpl(int nType, const char* pPayload); - // First invalidation. - tools::Rectangle m_aInvalidation; - /// Union of all invalidations. - tools::Rectangle m_aInvalidations; - Size m_aDocumentSize; - OString m_aTextSelection; - bool m_bFound; - std::vector m_aSearchResultSelection; - std::vector m_aSearchResultPart; - int m_nSelectionBeforeSearchResult; - int m_nSelectionAfterSearchResult; - int m_nInvalidations; - int m_nRedlineTableSizeChanged; - int m_nRedlineTableEntryModified; - int m_nTrackedChangeIndex; - bool m_bFullInvalidateSeen; - OString m_sHyperlinkText; - OString m_sHyperlinkLink; - OString m_aFormFieldButton; - OString m_aContentControl; - OString m_ShapeSelection; - struct - { - std::string text; - std::string rect; - } m_aTooltip; - TestLokCallbackWrapper m_callbackWrapper; -}; - -SwTiledRenderingTest::SwTiledRenderingTest() - : SwModelTestBase(u"/sw/qa/extras/tiledrendering/data/"_ustr), - m_bFound(true), - m_nSelectionBeforeSearchResult(0), - m_nSelectionAfterSearchResult(0), - m_nInvalidations(0), - m_nRedlineTableSizeChanged(0), - m_nRedlineTableEntryModified(0), - m_nTrackedChangeIndex(-1), - m_bFullInvalidateSeen(false), - m_callbackWrapper(&callback, this) -{ -} - -void SwTiledRenderingTest::setUp() -{ - SwModelTestBase::setUp(); - - SwGlobals::ensure(); - SW_MOD()->ClearRedlineAuthors(); - - comphelper::LibreOfficeKit::setActive(true); -} - -void SwTiledRenderingTest::tearDown() -{ - if (mxComponent.is()) - { - SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell(); - if (pWrtShell) - { - pWrtShell->GetSfxViewShell()->setLibreOfficeKitViewCallback(nullptr); - } - mxComponent->dispose(); - mxComponent.clear(); - } - m_callbackWrapper.clear(); - comphelper::LibreOfficeKit::setActive(false); - - test::BootstrapFixture::tearDown(); -} - -SwXTextDocument* SwTiledRenderingTest::createDoc(const char* pName) -{ - if (!pName) - createSwDoc(); - else - createSwDoc(pName); - - SwXTextDocument* pTextDocument = getSwTextDoc(); - pTextDocument->initializeForTiledRendering(uno::Sequence()); - return pTextDocument; -} - -void SwTiledRenderingTest::setupLibreOfficeKitViewCallback(SfxViewShell* pViewShell) -{ - pViewShell->setLibreOfficeKitViewCallback(&m_callbackWrapper); - m_callbackWrapper.setLOKViewId(SfxLokHelper::getView(pViewShell)); -} - -void SwTiledRenderingTest::callback(int nType, const char* pPayload, void* pData) -{ - static_cast(pData)->callbackImpl(nType, pPayload); -} - -void SwTiledRenderingTest::callbackImpl(int nType, const char* pPayload) -{ - OString aPayload(pPayload); - switch (nType) - { - case LOK_CALLBACK_INVALIDATE_TILES: - { - tools::Rectangle aInvalidation; - uno::Sequence aSeq = comphelper::string::convertCommaSeparated(OUString::createFromAscii(pPayload)); - if (std::string_view("EMPTY") == pPayload) - { - m_bFullInvalidateSeen = true; - return; - } - - CPPUNIT_ASSERT(aSeq.getLength() == 4 || aSeq.getLength() == 5); - aInvalidation.SetLeft(aSeq[0].toInt32()); - aInvalidation.SetTop(aSeq[1].toInt32()); - aInvalidation.setWidth(aSeq[2].toInt32()); - aInvalidation.setHeight(aSeq[3].toInt32()); - if (m_aInvalidation.IsEmpty()) - { - m_aInvalidation = aInvalidation; - } - m_aInvalidations.Union(aInvalidation); - ++m_nInvalidations; - } - break; - case LOK_CALLBACK_DOCUMENT_SIZE_CHANGED: - { - uno::Sequence aSeq = comphelper::string::convertCommaSeparated(OUString::createFromAscii(pPayload)); - CPPUNIT_ASSERT_EQUAL(static_cast(2), aSeq.getLength()); - m_aDocumentSize.setWidth(aSeq[0].toInt32()); - m_aDocumentSize.setHeight(aSeq[1].toInt32()); - } - break; - case LOK_CALLBACK_TEXT_SELECTION: - { - m_aTextSelection = pPayload; - if (m_aSearchResultSelection.empty()) - ++m_nSelectionBeforeSearchResult; - else - ++m_nSelectionAfterSearchResult; - } - break; - case LOK_CALLBACK_SEARCH_NOT_FOUND: - { - m_bFound = false; - } - break; - case LOK_CALLBACK_SEARCH_RESULT_SELECTION: - { - m_aSearchResultSelection.clear(); - boost::property_tree::ptree aTree; - std::stringstream aStream(pPayload); - boost::property_tree::read_json(aStream, aTree); - for (const boost::property_tree::ptree::value_type& rValue : aTree.get_child("searchResultSelection")) - { - m_aSearchResultSelection.emplace_back(rValue.second.get("rectangles").c_str()); - m_aSearchResultPart.push_back(std::atoi(rValue.second.get("part").c_str())); - } - } - break; - case LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED: - { - ++m_nRedlineTableSizeChanged; - } - break; - case LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED: - { - ++m_nRedlineTableEntryModified; - } - break; - case LOK_CALLBACK_STATE_CHANGED: - { - OString aTrackedChangeIndexPrefix(".uno:TrackedChangeIndex="_ostr); - if (aPayload.startsWith(aTrackedChangeIndexPrefix)) - { - OString sIndex = aPayload.copy(aTrackedChangeIndexPrefix.getLength()); - if (sIndex.isEmpty()) - m_nTrackedChangeIndex = -1; - else - m_nTrackedChangeIndex = sIndex.toInt32(); - } - } - break; - case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR: - { - if (comphelper::LibreOfficeKit::isViewIdForVisCursorInvalidation()) - { - boost::property_tree::ptree aTree; - std::stringstream aStream(pPayload); - boost::property_tree::read_json(aStream, aTree); - boost::property_tree::ptree &aChild = aTree.get_child("hyperlink"); - m_sHyperlinkText = OString(aChild.get("text", "")); - m_sHyperlinkLink = OString(aChild.get("link", "")); - } - } - break; - case LOK_CALLBACK_FORM_FIELD_BUTTON: - { - m_aFormFieldButton = OString(pPayload); - } - break; - case LOK_CALLBACK_CONTENT_CONTROL: - { - m_aContentControl = OString(pPayload); - } - break; - case LOK_CALLBACK_GRAPHIC_SELECTION: - { - m_ShapeSelection = OString(pPayload); - } - break; - case LOK_CALLBACK_TOOLTIP: - { - std::stringstream aStream(pPayload); - boost::property_tree::ptree aTree; - boost::property_tree::read_json(aStream, aTree); - m_aTooltip.text = aTree.get_child("text").get_value(); - m_aTooltip.rect = aTree.get_child("rectangle").get_value(); - } - break; - } - -} - CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testRegisterCallback) { createDoc("dummy.fodt"); @@ -741,234 +493,6 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPartHash) } } -namespace { - - /// A view callback tracks callbacks invoked on one specific view. - class ViewCallback final - { - SfxViewShell* mpViewShell; - int mnView; - public: - bool m_bOwnCursorInvalidated; - int m_nOwnCursorInvalidatedBy; - bool m_bOwnCursorAtOrigin; - tools::Rectangle m_aOwnCursor; - bool m_bViewCursorInvalidated; - tools::Rectangle m_aViewCursor; - bool m_bOwnSelectionSet; - bool m_bViewSelectionSet; - OString m_aViewSelection; - OString m_aViewRenderState; - bool m_bTilesInvalidated; - bool m_bViewCursorVisible; - bool m_bGraphicViewSelection; - bool m_bGraphicSelection; - bool m_bViewLock; - OString m_aDocColor; - /// Set if any callback was invoked. - bool m_bCalled; - /// Redline table size changed payload - boost::property_tree::ptree m_aRedlineTableChanged; - /// Redline table modified payload - boost::property_tree::ptree m_aRedlineTableModified; - /// Post-it / annotation payload. - boost::property_tree::ptree m_aComment; - TestLokCallbackWrapper m_callbackWrapper; - - ViewCallback(SfxViewShell* pViewShell = nullptr, std::function const & rBeforeInstallFunc = {}) - : m_bOwnCursorInvalidated(false), - m_nOwnCursorInvalidatedBy(-1), - m_bOwnCursorAtOrigin(false), - m_bViewCursorInvalidated(false), - m_bOwnSelectionSet(false), - m_bViewSelectionSet(false), - m_bTilesInvalidated(false), - m_bViewCursorVisible(false), - m_bGraphicViewSelection(false), - m_bGraphicSelection(false), - m_bViewLock(false), - m_bCalled(false), - m_callbackWrapper(&callback, this) - { - // Because one call-site wants to set the bool fields up before the callback is installed - if (rBeforeInstallFunc) - rBeforeInstallFunc(*this); - - mpViewShell = pViewShell ? pViewShell : SfxViewShell::Current(); - mpViewShell->setLibreOfficeKitViewCallback(&m_callbackWrapper); - mnView = SfxLokHelper::getView(); - m_callbackWrapper.setLOKViewId( mnView ); - } - - ~ViewCallback() - { - SfxLokHelper::setView(mnView); - mpViewShell->setLibreOfficeKitViewCallback(nullptr); - } - - static void callback(int nType, const char* pPayload, void* pData) - { - static_cast(pData)->callbackImpl(nType, pPayload); - } - - void callbackImpl(int nType, const char* pPayload) - { - OString aPayload(pPayload); - m_bCalled = true; - switch (nType) - { - case LOK_CALLBACK_INVALIDATE_TILES: - { - m_bTilesInvalidated = true; - } - break; - case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR: - { - m_bOwnCursorInvalidated = true; - - OString sRect; - if(comphelper::LibreOfficeKit::isViewIdForVisCursorInvalidation()) - { - std::stringstream aStream(pPayload); - boost::property_tree::ptree aTree; - boost::property_tree::read_json(aStream, aTree); - sRect = OString(aTree.get_child("rectangle").get_value()); - m_nOwnCursorInvalidatedBy = aTree.get_child("viewId").get_value(); - } - else - sRect = aPayload; - uno::Sequence aSeq = comphelper::string::convertCommaSeparated(OUString::fromUtf8(sRect)); - if (std::string_view("EMPTY") == pPayload) - return; - CPPUNIT_ASSERT_EQUAL(static_cast(4), aSeq.getLength()); - m_aOwnCursor.SetLeft(aSeq[0].toInt32()); - m_aOwnCursor.SetTop(aSeq[1].toInt32()); - m_aOwnCursor.setWidth(aSeq[2].toInt32()); - m_aOwnCursor.setHeight(aSeq[3].toInt32()); - if (m_aOwnCursor.Left() == 0 && m_aOwnCursor.Top() == 0) - m_bOwnCursorAtOrigin = true; - } - break; - case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR: - { - m_bViewCursorInvalidated = true; - std::stringstream aStream(pPayload); - boost::property_tree::ptree aTree; - boost::property_tree::read_json(aStream, aTree); - OString aRect( aTree.get_child("rectangle").get_value() ); - - uno::Sequence aSeq = comphelper::string::convertCommaSeparated(OUString::fromUtf8(aRect)); - if (std::string_view("EMPTY") == pPayload) - return; - CPPUNIT_ASSERT_EQUAL(static_cast(4), aSeq.getLength()); - m_aViewCursor.SetLeft(aSeq[0].toInt32()); - m_aViewCursor.SetTop(aSeq[1].toInt32()); - m_aViewCursor.setWidth(aSeq[2].toInt32()); - m_aViewCursor.setHeight(aSeq[3].toInt32()); - } - break; - case LOK_CALLBACK_TEXT_SELECTION: - { - m_bOwnSelectionSet = true; - } - break; - case LOK_CALLBACK_TEXT_VIEW_SELECTION: - { - m_bViewSelectionSet = true; - m_aViewSelection = aPayload; - } - break; - case LOK_CALLBACK_VIEW_CURSOR_VISIBLE: - { - std::stringstream aStream(pPayload); - boost::property_tree::ptree aTree; - boost::property_tree::read_json(aStream, aTree); - m_bViewCursorVisible = aTree.get_child("visible").get_value() == "true"; - } - break; - case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION: - { - std::stringstream aStream(pPayload); - boost::property_tree::ptree aTree; - boost::property_tree::read_json(aStream, aTree); - m_bGraphicViewSelection = aTree.get_child("selection").get_value() != "EMPTY"; - } - break; - case LOK_CALLBACK_GRAPHIC_SELECTION: - { - m_bGraphicSelection = aPayload != "EMPTY"; - } - break; - case LOK_CALLBACK_VIEW_LOCK: - { - std::stringstream aStream(pPayload); - boost::property_tree::ptree aTree; - boost::property_tree::read_json(aStream, aTree); - m_bViewLock = aTree.get_child("rectangle").get_value() != "EMPTY"; - } - break; - case LOK_CALLBACK_VIEW_RENDER_STATE: - { - m_aViewRenderState = pPayload; - } - break; - case LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED: - { - m_aRedlineTableChanged.clear(); - std::stringstream aStream(pPayload); - boost::property_tree::read_json(aStream, m_aRedlineTableChanged); - m_aRedlineTableChanged = m_aRedlineTableChanged.get_child("redline"); - } - break; - case LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED: - { - m_aRedlineTableModified.clear(); - std::stringstream aStream(pPayload); - boost::property_tree::read_json(aStream, m_aRedlineTableModified); - m_aRedlineTableModified = m_aRedlineTableModified.get_child("redline"); - } - break; - case LOK_CALLBACK_COMMENT: - { - m_aComment.clear(); - std::stringstream aStream(pPayload); - boost::property_tree::read_json(aStream, m_aComment); - m_aComment = m_aComment.get_child("comment"); - } - break; - case LOK_CALLBACK_DOCUMENT_BACKGROUND_COLOR: - { - m_aDocColor = aPayload; - break; - } - } - } - }; - - class TestResultListener : public cppu::WeakImplHelper - { - public: - sal_uInt32 m_nDocRepair; - - TestResultListener() : m_nDocRepair(0) - { - } - - virtual void SAL_CALL dispatchFinished(const css::frame::DispatchResultEvent& rEvent) override - { - if (rEvent.State == frame::DispatchResultState::SUCCESS) - { - rEvent.Result >>= m_nDocRepair; - } - } - - virtual void SAL_CALL disposing(const css::lang::EventObject&) override - { - } - }; - -} - CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testMissingInvalidation) { // Create two views. diff --git a/sw/qa/extras/tiledrendering/tiledrendering2.cxx b/sw/qa/extras/tiledrendering/tiledrendering2.cxx index 37aded0e0a45..f628042a447d 100644 --- a/sw/qa/extras/tiledrendering/tiledrendering2.cxx +++ b/sw/qa/extras/tiledrendering/tiledrendering2.cxx @@ -7,159 +7,26 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include - -#include +#include "tiledrenderingmodeltestbase.cxx" #include -#include -#include #include #include #include #include #include -#include -#include #include -#include #include #include -#include #include #include -#include -#include #include -#include -#include #include namespace { -/// Writer tests with comphelper::LibreOfficeKit::isActive() enabled, part 2. -class SwTiledRenderingTest : public SwModelTestBase -{ -public: - SwTiledRenderingTest(); - virtual void setUp() override; - virtual void tearDown() override; - -protected: - SwXTextDocument* createDoc(const char* pName = nullptr); -}; - -SwTiledRenderingTest::SwTiledRenderingTest() - : SwModelTestBase(u"/sw/qa/extras/tiledrendering/data/"_ustr) -{ -} - -void SwTiledRenderingTest::setUp() -{ - SwModelTestBase::setUp(); - SwGlobals::ensure(); - SW_MOD()->ClearRedlineAuthors(); - comphelper::LibreOfficeKit::setActive(true); -} - -void SwTiledRenderingTest::tearDown() -{ - if (mxComponent.is()) - { - mxComponent->dispose(); - mxComponent.clear(); - } - comphelper::LibreOfficeKit::setActive(false); - test::BootstrapFixture::tearDown(); -} - -SwXTextDocument* SwTiledRenderingTest::createDoc(const char* pName) -{ - if (!pName) - createSwDoc(); - else - createSwDoc(pName); - SwXTextDocument* pTextDocument = getSwTextDoc(); - pTextDocument->initializeForTiledRendering(uno::Sequence()); - return pTextDocument; -} - -/// Test callback that works with comphelper::LibreOfficeKit::setAnyInputCallback(). -class AnyInputCallback final -{ -public: - static bool callback(void* /*pData*/) { return true; } - - AnyInputCallback() { comphelper::LibreOfficeKit::setAnyInputCallback(&callback, this); } - - ~AnyInputCallback() { comphelper::LibreOfficeKit::setAnyInputCallback(nullptr, nullptr); } -}; - -/// A view callback tracks callbacks invoked on one specific view. -class ViewCallback final -{ - SfxViewShell* mpViewShell; - int mnView; - -public: - std::vector m_aStateChanges; - tools::Rectangle m_aInvalidations; - bool m_bFullInvalidateSeen = false; - TestLokCallbackWrapper m_callbackWrapper; - - ViewCallback() - : m_callbackWrapper(&callback, this) - { - mpViewShell = SfxViewShell::Current(); - mpViewShell->setLibreOfficeKitViewCallback(&m_callbackWrapper); - mnView = SfxLokHelper::getView(); - m_callbackWrapper.setLOKViewId(mnView); - } - - ~ViewCallback() - { - SfxLokHelper::setView(mnView); - mpViewShell->setLibreOfficeKitViewCallback(nullptr); - } - - static void callback(int nType, const char* pPayload, void* pData) - { - static_cast(pData)->callbackImpl(nType, pPayload); - } - - void callbackImpl(int nType, const char* pPayload) - { - switch (nType) - { - case LOK_CALLBACK_INVALIDATE_TILES: - { - if (std::string_view("EMPTY") == pPayload) - { - m_bFullInvalidateSeen = true; - return; - } - uno::Sequence aSeq - = comphelper::string::convertCommaSeparated(OUString::fromUtf8(pPayload)); - CPPUNIT_ASSERT_EQUAL(static_cast(4), aSeq.getLength()); - tools::Rectangle aInvalidation; - aInvalidation.SetLeft(aSeq[0].toInt32()); - aInvalidation.SetTop(aSeq[1].toInt32()); - aInvalidation.setWidth(aSeq[2].toInt32()); - aInvalidation.setHeight(aSeq[3].toInt32()); - m_aInvalidations.Union(aInvalidation); - break; - } - case LOK_CALLBACK_STATE_CHANGED: - { - m_aStateChanges.push_back(pPayload); - break; - } - } - } -}; - CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testStatusBarPageNumber) { // Given a document with 2 pages, first view on page 1, second view on page 2: @@ -225,30 +92,30 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPasteInvalidateNumRules) // Given a document with 3 pages: first page is ~empty, then page break, then pages 2 & 3 have // bullets: createDoc("numrules.odt"); - ViewCallback aView; SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell(); + setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell()); pWrtShell->SttEndDoc(/*bStt=*/true); pWrtShell->Down(/*bSelect=*/false); pWrtShell->Insert(u"test"_ustr); pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/true, 4, /*bBasicCall=*/false); dispatchCommand(mxComponent, u".uno:Cut"_ustr, {}); - aView.m_aInvalidations = tools::Rectangle(); - aView.m_bFullInvalidateSeen = false; + m_aInvalidations = tools::Rectangle(); + m_bFullInvalidateSeen = false; // When pasting at the end of page 1: dispatchCommand(mxComponent, u".uno:PasteUnformatted"_ustr, {}); // Then make sure we only invalidate page 1, not page 2 or page 3: - CPPUNIT_ASSERT(!aView.m_bFullInvalidateSeen); + CPPUNIT_ASSERT(!m_bFullInvalidateSeen); SwRootFrame* pLayout = pWrtShell->GetLayout(); SwFrame* pPage1 = pLayout->GetLower(); - CPPUNIT_ASSERT(aView.m_aInvalidations.Overlaps(pPage1->getFrameArea().SVRect())); + CPPUNIT_ASSERT(m_aInvalidations.Overlaps(pPage1->getFrameArea().SVRect())); SwFrame* pPage2 = pPage1->GetNext(); // Without the accompanying fix in place, this test would have failed, we invalidated page 2 and // page 3 as well. - CPPUNIT_ASSERT(!aView.m_aInvalidations.Overlaps(pPage2->getFrameArea().SVRect())); + CPPUNIT_ASSERT(!m_aInvalidations.Overlaps(pPage2->getFrameArea().SVRect())); SwFrame* pPage3 = pPage2->GetNext(); - CPPUNIT_ASSERT(!aView.m_aInvalidations.Overlaps(pPage3->getFrameArea().SVRect())); + CPPUNIT_ASSERT(!m_aInvalidations.Overlaps(pPage3->getFrameArea().SVRect())); } CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPasteInvalidateNumRulesBullet) @@ -256,32 +123,32 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testPasteInvalidateNumRulesBullet) // Given a document with 3 pages: first page is ~empty, then page break, then pages 2 & 3 have // bullets: createDoc("numrules.odt"); - ViewCallback aView; SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell(); + setupLibreOfficeKitViewCallback(pWrtShell->GetSfxViewShell()); pWrtShell->SttEndDoc(/*bStt=*/true); pWrtShell->Down(/*bSelect=*/false); pWrtShell->Insert(u"test"_ustr); pWrtShell->Left(SwCursorSkipMode::Chars, /*bSelect=*/true, 4, /*bBasicCall=*/false); dispatchCommand(mxComponent, u".uno:Cut"_ustr, {}); dispatchCommand(mxComponent, u".uno:DefaultBullet"_ustr, {}); - aView.m_aInvalidations = tools::Rectangle(); - aView.m_bFullInvalidateSeen = false; + m_aInvalidations = tools::Rectangle(); + m_bFullInvalidateSeen = false; // When pasting at the end of page 1, in a paragraph that is a bullet (a list, but not a // numbering): dispatchCommand(mxComponent, u".uno:PasteUnformatted"_ustr, {}); // Then make sure we only invalidate page 1, not page 2 or page 3: - CPPUNIT_ASSERT(!aView.m_bFullInvalidateSeen); + CPPUNIT_ASSERT(!m_bFullInvalidateSeen); SwRootFrame* pLayout = pWrtShell->GetLayout(); SwFrame* pPage1 = pLayout->GetLower(); - CPPUNIT_ASSERT(aView.m_aInvalidations.Overlaps(pPage1->getFrameArea().SVRect())); + CPPUNIT_ASSERT(m_aInvalidations.Overlaps(pPage1->getFrameArea().SVRect())); SwFrame* pPage2 = pPage1->GetNext(); // Without the accompanying fix in place, this test would have failed, we invalidated page 2 and // page 3 as well. - CPPUNIT_ASSERT(!aView.m_aInvalidations.Overlaps(pPage2->getFrameArea().SVRect())); + CPPUNIT_ASSERT(!m_aInvalidations.Overlaps(pPage2->getFrameArea().SVRect())); SwFrame* pPage3 = pPage2->GetNext(); - CPPUNIT_ASSERT(!aView.m_aInvalidations.Overlaps(pPage3->getFrameArea().SVRect())); + CPPUNIT_ASSERT(!m_aInvalidations.Overlaps(pPage3->getFrameArea().SVRect())); } CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testAsyncLayout) diff --git a/sw/qa/extras/tiledrendering/tiledrenderingmodeltestbase.cxx b/sw/qa/extras/tiledrendering/tiledrenderingmodeltestbase.cxx new file mode 100644 index 000000000000..62d41e0a63b0 --- /dev/null +++ b/sw/qa/extras/tiledrendering/tiledrenderingmodeltestbase.cxx @@ -0,0 +1,510 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/// Testsuite for the SwXTextDocument methods implementing the vcl::ITiledRenderable interface. +class SwTiledRenderingTest : public SwModelTestBase +{ +public: + SwTiledRenderingTest(); + virtual void setUp() override; + virtual void tearDown() override; + +protected: + SwXTextDocument* createDoc(const char* pName = nullptr); + void setupLibreOfficeKitViewCallback(SfxViewShell* pViewShell); + static void callback(int nType, const char* pPayload, void* pData); + void callbackImpl(int nType, const char* pPayload); + // First invalidation. + tools::Rectangle m_aInvalidation; + /// Union of all invalidations. + tools::Rectangle m_aInvalidations; + Size m_aDocumentSize; + OString m_aTextSelection; + bool m_bFound; + std::vector m_aSearchResultSelection; + std::vector m_aSearchResultPart; + int m_nSelectionBeforeSearchResult; + int m_nSelectionAfterSearchResult; + int m_nInvalidations; + int m_nRedlineTableSizeChanged; + int m_nRedlineTableEntryModified; + int m_nTrackedChangeIndex; + bool m_bFullInvalidateSeen; + OString m_sHyperlinkText; + OString m_sHyperlinkLink; + OString m_aFormFieldButton; + OString m_aContentControl; + OString m_ShapeSelection; + struct + { + std::string text; + std::string rect; + } m_aTooltip; + TestLokCallbackWrapper m_callbackWrapper; +}; + +SwTiledRenderingTest::SwTiledRenderingTest() + : SwModelTestBase(u"/sw/qa/extras/tiledrendering/data/"_ustr) + , m_bFound(true) + , m_nSelectionBeforeSearchResult(0) + , m_nSelectionAfterSearchResult(0) + , m_nInvalidations(0) + , m_nRedlineTableSizeChanged(0) + , m_nRedlineTableEntryModified(0) + , m_nTrackedChangeIndex(-1) + , m_bFullInvalidateSeen(false) + , m_callbackWrapper(&callback, this) +{ +} + +void SwTiledRenderingTest::setUp() +{ + SwModelTestBase::setUp(); + + SwGlobals::ensure(); + SW_MOD()->ClearRedlineAuthors(); + + comphelper::LibreOfficeKit::setActive(true); +} + +void SwTiledRenderingTest::tearDown() +{ + if (mxComponent.is()) + { + SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell(); + if (pWrtShell) + { + pWrtShell->GetSfxViewShell()->setLibreOfficeKitViewCallback(nullptr); + } + mxComponent->dispose(); + mxComponent.clear(); + } + m_callbackWrapper.clear(); + comphelper::LibreOfficeKit::setActive(false); + + test::BootstrapFixture::tearDown(); +} + +SwXTextDocument* SwTiledRenderingTest::createDoc(const char* pName) +{ + if (!pName) + createSwDoc(); + else + createSwDoc(pName); + + SwXTextDocument* pTextDocument = getSwTextDoc(); + pTextDocument->initializeForTiledRendering(uno::Sequence()); + return pTextDocument; +} + +void SwTiledRenderingTest::setupLibreOfficeKitViewCallback(SfxViewShell* pViewShell) +{ + pViewShell->setLibreOfficeKitViewCallback(&m_callbackWrapper); + m_callbackWrapper.setLOKViewId(SfxLokHelper::getView(pViewShell)); +} + +void SwTiledRenderingTest::callback(int nType, const char* pPayload, void* pData) +{ + static_cast(pData)->callbackImpl(nType, pPayload); +} + +void SwTiledRenderingTest::callbackImpl(int nType, const char* pPayload) +{ + OString aPayload(pPayload); + switch (nType) + { + case LOK_CALLBACK_INVALIDATE_TILES: + { + tools::Rectangle aInvalidation; + uno::Sequence aSeq + = comphelper::string::convertCommaSeparated(OUString::createFromAscii(pPayload)); + if (std::string_view("EMPTY") == pPayload) + { + m_bFullInvalidateSeen = true; + return; + } + + CPPUNIT_ASSERT(aSeq.getLength() == 4 || aSeq.getLength() == 5); + aInvalidation.SetLeft(aSeq[0].toInt32()); + aInvalidation.SetTop(aSeq[1].toInt32()); + aInvalidation.setWidth(aSeq[2].toInt32()); + aInvalidation.setHeight(aSeq[3].toInt32()); + if (m_aInvalidation.IsEmpty()) + { + m_aInvalidation = aInvalidation; + } + m_aInvalidations.Union(aInvalidation); + ++m_nInvalidations; + } + break; + case LOK_CALLBACK_DOCUMENT_SIZE_CHANGED: + { + uno::Sequence aSeq + = comphelper::string::convertCommaSeparated(OUString::createFromAscii(pPayload)); + CPPUNIT_ASSERT_EQUAL(static_cast(2), aSeq.getLength()); + m_aDocumentSize.setWidth(aSeq[0].toInt32()); + m_aDocumentSize.setHeight(aSeq[1].toInt32()); + } + break; + case LOK_CALLBACK_TEXT_SELECTION: + { + m_aTextSelection = pPayload; + if (m_aSearchResultSelection.empty()) + ++m_nSelectionBeforeSearchResult; + else + ++m_nSelectionAfterSearchResult; + } + break; + case LOK_CALLBACK_SEARCH_NOT_FOUND: + { + m_bFound = false; + } + break; + case LOK_CALLBACK_SEARCH_RESULT_SELECTION: + { + m_aSearchResultSelection.clear(); + boost::property_tree::ptree aTree; + std::stringstream aStream(pPayload); + boost::property_tree::read_json(aStream, aTree); + for (const boost::property_tree::ptree::value_type& rValue : + aTree.get_child("searchResultSelection")) + { + m_aSearchResultSelection.emplace_back( + rValue.second.get("rectangles").c_str()); + m_aSearchResultPart.push_back( + std::atoi(rValue.second.get("part").c_str())); + } + } + break; + case LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED: + { + ++m_nRedlineTableSizeChanged; + } + break; + case LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED: + { + ++m_nRedlineTableEntryModified; + } + break; + case LOK_CALLBACK_STATE_CHANGED: + { + OString aTrackedChangeIndexPrefix(".uno:TrackedChangeIndex="_ostr); + if (aPayload.startsWith(aTrackedChangeIndexPrefix)) + { + OString sIndex = aPayload.copy(aTrackedChangeIndexPrefix.getLength()); + if (sIndex.isEmpty()) + m_nTrackedChangeIndex = -1; + else + m_nTrackedChangeIndex = sIndex.toInt32(); + } + } + break; + case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR: + { + if (comphelper::LibreOfficeKit::isViewIdForVisCursorInvalidation()) + { + boost::property_tree::ptree aTree; + std::stringstream aStream(pPayload); + boost::property_tree::read_json(aStream, aTree); + boost::property_tree::ptree& aChild = aTree.get_child("hyperlink"); + m_sHyperlinkText = OString(aChild.get("text", "")); + m_sHyperlinkLink = OString(aChild.get("link", "")); + } + } + break; + case LOK_CALLBACK_FORM_FIELD_BUTTON: + { + m_aFormFieldButton = OString(pPayload); + } + break; + case LOK_CALLBACK_CONTENT_CONTROL: + { + m_aContentControl = OString(pPayload); + } + break; + case LOK_CALLBACK_GRAPHIC_SELECTION: + { + m_ShapeSelection = OString(pPayload); + } + break; + case LOK_CALLBACK_TOOLTIP: + { + std::stringstream aStream(pPayload); + boost::property_tree::ptree aTree; + boost::property_tree::read_json(aStream, aTree); + m_aTooltip.text = aTree.get_child("text").get_value(); + m_aTooltip.rect = aTree.get_child("rectangle").get_value(); + } + break; + } +} + +/// A view callback tracks callbacks invoked on one specific view. +class ViewCallback final +{ + SfxViewShell* mpViewShell; + int mnView; + +public: + bool m_bOwnCursorInvalidated; + int m_nOwnCursorInvalidatedBy; + bool m_bOwnCursorAtOrigin; + tools::Rectangle m_aOwnCursor; + bool m_bViewCursorInvalidated; + tools::Rectangle m_aViewCursor; + bool m_bOwnSelectionSet; + bool m_bViewSelectionSet; + OString m_aViewSelection; + OString m_aViewRenderState; + bool m_bTilesInvalidated; + bool m_bViewCursorVisible; + bool m_bGraphicViewSelection; + bool m_bGraphicSelection; + bool m_bViewLock; + OString m_aDocColor; + /// Set if any callback was invoked. + bool m_bCalled; + /// Redline table size changed payload + boost::property_tree::ptree m_aRedlineTableChanged; + /// Redline table modified payload + boost::property_tree::ptree m_aRedlineTableModified; + /// Post-it / annotation payload. + boost::property_tree::ptree m_aComment; + std::vector m_aStateChanges; + TestLokCallbackWrapper m_callbackWrapper; + + ViewCallback(SfxViewShell* pViewShell = nullptr, + std::function const& rBeforeInstallFunc = {}) + : m_bOwnCursorInvalidated(false) + , m_nOwnCursorInvalidatedBy(-1) + , m_bOwnCursorAtOrigin(false) + , m_bViewCursorInvalidated(false) + , m_bOwnSelectionSet(false) + , m_bViewSelectionSet(false) + , m_bTilesInvalidated(false) + , m_bViewCursorVisible(false) + , m_bGraphicViewSelection(false) + , m_bGraphicSelection(false) + , m_bViewLock(false) + , m_bCalled(false) + , m_callbackWrapper(&callback, this) + { + // Because one call-site wants to set the bool fields up before the callback is installed + if (rBeforeInstallFunc) + rBeforeInstallFunc(*this); + + mpViewShell = pViewShell ? pViewShell : SfxViewShell::Current(); + mpViewShell->setLibreOfficeKitViewCallback(&m_callbackWrapper); + mnView = SfxLokHelper::getView(); + m_callbackWrapper.setLOKViewId(mnView); + } + + ~ViewCallback() + { + SfxLokHelper::setView(mnView); + mpViewShell->setLibreOfficeKitViewCallback(nullptr); + } + + static void callback(int nType, const char* pPayload, void* pData) + { + static_cast(pData)->callbackImpl(nType, pPayload); + } + + void callbackImpl(int nType, const char* pPayload) + { + OString aPayload(pPayload); + m_bCalled = true; + switch (nType) + { + case LOK_CALLBACK_STATE_CHANGED: + { + m_aStateChanges.push_back(pPayload); + break; + } + case LOK_CALLBACK_INVALIDATE_TILES: + { + m_bTilesInvalidated = true; + } + break; + case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR: + { + m_bOwnCursorInvalidated = true; + + OString sRect; + if (comphelper::LibreOfficeKit::isViewIdForVisCursorInvalidation()) + { + std::stringstream aStream(pPayload); + boost::property_tree::ptree aTree; + boost::property_tree::read_json(aStream, aTree); + sRect = OString(aTree.get_child("rectangle").get_value()); + m_nOwnCursorInvalidatedBy = aTree.get_child("viewId").get_value(); + } + else + sRect = aPayload; + uno::Sequence aSeq + = comphelper::string::convertCommaSeparated(OUString::fromUtf8(sRect)); + if (std::string_view("EMPTY") == pPayload) + return; + CPPUNIT_ASSERT_EQUAL(static_cast(4), aSeq.getLength()); + m_aOwnCursor.SetLeft(aSeq[0].toInt32()); + m_aOwnCursor.SetTop(aSeq[1].toInt32()); + m_aOwnCursor.setWidth(aSeq[2].toInt32()); + m_aOwnCursor.setHeight(aSeq[3].toInt32()); + if (m_aOwnCursor.Left() == 0 && m_aOwnCursor.Top() == 0) + m_bOwnCursorAtOrigin = true; + } + break; + case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR: + { + m_bViewCursorInvalidated = true; + std::stringstream aStream(pPayload); + boost::property_tree::ptree aTree; + boost::property_tree::read_json(aStream, aTree); + OString aRect(aTree.get_child("rectangle").get_value()); + + uno::Sequence aSeq + = comphelper::string::convertCommaSeparated(OUString::fromUtf8(aRect)); + if (std::string_view("EMPTY") == pPayload) + return; + CPPUNIT_ASSERT_EQUAL(static_cast(4), aSeq.getLength()); + m_aViewCursor.SetLeft(aSeq[0].toInt32()); + m_aViewCursor.SetTop(aSeq[1].toInt32()); + m_aViewCursor.setWidth(aSeq[2].toInt32()); + m_aViewCursor.setHeight(aSeq[3].toInt32()); + } + break; + case LOK_CALLBACK_TEXT_SELECTION: + { + m_bOwnSelectionSet = true; + } + break; + case LOK_CALLBACK_TEXT_VIEW_SELECTION: + { + m_bViewSelectionSet = true; + m_aViewSelection = aPayload; + } + break; + case LOK_CALLBACK_VIEW_CURSOR_VISIBLE: + { + std::stringstream aStream(pPayload); + boost::property_tree::ptree aTree; + boost::property_tree::read_json(aStream, aTree); + m_bViewCursorVisible + = aTree.get_child("visible").get_value() == "true"; + } + break; + case LOK_CALLBACK_GRAPHIC_VIEW_SELECTION: + { + std::stringstream aStream(pPayload); + boost::property_tree::ptree aTree; + boost::property_tree::read_json(aStream, aTree); + m_bGraphicViewSelection + = aTree.get_child("selection").get_value() != "EMPTY"; + } + break; + case LOK_CALLBACK_GRAPHIC_SELECTION: + { + m_bGraphicSelection = aPayload != "EMPTY"; + } + break; + case LOK_CALLBACK_VIEW_LOCK: + { + std::stringstream aStream(pPayload); + boost::property_tree::ptree aTree; + boost::property_tree::read_json(aStream, aTree); + m_bViewLock = aTree.get_child("rectangle").get_value() != "EMPTY"; + } + break; + case LOK_CALLBACK_VIEW_RENDER_STATE: + { + m_aViewRenderState = pPayload; + } + break; + case LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED: + { + m_aRedlineTableChanged.clear(); + std::stringstream aStream(pPayload); + boost::property_tree::read_json(aStream, m_aRedlineTableChanged); + m_aRedlineTableChanged = m_aRedlineTableChanged.get_child("redline"); + } + break; + case LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED: + { + m_aRedlineTableModified.clear(); + std::stringstream aStream(pPayload); + boost::property_tree::read_json(aStream, m_aRedlineTableModified); + m_aRedlineTableModified = m_aRedlineTableModified.get_child("redline"); + } + break; + case LOK_CALLBACK_COMMENT: + { + m_aComment.clear(); + std::stringstream aStream(pPayload); + boost::property_tree::read_json(aStream, m_aComment); + m_aComment = m_aComment.get_child("comment"); + } + break; + case LOK_CALLBACK_DOCUMENT_BACKGROUND_COLOR: + { + m_aDocColor = aPayload; + break; + } + } + } +}; + +class TestResultListener : public cppu::WeakImplHelper +{ +public: + sal_uInt32 m_nDocRepair; + + TestResultListener() + : m_nDocRepair(0) + { + } + + virtual void SAL_CALL dispatchFinished(const css::frame::DispatchResultEvent& rEvent) override + { + if (rEvent.State == frame::DispatchResultState::SUCCESS) + { + rEvent.Result >>= m_nDocRepair; + } + } + + virtual void SAL_CALL disposing(const css::lang::EventObject&) override {} +}; + +/// Test callback that works with comphelper::LibreOfficeKit::setAnyInputCallback(). +class AnyInputCallback final +{ +public: + static bool callback(void* /*pData*/) { return true; } + + AnyInputCallback() { comphelper::LibreOfficeKit::setAnyInputCallback(&callback, this); } + + ~AnyInputCallback() { comphelper::LibreOfficeKit::setAnyInputCallback(nullptr, nullptr); } +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- 2.11.4.GIT