diff options
author | Jim Albamont <jim.albamont@kdab.com> | 2019-04-04 13:12:30 -0500 |
---|---|---|
committer | James Turner <james.turner@kdab.com> | 2019-04-08 08:07:49 +0000 |
commit | 985a61921fbcb893b89b8dde6eeaab5cf8c5dc1c (patch) | |
tree | 803306dfa93f67e1bd047ec09c93b0770cead878 /tests | |
parent | bf2c2e9bb2dd0b13cb2cb6728de0c2421fbafbb7 (diff) |
Fix backend node creation order using an initialization queue
Backend nodes should always be created from the top-most parent down
ensuring that every parent is created before its children. The original
way of creating backend nodes by calling _q_postConstructorInit
in a deferred manner from the QNode constructor breaks this because
backend node creation happens in the order that the nodes on the
front-end were created. This was often incorrect when reparenting
newly created nodes.
Fix by creating a queue of nodes needing a _q_postConstructorInit call
and only adding nodes to the queue if one of their ancestors is not
already in the queue. This ensures that _q_postConstructorInit is only
called for the top-most node in any subtree. This behavior exactly
matches the creation behavior when building a subtree and reparenting
it to a node with a backend.
Doing silly things like creating a node with a parent that has a backend
then immediately reparenting is now safe. After this patch, it should
be safe to assume that backend nodes can always find their backend
parent.
Adding only the top-most nodes to the queue and processing the entire
queue at one time also ensures that all creation events get sent in
the same batch. This fixes the problem of having backend nodes referring
to other backend nodes that haven't been created yet.
Task-number: QTBUG-74106
Task-number: QTBUG-73905
Change-Id: Idcf38d6c3164f6be4394a3b25554547414061059
Reviewed-by: Paul Lemire <paul.lemire@kdab.com>
Diffstat (limited to 'tests')
-rw-r--r-- | tests/auto/core/nodes/tst_nodes.cpp | 80 |
1 files changed, 80 insertions, 0 deletions
diff --git a/tests/auto/core/nodes/tst_nodes.cpp b/tests/auto/core/nodes/tst_nodes.cpp index 193d88c83..dad66c5d5 100644 --- a/tests/auto/core/nodes/tst_nodes.cpp +++ b/tests/auto/core/nodes/tst_nodes.cpp @@ -73,6 +73,7 @@ private slots: void checkParentChangeToNull(); void checkParentChangeToOtherParent(); void checkParentChangeFromExistingBackendParentToNewlyCreatedParent(); + void checkBackendNodesCreatedFromTopDown(); //QTBUG-74106 void removingSingleChildNodeFromNode(); void removingMultipleChildNodesFromNode(); @@ -902,6 +903,85 @@ void tst_Nodes::checkParentChangeFromExistingBackendParentToNewlyCreatedParent() } } +//Test creation changes happen for an entire subtree at once, starting at the top +// so that parents are always created before their children. Even if the front-end +// nodes are constructed in a different order. +void tst_Nodes::checkBackendNodesCreatedFromTopDown() +{ + // GIVEN + Qt3DCore::QScene scene; + ObserverSpy spy; + QScopedPointer<MyQNode> root(new MyQNode()); + root->setArbiterAndScene(&spy, &scene); + QScopedPointer<Qt3DCore::QNode> parentWithBackend(new MyQNode(root.data())); + + // create parent backend node + QCoreApplication::processEvents(); + QVERIFY(Qt3DCore::QNodePrivate::get(parentWithBackend.get())->m_hasBackendNode); + + // WHEN -> creating 3 nodes and setting one as a property on the other + // child2 is set as property on child1, but is created AFTER child1 + spy.events.clear(); + MyQNode *dummyParent(new MyQNode(parentWithBackend.data())); + MyQNode *child1(new MyQNode(parentWithBackend.data())); + MyQNode *child2(new MyQNode(dummyParent)); + child2->setNodeProperty(child1); + + // THEN - we should have no events because the new nodes have no backend yet + QCOMPARE(spy.events.count(), 0); + + // WHEN - create the backend nodes + QCoreApplication::processEvents(); + + // THEN + QVERIFY(dummyParent->parent() == parentWithBackend.data()); + QVERIFY(child1->parent() == parentWithBackend.data()); + QVERIFY(child2->parent() == dummyParent); + + // THEN + QCOMPARE(spy.events.size(), 5); + // 2 node creation change for dummyParent subtree (dummyParent and child2) + // 1 node added to children change (dummyParent to parent) + // 1 node created change for child1 + // 1 node added to children change (child1 to parent) + + { + // 1st event: dummyParent creation + const auto event1 = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QNodeCreatedChangeBase>(); + QVERIFY(!event1.isNull()); + QCOMPARE(event1->type(), Qt3DCore::NodeCreated); + QCOMPARE(event1->parentId(), parentWithBackend->id()); + QCOMPARE(event1->subjectId(), dummyParent->id()); + + // 2nd event: child2 creation (even though we constructed child1 first) + const auto event2 = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QNodeCreatedChangeBase>(); + QVERIFY(!event2.isNull()); + QCOMPARE(event2->type(), Qt3DCore::NodeCreated); + QCOMPARE(event2->parentId(), dummyParent->id()); + QCOMPARE(event2->subjectId(), child2->id()); + + // 3rd event: dummyParent added to parent + const auto event3 = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QPropertyNodeAddedChange>(); + QCOMPARE(event3->type(), Qt3DCore::PropertyValueAdded); + QCOMPARE(event3->addedNodeId(), dummyParent->id()); + QCOMPARE(event3->subjectId(), parentWithBackend->id()); + + // 4th event: child1 creation + const auto event4 = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QNodeCreatedChangeBase>(); + QVERIFY(!event4.isNull()); + QCOMPARE(event4->type(), Qt3DCore::NodeCreated); + QCOMPARE(event4->parentId(), parentWithBackend->id()); + QCOMPARE(event4->subjectId(), child1->id()); + + // 5th event: child 1 added to parent + const auto event5 = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QPropertyNodeAddedChange>(); + QCOMPARE(event5->type(), Qt3DCore::PropertyValueAdded); + QCOMPARE(event5->addedNodeId(), child1->id()); + QCOMPARE(event5->subjectId(), parentWithBackend->id()); + } +} + + void tst_Nodes::removingSingleChildNodeFromNode() { // GIVEN |