diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
commit | 267c6f2ac71f92999e969232431ba04678e7437e (patch) | |
tree | 358c9467650e1d0a1d7227a21dac2e3d08b622b2 /framework/qa/cppunit | |
parent | Initial commit. (diff) | |
download | libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip |
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'framework/qa/cppunit')
-rw-r--r-- | framework/qa/cppunit/data/double-loading.odt | bin | 0 -> 13729 bytes | |||
-rw-r--r-- | framework/qa/cppunit/data/empty.fodp | 2 | ||||
-rw-r--r-- | framework/qa/cppunit/dispatchtest.cxx | 204 | ||||
-rw-r--r-- | framework/qa/cppunit/loadenv.cxx | 78 | ||||
-rw-r--r-- | framework/qa/cppunit/services.cxx | 138 |
5 files changed, 422 insertions, 0 deletions
diff --git a/framework/qa/cppunit/data/double-loading.odt b/framework/qa/cppunit/data/double-loading.odt Binary files differnew file mode 100644 index 0000000000..ce990fc5c1 --- /dev/null +++ b/framework/qa/cppunit/data/double-loading.odt diff --git a/framework/qa/cppunit/data/empty.fodp b/framework/qa/cppunit/data/empty.fodp new file mode 100644 index 0000000000..3c2a4cf2cd --- /dev/null +++ b/framework/qa/cppunit/data/empty.fodp @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.presentation"><office:body><office:presentation><draw:page/></office:presentation></office:body></office:document> diff --git a/framework/qa/cppunit/dispatchtest.cxx b/framework/qa/cppunit/dispatchtest.cxx new file mode 100644 index 0000000000..7ba29ef9cb --- /dev/null +++ b/framework/qa/cppunit/dispatchtest.cxx @@ -0,0 +1,204 @@ +/* -*- 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 <cppuhelper/implbase.hxx> +#include <test/unoapi_test.hxx> + +#include <com/sun/star/frame/XDispatchProviderInterceptor.hpp> +#include <com/sun/star/frame/XInterceptorInfo.hpp> +#include <com/sun/star/util/URLTransformer.hpp> + +#include <rtl/ref.hxx> +#include <mutex> + +using namespace ::com::sun::star; + +namespace +{ +/// Sample interception implementation that asserts getInterceptedURLs() and queryDispatch() is in sync. +class MyInterceptor + : public cppu::WeakImplHelper<frame::XDispatchProviderInterceptor, frame::XInterceptorInfo> +{ + std::mutex maMutex; + uno::Reference<frame::XDispatchProvider> m_xMaster; + uno::Reference<frame::XDispatchProvider> m_xSlave; + uno::Sequence<OUString> m_aDisabledCommands; + int m_nExpected; + int m_nUnexpected; + +public: + MyInterceptor(); + + /// Number of queryDispatch() calls that operate on a command advertised by getInterceptedURLs(). + int getExpected(); + /// Number of queryDispatch() calls that operate on a command not advertised by getInterceptedURLs(). + int getUnexpected(); + + // frame::XInterceptorInfo + virtual uno::Sequence<OUString> SAL_CALL getInterceptedURLs() override; + + // frame::XDispatchProviderInterceptor + virtual void SAL_CALL setMasterDispatchProvider( + const uno::Reference<frame::XDispatchProvider>& xNewSupplier) override; + virtual uno::Reference<frame::XDispatchProvider> SAL_CALL getMasterDispatchProvider() override; + virtual void SAL_CALL + setSlaveDispatchProvider(const uno::Reference<frame::XDispatchProvider>& xNewSupplier) override; + virtual uno::Reference<frame::XDispatchProvider> SAL_CALL getSlaveDispatchProvider() override; + + // frame::XDispatchProvider + virtual uno::Sequence<uno::Reference<frame::XDispatch>> SAL_CALL + queryDispatches(const uno::Sequence<frame::DispatchDescriptor>& rRequests) override; + virtual uno::Reference<frame::XDispatch> + SAL_CALL queryDispatch(const util::URL& rURL, const OUString& rTargetFrameName, + sal_Int32 SearchFlags) override; +}; + +MyInterceptor::MyInterceptor() + : m_aDisabledCommands{ ".uno:Bold" } + , m_nExpected(0) + , m_nUnexpected(0) +{ +} + +int MyInterceptor::getExpected() +{ + std::unique_lock aGuard(maMutex); + int nRet = m_nExpected; + m_nExpected = 0; + return nRet; +} + +int MyInterceptor::getUnexpected() +{ + std::unique_lock aGuard(maMutex); + int nRet = m_nUnexpected; + m_nUnexpected = 0; + return nRet; +} + +uno::Sequence<OUString> MyInterceptor::getInterceptedURLs() { return m_aDisabledCommands; } + +void MyInterceptor::setMasterDispatchProvider( + const uno::Reference<frame::XDispatchProvider>& xNewSupplier) +{ + std::unique_lock aGuard(maMutex); + m_xMaster = xNewSupplier; +} + +uno::Reference<frame::XDispatchProvider> MyInterceptor::getMasterDispatchProvider() +{ + std::unique_lock aGuard(maMutex); + return m_xMaster; +} + +void MyInterceptor::setSlaveDispatchProvider( + const uno::Reference<frame::XDispatchProvider>& xNewSupplier) +{ + std::unique_lock aGuard(maMutex); + m_xSlave = xNewSupplier; +} + +uno::Reference<frame::XDispatchProvider> MyInterceptor::getSlaveDispatchProvider() +{ + std::unique_lock aGuard(maMutex); + return m_xSlave; +} + +uno::Sequence<uno::Reference<frame::XDispatch>> +MyInterceptor::queryDispatches(const uno::Sequence<frame::DispatchDescriptor>& rRequests) +{ + uno::Sequence<uno::Reference<frame::XDispatch>> aResult(rRequests.getLength()); + auto aResultRange = asNonConstRange(aResult); + + for (sal_Int32 i = 0; i < rRequests.getLength(); ++i) + { + aResultRange[i] = queryDispatch(rRequests[i].FeatureURL, rRequests[i].FrameName, + rRequests[i].SearchFlags); + } + + return aResult; +} + +uno::Reference<frame::XDispatch> MyInterceptor::queryDispatch(const util::URL& rURL, + const OUString& /*rTargetFrameName*/, + sal_Int32 /*SearchFlags*/) +{ + std::unique_lock aGuard(maMutex); + if (std::find(std::cbegin(m_aDisabledCommands), std::cend(m_aDisabledCommands), rURL.Complete) + != std::cend(m_aDisabledCommands)) + ++m_nExpected; + else + ++m_nUnexpected; + + return uno::Reference<frame::XDispatch>(); +} + +/// Tests how InterceptionHelper invokes a registered interceptor. +class DispatchTest : public UnoApiTest +{ +public: + DispatchTest() + : UnoApiTest("/framework/qa/cppunit/data/") + { + } +}; + +CPPUNIT_TEST_FIXTURE(DispatchTest, testInterception) +{ + mxComponent = loadFromDesktop("private:factory/swriter", "com.sun.star.text.TextDocument"); + uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY); + CPPUNIT_ASSERT(xModel.is()); + + uno::Reference<frame::XDispatchProviderInterception> xRegistration( + xModel->getCurrentController()->getFrame(), uno::UNO_QUERY); + CPPUNIT_ASSERT(xRegistration.is()); + + rtl::Reference<MyInterceptor> pInterceptor(new MyInterceptor()); + xRegistration->registerDispatchProviderInterceptor(pInterceptor); + + dispatchCommand(mxComponent, ".uno:Bold", {}); + CPPUNIT_ASSERT_GREATER(0, pInterceptor->getExpected()); + CPPUNIT_ASSERT_EQUAL(0, pInterceptor->getUnexpected()); + dispatchCommand(mxComponent, ".uno:Italic", {}); + // This was 1: MyInterceptor::queryDispatch() was called for .uno:Italic. + CPPUNIT_ASSERT_EQUAL(0, pInterceptor->getUnexpected()); +} + +CPPUNIT_TEST_FIXTURE(DispatchTest, testSfxOfficeDispatchDispose) +{ + // this test doesn't work with a new document because of aURL.Main check in SfxBaseController::dispatch() + loadFromFile(u"empty.fodp"); + uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY); + CPPUNIT_ASSERT(xModel.is()); + uno::Reference<frame::XController> xController(xModel->getCurrentController()); + CPPUNIT_ASSERT(xController.is()); + uno::Reference<frame::XDispatchProvider> xFrame(xController->getFrame(), uno::UNO_QUERY); + CPPUNIT_ASSERT(xFrame.is()); + + uno::Reference<util::XURLTransformer> xParser(util::URLTransformer::create(mxComponentContext)); + util::URL url; + url.Complete = xModel->getURL() + "#dummy"; + xParser->parseStrict(url); + + uno::Reference<frame::XDispatch> xDisp(xFrame->queryDispatch(url, "", 0)); + CPPUNIT_ASSERT(xDisp.is()); + + mxComponent->dispose(); + + util::URL urlSlot; + urlSlot.Complete = "slot:5598"; + xParser->parseStrict(urlSlot); + // crashed with UAF + xDisp->dispatch(urlSlot, {}); +} +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/qa/cppunit/loadenv.cxx b/framework/qa/cppunit/loadenv.cxx new file mode 100644 index 0000000000..c9aa2789d9 --- /dev/null +++ b/framework/qa/cppunit/loadenv.cxx @@ -0,0 +1,78 @@ +/* -*- 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 <test/unoapi_test.hxx> + +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/frame/XFrame.hpp> + +#include <comphelper/processfactory.hxx> +#include <vcl/scheduler.hxx> +#include <vcl/svapp.hxx> + +using namespace ::com::sun::star; + +namespace +{ +/// Covers framework/source/loadenv/ fixes. +class Test : public UnoApiTest +{ +public: + Test() + : UnoApiTest("/framework/qa/cppunit/data/") + { + } +}; + +class DocumentOpener +{ +public: + DECL_STATIC_LINK(DocumentOpener, OpenDocument, void*, void); +}; + +IMPL_STATIC_LINK(DocumentOpener, OpenDocument, void*, pArg, void) +{ + CPPUNIT_ASSERT(pArg); + auto pURL = static_cast<OUString*>(pArg); + uno::Reference<uno::XComponentContext> xComponentContext + = comphelper::getProcessComponentContext(); + uno::Reference<frame::XDesktop2> xDesktop = frame::Desktop::create(xComponentContext); + xDesktop->loadComponentFromURL(*pURL, "_default", 0, {}); + delete pURL; +} + +CPPUNIT_TEST_FIXTURE(Test, testDoubleLoading) +{ + // Try to load the same document twice. This is similar to trying to execute the soffice process + // twice: in that case the 2nd instance forwards to the 1st instance and then uses the same code + // path. + for (int i = 0; i < 2; ++i) + { + auto pURL = std::make_unique<OUString>(createFileURL(u"double-loading.odt")); + Application::PostUserEvent(LINK(nullptr, DocumentOpener, OpenDocument), pURL.release()); + } + Scheduler::ProcessEventsToIdle(); + + // Verify that the 2nd load didn't happen, since it's the same document. + uno::Reference<frame::XFrames> xFrames = mxDesktop->getFrames(); + // Without the accompanying fix in place, this failed with: + // - Expected: 1 + // - Actual : 2 + // i.e. the document was loaded twice, into two separate frames/windows. + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), xFrames->getCount()); + + // Close the document, now that we know we have a single one. + uno::Reference<frame::XFrame> xFrame(xFrames->getByIndex(0), uno::UNO_QUERY); + xFrame->getController()->getModel()->dispose(); +} +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/framework/qa/cppunit/services.cxx b/framework/qa/cppunit/services.cxx new file mode 100644 index 0000000000..be6c0def70 --- /dev/null +++ b/framework/qa/cppunit/services.cxx @@ -0,0 +1,138 @@ +/* -*- 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 <test/unoapi_test.hxx> + +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/frame/XComponentLoader.hpp> +#include <com/sun/star/frame/FrameSearchFlag.hpp> +#include <com/sun/star/util/URLTransformer.hpp> + +#include <comphelper/propertyvalue.hxx> +#include <salhelper/thread.hxx> +#include <vcl/svapp.hxx> +#include <vcl/scheduler.hxx> +#include <vcl/wrkwin.hxx> + +using namespace ::com::sun::star; + +namespace +{ +/// Covers framework/source/services/ fixes. +class Test : public UnoApiTest +{ +public: + Test() + : UnoApiTest("/framework/qa/cppunit/data/") + { + } +}; + +/// Invokes XFrameImpl::loadComponentFromURL() on a thread. +class TestThread : public salhelper::Thread +{ + uno::Reference<frame::XComponentLoader> mxComponentLoader; + uno::Reference<lang::XComponent>& mrComponent; + +public: + TestThread(const uno::Reference<frame::XComponentLoader>& xComponentLoader, + uno::Reference<lang::XComponent>& rComponent); + void execute() override; +}; + +TestThread::TestThread(const uno::Reference<frame::XComponentLoader>& xComponentLoader, + uno::Reference<lang::XComponent>& rComponent) + : salhelper::Thread("TestThread") + , mxComponentLoader(xComponentLoader) + , mrComponent(rComponent) +{ +} + +void TestThread::execute() +{ + sal_Int32 nSearchFlags = frame::FrameSearchFlag::AUTO; + uno::Sequence<beans::PropertyValue> aArguments = { + comphelper::makePropertyValue("OnMainThread", true), + }; + // Note how this is invoking loadComponentFromURL() on a frame, not on the desktop, as usual. + mrComponent = mxComponentLoader->loadComponentFromURL("private:factory/swriter", "_self", + nSearchFlags, aArguments); +} + +CPPUNIT_TEST_FIXTURE(Test, testLoadComponentFromURL) +{ + // Without the accompanying fix in place, this test would have failed with: + // thread 1: comphelper::SolarMutex::doRelease end: m_nCount is 1 + // thread 2: vcl::SolarThreadExecutor::execute: before SolarMutexReleaser ctor + // thread 2: comphelper::SolarMutex::doRelease start: m_nCount is 1, bUnlockAll is 1 + // thread 2: comphelper::SolarMutex::doRelease: failed IsCurrentThread() check, will abort + // Notice how thread 2 attempts to release the solar mutex while thread 1 holds it. + + // Create a default window, so by the time the thread would post a user event, it doesn't need + // the solar mutex to process a SendMessageW() call on Windows. + ScopedVclPtrInstance<WorkWindow> xWindow(nullptr, WB_APP | WB_STDWORK); + // Variable is not used, it holds the default window. + (void)xWindow; + + rtl::Reference<TestThread> xThread; + { + // Start the thread that will load the component, but hold the solar mutex for now, so we + // can see if it blocks. + SolarMutexGuard guard; + uno::Reference<frame::XFrame> xFrame = mxDesktop->findFrame("_blank", /*nSearchFlags=*/0); + uno::Reference<frame::XComponentLoader> xComponentLoader(xFrame, uno::UNO_QUERY); + xThread = new TestThread(xComponentLoader, mxComponent); + xThread->launch(); + // If loadComponentFromURL() doesn't lock the solar mutex, the test will abort here. + osl::Thread::wait(std::chrono::seconds(1)); + } + { + // Now release the solar mutex, so the thread can post the task on the main loop. + SolarMutexReleaser releaser; + osl::Thread::wait(std::chrono::seconds(1)); + } + { + // Spin the main loop. + SolarMutexGuard guard; + Scheduler::ProcessEventsToIdle(); + } + { + // Stop the thread. + SolarMutexReleaser releaser; + xThread->join(); + } +} + +CPPUNIT_TEST_FIXTURE(Test, testURLTransformer_parseSmart) +{ + // Without the accompanying fix in place, this test would have failed with + // "www.example.com:" treated as scheme, "/8080/foo/" as path, "bar?q=baz" + // as name, and "F" as fragment. + + css::util::URL aURL; + aURL.Complete = "www.example.com:8080/foo/bar?q=baz#F"; + css::uno::Reference xParser(css::util::URLTransformer::create(mxComponentContext)); + CPPUNIT_ASSERT(xParser->parseSmart(aURL, "http:")); + CPPUNIT_ASSERT_EQUAL(OUString("http://www.example.com:8080/foo/bar?q=baz#F"), aURL.Complete); + CPPUNIT_ASSERT_EQUAL(OUString("http://www.example.com:8080/foo/bar"), aURL.Main); + CPPUNIT_ASSERT_EQUAL(OUString("http://"), aURL.Protocol); + CPPUNIT_ASSERT(aURL.User.isEmpty()); + CPPUNIT_ASSERT(aURL.Password.isEmpty()); + CPPUNIT_ASSERT_EQUAL(OUString("www.example.com"), aURL.Server); + CPPUNIT_ASSERT_EQUAL(sal_Int16(8080), aURL.Port); + CPPUNIT_ASSERT_EQUAL(OUString("/foo/"), aURL.Path); + CPPUNIT_ASSERT_EQUAL(OUString("bar"), aURL.Name); + CPPUNIT_ASSERT_EQUAL(OUString("q=baz"), aURL.Arguments); + CPPUNIT_ASSERT_EQUAL(OUString("F"), aURL.Mark); +} +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |