1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  */
9 
10 #include <test/bootstrapfixture.hxx>
11 
12 #include <vcl/wrkwin.hxx>
13 #include <vcl/button.hxx>
14 #include <vcl/edit.hxx>
15 #include <vcl/combobox.hxx>
16 #include <vcl/field.hxx>
17 #include <vcl/virdev.hxx>
18 #include <vcl/tabctrl.hxx>
19 #include <vcl/dialog.hxx>
20 #include <vcl/layout.hxx>
21 #include <vcl/scheduler.hxx>
22 #include <com/sun/star/awt/XWindow.hpp>
23 #include <com/sun/star/lang/XComponent.hpp>
24 
25 class LifecycleTest : public test::BootstrapFixture
26 {
27     void testWidgets(vcl::Window *pParent);
28 
29 public:
LifecycleTest()30     LifecycleTest() : BootstrapFixture(true, false) {}
31 
32     void testCast();
33     void testVirtualDevice();
34     void testMultiDispose();
35     void testIsolatedWidgets();
36     void testParentedWidgets();
37     void testChildDispose();
38     void testPostDispose();
39     void testFocus();
40     void testLeakage();
41     void testToolkit();
42 
43     CPPUNIT_TEST_SUITE(LifecycleTest);
44     CPPUNIT_TEST(testCast);
45     CPPUNIT_TEST(testVirtualDevice);
46     CPPUNIT_TEST(testMultiDispose);
47     CPPUNIT_TEST(testIsolatedWidgets);
48     CPPUNIT_TEST(testParentedWidgets);
49     CPPUNIT_TEST(testChildDispose);
50     CPPUNIT_TEST(testPostDispose);
51     CPPUNIT_TEST(testFocus);
52     CPPUNIT_TEST(testLeakage);
53     CPPUNIT_TEST(testToolkit);
54     CPPUNIT_TEST_SUITE_END();
55 };
56 
57 // A compile time sanity check
testCast()58 void LifecycleTest::testCast()
59 {
60     ScopedVclPtrInstance< PushButton > xButton( nullptr, 0 );
61     ScopedVclPtr<vcl::Window> xWindow(xButton);
62 
63     ScopedVclPtrInstance< MetricField > xField( nullptr, 0 );
64     ScopedVclPtr<SpinField> xSpin(xField);
65     ScopedVclPtr<Edit> xEdit(xField);
66 
67 // the following line should NOT compile
68 //    VclPtr<PushButton> xButton2(xWindow);
69 }
70 
testVirtualDevice()71 void LifecycleTest::testVirtualDevice()
72 {
73     VclPtr<VirtualDevice> pVDev = VclPtr< VirtualDevice >::Create();
74     ScopedVclPtrInstance< VirtualDevice > pVDev2;
75     VclPtrInstance<VirtualDevice> pVDev3;
76     VclPtrInstance<VirtualDevice> pVDev4(DeviceFormat::BITMASK);
77     CPPUNIT_ASSERT(!!pVDev && !!pVDev2 && !!pVDev3 && !!pVDev4);
78 }
79 
testMultiDispose()80 void LifecycleTest::testMultiDispose()
81 {
82     VclPtrInstance<WorkWindow> xWin(nullptr, WB_APP|WB_STDWORK);
83     CPPUNIT_ASSERT(xWin.get() != nullptr);
84     xWin->disposeOnce();
85     xWin->disposeOnce();
86     xWin->disposeOnce();
87     CPPUNIT_ASSERT(!xWin->GetWindow(GetWindowType::Parent));
88     CPPUNIT_ASSERT(!xWin->GetChild(0));
89     CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(0), xWin->GetChildCount());
90 }
91 
testWidgets(vcl::Window * pParent)92 void LifecycleTest::testWidgets(vcl::Window *pParent)
93 {
94     {
95         ScopedVclPtrInstance< PushButton > aPtr( pParent );
96         (void)aPtr; // silence unused variable warning
97     }
98     {
99         ScopedVclPtrInstance< OKButton > aPtr( pParent );
100         (void)aPtr; // silence unused variable warning
101     }
102     {
103         ScopedVclPtrInstance< CancelButton > aPtr( pParent );
104         (void)aPtr; // silence unused variable warning
105     }
106     {
107         ScopedVclPtrInstance< HelpButton > aPtr( pParent );
108         (void)aPtr; // silence unused variable warning
109     }
110 
111     // Some widgets really insist on adoption.
112     if (pParent)
113     {
114         {
115             ScopedVclPtrInstance< CheckBox > aPtr( pParent );
116             (void)aPtr; // silence unused variable warning
117         }
118         {
119             ScopedVclPtrInstance< Edit > aPtr( pParent );
120             (void)aPtr; // silence unused variable warning
121         }
122         {
123             ScopedVclPtrInstance< ComboBox > aPtr( pParent );
124             (void)aPtr; // silence unused variable warning
125         }
126         {
127             ScopedVclPtrInstance< RadioButton > aPtr( pParent );
128             (void)aPtr; // silence unused variable warning
129         }
130     }
131 }
132 
testIsolatedWidgets()133 void LifecycleTest::testIsolatedWidgets()
134 {
135     testWidgets(nullptr);
136 }
137 
testParentedWidgets()138 void LifecycleTest::testParentedWidgets()
139 {
140     ScopedVclPtrInstance<WorkWindow> xWin(nullptr, WB_APP|WB_STDWORK);
141     CPPUNIT_ASSERT(xWin.get() != nullptr);
142     xWin->Show();
143     testWidgets(xWin);
144 }
145 
146 class DisposableChild : public vcl::Window
147 {
148 public:
DisposableChild(vcl::Window * pParent)149     explicit DisposableChild(vcl::Window *pParent) : vcl::Window(pParent) {}
~DisposableChild()150     virtual ~DisposableChild() override
151     {
152         disposeOnce();
153     }
154 };
155 
testChildDispose()156 void LifecycleTest::testChildDispose()
157 {
158     VclPtrInstance<WorkWindow> xWin(nullptr, WB_APP|WB_STDWORK);
159     CPPUNIT_ASSERT(xWin.get() != nullptr);
160     VclPtrInstance< DisposableChild > xChild( xWin.get() );
161     xWin->Show();
162     xChild->disposeOnce();
163     xWin->disposeOnce();
164 }
165 
testPostDispose()166 void LifecycleTest::testPostDispose()
167 {
168     VclPtrInstance<WorkWindow> xWin(nullptr, WB_APP|WB_STDWORK);
169     xWin->disposeOnce();
170 
171     // check selected methods continue to work post-dispose
172     CPPUNIT_ASSERT(!xWin->GetParent());
173     xWin->Show();
174     CPPUNIT_ASSERT(!xWin->IsReallyShown());
175     CPPUNIT_ASSERT(!xWin->IsEnabled());
176     CPPUNIT_ASSERT(!xWin->IsInputEnabled());
177     CPPUNIT_ASSERT(!xWin->GetChild(0));
178     CPPUNIT_ASSERT(!xWin->GetWindow(GetWindowType::Parent));
179 }
180 
181 class FocusCrashPostDispose : public TabControl
182 {
183 public:
FocusCrashPostDispose(vcl::Window * pParent)184     explicit FocusCrashPostDispose(vcl::Window *pParent) :
185         TabControl(pParent, 0)
186     {
187     }
PreNotify(NotifyEvent &)188     virtual bool PreNotify( NotifyEvent& ) override
189     {
190         return false;
191     }
EventNotify(NotifyEvent &)192     virtual bool EventNotify( NotifyEvent& ) override
193     {
194         return false;
195     }
GetFocus()196     virtual void GetFocus() override
197     {
198         CPPUNIT_FAIL("get focus");
199     }
LoseFocus()200     virtual void LoseFocus() override
201     {
202         CPPUNIT_FAIL("this should never be called");
203     }
204 };
205 
testFocus()206 void LifecycleTest::testFocus()
207 {
208     ScopedVclPtrInstance<WorkWindow> xWin(nullptr, WB_APP|WB_STDWORK);
209     ScopedVclPtrInstance< FocusCrashPostDispose > xChild(xWin);
210     xWin->Show();
211     xChild->GrabFocus();
212     // process asynchronous ToTop
213     Scheduler::ProcessTaskScheduling();
214     // FIXME: really awful to test focus issues without showing windows.
215     // CPPUNIT_ASSERT(xChild->HasFocus());
216 }
217 
218 template <class vcl_type>
219 class LeakTestClass : public vcl_type
220 {
221     bool &mrDeleted;
222 public:
223     template<typename... Arg>
LeakTestClass(bool & bDeleted,Arg &&...arg)224         LeakTestClass(bool &bDeleted, Arg &&... arg) :
225             vcl_type(std::forward<Arg>(arg)...),
226             mrDeleted(bDeleted)
227     {
228         mrDeleted = false;
229     }
~LeakTestClass()230     ~LeakTestClass()
231     {
232         mrDeleted = true;
233     }
234 };
235 
236 class LeakTestObject
237 {
238     bool                mbDeleted;
239     VclPtr<vcl::Window> mxRef;
240     void               *mpRef;
LeakTestObject()241     LeakTestObject()
242         : mbDeleted(false)
243         , mpRef(nullptr)
244     {
245     }
246 public:
247     template<typename vcl_type, typename... Arg> static LeakTestObject *
Create(Arg &&...arg)248         Create(Arg &&... arg)
249     {
250         LeakTestObject *pNew = new LeakTestObject();
251         pNew->mxRef = VclPtr< LeakTestClass< vcl_type > >::Create( pNew->mbDeleted,
252                                                                    std::forward<Arg>(arg)...);
253         pNew->mpRef = static_cast<void *>(static_cast<vcl::Window *>(pNew->mxRef));
254         return pNew;
255     }
getRef() const256     const VclPtr<vcl::Window>& getRef() const { return mxRef; }
disposeAndClear()257     void disposeAndClear()
258     {
259         mxRef.disposeAndClear();
260     }
assertDeleted()261     void assertDeleted()
262     {
263         if (!mbDeleted)
264         {
265             OUStringBuffer aMsg = "Type '";
266             vcl::Window *pWin = static_cast<vcl::Window *>(mpRef);
267             aMsg.appendAscii(typeid(*pWin).name());
268             aMsg.append("' not freed after dispose");
269             CPPUNIT_FAIL(OUStringToOString(aMsg.makeStringAndClear(),
270                                            RTL_TEXTENCODING_UTF8).getStr());
271         }
272     }
273 };
274 
testLeakage()275 void LifecycleTest::testLeakage()
276 {
277     std::vector<LeakTestObject *> aObjects;
278 
279     // Create objects
280     aObjects.push_back(LeakTestObject::Create<WorkWindow>(nullptr, WB_APP|WB_STDWORK));
281     VclPtr<vcl::Window> xParent = aObjects.back()->getRef();
282 
283     aObjects.push_back(LeakTestObject::Create<PushButton>(xParent));
284     aObjects.push_back(LeakTestObject::Create<OKButton>(xParent));
285     aObjects.push_back(LeakTestObject::Create<CancelButton>(xParent));
286     aObjects.push_back(LeakTestObject::Create<HelpButton>(xParent));
287     aObjects.push_back(LeakTestObject::Create<CheckBox>(xParent));
288     aObjects.push_back(LeakTestObject::Create<Edit>(xParent));
289     aObjects.push_back(LeakTestObject::Create<ComboBox>(xParent));
290     aObjects.push_back(LeakTestObject::Create<RadioButton>(xParent));
291 
292     { // something that looks like a dialog
293         aObjects.push_back(LeakTestObject::Create<Dialog>(xParent,WB_CLIPCHILDREN|WB_MOVEABLE|WB_3DLOOK|WB_CLOSEABLE|WB_SIZEABLE));
294         VclPtr<vcl::Window> xDlgParent = aObjects.back()->getRef();
295         aObjects.push_back(LeakTestObject::Create<VclVBox>(xDlgParent));
296         VclPtr<vcl::Window> xVBox = aObjects.back()->getRef();
297         aObjects.push_back(LeakTestObject::Create<VclVButtonBox>(xVBox));
298     }
299 
300     aObjects.push_back(LeakTestObject::Create<Dialog>(xParent, "PrintProgressDialog", "vcl/ui/printprogressdialog.ui"));
301     xParent.clear();
302 
303     for (auto i = aObjects.rbegin(); i != aObjects.rend(); ++i)
304         (*i)->getRef()->Show();
305 
306     for (auto i = aObjects.rbegin(); i != aObjects.rend(); ++i)
307         (*i)->disposeAndClear();
308 
309     for (auto i = aObjects.begin(); i != aObjects.end(); ++i)
310         (*i)->assertDeleted();
311 
312     for (auto i = aObjects.begin(); i != aObjects.end(); ++i)
313         delete *i;
314 }
315 
testToolkit()316 void LifecycleTest::testToolkit()
317 {
318     LeakTestObject *pVclWin = LeakTestObject::Create<WorkWindow>(nullptr, WB_APP|WB_STDWORK);
319     css::uno::Reference<css::awt::XWindow> xWindow(pVclWin->getRef()->GetComponentInterface(), css::uno::UNO_QUERY);
320     CPPUNIT_ASSERT(xWindow.is());
321 
322     // test UNO dispose
323     css::uno::Reference<css::lang::XComponent> xWinComponent = xWindow;
324     CPPUNIT_ASSERT(xWinComponent.is());
325     CPPUNIT_ASSERT(!pVclWin->getRef()->IsDisposed());
326     xWinComponent->dispose();
327     CPPUNIT_ASSERT(pVclWin->getRef()->IsDisposed());
328 
329     // test UNO cleanup
330     xWinComponent.clear();
331     xWindow.clear();
332     pVclWin->disposeAndClear();
333     pVclWin->assertDeleted();
334 
335     delete pVclWin;
336 }
337 
338 CPPUNIT_TEST_SUITE_REGISTRATION(LifecycleTest);
339 
340 CPPUNIT_PLUGIN_IMPLEMENT();
341 
342 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
343