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 | #include "testtypes.h" |
29 | |
30 | #include <QUrl> |
31 | #include <QDir> |
32 | #include <QDebug> |
33 | #include <qtest.h> |
34 | #include <QPointer> |
35 | #include <QFileInfo> |
36 | #include <QQmlEngine> |
37 | #include <QQmlContext> |
38 | #include <QQmlProperty> |
39 | #include <QQmlComponent> |
40 | #include <QQmlIncubator> |
41 | #include "../../shared/util.h" |
42 | #include <private/qjsvalue_p.h> |
43 | #include <private/qqmlincubator_p.h> |
44 | #include <private/qqmlobjectcreator_p.h> |
45 | |
46 | class tst_qqmlincubator : public QQmlDataTest |
47 | { |
48 | Q_OBJECT |
49 | public: |
50 | tst_qqmlincubator() {} |
51 | |
52 | private slots: |
53 | void initTestCase(); |
54 | |
55 | void incubationMode(); |
56 | void objectDeleted(); |
57 | void clear(); |
58 | void noIncubationController(); |
59 | void forceCompletion(); |
60 | void setInitialState(); |
61 | void clearDuringCompletion(); |
62 | void objectDeletionAfterInit(); |
63 | void recursiveClear(); |
64 | void statusChanged(); |
65 | void asynchronousIfNested(); |
66 | void nestedComponent(); |
67 | void chainedAsynchronousIfNested(); |
68 | void chainedAsynchronousIfNestedOnCompleted(); |
69 | void chainedAsynchronousClear(); |
70 | void selfDelete(); |
71 | void contextDelete(); |
72 | void garbageCollection(); |
73 | void requiredProperties(); |
74 | void deleteInSetInitialState(); |
75 | |
76 | private: |
77 | QQmlIncubationController controller; |
78 | QQmlEngine engine; |
79 | }; |
80 | |
81 | #define VERIFY_ERRORS(component, errorfile) \ |
82 | if (!errorfile) { \ |
83 | if (qgetenv("DEBUG") != "" && !component.errors().isEmpty()) \ |
84 | qWarning() << "Unexpected Errors:" << component.errors(); \ |
85 | QVERIFY(!component.isError()); \ |
86 | QVERIFY(component.errors().isEmpty()); \ |
87 | } else { \ |
88 | QFile file(QQmlDataTest::instance()->testFile(errorfile)); \ |
89 | QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text)); \ |
90 | QByteArray data = file.readAll(); \ |
91 | file.close(); \ |
92 | QList<QByteArray> expected = data.split('\n'); \ |
93 | expected.removeAll(QByteArray("")); \ |
94 | QList<QQmlError> errors = component.errors(); \ |
95 | QList<QByteArray> actual; \ |
96 | for (int ii = 0; ii < errors.count(); ++ii) { \ |
97 | const QQmlError &error = errors.at(ii); \ |
98 | QByteArray errorStr = QByteArray::number(error.line()) + ':' + \ |
99 | QByteArray::number(error.column()) + ':' + \ |
100 | error.description().toUtf8(); \ |
101 | actual << errorStr; \ |
102 | } \ |
103 | if (qgetenv("DEBUG") != "" && expected != actual) \ |
104 | qWarning() << "Expected:" << expected << "Actual:" << actual; \ |
105 | QCOMPARE(expected, actual); \ |
106 | } |
107 | |
108 | void tst_qqmlincubator::initTestCase() |
109 | { |
110 | QQmlDataTest::initTestCase(); |
111 | registerTypes(); |
112 | engine.setIncubationController(&controller); |
113 | } |
114 | |
115 | void tst_qqmlincubator::incubationMode() |
116 | { |
117 | { |
118 | QQmlIncubator incubator; |
119 | QCOMPARE(incubator.incubationMode(), QQmlIncubator::Asynchronous); |
120 | } |
121 | { |
122 | QQmlIncubator incubator(QQmlIncubator::Asynchronous); |
123 | QCOMPARE(incubator.incubationMode(), QQmlIncubator::Asynchronous); |
124 | } |
125 | { |
126 | QQmlIncubator incubator(QQmlIncubator::Synchronous); |
127 | QCOMPARE(incubator.incubationMode(), QQmlIncubator::Synchronous); |
128 | } |
129 | { |
130 | QQmlIncubator incubator(QQmlIncubator::AsynchronousIfNested); |
131 | QCOMPARE(incubator.incubationMode(), QQmlIncubator::AsynchronousIfNested); |
132 | } |
133 | } |
134 | |
135 | void tst_qqmlincubator::objectDeleted() |
136 | { |
137 | { |
138 | QQmlEngine engine; |
139 | QQmlIncubationController controller; |
140 | engine.setIncubationController(&controller); |
141 | SelfRegisteringType::clearMe(); |
142 | |
143 | QQmlComponent component(&engine, testFileUrl(fileName: "objectDeleted.qml" )); |
144 | QVERIFY(component.isReady()); |
145 | |
146 | QQmlIncubator incubator; |
147 | component.create(incubator); |
148 | |
149 | QCOMPARE(incubator.status(), QQmlIncubator::Loading); |
150 | QVERIFY(!SelfRegisteringType::me()); |
151 | |
152 | while (SelfRegisteringOuterType::me() == nullptr && incubator.isLoading()) { |
153 | std::atomic<bool> b{false}; |
154 | controller.incubateWhile(flag: &b); |
155 | } |
156 | |
157 | QVERIFY(SelfRegisteringOuterType::me() != nullptr); |
158 | QVERIFY(incubator.isLoading()); |
159 | |
160 | while (SelfRegisteringType::me() == nullptr && incubator.isLoading()) { |
161 | std::atomic<bool> b{false}; |
162 | controller.incubateWhile(flag: &b); |
163 | } |
164 | |
165 | delete SelfRegisteringType::me(); |
166 | |
167 | { |
168 | std::atomic<bool> b{true}; |
169 | controller.incubateWhile(flag: &b); |
170 | } |
171 | |
172 | QVERIFY(incubator.isError()); |
173 | VERIFY_ERRORS(incubator, "objectDeleted.errors.txt" ); |
174 | QVERIFY(!incubator.object()); |
175 | } |
176 | QVERIFY(SelfRegisteringOuterType::beenDeleted); |
177 | } |
178 | |
179 | void tst_qqmlincubator::clear() |
180 | { |
181 | SelfRegisteringType::clearMe(); |
182 | |
183 | QQmlComponent component(&engine, testFileUrl(fileName: "clear.qml" )); |
184 | QVERIFY(component.isReady()); |
185 | |
186 | // Clear in null state |
187 | { |
188 | QQmlIncubator incubator; |
189 | QVERIFY(incubator.isNull()); |
190 | incubator.clear(); // no effect |
191 | QVERIFY(incubator.isNull()); |
192 | } |
193 | |
194 | // Clear in loading state |
195 | { |
196 | QQmlIncubator incubator; |
197 | component.create(incubator); |
198 | QVERIFY(incubator.isLoading()); |
199 | incubator.clear(); |
200 | QVERIFY(incubator.isNull()); |
201 | } |
202 | |
203 | // Clear mid load |
204 | { |
205 | QQmlIncubator incubator; |
206 | component.create(incubator); |
207 | |
208 | while (SelfRegisteringType::me() == nullptr && incubator.isLoading()) { |
209 | std::atomic<bool> b{false}; |
210 | controller.incubateWhile(flag: &b); |
211 | } |
212 | |
213 | QVERIFY(incubator.isLoading()); |
214 | QVERIFY(SelfRegisteringType::me() != nullptr); |
215 | QPointer<SelfRegisteringType> srt = SelfRegisteringType::me(); |
216 | |
217 | incubator.clear(); |
218 | QVERIFY(incubator.isNull()); |
219 | QVERIFY(srt.isNull()); |
220 | } |
221 | |
222 | // Clear in ready state |
223 | { |
224 | QQmlIncubator incubator; |
225 | component.create(incubator); |
226 | |
227 | { |
228 | std::atomic<bool> b{true}; |
229 | controller.incubateWhile(flag: &b); |
230 | } |
231 | |
232 | QVERIFY(incubator.isReady()); |
233 | QVERIFY(incubator.object() != nullptr); |
234 | QPointer<QObject> obj = incubator.object(); |
235 | |
236 | incubator.clear(); |
237 | QVERIFY(incubator.isNull()); |
238 | QVERIFY(!incubator.object()); |
239 | QVERIFY(!obj.isNull()); |
240 | |
241 | delete obj; |
242 | QVERIFY(obj.isNull()); |
243 | } |
244 | } |
245 | |
246 | void tst_qqmlincubator::noIncubationController() |
247 | { |
248 | // All incubators should behave synchronously when there is no controller |
249 | |
250 | QQmlEngine engine; |
251 | QQmlComponent component(&engine, testFileUrl(fileName: "noIncubationController.qml" )); |
252 | |
253 | QVERIFY(component.isReady()); |
254 | |
255 | { |
256 | QQmlIncubator incubator(QQmlIncubator::Asynchronous); |
257 | component.create(incubator); |
258 | QVERIFY(incubator.isReady()); |
259 | QVERIFY(incubator.object()); |
260 | QCOMPARE(incubator.object()->property("testValue" ).toInt(), 1913); |
261 | delete incubator.object(); |
262 | } |
263 | |
264 | { |
265 | QQmlIncubator incubator(QQmlIncubator::AsynchronousIfNested); |
266 | component.create(incubator); |
267 | QVERIFY(incubator.isReady()); |
268 | QVERIFY(incubator.object()); |
269 | QCOMPARE(incubator.object()->property("testValue" ).toInt(), 1913); |
270 | delete incubator.object(); |
271 | } |
272 | |
273 | { |
274 | QQmlIncubator incubator(QQmlIncubator::Synchronous); |
275 | component.create(incubator); |
276 | QVERIFY(incubator.isReady()); |
277 | QVERIFY(incubator.object()); |
278 | QCOMPARE(incubator.object()->property("testValue" ).toInt(), 1913); |
279 | delete incubator.object(); |
280 | } |
281 | } |
282 | |
283 | void tst_qqmlincubator::forceCompletion() |
284 | { |
285 | QQmlComponent component(&engine, testFileUrl(fileName: "forceCompletion.qml" )); |
286 | QVERIFY(component.isReady()); |
287 | |
288 | { |
289 | // forceCompletion on a null incubator does nothing |
290 | QQmlIncubator incubator; |
291 | QVERIFY(incubator.isNull()); |
292 | incubator.forceCompletion(); |
293 | QVERIFY(incubator.isNull()); |
294 | } |
295 | |
296 | { |
297 | // forceCompletion immediately after creating an asynchronous object completes it |
298 | QQmlIncubator incubator; |
299 | QVERIFY(incubator.isNull()); |
300 | component.create(incubator); |
301 | QVERIFY(incubator.isLoading()); |
302 | |
303 | incubator.forceCompletion(); |
304 | |
305 | QVERIFY(incubator.isReady()); |
306 | QVERIFY(incubator.object() != nullptr); |
307 | QCOMPARE(incubator.object()->property("testValue" ).toInt(), 3499); |
308 | |
309 | delete incubator.object(); |
310 | } |
311 | |
312 | { |
313 | // forceCompletion during creation completes it |
314 | SelfRegisteringType::clearMe(); |
315 | |
316 | QQmlIncubator incubator; |
317 | QVERIFY(incubator.isNull()); |
318 | component.create(incubator); |
319 | QVERIFY(incubator.isLoading()); |
320 | |
321 | while (SelfRegisteringType::me() == nullptr && incubator.isLoading()) { |
322 | std::atomic<bool> b{false}; |
323 | controller.incubateWhile(flag: &b); |
324 | } |
325 | |
326 | QVERIFY(SelfRegisteringType::me() != nullptr); |
327 | QVERIFY(incubator.isLoading()); |
328 | |
329 | incubator.forceCompletion(); |
330 | |
331 | QVERIFY(incubator.isReady()); |
332 | QVERIFY(incubator.object() != nullptr); |
333 | QCOMPARE(incubator.object()->property("testValue" ).toInt(), 3499); |
334 | |
335 | delete incubator.object(); |
336 | } |
337 | |
338 | { |
339 | // forceCompletion on a ready incubator has no effect |
340 | QQmlIncubator incubator; |
341 | QVERIFY(incubator.isNull()); |
342 | component.create(incubator); |
343 | QVERIFY(incubator.isLoading()); |
344 | |
345 | incubator.forceCompletion(); |
346 | |
347 | QVERIFY(incubator.isReady()); |
348 | QVERIFY(incubator.object() != nullptr); |
349 | QCOMPARE(incubator.object()->property("testValue" ).toInt(), 3499); |
350 | |
351 | incubator.forceCompletion(); |
352 | |
353 | QVERIFY(incubator.isReady()); |
354 | QVERIFY(incubator.object() != nullptr); |
355 | QCOMPARE(incubator.object()->property("testValue" ).toInt(), 3499); |
356 | |
357 | delete incubator.object(); |
358 | } |
359 | } |
360 | |
361 | void tst_qqmlincubator::setInitialState() |
362 | { |
363 | QQmlComponent component(&engine, testFileUrl(fileName: "setInitialState.qml" )); |
364 | QVERIFY(component.isReady()); |
365 | |
366 | struct MyIncubator : public QQmlIncubator |
367 | { |
368 | MyIncubator(QQmlIncubator::IncubationMode mode) |
369 | : QQmlIncubator(mode) {} |
370 | |
371 | virtual void setInitialState(QObject *o) { |
372 | QQmlProperty::write(o, "test2" , 19); |
373 | QQmlProperty::write(o, "testData1" , 201); |
374 | } |
375 | }; |
376 | |
377 | { |
378 | MyIncubator incubator(QQmlIncubator::Asynchronous); |
379 | component.create(incubator); |
380 | QVERIFY(incubator.isLoading()); |
381 | std::atomic<bool> b{true}; |
382 | controller.incubateWhile(flag: &b); |
383 | QVERIFY(incubator.isReady()); |
384 | QVERIFY(incubator.object()); |
385 | QCOMPARE(incubator.object()->property("myValueFunctionCalled" ).toBool(), false); |
386 | QCOMPARE(incubator.object()->property("test1" ).toInt(), 502); |
387 | QCOMPARE(incubator.object()->property("test2" ).toInt(), 19); |
388 | delete incubator.object(); |
389 | } |
390 | |
391 | { |
392 | MyIncubator incubator(QQmlIncubator::Synchronous); |
393 | component.create(incubator); |
394 | QVERIFY(incubator.isReady()); |
395 | QVERIFY(incubator.object()); |
396 | QCOMPARE(incubator.object()->property("myValueFunctionCalled" ).toBool(), false); |
397 | QCOMPARE(incubator.object()->property("test1" ).toInt(), 502); |
398 | QCOMPARE(incubator.object()->property("test2" ).toInt(), 19); |
399 | delete incubator.object(); |
400 | } |
401 | } |
402 | |
403 | void tst_qqmlincubator::clearDuringCompletion() |
404 | { |
405 | CompletionRegisteringType::clearMe(); |
406 | SelfRegisteringType::clearMe(); |
407 | |
408 | QQmlComponent component(&engine, testFileUrl(fileName: "clearDuringCompletion.qml" )); |
409 | QVERIFY(component.isReady()); |
410 | |
411 | QQmlIncubator incubator; |
412 | component.create(incubator); |
413 | |
414 | QCOMPARE(incubator.status(), QQmlIncubator::Loading); |
415 | QVERIFY(!CompletionRegisteringType::me()); |
416 | |
417 | while (CompletionRegisteringType::me() == nullptr && incubator.isLoading()) { |
418 | std::atomic<bool> b{false}; |
419 | controller.incubateWhile(flag: &b); |
420 | } |
421 | |
422 | QVERIFY(CompletionRegisteringType::me() != nullptr); |
423 | QVERIFY(SelfRegisteringType::me() != nullptr); |
424 | QVERIFY(incubator.isLoading()); |
425 | |
426 | QPointer<QObject> srt = SelfRegisteringType::me(); |
427 | |
428 | incubator.clear(); |
429 | QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete); |
430 | QCoreApplication::processEvents(); |
431 | QVERIFY(incubator.isNull()); |
432 | QVERIFY(srt.isNull()); |
433 | } |
434 | |
435 | void tst_qqmlincubator::objectDeletionAfterInit() |
436 | { |
437 | QQmlComponent component(&engine, testFileUrl(fileName: "clear.qml" )); |
438 | QVERIFY(component.isReady()); |
439 | |
440 | struct MyIncubator : public QQmlIncubator |
441 | { |
442 | MyIncubator(QQmlIncubator::IncubationMode mode) |
443 | : QQmlIncubator(mode), obj(nullptr) {} |
444 | |
445 | virtual void setInitialState(QObject *o) { |
446 | obj = o; |
447 | } |
448 | |
449 | QObject *obj; |
450 | }; |
451 | |
452 | SelfRegisteringType::clearMe(); |
453 | MyIncubator incubator(QQmlIncubator::Asynchronous); |
454 | component.create(incubator); |
455 | |
456 | while (!incubator.obj && incubator.isLoading()) { |
457 | std::atomic<bool> b{false}; |
458 | controller.incubateWhile(flag: &b); |
459 | } |
460 | |
461 | QVERIFY(incubator.isLoading()); |
462 | QVERIFY(SelfRegisteringType::me() != nullptr); |
463 | |
464 | delete incubator.obj; |
465 | |
466 | incubator.clear(); |
467 | QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete); |
468 | QCoreApplication::processEvents(); |
469 | QVERIFY(incubator.isNull()); |
470 | } |
471 | |
472 | class Switcher : public QObject |
473 | { |
474 | Q_OBJECT |
475 | public: |
476 | Switcher(QQmlEngine *e) : QObject(), engine(e) { } |
477 | |
478 | struct MyIncubator : public QQmlIncubator |
479 | { |
480 | MyIncubator(QQmlIncubator::IncubationMode mode, QObject *s) |
481 | : QQmlIncubator(mode), switcher(s) {} |
482 | |
483 | virtual void setInitialState(QObject *o) { |
484 | if (o->objectName() == "switchMe" ) |
485 | connect(sender: o, SIGNAL(switchMe()), receiver: switcher, SLOT(switchIt())); |
486 | } |
487 | |
488 | QObject *switcher; |
489 | }; |
490 | |
491 | void start() |
492 | { |
493 | incubator = new MyIncubator(QQmlIncubator::Synchronous, this); |
494 | component = new QQmlComponent(engine, QQmlDataTest::instance()->testFileUrl(fileName: "recursiveClear.1.qml" )); |
495 | component->create(*incubator); |
496 | } |
497 | |
498 | QQmlEngine *engine; |
499 | MyIncubator *incubator; |
500 | QQmlComponent *component; |
501 | |
502 | public slots: |
503 | void switchIt() { |
504 | component->deleteLater(); |
505 | incubator->clear(); |
506 | component = new QQmlComponent(engine, QQmlDataTest::instance()->testFileUrl(fileName: "recursiveClear.2.qml" )); |
507 | component->create(*incubator); |
508 | } |
509 | }; |
510 | |
511 | void tst_qqmlincubator::recursiveClear() |
512 | { |
513 | Switcher switcher(&engine); |
514 | switcher.start(); |
515 | } |
516 | |
517 | void tst_qqmlincubator::statusChanged() |
518 | { |
519 | class MyIncubator : public QQmlIncubator |
520 | { |
521 | public: |
522 | MyIncubator(QQmlIncubator::IncubationMode mode = QQmlIncubator::Asynchronous) |
523 | : QQmlIncubator(mode) {} |
524 | |
525 | QList<int> statuses; |
526 | protected: |
527 | virtual void statusChanged(Status s) { statuses << s; } |
528 | virtual void setInitialState(QObject *) { statuses << -1; } |
529 | }; |
530 | |
531 | { |
532 | QQmlComponent component(&engine, testFileUrl(fileName: "statusChanged.qml" )); |
533 | QVERIFY(component.isReady()); |
534 | |
535 | MyIncubator incubator(QQmlIncubator::Synchronous); |
536 | component.create(incubator); |
537 | QVERIFY(incubator.isReady()); |
538 | QCOMPARE(incubator.statuses.count(), 3); |
539 | QCOMPARE(incubator.statuses.at(0), int(QQmlIncubator::Loading)); |
540 | QCOMPARE(incubator.statuses.at(1), -1); |
541 | QCOMPARE(incubator.statuses.at(2), int(QQmlIncubator::Ready)); |
542 | delete incubator.object(); |
543 | } |
544 | |
545 | { |
546 | QQmlComponent component(&engine, testFileUrl(fileName: "statusChanged.qml" )); |
547 | QVERIFY(component.isReady()); |
548 | |
549 | MyIncubator incubator(QQmlIncubator::Asynchronous); |
550 | component.create(incubator); |
551 | QVERIFY(incubator.isLoading()); |
552 | QCOMPARE(incubator.statuses.count(), 1); |
553 | QCOMPARE(incubator.statuses.at(0), int(QQmlIncubator::Loading)); |
554 | |
555 | { |
556 | std::atomic<bool> b{true}; |
557 | controller.incubateWhile(flag: &b); |
558 | } |
559 | |
560 | QCOMPARE(incubator.statuses.count(), 3); |
561 | QCOMPARE(incubator.statuses.at(0), int(QQmlIncubator::Loading)); |
562 | QCOMPARE(incubator.statuses.at(1), -1); |
563 | QCOMPARE(incubator.statuses.at(2), int(QQmlIncubator::Ready)); |
564 | delete incubator.object(); |
565 | } |
566 | |
567 | { |
568 | QQmlComponent component2(&engine, testFileUrl(fileName: "statusChanged.nested.qml" )); |
569 | QVERIFY(component2.isReady()); |
570 | |
571 | MyIncubator incubator(QQmlIncubator::Asynchronous); |
572 | component2.create(incubator); |
573 | QVERIFY(incubator.isLoading()); |
574 | QCOMPARE(incubator.statuses.count(), 1); |
575 | QCOMPARE(incubator.statuses.at(0), int(QQmlIncubator::Loading)); |
576 | |
577 | { |
578 | std::atomic<bool> b{true}; |
579 | controller.incubateWhile(flag: &b); |
580 | } |
581 | |
582 | QVERIFY(incubator.isReady()); |
583 | QCOMPARE(incubator.statuses.count(), 3); |
584 | QCOMPARE(incubator.statuses.at(0), int(QQmlIncubator::Loading)); |
585 | QCOMPARE(incubator.statuses.at(1), -1); |
586 | QCOMPARE(incubator.statuses.at(2), int(QQmlIncubator::Ready)); |
587 | delete incubator.object(); |
588 | } |
589 | } |
590 | |
591 | void tst_qqmlincubator::asynchronousIfNested() |
592 | { |
593 | // Asynchronous if nested within a finalized context behaves synchronously |
594 | { |
595 | QQmlComponent component(&engine, testFileUrl(fileName: "asynchronousIfNested.1.qml" )); |
596 | QVERIFY(component.isReady()); |
597 | |
598 | QObject *object = component.create(); |
599 | QVERIFY(object != nullptr); |
600 | QCOMPARE(object->property("a" ).toInt(), 10); |
601 | |
602 | QQmlIncubator incubator(QQmlIncubator::AsynchronousIfNested); |
603 | component.create(incubator, context: nullptr, forContext: qmlContext(object)); |
604 | |
605 | QVERIFY(incubator.isReady()); |
606 | QVERIFY(incubator.object()); |
607 | QCOMPARE(incubator.object()->property("a" ).toInt(), 10); |
608 | delete incubator.object(); |
609 | delete object; |
610 | } |
611 | |
612 | // Asynchronous if nested within an executing context behaves asynchronously, but prevents |
613 | // the parent from finishing |
614 | { |
615 | SelfRegisteringType::clearMe(); |
616 | |
617 | QQmlComponent component(&engine, testFileUrl(fileName: "asynchronousIfNested.2.qml" )); |
618 | QVERIFY(component.isReady()); |
619 | |
620 | QQmlIncubator incubator; |
621 | component.create(incubator); |
622 | |
623 | QVERIFY(incubator.isLoading()); |
624 | QVERIFY(!SelfRegisteringType::me()); |
625 | while (SelfRegisteringType::me() == nullptr && incubator.isLoading()) { |
626 | std::atomic<bool> b{false}; |
627 | controller.incubateWhile(flag: &b); |
628 | } |
629 | |
630 | QVERIFY(SelfRegisteringType::me() != nullptr); |
631 | QVERIFY(incubator.isLoading()); |
632 | |
633 | QQmlIncubator nested(QQmlIncubator::AsynchronousIfNested); |
634 | component.create(nested, context: nullptr, forContext: qmlContext(SelfRegisteringType::me())); |
635 | QVERIFY(nested.isLoading()); |
636 | |
637 | while (nested.isLoading()) { |
638 | QVERIFY(incubator.isLoading()); |
639 | std::atomic<bool> b{false}; |
640 | controller.incubateWhile(flag: &b); |
641 | } |
642 | |
643 | QVERIFY(nested.isReady()); |
644 | QVERIFY(incubator.isLoading()); |
645 | |
646 | { |
647 | std::atomic<bool> b{true}; |
648 | controller.incubateWhile(flag: &b); |
649 | } |
650 | |
651 | QVERIFY(nested.isReady()); |
652 | QVERIFY(incubator.isReady()); |
653 | |
654 | delete nested.object(); |
655 | delete incubator.object(); |
656 | } |
657 | |
658 | // AsynchronousIfNested within a synchronous AsynchronousIfNested behaves synchronously |
659 | { |
660 | SelfRegisteringType::clearMe(); |
661 | |
662 | QQmlComponent component(&engine, testFileUrl(fileName: "asynchronousIfNested.3.qml" )); |
663 | QVERIFY(component.isReady()); |
664 | |
665 | struct CallbackData { |
666 | CallbackData(QQmlEngine *e) : engine(e), pass(false) {} |
667 | QQmlEngine *engine; |
668 | bool pass; |
669 | static void callback(CallbackRegisteringType *o, void *data) { |
670 | CallbackData *d = (CallbackData *)data; |
671 | |
672 | QQmlComponent c(d->engine, QQmlDataTest::instance()->testFileUrl(fileName: "asynchronousIfNested.1.qml" )); |
673 | if (!c.isReady()) return; |
674 | |
675 | QQmlIncubator incubator(QQmlIncubator::AsynchronousIfNested); |
676 | c.create(incubator, context: nullptr, forContext: qmlContext(o)); |
677 | |
678 | if (!incubator.isReady()) return; |
679 | |
680 | if (incubator.object()->property(name: "a" ).toInt() != 10) return; |
681 | |
682 | d->pass = true; |
683 | } |
684 | }; |
685 | |
686 | CallbackData cd(&engine); |
687 | CallbackRegisteringType::registerCallback(&CallbackData::callback, &cd); |
688 | |
689 | QQmlIncubator incubator(QQmlIncubator::AsynchronousIfNested); |
690 | component.create(incubator); |
691 | |
692 | QVERIFY(incubator.isReady()); |
693 | QCOMPARE(cd.pass, true); |
694 | |
695 | delete incubator.object(); |
696 | } |
697 | } |
698 | |
699 | void tst_qqmlincubator::nestedComponent() |
700 | { |
701 | QQmlComponent component(&engine, testFileUrl(fileName: "nestedComponent.qml" )); |
702 | QVERIFY(component.isReady()); |
703 | |
704 | QObject *object = component.create(); |
705 | |
706 | QQmlComponent *nested = object->property(name: "c" ).value<QQmlComponent*>(); |
707 | QVERIFY(nested); |
708 | QVERIFY(nested->isReady()); |
709 | |
710 | // Test without incubator |
711 | { |
712 | QObject *nestedObject = nested->create(); |
713 | QCOMPARE(nestedObject->property("value" ).toInt(), 19988); |
714 | delete nestedObject; |
715 | } |
716 | |
717 | // Test with incubator |
718 | { |
719 | QQmlIncubator incubator(QQmlIncubator::Synchronous); |
720 | nested->create(incubator); |
721 | QVERIFY(incubator.isReady()); |
722 | QVERIFY(incubator.object()); |
723 | QCOMPARE(incubator.object()->property("value" ).toInt(), 19988); |
724 | delete incubator.object(); |
725 | } |
726 | |
727 | delete object; |
728 | } |
729 | |
730 | // Checks that a new AsynchronousIfNested incubator can be correctly started in the |
731 | // statusChanged() callback of another. |
732 | void tst_qqmlincubator::chainedAsynchronousIfNested() |
733 | { |
734 | SelfRegisteringType::clearMe(); |
735 | |
736 | QQmlComponent component(&engine, testFileUrl(fileName: "chainedAsynchronousIfNested.qml" )); |
737 | QVERIFY(component.isReady()); |
738 | |
739 | QQmlIncubator incubator(QQmlIncubator::Asynchronous); |
740 | component.create(incubator); |
741 | |
742 | QVERIFY(incubator.isLoading()); |
743 | QVERIFY(!SelfRegisteringType::me()); |
744 | |
745 | while (SelfRegisteringType::me() == nullptr && incubator.isLoading()) { |
746 | std::atomic<bool> b{false}; |
747 | controller.incubateWhile(flag: &b); |
748 | } |
749 | |
750 | QVERIFY(SelfRegisteringType::me() != nullptr); |
751 | QVERIFY(incubator.isLoading()); |
752 | |
753 | struct MyIncubator : public QQmlIncubator { |
754 | MyIncubator(MyIncubator *next, QQmlComponent *component, QQmlContext *ctxt) |
755 | : QQmlIncubator(AsynchronousIfNested), next(next), component(component), ctxt(ctxt) {} |
756 | |
757 | protected: |
758 | virtual void statusChanged(Status s) { |
759 | if (s == Ready && next) |
760 | component->create(*next, context: nullptr, forContext: ctxt); |
761 | } |
762 | |
763 | private: |
764 | MyIncubator *next; |
765 | QQmlComponent *component; |
766 | QQmlContext *ctxt; |
767 | }; |
768 | |
769 | MyIncubator incubator2(nullptr, &component, nullptr); |
770 | MyIncubator incubator1(&incubator2, &component, qmlContext(SelfRegisteringType::me())); |
771 | |
772 | component.create(incubator1, context: nullptr, forContext: qmlContext(SelfRegisteringType::me())); |
773 | |
774 | QVERIFY(incubator.isLoading()); |
775 | QVERIFY(incubator1.isLoading()); |
776 | QVERIFY(incubator2.isNull()); |
777 | |
778 | while (incubator1.isLoading()) { |
779 | QVERIFY(incubator.isLoading()); |
780 | QVERIFY(incubator1.isLoading()); |
781 | QVERIFY(incubator2.isNull()); |
782 | |
783 | std::atomic<bool> b{false}; |
784 | controller.incubateWhile(flag: &b); |
785 | } |
786 | |
787 | QVERIFY(incubator.isLoading()); |
788 | QVERIFY(incubator1.isReady()); |
789 | QVERIFY(incubator2.isLoading()); |
790 | |
791 | while (incubator2.isLoading()) { |
792 | QVERIFY(incubator.isLoading()); |
793 | QVERIFY(incubator1.isReady()); |
794 | QVERIFY(incubator2.isLoading()); |
795 | |
796 | std::atomic<bool> b{false}; |
797 | controller.incubateWhile(flag: &b); |
798 | } |
799 | |
800 | QVERIFY(incubator1.isReady()); |
801 | QVERIFY(incubator2.isReady()); |
802 | if (incubator.isLoading()) { |
803 | std::atomic<bool> b{true}; |
804 | controller.incubateWhile(flag: &b); |
805 | } |
806 | |
807 | QVERIFY(incubator.isReady()); |
808 | QVERIFY(incubator1.isReady()); |
809 | QVERIFY(incubator2.isReady()); |
810 | } |
811 | |
812 | // Checks that new AsynchronousIfNested incubators can be correctly chained if started in |
813 | // componentCompleted(). |
814 | void tst_qqmlincubator::chainedAsynchronousIfNestedOnCompleted() |
815 | { |
816 | SelfRegisteringType::clearMe(); |
817 | |
818 | QQmlComponent component(&engine, testFileUrl(fileName: "chainInCompletion.qml" )); |
819 | QVERIFY(component.isReady()); |
820 | |
821 | QQmlComponent c1(&engine, testFileUrl(fileName: "chainedAsynchronousIfNested.qml" )); |
822 | QVERIFY(c1.isReady()); |
823 | |
824 | struct MyIncubator : public QQmlIncubator { |
825 | MyIncubator(MyIncubator *next, QQmlComponent *component, QQmlContext *ctxt) |
826 | : QQmlIncubator(AsynchronousIfNested), next(next), component(component), ctxt(ctxt) {} |
827 | |
828 | protected: |
829 | virtual void statusChanged(Status s) { |
830 | if (s == Ready && next) { |
831 | component->create(*next, context: nullptr, forContext: ctxt); |
832 | } |
833 | } |
834 | |
835 | private: |
836 | MyIncubator *next; |
837 | QQmlComponent *component; |
838 | QQmlContext *ctxt; |
839 | }; |
840 | |
841 | struct CallbackData { |
842 | CallbackData(QQmlComponent *c, MyIncubator *i, QQmlContext *ct) |
843 | : component(c), incubator(i), ctxt(ct) {} |
844 | QQmlComponent *component; |
845 | MyIncubator *incubator; |
846 | QQmlContext *ctxt; |
847 | static void callback(CompletionCallbackType *, void *data) { |
848 | CallbackData *d = (CallbackData *)data; |
849 | d->component->create(*d->incubator, context: nullptr, forContext: d->ctxt); |
850 | } |
851 | }; |
852 | |
853 | QQmlIncubator incubator(QQmlIncubator::Asynchronous); |
854 | component.create(incubator); |
855 | |
856 | QVERIFY(incubator.isLoading()); |
857 | QVERIFY(!SelfRegisteringType::me()); |
858 | |
859 | while (SelfRegisteringType::me() == nullptr && incubator.isLoading()) { |
860 | std::atomic<bool> b{false}; |
861 | controller.incubateWhile(flag: &b); |
862 | } |
863 | |
864 | QVERIFY(SelfRegisteringType::me() != nullptr); |
865 | QVERIFY(incubator.isLoading()); |
866 | |
867 | MyIncubator incubator3(nullptr, &c1, qmlContext(SelfRegisteringType::me())); |
868 | MyIncubator incubator2(&incubator3, &c1, qmlContext(SelfRegisteringType::me())); |
869 | MyIncubator incubator1(&incubator2, &c1, qmlContext(SelfRegisteringType::me())); |
870 | |
871 | // start incubator1 in componentComplete |
872 | CallbackData cd(&c1, &incubator1, qmlContext(SelfRegisteringType::me())); |
873 | CompletionCallbackType::registerCallback(&CallbackData::callback, &cd); |
874 | |
875 | while (!incubator1.isLoading()) { |
876 | QVERIFY(incubator.isLoading()); |
877 | QVERIFY(incubator2.isNull()); |
878 | QVERIFY(incubator3.isNull()); |
879 | |
880 | std::atomic<bool> b{false}; |
881 | controller.incubateWhile(flag: &b); |
882 | } |
883 | |
884 | QVERIFY(incubator.isLoading()); |
885 | QVERIFY(incubator1.isLoading()); |
886 | QVERIFY(incubator2.isNull()); |
887 | QVERIFY(incubator3.isNull()); |
888 | |
889 | while (incubator1.isLoading()) { |
890 | QVERIFY(incubator.isLoading()); |
891 | QVERIFY(incubator1.isLoading()); |
892 | QVERIFY(incubator2.isNull()); |
893 | QVERIFY(incubator3.isNull()); |
894 | |
895 | std::atomic<bool> b{false}; |
896 | controller.incubateWhile(flag: &b); |
897 | } |
898 | |
899 | QVERIFY(incubator.isLoading()); |
900 | QVERIFY(incubator1.isReady()); |
901 | QVERIFY(incubator2.isLoading()); |
902 | QVERIFY(incubator3.isNull()); |
903 | |
904 | while (incubator2.isLoading()) { |
905 | QVERIFY(incubator.isLoading()); |
906 | QVERIFY(incubator1.isReady()); |
907 | QVERIFY(incubator2.isLoading()); |
908 | QVERIFY(incubator3.isNull()); |
909 | |
910 | std::atomic<bool> b{false}; |
911 | controller.incubateWhile(flag: &b); |
912 | } |
913 | |
914 | QVERIFY(incubator.isLoading()); |
915 | QVERIFY(incubator1.isReady()); |
916 | QVERIFY(incubator2.isReady()); |
917 | QVERIFY(incubator3.isLoading()); |
918 | |
919 | while (incubator3.isLoading()) { |
920 | QVERIFY(incubator.isLoading()); |
921 | QVERIFY(incubator1.isReady()); |
922 | QVERIFY(incubator2.isReady()); |
923 | QVERIFY(incubator3.isLoading()); |
924 | |
925 | std::atomic<bool> b{false}; |
926 | controller.incubateWhile(flag: &b); |
927 | } |
928 | |
929 | { |
930 | std::atomic<bool> b{true}; |
931 | controller.incubateWhile(flag: &b); |
932 | } |
933 | |
934 | QVERIFY(incubator.isReady()); |
935 | QVERIFY(incubator1.isReady()); |
936 | QVERIFY(incubator2.isReady()); |
937 | QVERIFY(incubator3.isReady()); |
938 | } |
939 | |
940 | // Checks that new AsynchronousIfNested incubators can be correctly cleared if started in |
941 | // componentCompleted(). |
942 | void tst_qqmlincubator::chainedAsynchronousClear() |
943 | { |
944 | SelfRegisteringType::clearMe(); |
945 | |
946 | QQmlComponent component(&engine, testFileUrl(fileName: "chainInCompletion.qml" )); |
947 | QVERIFY(component.isReady()); |
948 | |
949 | QQmlComponent c1(&engine, testFileUrl(fileName: "chainedAsynchronousIfNested.qml" )); |
950 | QVERIFY(c1.isReady()); |
951 | |
952 | struct MyIncubator : public QQmlIncubator { |
953 | MyIncubator(MyIncubator *next, QQmlComponent *component, QQmlContext *ctxt) |
954 | : QQmlIncubator(AsynchronousIfNested), next(next), component(component), ctxt(ctxt) {} |
955 | |
956 | protected: |
957 | virtual void statusChanged(Status s) { |
958 | if (s == Ready && next) { |
959 | component->create(*next, context: nullptr, forContext: ctxt); |
960 | } |
961 | } |
962 | |
963 | private: |
964 | MyIncubator *next; |
965 | QQmlComponent *component; |
966 | QQmlContext *ctxt; |
967 | }; |
968 | |
969 | struct CallbackData { |
970 | CallbackData(QQmlComponent *c, MyIncubator *i, QQmlContext *ct) |
971 | : component(c), incubator(i), ctxt(ct) {} |
972 | QQmlComponent *component; |
973 | MyIncubator *incubator; |
974 | QQmlContext *ctxt; |
975 | static void callback(CompletionCallbackType *, void *data) { |
976 | CallbackData *d = (CallbackData *)data; |
977 | d->component->create(*d->incubator, context: nullptr, forContext: d->ctxt); |
978 | } |
979 | }; |
980 | |
981 | QQmlIncubator incubator(QQmlIncubator::Asynchronous); |
982 | component.create(incubator); |
983 | |
984 | QVERIFY(incubator.isLoading()); |
985 | QVERIFY(!SelfRegisteringType::me()); |
986 | |
987 | while (SelfRegisteringType::me() == nullptr && incubator.isLoading()) { |
988 | std::atomic<bool> b{false}; |
989 | controller.incubateWhile(flag: &b); |
990 | } |
991 | |
992 | QVERIFY(SelfRegisteringType::me() != nullptr); |
993 | QVERIFY(incubator.isLoading()); |
994 | |
995 | MyIncubator incubator3(nullptr, &c1, qmlContext(SelfRegisteringType::me())); |
996 | MyIncubator incubator2(&incubator3, &c1, qmlContext(SelfRegisteringType::me())); |
997 | MyIncubator incubator1(&incubator2, &c1, qmlContext(SelfRegisteringType::me())); |
998 | |
999 | // start incubator1 in componentComplete |
1000 | CallbackData cd(&c1, &incubator1, qmlContext(SelfRegisteringType::me())); |
1001 | CompletionCallbackType::registerCallback(&CallbackData::callback, &cd); |
1002 | |
1003 | while (!incubator1.isLoading()) { |
1004 | QVERIFY(incubator.isLoading()); |
1005 | QVERIFY(incubator2.isNull()); |
1006 | QVERIFY(incubator3.isNull()); |
1007 | |
1008 | std::atomic<bool> b{false}; |
1009 | controller.incubateWhile(flag: &b); |
1010 | } |
1011 | |
1012 | QVERIFY(incubator.isLoading()); |
1013 | QVERIFY(incubator1.isLoading()); |
1014 | QVERIFY(incubator2.isNull()); |
1015 | QVERIFY(incubator3.isNull()); |
1016 | |
1017 | while (incubator1.isLoading()) { |
1018 | QVERIFY(incubator.isLoading()); |
1019 | QVERIFY(incubator1.isLoading()); |
1020 | QVERIFY(incubator2.isNull()); |
1021 | QVERIFY(incubator3.isNull()); |
1022 | |
1023 | std::atomic<bool> b{false}; |
1024 | controller.incubateWhile(flag: &b); |
1025 | } |
1026 | |
1027 | QVERIFY(incubator.isLoading()); |
1028 | QVERIFY(incubator1.isReady()); |
1029 | QVERIFY(incubator2.isLoading()); |
1030 | QVERIFY(incubator3.isNull()); |
1031 | |
1032 | while (incubator2.isLoading()) { |
1033 | QVERIFY(incubator.isLoading()); |
1034 | QVERIFY(incubator1.isReady()); |
1035 | QVERIFY(incubator2.isLoading()); |
1036 | QVERIFY(incubator3.isNull()); |
1037 | |
1038 | std::atomic<bool> b{false}; |
1039 | controller.incubateWhile(flag: &b); |
1040 | } |
1041 | |
1042 | QVERIFY(incubator.isLoading()); |
1043 | QVERIFY(incubator1.isReady()); |
1044 | QVERIFY(incubator2.isReady()); |
1045 | QVERIFY(incubator3.isLoading()); |
1046 | |
1047 | // Any in loading state will become null when cleared. |
1048 | incubator.clear(); |
1049 | |
1050 | QVERIFY(incubator.isNull()); |
1051 | QVERIFY(incubator1.isReady()); |
1052 | QVERIFY(incubator2.isReady()); |
1053 | QVERIFY(incubator3.isNull()); |
1054 | } |
1055 | |
1056 | void tst_qqmlincubator::selfDelete() |
1057 | { |
1058 | struct MyIncubator : public QQmlIncubator { |
1059 | MyIncubator(bool *done, Status status, IncubationMode mode) |
1060 | : QQmlIncubator(mode), done(done), status(status) {} |
1061 | |
1062 | protected: |
1063 | virtual void statusChanged(Status s) { |
1064 | if (s == status) { |
1065 | *done = true; |
1066 | if (s == Ready) delete object(); |
1067 | delete this; |
1068 | } |
1069 | } |
1070 | |
1071 | private: |
1072 | bool *done; |
1073 | Status status; |
1074 | }; |
1075 | |
1076 | { |
1077 | QQmlComponent component(&engine, testFileUrl(fileName: "selfDelete.qml" )); |
1078 | |
1079 | #define DELETE_TEST(status, mode) { \ |
1080 | bool done = false; \ |
1081 | component.create(*(new MyIncubator(&done, status, mode))); \ |
1082 | std::atomic<bool> True{true}; \ |
1083 | controller.incubateWhile(&True); \ |
1084 | QVERIFY(done == true); \ |
1085 | } |
1086 | |
1087 | DELETE_TEST(QQmlIncubator::Loading, QQmlIncubator::Synchronous); |
1088 | DELETE_TEST(QQmlIncubator::Ready, QQmlIncubator::Synchronous); |
1089 | DELETE_TEST(QQmlIncubator::Loading, QQmlIncubator::Asynchronous); |
1090 | DELETE_TEST(QQmlIncubator::Ready, QQmlIncubator::Asynchronous); |
1091 | |
1092 | #undef DELETE_TEST |
1093 | } |
1094 | |
1095 | // Delete within error status |
1096 | { |
1097 | SelfRegisteringType::clearMe(); |
1098 | |
1099 | QQmlComponent component(&engine, testFileUrl(fileName: "objectDeleted.qml" )); |
1100 | QVERIFY(component.isReady()); |
1101 | |
1102 | bool done = false; |
1103 | MyIncubator *incubator = new MyIncubator(&done, QQmlIncubator::Error, |
1104 | QQmlIncubator::Asynchronous); |
1105 | component.create(*incubator); |
1106 | |
1107 | QCOMPARE(incubator->QQmlIncubator::status(), QQmlIncubator::Loading); |
1108 | QVERIFY(!SelfRegisteringType::me()); |
1109 | |
1110 | while (SelfRegisteringType::me() == nullptr && incubator->isLoading()) { |
1111 | std::atomic<bool> b{false}; |
1112 | controller.incubateWhile(flag: &b); |
1113 | } |
1114 | |
1115 | QVERIFY(SelfRegisteringType::me() != nullptr); |
1116 | QVERIFY(incubator->isLoading()); |
1117 | |
1118 | // We have to cheat and manually remove it from the creator->allCreatedObjects |
1119 | // otherwise we will do a double delete |
1120 | QQmlIncubatorPrivate *incubatorPriv = QQmlIncubatorPrivate::get(incubator); |
1121 | incubatorPriv->creator->allCreatedObjects().pop(); |
1122 | delete SelfRegisteringType::me(); |
1123 | |
1124 | { |
1125 | std::atomic<bool> b{true}; |
1126 | controller.incubateWhile(flag: &b); |
1127 | } |
1128 | |
1129 | QVERIFY(done); |
1130 | } |
1131 | } |
1132 | |
1133 | // Test that QML doesn't crash if the context is deleted prior to the incubator |
1134 | // first executing. |
1135 | void tst_qqmlincubator::contextDelete() |
1136 | { |
1137 | QQmlContext *context = new QQmlContext(engine.rootContext()); |
1138 | QQmlComponent component(&engine, testFileUrl(fileName: "contextDelete.qml" )); |
1139 | |
1140 | QQmlIncubator incubator; |
1141 | component.create(incubator, context); |
1142 | |
1143 | delete context; |
1144 | |
1145 | { |
1146 | std::atomic<bool> b{false}; |
1147 | controller.incubateWhile(flag: &b); |
1148 | } |
1149 | } |
1150 | |
1151 | // QTBUG-53111 |
1152 | void tst_qqmlincubator::garbageCollection() |
1153 | { |
1154 | QQmlComponent component(&engine, testFileUrl(fileName: "garbageCollection.qml" )); |
1155 | QScopedPointer<QObject> obj(component.create()); |
1156 | |
1157 | engine.collectGarbage(); |
1158 | |
1159 | std::atomic<bool> b{true}; |
1160 | controller.incubateWhile(flag: &b); |
1161 | |
1162 | // verify incubation completed (the incubator was not prematurely collected) |
1163 | QVariant incubatorVariant; |
1164 | QMetaObject::invokeMethod(obj: obj.data(), member: "getAndClearIncubator" , Q_RETURN_ARG(QVariant, incubatorVariant)); |
1165 | QJSValue strongRef = incubatorVariant.value<QJSValue>(); |
1166 | QVERIFY(!strongRef.isNull() && !strongRef.isUndefined()); |
1167 | |
1168 | // turn the last strong reference to the incubator into a weak one and collect |
1169 | QV4::WeakValue weakIncubatorRef; |
1170 | weakIncubatorRef.set(engine: QQmlEnginePrivate::getV4Engine(e: &engine), value: *QJSValuePrivate::getValue(jsval: &strongRef)); |
1171 | strongRef = QJSValue(); |
1172 | incubatorVariant.clear(); |
1173 | |
1174 | // verify incubator is correctly collected now that incubation is complete and all references are gone |
1175 | engine.collectGarbage(); |
1176 | QVERIFY(weakIncubatorRef.isNullOrUndefined()); |
1177 | } |
1178 | |
1179 | void tst_qqmlincubator::requiredProperties() |
1180 | { |
1181 | { |
1182 | QQmlComponent component(&engine, testFileUrl(fileName: "requiredProperty.qml" )); |
1183 | QVERIFY(component.isReady()); |
1184 | // forceCompletion immediately after creating an asynchronous object completes it |
1185 | QQmlIncubator incubator; |
1186 | incubator.setInitialProperties({{"requiredProperty" , 42}}); |
1187 | QVERIFY(incubator.isNull()); |
1188 | component.create(incubator); |
1189 | QVERIFY(incubator.isLoading()); |
1190 | |
1191 | incubator.forceCompletion(); |
1192 | |
1193 | QVERIFY(incubator.isReady()); |
1194 | QVERIFY(incubator.object() != nullptr); |
1195 | QCOMPARE(incubator.object()->property("requiredProperty" ).toInt(), 42); |
1196 | |
1197 | delete incubator.object(); |
1198 | } |
1199 | { |
1200 | QQmlComponent component(&engine, testFileUrl(fileName: "requiredProperty.qml" )); |
1201 | QVERIFY(component.isReady()); |
1202 | // forceCompletion immediately after creating an asynchronous object completes it |
1203 | QQmlIncubator incubator; |
1204 | QVERIFY(incubator.isNull()); |
1205 | component.create(incubator); |
1206 | QVERIFY(incubator.isLoading()); |
1207 | |
1208 | incubator.forceCompletion(); |
1209 | |
1210 | QVERIFY(incubator.isError()); |
1211 | auto error = incubator.errors().first(); |
1212 | QVERIFY(error.description().contains(QLatin1String("Required property requiredProperty was not initialized" ))); |
1213 | QVERIFY(incubator.object() == nullptr); |
1214 | } |
1215 | } |
1216 | |
1217 | class DeletingIncubator : public QQmlIncubator |
1218 | { |
1219 | |
1220 | |
1221 | // QQmlIncubator interface |
1222 | protected: |
1223 | void statusChanged(Status) override |
1224 | { |
1225 | |
1226 | } |
1227 | void setInitialState(QObject *obj) override |
1228 | { |
1229 | delete obj; |
1230 | clear(); |
1231 | } |
1232 | }; |
1233 | |
1234 | void tst_qqmlincubator::deleteInSetInitialState() |
1235 | { |
1236 | QQmlComponent component(&engine, testFileUrl(fileName: "requiredProperty.qml" )); |
1237 | QVERIFY(component.isReady()); |
1238 | // forceCompletion immediately after creating an asynchronous object completes it |
1239 | DeletingIncubator incubator; |
1240 | incubator.setInitialProperties({{"requiredProperty" , 42}}); |
1241 | QVERIFY(incubator.isNull()); |
1242 | component.create(incubator); |
1243 | QVERIFY(incubator.isLoading()); |
1244 | incubator.forceCompletion(); // no crash |
1245 | QVERIFY(incubator.isNull()); |
1246 | QCOMPARE(incubator.object(), nullptr); // object was deleted |
1247 | } |
1248 | |
1249 | QTEST_MAIN(tst_qqmlincubator) |
1250 | |
1251 | #include "tst_qqmlincubator.moc" |
1252 | |