Line data Source code
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 <unotest/filters-test.hxx>
11 : #include <test/bootstrapfixture.hxx>
12 :
13 : #include <vcl/wrkwin.hxx>
14 : #include <vcl/button.hxx>
15 : #include <vcl/edit.hxx>
16 : #include <vcl/combobox.hxx>
17 : #include <vcl/field.hxx>
18 : #include <vcl/virdev.hxx>
19 : #include <vcl/tabctrl.hxx>
20 : #include <vcl/dialog.hxx>
21 : #include <vcl/layout.hxx>
22 : #include <vcl/svapp.hxx>
23 :
24 18 : class LifecycleTest : public test::BootstrapFixture
25 : {
26 : void testWidgets(vcl::Window *pParent);
27 :
28 : public:
29 9 : LifecycleTest() : BootstrapFixture(true, false) {}
30 :
31 : void testCast();
32 : void testVirtualDevice();
33 : void testMultiDispose();
34 : void testIsolatedWidgets();
35 : void testParentedWidgets();
36 : void testChildDispose();
37 : void testPostDispose();
38 : void testFocus();
39 : void testLeakage();
40 :
41 2 : CPPUNIT_TEST_SUITE(LifecycleTest);
42 1 : CPPUNIT_TEST(testCast);
43 1 : CPPUNIT_TEST(testVirtualDevice);
44 1 : CPPUNIT_TEST(testMultiDispose);
45 1 : CPPUNIT_TEST(testIsolatedWidgets);
46 1 : CPPUNIT_TEST(testParentedWidgets);
47 1 : CPPUNIT_TEST(testChildDispose);
48 1 : CPPUNIT_TEST(testPostDispose);
49 1 : CPPUNIT_TEST(testFocus);
50 1 : CPPUNIT_TEST(testLeakage);
51 5 : CPPUNIT_TEST_SUITE_END();
52 : };
53 :
54 : // A compile time sanity check
55 1 : void LifecycleTest::testCast()
56 : {
57 1 : ScopedVclPtrInstance< PushButton > xButton( nullptr, 0 );
58 2 : ScopedVclPtr<vcl::Window> xWindow(xButton);
59 :
60 2 : ScopedVclPtrInstance< MetricField > xField( nullptr, 0 );
61 2 : ScopedVclPtr<SpinField> xSpin(xField);
62 2 : ScopedVclPtr<Edit> xEdit(xField);
63 :
64 : // the following line should NOT compile
65 : // VclPtr<PushButton> xButton2(xWindow);
66 1 : }
67 :
68 1 : void LifecycleTest::testVirtualDevice()
69 : {
70 1 : VclPtr<VirtualDevice> pVDev = VclPtr< VirtualDevice >::Create();
71 2 : ScopedVclPtrInstance< VirtualDevice > pVDev2;
72 2 : VclPtrInstance<VirtualDevice> pVDev3;
73 2 : VclPtrInstance<VirtualDevice> pVDev4( 1 );
74 2 : CPPUNIT_ASSERT(!!pVDev && !!pVDev2 && !!pVDev3 && !!pVDev4);
75 1 : }
76 :
77 1 : void LifecycleTest::testMultiDispose()
78 : {
79 1 : VclPtrInstance<WorkWindow> xWin(nullptr, WB_APP|WB_STDWORK);
80 1 : CPPUNIT_ASSERT(xWin.get() != NULL);
81 1 : xWin->disposeOnce();
82 1 : xWin->disposeOnce();
83 1 : xWin->disposeOnce();
84 1 : CPPUNIT_ASSERT(xWin->GetWindow(GetWindowType::Parent) == NULL);
85 1 : CPPUNIT_ASSERT(xWin->GetChild(0) == NULL);
86 1 : CPPUNIT_ASSERT(xWin->GetChildCount() == 0);
87 1 : }
88 :
89 2 : void LifecycleTest::testWidgets(vcl::Window *pParent)
90 : {
91 2 : { ScopedVclPtrInstance< PushButton > aPtr( pParent ); }
92 2 : { ScopedVclPtrInstance< OKButton > aPtr( pParent ); }
93 2 : { ScopedVclPtrInstance< CancelButton > aPtr( pParent ); }
94 2 : { ScopedVclPtrInstance< HelpButton > aPtr( pParent ); }
95 :
96 : // Some widgets really insist on adoption.
97 2 : if (pParent)
98 : {
99 1 : { ScopedVclPtrInstance< CheckBox > aPtr( pParent ); }
100 1 : { ScopedVclPtrInstance< Edit > aPtr( pParent ); }
101 1 : { ScopedVclPtrInstance< ComboBox > aPtr( pParent ); }
102 1 : { ScopedVclPtrInstance< RadioButton > aPtr( pParent ); }
103 : }
104 2 : }
105 :
106 1 : void LifecycleTest::testIsolatedWidgets()
107 : {
108 1 : testWidgets(NULL);
109 1 : }
110 :
111 1 : void LifecycleTest::testParentedWidgets()
112 : {
113 1 : ScopedVclPtrInstance<WorkWindow> xWin(nullptr, WB_APP|WB_STDWORK);
114 1 : CPPUNIT_ASSERT(xWin.get() != NULL);
115 1 : xWin->Show();
116 1 : testWidgets(xWin);
117 1 : }
118 :
119 : class DisposableChild : public vcl::Window
120 : {
121 : public:
122 1 : explicit DisposableChild(vcl::Window *pParent) : vcl::Window(pParent) {}
123 2 : virtual ~DisposableChild()
124 2 : {
125 1 : disposeOnce();
126 2 : }
127 : };
128 :
129 1 : void LifecycleTest::testChildDispose()
130 : {
131 1 : VclPtrInstance<WorkWindow> xWin(nullptr, WB_APP|WB_STDWORK);
132 1 : CPPUNIT_ASSERT(xWin.get() != NULL);
133 2 : VclPtrInstance< DisposableChild > xChild( xWin.get() );
134 1 : xWin->Show();
135 1 : xChild->disposeOnce();
136 2 : xWin->disposeOnce();
137 1 : }
138 :
139 1 : void LifecycleTest::testPostDispose()
140 : {
141 1 : VclPtrInstance<WorkWindow> xWin(nullptr, WB_APP|WB_STDWORK);
142 1 : xWin->disposeOnce();
143 :
144 : // check selected methods continue to work post-dispose
145 1 : CPPUNIT_ASSERT(!xWin->GetParent());
146 1 : xWin->Show();
147 1 : CPPUNIT_ASSERT(!xWin->IsReallyShown());
148 1 : CPPUNIT_ASSERT(!xWin->IsEnabled());
149 1 : CPPUNIT_ASSERT(!xWin->IsInputEnabled());
150 1 : CPPUNIT_ASSERT(!xWin->GetChild(0));
151 1 : CPPUNIT_ASSERT(!xWin->GetWindow(GetWindowType::Parent));
152 1 : }
153 :
154 2 : class FocusCrashPostDispose : public TabControl
155 : {
156 : public:
157 1 : explicit FocusCrashPostDispose(vcl::Window *pParent) :
158 1 : TabControl(pParent, 0)
159 : {
160 1 : }
161 0 : virtual bool PreNotify( NotifyEvent& ) SAL_OVERRIDE
162 : {
163 0 : return false;
164 : }
165 0 : virtual bool Notify( NotifyEvent& ) SAL_OVERRIDE
166 : {
167 0 : return false;
168 : }
169 0 : virtual void GetFocus() SAL_OVERRIDE
170 : {
171 0 : CPPUNIT_FAIL("get focus");
172 0 : }
173 0 : virtual void LoseFocus() SAL_OVERRIDE
174 : {
175 0 : CPPUNIT_FAIL("this should never be called");
176 0 : }
177 : };
178 :
179 1 : void LifecycleTest::testFocus()
180 : {
181 1 : ScopedVclPtrInstance<WorkWindow> xWin(nullptr, WB_APP|WB_STDWORK);
182 2 : ScopedVclPtrInstance< FocusCrashPostDispose > xChild(xWin);
183 1 : xWin->Show();
184 1 : xChild->GrabFocus();
185 : // process asynchronous ToTop
186 2 : Scheduler::ProcessTaskScheduling(false);
187 : // FIXME: really awful to test focus issues without showing windows.
188 : // CPPUNIT_ASSERT(xChild->HasFocus());
189 1 : }
190 :
191 : template <class vcl_type>
192 : class LeakTestClass : public vcl_type
193 : {
194 : bool &mrDeleted;
195 : public:
196 : template<typename... Arg>
197 13 : LeakTestClass(bool &bDeleted, Arg &&... arg) :
198 15 : vcl_type(std::forward<Arg>(arg)...),
199 15 : mrDeleted(bDeleted)
200 : {
201 13 : mrDeleted = false;
202 13 : }
203 26 : ~LeakTestClass()
204 : {
205 13 : mrDeleted = true;
206 39 : }
207 : };
208 :
209 13 : class LeakTestObject
210 : {
211 : bool mbDeleted;
212 : VclPtr<vcl::Window> mxRef;
213 : void *mpRef;
214 13 : LeakTestObject() {}
215 : public:
216 : template<typename vcl_type, typename... Arg> static LeakTestObject *
217 13 : Create(Arg &&... arg)
218 : {
219 13 : LeakTestObject *pNew = new LeakTestObject();
220 13 : pNew->mxRef = VclPtr< LeakTestClass< vcl_type > >::Create( pNew->mbDeleted,
221 13 : std::forward<Arg>(arg)...);
222 13 : pNew->mpRef = static_cast<void *>(static_cast<vcl::Window *>(pNew->mxRef));
223 13 : return pNew;
224 : }
225 16 : VclPtr<vcl::Window> getRef() { return mxRef; }
226 13 : void disposeAndClear()
227 : {
228 13 : mxRef.disposeAndClear();
229 13 : }
230 13 : void assertDeleted()
231 : {
232 13 : if (!mbDeleted)
233 : {
234 0 : OUStringBuffer aMsg = "Type '";
235 0 : vcl::Window *pWin = static_cast<vcl::Window *>(mpRef);
236 0 : aMsg.appendAscii(typeid(*pWin).name());
237 0 : aMsg.append("' not freed after dispose");
238 0 : CPPUNIT_FAIL(OUStringToOString(aMsg.makeStringAndClear(),
239 0 : RTL_TEXTENCODING_UTF8).getStr());
240 : }
241 13 : }
242 : };
243 :
244 1 : void LifecycleTest::testLeakage()
245 : {
246 1 : std::vector<LeakTestObject *> aObjects;
247 :
248 : // Create objects
249 1 : aObjects.push_back(LeakTestObject::Create<WorkWindow>(nullptr, WB_APP|WB_STDWORK));
250 2 : VclPtr<vcl::Window> xParent = aObjects.back()->getRef();
251 :
252 1 : aObjects.push_back(LeakTestObject::Create<PushButton>(xParent));
253 1 : aObjects.push_back(LeakTestObject::Create<OKButton>(xParent));
254 1 : aObjects.push_back(LeakTestObject::Create<CancelButton>(xParent));
255 1 : aObjects.push_back(LeakTestObject::Create<HelpButton>(xParent));
256 1 : aObjects.push_back(LeakTestObject::Create<CheckBox>(xParent));
257 1 : aObjects.push_back(LeakTestObject::Create<Edit>(xParent));
258 1 : aObjects.push_back(LeakTestObject::Create<ComboBox>(xParent));
259 1 : aObjects.push_back(LeakTestObject::Create<RadioButton>(xParent));
260 :
261 : { // something that looks like a dialog
262 1 : aObjects.push_back(LeakTestObject::Create<Dialog>(xParent,WB_CLIPCHILDREN|WB_MOVEABLE|WB_3DLOOK|WB_CLOSEABLE|WB_SIZEABLE));
263 1 : VclPtr<vcl::Window> xDlgParent = aObjects.back()->getRef();
264 1 : aObjects.push_back(LeakTestObject::Create<VclVBox>(xDlgParent));
265 2 : VclPtr<vcl::Window> xVBox = aObjects.back()->getRef();
266 2 : aObjects.push_back(LeakTestObject::Create<VclVButtonBox>(xVBox));
267 : }
268 :
269 : #if 0 // FIXME - would be good to get internal paths working.
270 : aObjects.push_back(LeakTestObject::Create<ModelessDialog>(xParent, "PrintProgressDialog", "vcl/ui/printprogressdialog.ui"));
271 : #endif
272 1 : aObjects.push_back(LeakTestObject::Create<ModalDialog>(xParent));
273 1 : xParent.clear();
274 :
275 14 : for (auto i = aObjects.rbegin(); i != aObjects.rend(); ++i)
276 13 : (*i)->getRef()->Show();
277 :
278 14 : for (auto i = aObjects.rbegin(); i != aObjects.rend(); ++i)
279 13 : (*i)->disposeAndClear();
280 :
281 14 : for (auto i = aObjects.begin(); i != aObjects.end(); ++i)
282 13 : (*i)->assertDeleted();
283 :
284 14 : for (auto i = aObjects.begin(); i != aObjects.end(); ++i)
285 14 : delete *i;
286 1 : }
287 :
288 1 : CPPUNIT_TEST_SUITE_REGISTRATION(LifecycleTest);
289 :
290 4 : CPPUNIT_PLUGIN_IMPLEMENT();
291 :
292 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|