diff options
Diffstat (limited to '')
-rw-r--r-- | vcl/qa/cppunit/lifecycle.cxx | 357 |
1 files changed, 357 insertions, 0 deletions
diff --git a/vcl/qa/cppunit/lifecycle.cxx b/vcl/qa/cppunit/lifecycle.cxx new file mode 100644 index 000000000..a4a3f5d9e --- /dev/null +++ b/vcl/qa/cppunit/lifecycle.cxx @@ -0,0 +1,357 @@ +/* -*- 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/bootstrapfixture.hxx> + +#include <vcl/wrkwin.hxx> +#include <vcl/edit.hxx> +#include <vcl/toolkit/button.hxx> +#include <vcl/toolkit/combobox.hxx> +#include <vcl/toolkit/dialog.hxx> +#include <vcl/toolkit/field.hxx> +#include <vcl/virdev.hxx> +#include <vcl/tabctrl.hxx> +#include <vcl/layout.hxx> +#include <vcl/scheduler.hxx> +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/awt/XWindowPeer.hpp> +#include <com/sun/star/lang/XComponent.hpp> + +class LifecycleTest : public test::BootstrapFixture +{ + void testWidgets(vcl::Window *pParent); + +public: + LifecycleTest() : BootstrapFixture(true, false) {} + + void testCast(); + void testVirtualDevice(); + void testMultiDispose(); + void testIsolatedWidgets(); + void testParentedWidgets(); + void testChildDispose(); + void testPostDispose(); + void testFocus(); + void testLeakage(); + void testToolkit(); + + CPPUNIT_TEST_SUITE(LifecycleTest); + CPPUNIT_TEST(testCast); + CPPUNIT_TEST(testVirtualDevice); + CPPUNIT_TEST(testMultiDispose); + CPPUNIT_TEST(testIsolatedWidgets); + CPPUNIT_TEST(testParentedWidgets); + CPPUNIT_TEST(testChildDispose); + CPPUNIT_TEST(testPostDispose); + CPPUNIT_TEST(testFocus); + CPPUNIT_TEST(testLeakage); + CPPUNIT_TEST(testToolkit); + CPPUNIT_TEST_SUITE_END(); +}; + +// A compile time sanity check +void LifecycleTest::testCast() +{ + ScopedVclPtrInstance< PushButton > xButton( nullptr, 0 ); + ScopedVclPtr<vcl::Window> xWindow(xButton); + + ScopedVclPtrInstance< MetricField > xField( nullptr, 0 ); + ScopedVclPtr<SpinField> xSpin(xField); + ScopedVclPtr<Edit> xEdit(xField); + +// the following line should NOT compile +// VclPtr<PushButton> xButton2(xWindow); +} + +void LifecycleTest::testVirtualDevice() +{ + VclPtr<VirtualDevice> pVDev = VclPtr< VirtualDevice >::Create(); + ScopedVclPtrInstance< VirtualDevice > pVDev2; + VclPtrInstance<VirtualDevice> pVDev3; + VclPtrInstance<VirtualDevice> pVDev4(DeviceFormat::BITMASK); + CPPUNIT_ASSERT(!!pVDev && !!pVDev2 && !!pVDev3 && !!pVDev4); + pVDev.disposeAndClear(); + pVDev4.disposeAndClear(); +} + +void LifecycleTest::testMultiDispose() +{ + VclPtrInstance<WorkWindow> xWin(nullptr, WB_APP|WB_STDWORK); + CPPUNIT_ASSERT(xWin.get() != nullptr); + xWin->disposeOnce(); + xWin->disposeOnce(); + xWin->disposeOnce(); + CPPUNIT_ASSERT(!xWin->GetWindow(GetWindowType::Parent)); + CPPUNIT_ASSERT(!xWin->GetChild(0)); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(0), xWin->GetChildCount()); +} + +void LifecycleTest::testWidgets(vcl::Window *pParent) +{ + { + ScopedVclPtrInstance< PushButton > aPtr( pParent ); + (void)aPtr; // silence unused variable warning + } + { + ScopedVclPtrInstance< OKButton > aPtr( pParent ); + (void)aPtr; // silence unused variable warning + } + { + ScopedVclPtrInstance< CancelButton > aPtr( pParent ); + (void)aPtr; // silence unused variable warning + } + { + ScopedVclPtrInstance< HelpButton > aPtr( pParent ); + (void)aPtr; // silence unused variable warning + } + + // Some widgets really insist on adoption. + if (pParent) + { + { + ScopedVclPtrInstance< CheckBox > aPtr( pParent ); + (void)aPtr; // silence unused variable warning + } + { + ScopedVclPtrInstance< Edit > aPtr( pParent ); + (void)aPtr; // silence unused variable warning + } + { + ScopedVclPtrInstance< ComboBox > aPtr( pParent ); + (void)aPtr; // silence unused variable warning + } + { + ScopedVclPtrInstance< RadioButton > aPtr( pParent ); + (void)aPtr; // silence unused variable warning + } + } +} + +void LifecycleTest::testIsolatedWidgets() +{ + testWidgets(nullptr); +} + +void LifecycleTest::testParentedWidgets() +{ + ScopedVclPtrInstance<WorkWindow> xWin(nullptr, WB_APP|WB_STDWORK); + CPPUNIT_ASSERT(xWin.get() != nullptr); + xWin->Show(); + testWidgets(xWin); +} + +namespace { + +class DisposableChild : public vcl::Window +{ +public: + explicit DisposableChild(vcl::Window *pParent) : vcl::Window(pParent) {} + virtual ~DisposableChild() override + { + disposeOnce(); + } +}; + +} + +void LifecycleTest::testChildDispose() +{ + VclPtrInstance<WorkWindow> xWin(nullptr, WB_APP|WB_STDWORK); + CPPUNIT_ASSERT(xWin.get() != nullptr); + VclPtrInstance< DisposableChild > xChild( xWin.get() ); + xWin->Show(); + xChild->disposeOnce(); + xWin->disposeOnce(); +} + +void LifecycleTest::testPostDispose() +{ + VclPtrInstance<WorkWindow> xWin(nullptr, WB_APP|WB_STDWORK); + xWin->disposeOnce(); + + // check selected methods continue to work post-dispose + CPPUNIT_ASSERT(!xWin->GetParent()); + xWin->Show(); + CPPUNIT_ASSERT(!xWin->IsReallyShown()); + CPPUNIT_ASSERT(!xWin->IsEnabled()); + CPPUNIT_ASSERT(!xWin->IsInputEnabled()); + CPPUNIT_ASSERT(!xWin->GetChild(0)); + CPPUNIT_ASSERT(!xWin->GetWindow(GetWindowType::Parent)); +} + +namespace { + +class FocusCrashPostDispose : public TabControl +{ +public: + explicit FocusCrashPostDispose(vcl::Window *pParent) : + TabControl(pParent, 0) + { + } + virtual bool PreNotify( NotifyEvent& ) override + { + return false; + } + virtual bool EventNotify( NotifyEvent& ) override + { + return false; + } + virtual void GetFocus() override + { + CPPUNIT_FAIL("get focus"); + } + virtual void LoseFocus() override + { + CPPUNIT_FAIL("this should never be called"); + } +}; + +} + +void LifecycleTest::testFocus() +{ + ScopedVclPtrInstance<WorkWindow> xWin(nullptr, WB_APP|WB_STDWORK); + ScopedVclPtrInstance< FocusCrashPostDispose > xChild(xWin); + xWin->Show(); + xChild->GrabFocus(); + // process asynchronous ToTop + Scheduler::ProcessTaskScheduling(); + // FIXME: really awful to test focus issues without showing windows. + // CPPUNIT_ASSERT(xChild->HasFocus()); +} + +namespace { + +template <class vcl_type> +class LeakTestClass : public vcl_type +{ + bool &mrDeleted; +public: + template<typename... Arg> + LeakTestClass(bool &bDeleted, Arg &&... arg) : + vcl_type(std::forward<Arg>(arg)...), + mrDeleted(bDeleted) + { + mrDeleted = false; + } + ~LeakTestClass() + { + mrDeleted = true; + } +}; + +class LeakTestObject +{ + bool mbDeleted; + VclPtr<vcl::Window> mxRef; + void *mpRef; + LeakTestObject() + : mbDeleted(false) + , mpRef(nullptr) + { + } +public: + template<typename vcl_type, typename... Arg> static LeakTestObject * + Create(Arg &&... arg) + { + LeakTestObject *pNew = new LeakTestObject(); + pNew->mxRef = VclPtr< LeakTestClass< vcl_type > >::Create( pNew->mbDeleted, + std::forward<Arg>(arg)...); + pNew->mpRef = static_cast<void *>(static_cast<vcl::Window *>(pNew->mxRef)); + return pNew; + } + const VclPtr<vcl::Window>& getRef() const { return mxRef; } + void disposeAndClear() + { + mxRef.disposeAndClear(); + } + void assertDeleted() + { + if (!mbDeleted) + { + OUStringBuffer aMsg = "Type '"; + vcl::Window *pWin = static_cast<vcl::Window *>(mpRef); + aMsg.appendAscii(typeid(*pWin).name()); + aMsg.append("' not freed after dispose"); + CPPUNIT_FAIL(OUStringToOString(aMsg.makeStringAndClear(), + RTL_TEXTENCODING_UTF8).getStr()); + } + } +}; + +} + +void LifecycleTest::testLeakage() +{ + std::vector<LeakTestObject *> aObjects; + + // Create objects + aObjects.push_back(LeakTestObject::Create<WorkWindow>(nullptr, WB_APP|WB_STDWORK)); + VclPtr<vcl::Window> xParent = aObjects.back()->getRef(); + + aObjects.push_back(LeakTestObject::Create<PushButton>(xParent)); + aObjects.push_back(LeakTestObject::Create<OKButton>(xParent)); + aObjects.push_back(LeakTestObject::Create<CancelButton>(xParent)); + aObjects.push_back(LeakTestObject::Create<HelpButton>(xParent)); + aObjects.push_back(LeakTestObject::Create<CheckBox>(xParent)); + aObjects.push_back(LeakTestObject::Create<Edit>(xParent)); + aObjects.push_back(LeakTestObject::Create<ComboBox>(xParent)); + aObjects.push_back(LeakTestObject::Create<RadioButton>(xParent)); + + { // something that looks like a dialog + aObjects.push_back(LeakTestObject::Create<Dialog>(xParent,WB_CLIPCHILDREN|WB_MOVEABLE|WB_3DLOOK|WB_CLOSEABLE|WB_SIZEABLE)); + VclPtr<vcl::Window> xDlgParent = aObjects.back()->getRef(); + aObjects.push_back(LeakTestObject::Create<VclVBox>(xDlgParent)); + VclPtr<vcl::Window> xVBox = aObjects.back()->getRef(); + aObjects.push_back(LeakTestObject::Create<VclVButtonBox>(xVBox)); + } + + aObjects.push_back(LeakTestObject::Create<Dialog>(xParent, "PrintProgressDialog", "vcl/ui/printprogressdialog.ui")); + xParent.clear(); + + for (auto i = aObjects.rbegin(); i != aObjects.rend(); ++i) + (*i)->getRef()->Show(); + + for (auto i = aObjects.rbegin(); i != aObjects.rend(); ++i) + (*i)->disposeAndClear(); + + for (auto i = aObjects.begin(); i != aObjects.end(); ++i) + (*i)->assertDeleted(); + + for (auto i = aObjects.begin(); i != aObjects.end(); ++i) + delete *i; +} + +void LifecycleTest::testToolkit() +{ + LeakTestObject *pVclWin = LeakTestObject::Create<WorkWindow>(nullptr, WB_APP|WB_STDWORK); + css::uno::Reference<css::awt::XWindow> xWindow(pVclWin->getRef()->GetComponentInterface(), css::uno::UNO_QUERY); + CPPUNIT_ASSERT(xWindow.is()); + + // test UNO dispose + css::uno::Reference<css::lang::XComponent> xWinComponent = xWindow; + CPPUNIT_ASSERT(xWinComponent.is()); + CPPUNIT_ASSERT(!pVclWin->getRef()->IsDisposed()); + xWinComponent->dispose(); + CPPUNIT_ASSERT(pVclWin->getRef()->IsDisposed()); + + // test UNO cleanup + xWinComponent.clear(); + xWindow.clear(); + pVclWin->disposeAndClear(); + pVclWin->assertDeleted(); + + delete pVclWin; +} + +CPPUNIT_TEST_SUITE_REGISTRATION(LifecycleTest); + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |