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