1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the test suite of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | #include <QtTest/QtTest> |
30 | |
31 | #include <QPointer> |
32 | #ifndef QT_NO_WIDGETS |
33 | #include <QWidget> |
34 | #endif |
35 | |
36 | class tst_QPointer : public QObject |
37 | { |
38 | Q_OBJECT |
39 | public: |
40 | inline tst_QPointer *me() const |
41 | { return const_cast<tst_QPointer *>(this); } |
42 | |
43 | private slots: |
44 | void constructors(); |
45 | void destructor(); |
46 | void assignment_operators(); |
47 | void equality_operators(); |
48 | void swap(); |
49 | void isNull(); |
50 | void dereference_operators(); |
51 | void disconnect(); |
52 | void castDuringDestruction(); |
53 | void threadSafety(); |
54 | |
55 | void qvariantCast(); |
56 | void constPointer(); |
57 | void constQPointer(); |
58 | }; |
59 | |
60 | void tst_QPointer::constructors() |
61 | { |
62 | QPointer<QObject> p1; |
63 | QPointer<QObject> p2(this); |
64 | QPointer<QObject> p3(p2); |
65 | QCOMPARE(p1, QPointer<QObject>(0)); |
66 | QCOMPARE(p2, QPointer<QObject>(this)); |
67 | QCOMPARE(p3, QPointer<QObject>(this)); |
68 | } |
69 | |
70 | void tst_QPointer::destructor() |
71 | { |
72 | // Make two QPointer's to the same object |
73 | QObject *object = new QObject; |
74 | QPointer<QObject> p1 = object; |
75 | QPointer<QObject> p2 = object; |
76 | // Check that they point to the correct object |
77 | QCOMPARE(p1, QPointer<QObject>(object)); |
78 | QCOMPARE(p2, QPointer<QObject>(object)); |
79 | QCOMPARE(p1, p2); |
80 | // Destroy the guarded object |
81 | delete object; |
82 | // Check that both pointers were zeroed |
83 | QCOMPARE(p1, QPointer<QObject>(0)); |
84 | QCOMPARE(p2, QPointer<QObject>(0)); |
85 | QCOMPARE(p1, p2); |
86 | } |
87 | |
88 | void tst_QPointer::assignment_operators() |
89 | { |
90 | QPointer<QObject> p1; |
91 | QPointer<QObject> p2; |
92 | |
93 | // Test assignment with a QObject-derived object pointer |
94 | p1 = this; |
95 | p2 = p1; |
96 | QCOMPARE(p1, QPointer<QObject>(this)); |
97 | QCOMPARE(p2, QPointer<QObject>(this)); |
98 | QCOMPARE(p1, QPointer<QObject>(p2)); |
99 | |
100 | // Test assignment with a null pointer |
101 | p1 = 0; |
102 | p2 = p1; |
103 | QCOMPARE(p1, QPointer<QObject>(0)); |
104 | QCOMPARE(p2, QPointer<QObject>(0)); |
105 | QCOMPARE(p1, QPointer<QObject>(p2)); |
106 | |
107 | // Test assignment with a real QObject pointer |
108 | QObject *object = new QObject; |
109 | |
110 | p1 = object; |
111 | p2 = p1; |
112 | QCOMPARE(p1, QPointer<QObject>(object)); |
113 | QCOMPARE(p2, QPointer<QObject>(object)); |
114 | QCOMPARE(p1, QPointer<QObject>(p2)); |
115 | |
116 | // Test assignment with the same pointer that's already guarded |
117 | p1 = object; |
118 | p2 = p1; |
119 | QCOMPARE(p1, QPointer<QObject>(object)); |
120 | QCOMPARE(p2, QPointer<QObject>(object)); |
121 | QCOMPARE(p1, QPointer<QObject>(p2)); |
122 | |
123 | // Cleanup |
124 | delete object; |
125 | } |
126 | |
127 | void tst_QPointer::equality_operators() |
128 | { |
129 | QPointer<QObject> p1; |
130 | QPointer<QObject> p2; |
131 | |
132 | QVERIFY(p1 == p2); |
133 | |
134 | QObject *object = 0; |
135 | #ifndef QT_NO_WIDGETS |
136 | QWidget *widget = 0; |
137 | #endif |
138 | |
139 | p1 = object; |
140 | QVERIFY(p1 == p2); |
141 | QVERIFY(p1 == object); |
142 | p2 = object; |
143 | QVERIFY(p2 == p1); |
144 | QVERIFY(p2 == object); |
145 | |
146 | p1 = this; |
147 | QVERIFY(p1 != p2); |
148 | p2 = p1; |
149 | QVERIFY(p1 == p2); |
150 | |
151 | // compare to zero |
152 | p1 = 0; |
153 | QVERIFY(p1 == 0); |
154 | QVERIFY(0 == p1); |
155 | QVERIFY(p2 != 0); |
156 | QVERIFY(0 != p2); |
157 | QVERIFY(p1 == object); |
158 | QVERIFY(object == p1); |
159 | QVERIFY(p2 != object); |
160 | QVERIFY(object != p2); |
161 | #ifndef QT_NO_WIDGETS |
162 | QVERIFY(p1 == widget); |
163 | QVERIFY(widget == p1); |
164 | QVERIFY(p2 != widget); |
165 | QVERIFY(widget != p2); |
166 | #endif |
167 | } |
168 | |
169 | void tst_QPointer::swap() |
170 | { |
171 | QPointer<QObject> c1, c2; |
172 | { |
173 | QObject o; |
174 | c1 = &o; |
175 | QVERIFY(c2.isNull()); |
176 | QCOMPARE(c1.data(), &o); |
177 | c1.swap(other&: c2); |
178 | QVERIFY(c1.isNull()); |
179 | QCOMPARE(c2.data(), &o); |
180 | } |
181 | QVERIFY(c1.isNull()); |
182 | QVERIFY(c2.isNull()); |
183 | } |
184 | |
185 | void tst_QPointer::isNull() |
186 | { |
187 | QPointer<QObject> p1; |
188 | QVERIFY(p1.isNull()); |
189 | p1 = this; |
190 | QVERIFY(!p1.isNull()); |
191 | p1 = 0; |
192 | QVERIFY(p1.isNull()); |
193 | } |
194 | |
195 | void tst_QPointer::dereference_operators() |
196 | { |
197 | QPointer<tst_QPointer> p1 = this; |
198 | QPointer<tst_QPointer> p2; |
199 | |
200 | // operator->() -- only makes sense if not null |
201 | QObject *object = p1->me(); |
202 | QCOMPARE(object, this); |
203 | |
204 | // operator*() -- only makes sense if not null |
205 | QObject &ref = *p1; |
206 | QCOMPARE(&ref, this); |
207 | |
208 | // operator T*() |
209 | QCOMPARE(static_cast<QObject *>(p1), this); |
210 | QCOMPARE(static_cast<QObject *>(p2), static_cast<QObject *>(0)); |
211 | |
212 | // data() |
213 | QCOMPARE(p1.data(), this); |
214 | QCOMPARE(p2.data(), static_cast<QObject *>(0)); |
215 | } |
216 | |
217 | void tst_QPointer::disconnect() |
218 | { |
219 | // Verify that pointer remains guarded when all signals are disconencted. |
220 | QPointer<QObject> p1 = new QObject; |
221 | QVERIFY(!p1.isNull()); |
222 | p1->disconnect(); |
223 | QVERIFY(!p1.isNull()); |
224 | delete static_cast<QObject *>(p1); |
225 | QVERIFY(p1.isNull()); |
226 | } |
227 | |
228 | class ChildObject : public QObject |
229 | { |
230 | QPointer<QObject> guardedPointer; |
231 | |
232 | public: |
233 | ChildObject(QObject *parent) |
234 | : QObject(parent), guardedPointer(parent) |
235 | { } |
236 | ~ChildObject(); |
237 | }; |
238 | |
239 | ChildObject::~ChildObject() |
240 | { |
241 | QCOMPARE(static_cast<QObject *>(guardedPointer), static_cast<QObject *>(0)); |
242 | QCOMPARE(qobject_cast<QObject *>(guardedPointer), static_cast<QObject *>(0)); |
243 | } |
244 | |
245 | #ifndef QT_NO_WIDGETS |
246 | class ChildWidget : public QWidget |
247 | { |
248 | QPointer<QWidget> guardedPointer; |
249 | |
250 | public: |
251 | ChildWidget(QWidget *parent) |
252 | : QWidget(parent), guardedPointer(parent) |
253 | { } |
254 | ~ChildWidget(); |
255 | }; |
256 | |
257 | ChildWidget::~ChildWidget() |
258 | { |
259 | QCOMPARE(static_cast<QWidget *>(guardedPointer), parentWidget()); |
260 | QCOMPARE(qobject_cast<QWidget *>(guardedPointer), parentWidget()); |
261 | } |
262 | #endif |
263 | |
264 | class DerivedChild; |
265 | |
266 | class DerivedParent : public QObject |
267 | { |
268 | Q_OBJECT |
269 | |
270 | DerivedChild *derivedChild; |
271 | |
272 | public: |
273 | DerivedParent(); |
274 | ~DerivedParent(); |
275 | }; |
276 | |
277 | class DerivedChild : public QObject |
278 | { |
279 | Q_OBJECT |
280 | |
281 | DerivedParent *parentPointer; |
282 | QPointer<DerivedParent> guardedParentPointer; |
283 | |
284 | public: |
285 | DerivedChild(DerivedParent *parent) |
286 | : QObject(parent), parentPointer(parent), guardedParentPointer(parent) |
287 | { } |
288 | ~DerivedChild(); |
289 | }; |
290 | |
291 | DerivedParent::DerivedParent() |
292 | : QObject() |
293 | { |
294 | derivedChild = new DerivedChild(this); |
295 | } |
296 | |
297 | DerivedParent::~DerivedParent() |
298 | { |
299 | delete derivedChild; |
300 | } |
301 | |
302 | DerivedChild::~DerivedChild() |
303 | { |
304 | QCOMPARE(static_cast<DerivedParent *>(guardedParentPointer), parentPointer); |
305 | QCOMPARE(qobject_cast<DerivedParent *>(guardedParentPointer), parentPointer); |
306 | } |
307 | |
308 | void tst_QPointer::castDuringDestruction() |
309 | { |
310 | { |
311 | QObject *parentObject = new QObject(); |
312 | (void) new ChildObject(parentObject); |
313 | delete parentObject; |
314 | } |
315 | |
316 | #ifndef QT_NO_WIDGETS |
317 | { |
318 | QWidget *parentWidget = new QWidget(); |
319 | (void) new ChildWidget(parentWidget); |
320 | delete parentWidget; |
321 | } |
322 | #endif |
323 | |
324 | { |
325 | delete new DerivedParent(); |
326 | } |
327 | } |
328 | |
329 | class TestRunnable : public QObject, public QRunnable { |
330 | void run() { |
331 | QPointer<QObject> obj1 = new QObject; |
332 | QPointer<QObject> obj2 = new QObject; |
333 | obj1->moveToThread(thread: thread()); // this is the owner thread |
334 | obj1->deleteLater(); // the delete will happen in the owner thread |
335 | obj2->moveToThread(thread: thread()); // this is the owner thread |
336 | obj2->deleteLater(); // the delete will happen in the owner thread |
337 | } |
338 | }; |
339 | |
340 | void tst_QPointer::threadSafety() |
341 | { |
342 | |
343 | QThread owner; |
344 | owner.start(); |
345 | |
346 | QThreadPool pool; |
347 | for (int i = 0; i < 300; i++) { |
348 | QPointer<TestRunnable> task = new TestRunnable; |
349 | task->setAutoDelete(true); |
350 | task->moveToThread(thread: &owner); |
351 | pool.start(runnable: task); |
352 | } |
353 | pool.waitForDone(); |
354 | |
355 | owner.quit(); |
356 | owner.wait(); |
357 | } |
358 | |
359 | void tst_QPointer::qvariantCast() |
360 | { |
361 | QFile file; |
362 | QPointer<QFile> tracking = &file; |
363 | tracking->setObjectName("A test name" ); |
364 | QVariant v = QVariant::fromValue(value: tracking); |
365 | |
366 | { |
367 | QPointer<QObject> other = qPointerFromVariant<QObject>(variant: v); |
368 | QCOMPARE(other->objectName(), QString::fromLatin1("A test name" )); |
369 | } |
370 | { |
371 | QPointer<QIODevice> other = qPointerFromVariant<QIODevice>(variant: v); |
372 | QCOMPARE(other->objectName(), QString::fromLatin1("A test name" )); |
373 | } |
374 | { |
375 | QPointer<QFile> other = qPointerFromVariant<QFile>(variant: v); |
376 | QCOMPARE(other->objectName(), QString::fromLatin1("A test name" )); |
377 | } |
378 | { |
379 | QPointer<QThread> other = qPointerFromVariant<QThread>(variant: v); |
380 | QVERIFY(!other); |
381 | } |
382 | { |
383 | QPointer<QFile> toBeDeleted = new QFile; |
384 | QVariant deletedVariant = QVariant::fromValue(value: toBeDeleted); |
385 | delete toBeDeleted; |
386 | QPointer<QObject> deleted = qPointerFromVariant<QObject>(variant: deletedVariant); |
387 | QVERIFY(!deleted); |
388 | } |
389 | |
390 | // Intentionally does not compile. |
391 | // QPointer<int> sop = qPointerFromVariant<int>(v); |
392 | } |
393 | |
394 | void tst_QPointer::constPointer() |
395 | { |
396 | // Compile-time test that QPointer<const T> works. |
397 | QPointer<const QFile> fp = new QFile; |
398 | delete fp.data(); |
399 | } |
400 | |
401 | void tst_QPointer::constQPointer() |
402 | { |
403 | // Check that const QPointers work. It's a bit weird to mark a pointer |
404 | // const if its value can change, but the shallow-const principle in C/C++ |
405 | // allows this, and people use it, so document it with a test. |
406 | // |
407 | // It's unlikely that this test will fail in and out of itself, but it |
408 | // presents the use-case to static and dynamic checkers that can raise |
409 | // a warning (hopefully) should this become an issue. |
410 | QObject *o = new QObject(this); |
411 | const QPointer<QObject> p = o; |
412 | delete o; |
413 | QVERIFY(!p); |
414 | } |
415 | |
416 | |
417 | QTEST_MAIN(tst_QPointer) |
418 | #include "tst_qpointer.moc" |
419 | |