/* -*- 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 #include 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 xWindow(xButton); ScopedVclPtrInstance< MetricField > xField( nullptr, 0 ); ScopedVclPtr xSpin(xField); ScopedVclPtr xEdit(xField); // the following line should NOT compile // VclPtr xButton2(xWindow); } void LifecycleTest::testVirtualDevice() { VclPtr pVDev = VclPtr< VirtualDevice >::Create(); ScopedVclPtrInstance< VirtualDevice > pVDev2; VclPtrInstance pVDev3; VclPtrInstance pVDev4(DeviceFormat::BITMASK); CPPUNIT_ASSERT(!!pVDev && !!pVDev2 && !!pVDev3 && !!pVDev4); } void LifecycleTest::testMultiDispose() { VclPtrInstance 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(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 xWin(nullptr, WB_APP|WB_STDWORK); CPPUNIT_ASSERT(xWin.get() != nullptr); xWin->Show(); testWidgets(xWin); } class DisposableChild : public vcl::Window { public: explicit DisposableChild(vcl::Window *pParent) : vcl::Window(pParent) {} virtual ~DisposableChild() override { disposeOnce(); } }; void LifecycleTest::testChildDispose() { VclPtrInstance 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 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)); } 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 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()); } template class LeakTestClass : public vcl_type { bool &mrDeleted; public: template LeakTestClass(bool &bDeleted, Arg &&... arg) : vcl_type(std::forward(arg)...), mrDeleted(bDeleted) { mrDeleted = false; } ~LeakTestClass() { mrDeleted = true; } }; class LeakTestObject { bool mbDeleted; VclPtr mxRef; void *mpRef; LeakTestObject() : mbDeleted(false) , mpRef(nullptr) { } public: template static LeakTestObject * Create(Arg &&... arg) { LeakTestObject *pNew = new LeakTestObject(); pNew->mxRef = VclPtr< LeakTestClass< vcl_type > >::Create( pNew->mbDeleted, std::forward(arg)...); pNew->mpRef = static_cast(static_cast(pNew->mxRef)); return pNew; } const VclPtr& getRef() { return mxRef; } void disposeAndClear() { mxRef.disposeAndClear(); } void assertDeleted() { if (!mbDeleted) { OUStringBuffer aMsg = "Type '"; vcl::Window *pWin = static_cast(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 aObjects; // Create objects aObjects.push_back(LeakTestObject::Create(nullptr, WB_APP|WB_STDWORK)); VclPtr xParent = aObjects.back()->getRef(); aObjects.push_back(LeakTestObject::Create(xParent)); aObjects.push_back(LeakTestObject::Create(xParent)); aObjects.push_back(LeakTestObject::Create(xParent)); aObjects.push_back(LeakTestObject::Create(xParent)); aObjects.push_back(LeakTestObject::Create(xParent)); aObjects.push_back(LeakTestObject::Create(xParent)); aObjects.push_back(LeakTestObject::Create(xParent)); aObjects.push_back(LeakTestObject::Create(xParent)); { // something that looks like a dialog aObjects.push_back(LeakTestObject::Create(xParent,WB_CLIPCHILDREN|WB_MOVEABLE|WB_3DLOOK|WB_CLOSEABLE|WB_SIZEABLE)); VclPtr xDlgParent = aObjects.back()->getRef(); aObjects.push_back(LeakTestObject::Create(xDlgParent)); VclPtr xVBox = aObjects.back()->getRef(); aObjects.push_back(LeakTestObject::Create(xVBox)); } #if 0 // FIXME - would be good to get internal paths working. aObjects.push_back(LeakTestObject::Create(xParent, "PrintProgressDialog", "vcl/ui/printprogressdialog.ui")); #endif aObjects.push_back(LeakTestObject::Create(xParent)); 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(nullptr, WB_APP|WB_STDWORK); css::uno::Reference xWindow(pVclWin->getRef()->GetComponentInterface(), css::uno::UNO_QUERY); CPPUNIT_ASSERT(xWindow.is()); // test UNO dispose css::uno::Reference xWinComponent(xWindow, css::uno::UNO_QUERY); 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: */