mirror of https://github.com/qt/qt3d.git
921 lines
29 KiB
C++
921 lines
29 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
|
|
** Contact: https://www.qt.io/licensing/
|
|
**
|
|
** This file is part of the Qt3D module of the Qt Toolkit.
|
|
**
|
|
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
|
|
** Commercial License Usage
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
** accordance with the commercial license agreement provided with the
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
|
**
|
|
** GNU General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
** General Public License version 3 as published by the Free Software
|
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
** included in the packaging of this file. Please review the following
|
|
** information to ensure the GNU General Public License requirements will
|
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
|
**
|
|
** $QT_END_LICENSE$
|
|
**
|
|
****************************************************************************/
|
|
|
|
#include <QtTest/QTest>
|
|
#include <Qt3DCore/qnode.h>
|
|
#include <Qt3DCore/qentity.h>
|
|
#include <Qt3DCore/qcomponent.h>
|
|
#include <Qt3DCore/private/qscene_p.h>
|
|
#include <Qt3DCore/qpropertyupdatedchange.h>
|
|
#include <Qt3DCore/qcomponentaddedchange.h>
|
|
#include <Qt3DCore/qcomponentremovedchange.h>
|
|
#include <Qt3DCore/qnodedestroyedchange.h>
|
|
#include <Qt3DCore/qpropertynodeaddedchange.h>
|
|
#include <Qt3DCore/qpropertynoderemovedchange.h>
|
|
#include <private/qpostman_p.h>
|
|
|
|
#include <Qt3DCore/private/qlockableobserverinterface_p.h>
|
|
#include <Qt3DCore/private/qnode_p.h>
|
|
|
|
class tst_Nodes : public QObject
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
tst_Nodes() : QObject()
|
|
{
|
|
qRegisterMetaType<Qt3DCore::QNode*>();
|
|
}
|
|
~tst_Nodes() {}
|
|
|
|
private slots:
|
|
void defaultNodeConstruction();
|
|
void defaultComponentConstruction();
|
|
void defaultEntityConstrution();
|
|
|
|
void appendSingleChildNodeToNodeNoSceneExplicitParenting();
|
|
void appendSingleChildNodeToNodeNoSceneImplicitParenting();
|
|
void appendMultipleChildNodesToNodeNoScene();
|
|
|
|
void appendSingleChildNodeToNodeSceneExplicitParenting();
|
|
void appendSingleChildNodeToNodeSceneImplicitParenting();
|
|
void appendMultipleChildNodesToNodeScene();
|
|
|
|
void checkParentChangeToNull();
|
|
void checkParentChangeToOtherParent();
|
|
|
|
void removingSingleChildNodeFromNode();
|
|
void removingMultipleChildNodesFromNode();
|
|
|
|
void appendingChildEntitiesToNode();
|
|
void removingChildEntitiesFromNode();
|
|
|
|
void appendingComponentToEntity();
|
|
void appendingParentlessComponentToEntity();
|
|
void removingComponentFromEntity();
|
|
|
|
void changeCustomProperty();
|
|
void checkDestruction();
|
|
};
|
|
|
|
class ObserverSpy;
|
|
class SimplePostman : public Qt3DCore::QAbstractPostman
|
|
{
|
|
public:
|
|
SimplePostman(ObserverSpy *spy)
|
|
: m_spy(spy)
|
|
{}
|
|
|
|
void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &) Q_DECL_FINAL {};
|
|
void setScene(Qt3DCore::QScene *) Q_DECL_FINAL {};
|
|
void notifyBackend(const Qt3DCore::QSceneChangePtr &change) Q_DECL_FINAL;
|
|
|
|
private:
|
|
ObserverSpy *m_spy;
|
|
};
|
|
|
|
class ObserverSpy : public Qt3DCore::QAbstractArbiter
|
|
{
|
|
public:
|
|
class ChangeRecord : public QPair<Qt3DCore::QSceneChangePtr, bool>
|
|
{
|
|
public:
|
|
ChangeRecord(const Qt3DCore::QSceneChangePtr &event, bool locked)
|
|
: QPair<Qt3DCore::QSceneChangePtr, bool>(event, locked)
|
|
{}
|
|
|
|
Qt3DCore::QSceneChangePtr change() const { return first; }
|
|
|
|
bool wasLocked() const { return second; }
|
|
};
|
|
|
|
ObserverSpy()
|
|
: Qt3DCore::QAbstractArbiter()
|
|
, m_postman(new SimplePostman(this))
|
|
{
|
|
}
|
|
|
|
~ObserverSpy()
|
|
{
|
|
}
|
|
|
|
void sceneChangeEventWithLock(const Qt3DCore::QSceneChangePtr &e) Q_DECL_OVERRIDE
|
|
{
|
|
events << ChangeRecord(e, true);
|
|
}
|
|
|
|
void sceneChangeEventWithLock(const Qt3DCore::QSceneChangeList &e) Q_DECL_OVERRIDE
|
|
{
|
|
for (size_t i = 0, m = e.size(); i < m; ++i) {
|
|
events << ChangeRecord(e.at(i), false);
|
|
}
|
|
}
|
|
|
|
void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e) Q_DECL_OVERRIDE
|
|
{
|
|
events << ChangeRecord(e, false);
|
|
}
|
|
|
|
Qt3DCore::QAbstractPostman *postman() const Q_DECL_FINAL
|
|
{
|
|
return m_postman.data();
|
|
}
|
|
|
|
QList<ChangeRecord> events;
|
|
QScopedPointer<SimplePostman> m_postman;
|
|
};
|
|
|
|
void SimplePostman::notifyBackend(const Qt3DCore::QSceneChangePtr &change)
|
|
{
|
|
m_spy->sceneChangeEventWithLock(change);
|
|
}
|
|
|
|
class MyQNode : public Qt3DCore::QNode
|
|
{
|
|
Q_OBJECT
|
|
Q_PROPERTY(QString customProperty READ customProperty WRITE setCustomProperty NOTIFY customPropertyChanged)
|
|
public:
|
|
explicit MyQNode(Qt3DCore::QNode *parent = 0)
|
|
: QNode(parent)
|
|
{}
|
|
|
|
~MyQNode()
|
|
{
|
|
}
|
|
|
|
void setCustomProperty(const QString &s)
|
|
{
|
|
if (m_customProperty == s)
|
|
return;
|
|
|
|
m_customProperty = s;
|
|
emit customPropertyChanged();
|
|
}
|
|
|
|
QString customProperty() const
|
|
{
|
|
return m_customProperty;
|
|
}
|
|
|
|
void setArbiterAndScene(Qt3DCore::QAbstractArbiter *arbiter,
|
|
Qt3DCore::QScene *scene = nullptr)
|
|
{
|
|
Q_ASSERT(arbiter);
|
|
if (scene)
|
|
scene->setArbiter(arbiter);
|
|
Qt3DCore::QNodePrivate::get(this)->setScene(scene);
|
|
Qt3DCore::QNodePrivate::get(this)->setArbiter(arbiter);
|
|
}
|
|
|
|
signals:
|
|
void customPropertyChanged();
|
|
|
|
protected:
|
|
QString m_customProperty;
|
|
};
|
|
|
|
class MyQEntity : public Qt3DCore::QEntity
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
explicit MyQEntity(Qt3DCore::QNode *parent = 0)
|
|
: QEntity(parent)
|
|
{}
|
|
|
|
~MyQEntity()
|
|
{
|
|
}
|
|
|
|
void setArbiterAndScene(Qt3DCore::QAbstractArbiter *arbiter,
|
|
Qt3DCore::QScene *scene = nullptr)
|
|
{
|
|
Q_ASSERT(arbiter);
|
|
if (scene)
|
|
scene->setArbiter(arbiter);
|
|
Qt3DCore::QNodePrivate::get(this)->setScene(scene);
|
|
Qt3DCore::QNodePrivate::get(this)->setArbiter(arbiter);
|
|
}
|
|
};
|
|
|
|
class MyQComponent : public Qt3DCore::QComponent
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
explicit MyQComponent(Qt3DCore::QNode *parent = 0) : QComponent(parent)
|
|
{}
|
|
};
|
|
|
|
|
|
void tst_Nodes::defaultNodeConstruction()
|
|
{
|
|
// GIVEN
|
|
QScopedPointer<MyQNode> node(new MyQNode());
|
|
|
|
// THEN
|
|
QVERIFY(node != nullptr);
|
|
QVERIFY(node->children().isEmpty());
|
|
|
|
// GIVEN
|
|
MyQNode *node2 = new MyQNode(node.data());
|
|
QCoreApplication::processEvents();
|
|
|
|
// THEN
|
|
QVERIFY(node2->parent() == node.data());
|
|
QVERIFY(!node->children().isEmpty());
|
|
QVERIFY(node2->children().isEmpty());
|
|
}
|
|
|
|
void tst_Nodes::defaultComponentConstruction()
|
|
{
|
|
// GIVEN
|
|
QScopedPointer<MyQComponent> comp(new MyQComponent());
|
|
MyQComponent *comp2 = new MyQComponent(comp.data());
|
|
QCoreApplication::processEvents();
|
|
|
|
// THEN
|
|
QVERIFY(comp != nullptr);
|
|
QCOMPARE(comp2->parent(), comp.data());
|
|
}
|
|
|
|
void tst_Nodes::defaultEntityConstrution()
|
|
{
|
|
// GIVEN
|
|
QScopedPointer<Qt3DCore::QEntity> entity(new Qt3DCore::QEntity());
|
|
Qt3DCore::QEntity *entity2 = new Qt3DCore::QEntity(entity.data());
|
|
QCoreApplication::processEvents();
|
|
|
|
// THEN
|
|
QVERIFY(entity->components().isEmpty());
|
|
QVERIFY(entity2->components().isEmpty());
|
|
QCOMPARE(entity2->parent(), entity.data());
|
|
}
|
|
|
|
void tst_Nodes::appendSingleChildNodeToNodeNoSceneExplicitParenting()
|
|
{
|
|
// Check nodes added when no scene is set
|
|
// GIVEN
|
|
ObserverSpy spy;
|
|
QScopedPointer<MyQNode> node(new MyQNode());
|
|
node->setArbiterAndScene(&spy);
|
|
|
|
// THEN
|
|
QVERIFY(Qt3DCore::QNodePrivate::get(node.data())->scene() == nullptr);
|
|
// WHEN
|
|
QScopedPointer<MyQNode> child(new MyQNode());
|
|
|
|
// THEN
|
|
QVERIFY(child->parent() == nullptr);
|
|
|
|
// WHEN
|
|
child->setParent(node.data());
|
|
|
|
// THEN
|
|
QVERIFY(child->parent() == node.data());
|
|
QVERIFY(child->parentNode() == node.data());
|
|
QCOMPARE(node->children().count(), 1);
|
|
|
|
// Events are only sent when a scene is set on the root node
|
|
QCOMPARE(spy.events.size(), 0);
|
|
}
|
|
|
|
void tst_Nodes::appendSingleChildNodeToNodeNoSceneImplicitParenting()
|
|
{
|
|
// Check nodes added when no scene is set
|
|
// GIVEN
|
|
ObserverSpy spy;
|
|
QScopedPointer<MyQNode> node(new MyQNode());
|
|
node->setArbiterAndScene(&spy);
|
|
|
|
// THEN
|
|
QVERIFY(Qt3DCore::QNodePrivate::get(node.data())->scene() == nullptr);
|
|
// WHEN
|
|
QScopedPointer<MyQNode> child(new MyQNode(node.data()));
|
|
QCoreApplication::processEvents();
|
|
|
|
// THEN
|
|
QVERIFY(child->parent() == node.data());
|
|
QVERIFY(child->parentNode() == node.data());
|
|
QCOMPARE(node->children().count(), 1);
|
|
|
|
// Events are only sent when a scene is set on the root node
|
|
QCOMPARE(spy.events.size(), 0);
|
|
}
|
|
|
|
void tst_Nodes::appendMultipleChildNodesToNodeNoScene()
|
|
{
|
|
// Check multiple nodes added with no scene set
|
|
// GIVEN
|
|
ObserverSpy spy;
|
|
QScopedPointer<MyQNode> node(new MyQNode());
|
|
node->setArbiterAndScene(&spy);
|
|
|
|
// THEN
|
|
QVERIFY(Qt3DCore::QNodePrivate::get(node.data())->scene() == nullptr);
|
|
// WHEN
|
|
for (int i = 0; i < 10; i++) {
|
|
// WHEN
|
|
Qt3DCore::QNode *child = nullptr;
|
|
if (i % 2 == 0) {
|
|
child = new MyQNode(node.data());
|
|
QCoreApplication::processEvents();
|
|
} else {
|
|
child = new MyQNode();
|
|
child->setParent(node.data());
|
|
}
|
|
// THEN
|
|
QVERIFY(child->parent() == node.data());
|
|
}
|
|
|
|
// THEN
|
|
QCOMPARE(node->children().count(), 10);
|
|
|
|
// Events are only sent when a scene is set on the root node
|
|
QCOMPARE(spy.events.size(), 0);
|
|
}
|
|
|
|
void tst_Nodes::appendSingleChildNodeToNodeSceneExplicitParenting()
|
|
{
|
|
// Check nodes added when scene is set
|
|
// GIVEN
|
|
Qt3DCore::QScene scene;
|
|
ObserverSpy spy;
|
|
QScopedPointer<MyQNode> node(new MyQNode());
|
|
// WHEN
|
|
node->setArbiterAndScene(&spy, &scene);
|
|
// THEN
|
|
QVERIFY(Qt3DCore::QNodePrivate::get(node.data())->scene() != nullptr);
|
|
|
|
// WHEN
|
|
QScopedPointer<MyQNode> child(new MyQNode());
|
|
|
|
// THEN
|
|
QVERIFY(child->parent() == nullptr);
|
|
QVERIFY(Qt3DCore::QNodePrivate::get(child.data())->scene() == nullptr);
|
|
|
|
// WHEN
|
|
child->setParent(node.data());
|
|
|
|
// THEN
|
|
QVERIFY(child->parent() == node.data());
|
|
QVERIFY(child->parentNode() == node.data());
|
|
QCOMPARE(spy.events.size(), 2);
|
|
QCOMPARE(node->children().count(), 1);
|
|
QVERIFY(Qt3DCore::QNodePrivate::get(child.data())->scene() != nullptr);
|
|
|
|
// Creation event
|
|
QVERIFY(spy.events.first().wasLocked());
|
|
Qt3DCore::QNodeCreatedChangeBasePtr creationEvent = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QNodeCreatedChangeBase>();
|
|
QVERIFY(creationEvent);
|
|
QCOMPARE(creationEvent->subjectId(), child->id());
|
|
QCOMPARE(creationEvent->metaObject(), child->metaObject());
|
|
QCOMPARE(creationEvent->isNodeEnabled(), child->isEnabled());
|
|
QCOMPARE(creationEvent->parentId(), child->parentNode()->id());
|
|
|
|
// Node Added event
|
|
QVERIFY(spy.events.first().wasLocked());
|
|
Qt3DCore::QPropertyNodeAddedChangePtr additionEvent = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QPropertyNodeAddedChange>();
|
|
QVERIFY(additionEvent);
|
|
QCOMPARE(additionEvent->subjectId(), node->id());
|
|
QCOMPARE(additionEvent->addedNodeId(), child->id());
|
|
QCOMPARE(additionEvent->metaObject(), child->metaObject());
|
|
}
|
|
|
|
void tst_Nodes::appendSingleChildNodeToNodeSceneImplicitParenting()
|
|
{
|
|
// Check nodes added when scene is set
|
|
// GIVEN
|
|
Qt3DCore::QScene scene;
|
|
ObserverSpy spy;
|
|
QScopedPointer<MyQNode> node(new MyQNode());
|
|
|
|
// WHEN
|
|
node->setArbiterAndScene(&spy, &scene);
|
|
// THEN
|
|
QVERIFY(Qt3DCore::QNodePrivate::get(node.data())->scene() != nullptr);
|
|
|
|
// WHEN
|
|
QScopedPointer<MyQNode> child(new MyQNode(node.data()));
|
|
QCoreApplication::processEvents();
|
|
|
|
// THEN
|
|
QVERIFY(child->parent() == node.data());
|
|
QVERIFY(child->parentNode() == node.data());
|
|
QVERIFY(Qt3DCore::QNodePrivate::get(child.data())->scene() != nullptr);
|
|
|
|
QCOMPARE(spy.events.size(), 2);
|
|
QVERIFY(spy.events.first().wasLocked());
|
|
QCOMPARE(node->children().count(), 1);
|
|
|
|
// Creation event
|
|
QVERIFY(spy.events.first().wasLocked());
|
|
Qt3DCore::QNodeCreatedChangeBasePtr creationEvent = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QNodeCreatedChangeBase>();
|
|
QVERIFY(creationEvent);
|
|
QCOMPARE(creationEvent->subjectId(), child->id());
|
|
QCOMPARE(creationEvent->metaObject(), child->metaObject());
|
|
QCOMPARE(creationEvent->isNodeEnabled(), child->isEnabled());
|
|
QCOMPARE(creationEvent->parentId(), child->parentNode()->id());
|
|
|
|
// Node Added event
|
|
QVERIFY(spy.events.first().wasLocked());
|
|
Qt3DCore::QPropertyNodeAddedChangePtr additionEvent = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QPropertyNodeAddedChange>();
|
|
QVERIFY(additionEvent);
|
|
QCOMPARE(additionEvent->subjectId(), node->id());
|
|
QCOMPARE(additionEvent->addedNodeId(), child->id());
|
|
QCOMPARE(additionEvent->metaObject(), child->metaObject());
|
|
}
|
|
|
|
void tst_Nodes::appendMultipleChildNodesToNodeScene()
|
|
{
|
|
// Check nodes added when scene is set
|
|
|
|
// GIVEN
|
|
Qt3DCore::QScene scene;
|
|
ObserverSpy spy;
|
|
QScopedPointer<MyQNode> node(new MyQNode());
|
|
|
|
// WHEN
|
|
node->setArbiterAndScene(&spy, &scene);
|
|
// THEN
|
|
QVERIFY(Qt3DCore::QNodePrivate::get(node.data())->scene() != nullptr);
|
|
|
|
// WHEN
|
|
const auto childCount = 10;
|
|
for (int i = 0; i < childCount; i++) {
|
|
// WHEN
|
|
Qt3DCore::QNode *child = nullptr;
|
|
if (i % 2 == 0) {
|
|
child = new MyQNode(node.data());
|
|
} else {
|
|
child = new MyQNode();
|
|
child->setParent(node.data());
|
|
}
|
|
|
|
// THEN parent and scene should be set synchronously
|
|
QVERIFY(child->parent() == node.data());
|
|
QVERIFY(Qt3DCore::QNodePrivate::get(child)->scene() == Qt3DCore::QNodePrivate::get(node.data())->m_scene);
|
|
}
|
|
// THEN
|
|
QCOMPARE(node->children().count(), 10);
|
|
|
|
// WHEN
|
|
QCoreApplication::processEvents();
|
|
|
|
// THEN backend is notified after the event loop spins. The recorded events are a little
|
|
// tricky to understand and differs for children with the parent being set at construction
|
|
// time (even children and ids) and the children being created without a parent and then
|
|
// explicitly calling setParent() after (odd children and ids).
|
|
//
|
|
// Even children:
|
|
// child constructed
|
|
// notifications to backend scheduled via the event loop as object is not yet fully constructed
|
|
//
|
|
// Odd children:
|
|
// child constructed
|
|
// parent set
|
|
// notifications to backend sent immediately as object is fully constructed
|
|
//
|
|
// With this in mind, the recorded events should show:
|
|
//
|
|
// for each odd child:
|
|
// odd child creation
|
|
// child addition to parent of odd child
|
|
//
|
|
// followed by:
|
|
//
|
|
// for each even child:
|
|
// even child construction
|
|
// child addition to parent of even child
|
|
//
|
|
const auto expectedEventCount = 2 * childCount;
|
|
QCOMPARE(spy.events.size(), 10 * 2);
|
|
|
|
for (auto i = 0; i < expectedEventCount; i += 2) {
|
|
const auto creationRecord = spy.events.at(i);
|
|
QVERIFY(creationRecord.wasLocked());
|
|
const auto childIndex = i < 10
|
|
? (i / 2) * 2 + 1
|
|
: (i - 10) / 2 * 2;
|
|
Qt3DCore::QNode *child = node->childNodes().at(childIndex);
|
|
|
|
const Qt3DCore::QNodeCreatedChangeBasePtr event = creationRecord.change().dynamicCast<Qt3DCore::QNodeCreatedChangeBase>();
|
|
QVERIFY(event != nullptr);
|
|
QCOMPARE(event->subjectId(), child->id());
|
|
QCOMPARE(event->metaObject(), child->metaObject());
|
|
QCOMPARE(event->isNodeEnabled(), child->isEnabled());
|
|
|
|
const auto additionRecord = spy.events.at(i + 1);
|
|
Qt3DCore::QPropertyNodeAddedChangePtr additionEvent = additionRecord.change().dynamicCast<Qt3DCore::QPropertyNodeAddedChange>();
|
|
QCOMPARE(additionEvent->subjectId(), node->id());
|
|
QCOMPARE(additionEvent->addedNodeId(), child->id());
|
|
QCOMPARE(additionEvent->metaObject(), child->metaObject());
|
|
}
|
|
}
|
|
|
|
void tst_Nodes::checkParentChangeToNull()
|
|
{
|
|
// GIVEN
|
|
Qt3DCore::QScene scene;
|
|
ObserverSpy spy;
|
|
QScopedPointer<MyQNode> root(new MyQNode());
|
|
|
|
// WHEN
|
|
root->setArbiterAndScene(&spy, &scene);
|
|
QScopedPointer<Qt3DCore::QNode> child(new MyQNode(root.data()));
|
|
QCoreApplication::processEvents();
|
|
|
|
// THEN
|
|
QVERIFY(child->parent() == root.data());
|
|
QCOMPARE(spy.events.size(), 2);
|
|
QCOMPARE(root->children().size(), 1);
|
|
|
|
// WHEN
|
|
spy.events.clear();
|
|
child->setParent(Q_NODE_NULLPTR);
|
|
|
|
// THEN
|
|
QVERIFY(child->parent() == nullptr);
|
|
QCOMPARE(root->children().size(), 0);
|
|
QCOMPARE(spy.events.size(), 2);
|
|
|
|
QVERIFY(spy.events.first().wasLocked());
|
|
const Qt3DCore::QPropertyNodeRemovedChangePtr removalEvent = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QPropertyNodeRemovedChange>();
|
|
QVERIFY(removalEvent);
|
|
QCOMPARE(removalEvent->subjectId(), root->id());
|
|
QCOMPARE(removalEvent->removedNodeId(), child->id());
|
|
QCOMPARE(removalEvent->metaObject(), child->metaObject());
|
|
|
|
QVERIFY(spy.events.first().wasLocked());
|
|
const Qt3DCore::QNodeDestroyedChangePtr destructionEvent = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QNodeDestroyedChange>();
|
|
QVERIFY(destructionEvent);
|
|
QCOMPARE(destructionEvent->type(), Qt3DCore::NodeDeleted);
|
|
QCOMPARE(destructionEvent->subjectId(), child->id());
|
|
QCOMPARE(destructionEvent->subtreeIdsAndTypes().size(), 1);
|
|
QCOMPARE(destructionEvent->subtreeIdsAndTypes().first().id, child->id());
|
|
QCOMPARE(destructionEvent->subtreeIdsAndTypes().first().type, child->metaObject());
|
|
}
|
|
|
|
void tst_Nodes::checkParentChangeToOtherParent()
|
|
{
|
|
// GIVEN
|
|
Qt3DCore::QScene scene;
|
|
ObserverSpy spy;
|
|
QScopedPointer<MyQNode> root(new MyQNode());
|
|
root->setArbiterAndScene(&spy, &scene);
|
|
QScopedPointer<MyQNode> parent1(new MyQNode(root.data()));
|
|
QScopedPointer<MyQNode> parent2(new MyQNode(root.data()));
|
|
QCoreApplication::processEvents();
|
|
|
|
// THEN
|
|
QCOMPARE(spy.events.size(), 4); // 2 x (1 node created change, 1 node added to children change)
|
|
|
|
// WHEN
|
|
spy.events.clear();
|
|
QScopedPointer<Qt3DCore::QNode> child(new MyQNode(parent1.data()));
|
|
QCoreApplication::processEvents();
|
|
|
|
// THEN
|
|
QVERIFY(child->parent() == parent1.data());
|
|
QCOMPARE(parent1->children().size(), 1);
|
|
QCOMPARE(parent2->children().size(), 0);
|
|
QVERIFY(Qt3DCore::QNodePrivate::get(child.data())->scene() != nullptr);
|
|
QCOMPARE(spy.events.size(), 2); // 1 node created change, 1 node added to children change
|
|
|
|
// WHEN
|
|
spy.events.clear();
|
|
child->setParent(parent2.data());
|
|
|
|
// THEN
|
|
QVERIFY(child->parent() == parent2.data());
|
|
QCOMPARE(parent1->children().size(), 0);
|
|
QCOMPARE(parent2->children().size(), 1);
|
|
QCOMPARE(spy.events.size(), 2);
|
|
|
|
// CHECK event 1 is a Node Removed event
|
|
QVERIFY(spy.events.first().wasLocked());
|
|
const Qt3DCore::QPropertyNodeRemovedChangePtr event = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QPropertyNodeRemovedChange>();
|
|
QCOMPARE(event->type(), Qt3DCore::PropertyValueRemoved);
|
|
QCOMPARE(event->subjectId(), parent1->id());
|
|
QCOMPARE(event->removedNodeId(), child->id());
|
|
QCOMPARE(event->metaObject(), child->metaObject());
|
|
|
|
// CHECK event 2 is a Node Added event
|
|
QVERIFY(spy.events.last().wasLocked());
|
|
const Qt3DCore::QPropertyNodeAddedChangePtr event2 = spy.events.last().change().dynamicCast<Qt3DCore::QPropertyNodeAddedChange>();
|
|
QCOMPARE(event2->type(), Qt3DCore::PropertyValueAdded);
|
|
QCOMPARE(event2->subjectId(), parent2->id());
|
|
QCOMPARE(event2->addedNodeId(), child->id());
|
|
QCOMPARE(event2->metaObject(), child->metaObject());
|
|
}
|
|
|
|
void tst_Nodes::removingSingleChildNodeFromNode()
|
|
{
|
|
// GIVEN
|
|
Qt3DCore::QScene scene;
|
|
ObserverSpy spy;
|
|
QScopedPointer<MyQNode> root(new MyQNode());
|
|
QScopedPointer<Qt3DCore::QNode> child(new MyQNode());
|
|
|
|
// WHEN
|
|
root->setArbiterAndScene(&spy, &scene);
|
|
child->setParent(root.data());
|
|
|
|
// Clear any creation event
|
|
spy.events.clear();
|
|
|
|
// THEN
|
|
QVERIFY(root->children().count() == 1);
|
|
QVERIFY(child->parentNode() == root.data());
|
|
|
|
// WHEN
|
|
child->setParent(Q_NODE_NULLPTR);
|
|
|
|
// THEN
|
|
QVERIFY(child->parent() == nullptr);
|
|
QVERIFY(root->children().count() == 0);
|
|
|
|
QCOMPARE(spy.events.size(), 2);
|
|
|
|
QVERIFY(spy.events.first().wasLocked());
|
|
const Qt3DCore::QPropertyNodeRemovedChangePtr removalEvent = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QPropertyNodeRemovedChange>();
|
|
QCOMPARE(removalEvent->subjectId(), root->id());
|
|
QCOMPARE(removalEvent->removedNodeId(), child->id());
|
|
QCOMPARE(removalEvent->metaObject(), child->metaObject());
|
|
|
|
QVERIFY(spy.events.first().wasLocked());
|
|
const Qt3DCore::QNodeDestroyedChangePtr destructionEvent = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QNodeDestroyedChange>();
|
|
QCOMPARE(destructionEvent->type(), Qt3DCore::NodeDeleted);
|
|
QCOMPARE(destructionEvent->subjectId(), child->id());
|
|
QCOMPARE(destructionEvent->subtreeIdsAndTypes().size(), 1);
|
|
QCOMPARE(destructionEvent->subtreeIdsAndTypes().first().id, child->id());
|
|
QCOMPARE(destructionEvent->subtreeIdsAndTypes().first().type, child->metaObject());
|
|
}
|
|
|
|
void tst_Nodes::removingMultipleChildNodesFromNode()
|
|
{
|
|
// GIVEN
|
|
Qt3DCore::QScene scene;
|
|
ObserverSpy spy;
|
|
QScopedPointer<MyQNode> root(new MyQNode());
|
|
|
|
// WHEN
|
|
root->setArbiterAndScene(&spy, &scene);
|
|
|
|
// THEN
|
|
QVERIFY(Qt3DCore::QNodePrivate::get(root.data())->scene() != nullptr);
|
|
|
|
// WHEN
|
|
Qt3DCore::QNodeIdVector childIds(10);
|
|
for (int i = 0; i < 10; i++) {
|
|
auto child = new MyQNode(root.data());
|
|
childIds[i] = child->id();
|
|
}
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
// THEN
|
|
QCOMPARE(root->children().count(), 10);
|
|
QCOMPARE(spy.events.size(), 20);
|
|
|
|
// WHEN
|
|
spy.events.clear();
|
|
Q_FOREACH (QObject *c, root->children())
|
|
delete c;
|
|
|
|
// THEN
|
|
QVERIFY(root->children().count() == 0);
|
|
QCOMPARE(spy.events.size(), 20);
|
|
int i = 0;
|
|
Q_FOREACH (const ObserverSpy::ChangeRecord &r, spy.events) {
|
|
QVERIFY(r.wasLocked());
|
|
const Qt3DCore::QNodeId childId = childIds.at(i / 2);
|
|
if (i % 2 == 0) {
|
|
Qt3DCore::QPropertyNodeRemovedChangePtr additionEvent = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QPropertyNodeRemovedChange>();
|
|
QCOMPARE(additionEvent->subjectId(), root->id());
|
|
QCOMPARE(additionEvent->removedNodeId(), childId);
|
|
QCOMPARE(additionEvent->metaObject(), &MyQNode::staticMetaObject);
|
|
} else {
|
|
const Qt3DCore::QNodeDestroyedChangePtr event = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QNodeDestroyedChange>();
|
|
QCOMPARE(event->subjectId(), childId);
|
|
QCOMPARE(event->subtreeIdsAndTypes().count(), 1);
|
|
QCOMPARE(event->subtreeIdsAndTypes().first().id, childId);
|
|
QCOMPARE(event->subtreeIdsAndTypes().first().type, &MyQNode::staticMetaObject);
|
|
QCOMPARE(event->type(), Qt3DCore::NodeDeleted);
|
|
}
|
|
++i;
|
|
}
|
|
}
|
|
|
|
void tst_Nodes::appendingChildEntitiesToNode()
|
|
{
|
|
// GIVEN
|
|
QScopedPointer<MyQNode> root(new MyQNode());
|
|
|
|
// WHEN
|
|
Qt3DCore::QEntity *childEntity = new Qt3DCore::QEntity(root.data());
|
|
QCoreApplication::processEvents();
|
|
|
|
// THEN
|
|
QVERIFY(root->children().first() == childEntity);
|
|
QVERIFY(childEntity->parentEntity() == nullptr);
|
|
QVERIFY(childEntity->parentNode() == root.data());
|
|
}
|
|
|
|
void tst_Nodes::removingChildEntitiesFromNode()
|
|
{
|
|
// GIVEN
|
|
QScopedPointer<MyQNode> root(new MyQNode());
|
|
|
|
// WHEN
|
|
Qt3DCore::QEntity *childEntity = new Qt3DCore::QEntity(root.data());
|
|
QCoreApplication::processEvents();
|
|
|
|
// THEN
|
|
QVERIFY(root->children().first() == childEntity);
|
|
QVERIFY(childEntity->parentEntity() == nullptr);
|
|
QVERIFY(childEntity->parentNode() == root.data());
|
|
|
|
// WHEN
|
|
childEntity->setParent(Q_NODE_NULLPTR);
|
|
|
|
// THEN
|
|
QVERIFY(root->children().isEmpty());
|
|
QVERIFY(childEntity->parentNode() == nullptr);
|
|
QVERIFY(childEntity->parent() == nullptr);
|
|
}
|
|
|
|
void tst_Nodes::appendingParentlessComponentToEntity()
|
|
{
|
|
// GIVEN
|
|
ObserverSpy spy;
|
|
QScopedPointer<MyQEntity> entity(new MyQEntity());
|
|
entity->setArbiterAndScene(&spy);
|
|
MyQComponent *comp = new MyQComponent();
|
|
|
|
// THEN
|
|
QVERIFY(entity->parentNode() == nullptr);
|
|
QVERIFY(entity->children().count() == 0);
|
|
QVERIFY(entity->components().empty());
|
|
QVERIFY(comp->parentNode() == nullptr);
|
|
|
|
// WHEN
|
|
entity->addComponent(comp);
|
|
|
|
// THEN
|
|
QVERIFY(entity->components().count() == 1);
|
|
QVERIFY(entity->components().first() == comp);
|
|
QVERIFY(comp->parentNode() == entity.data());
|
|
QCOMPARE(spy.events.size(), 1);
|
|
QVERIFY(spy.events.first().wasLocked());
|
|
|
|
// Note: since QEntity has no scene in this test case, we only have the
|
|
// ComponentAdded event In theory we should also get the NodeCreated event
|
|
// when setting the parent but that doesn't happen since no scene is
|
|
// actually set on the entity and that QNodePrivate::_q_addChild will
|
|
// return early in such a case.
|
|
|
|
// Check that we received ComponentAdded
|
|
const auto event = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QComponentAddedChange>();
|
|
QCOMPARE(event->type(), Qt3DCore::ComponentAdded);
|
|
QCOMPARE(event->subjectId(), entity->id());
|
|
QCOMPARE(event->entityId(), entity->id());
|
|
QCOMPARE(event->componentId(), comp->id());
|
|
QCOMPARE(event->componentMetaObject(), comp->metaObject());
|
|
}
|
|
|
|
void tst_Nodes::appendingComponentToEntity()
|
|
{
|
|
// GIVEN
|
|
ObserverSpy spy;
|
|
QScopedPointer<MyQEntity> entity(new MyQEntity());
|
|
entity->setArbiterAndScene(&spy);
|
|
MyQComponent *comp = new MyQComponent(entity.data());
|
|
QCoreApplication::processEvents();
|
|
|
|
// THEN
|
|
QVERIFY(entity->parentNode() == nullptr);
|
|
QVERIFY(entity->children().count() == 1);
|
|
QVERIFY(entity->components().empty());
|
|
QVERIFY(comp->parentNode() == entity.data());
|
|
|
|
// WHEN
|
|
entity->addComponent(comp);
|
|
|
|
// THEN
|
|
QVERIFY(entity->components().count() == 1);
|
|
QVERIFY(entity->components().first() == comp);
|
|
QVERIFY(comp->parentNode() == entity.data());
|
|
QCOMPARE(spy.events.size(), 1);
|
|
QVERIFY(spy.events.first().wasLocked());
|
|
const auto event = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QComponentAddedChange>();
|
|
QCOMPARE(event->type(), Qt3DCore::ComponentAdded);
|
|
QCOMPARE(event->subjectId(), entity->id());
|
|
QCOMPARE(event->entityId(), entity->id());
|
|
QCOMPARE(event->componentId(), comp->id());
|
|
QCOMPARE(event->componentMetaObject(), comp->metaObject());
|
|
}
|
|
|
|
void tst_Nodes::removingComponentFromEntity()
|
|
{
|
|
// GIVEN
|
|
ObserverSpy spy;
|
|
QScopedPointer<MyQEntity> entity(new MyQEntity());
|
|
entity->setArbiterAndScene(&spy);
|
|
MyQComponent *comp = new MyQComponent();
|
|
|
|
// WHEN
|
|
entity->addComponent(comp);
|
|
|
|
// THEN
|
|
QVERIFY(entity->components().count() == 1);
|
|
QCOMPARE(entity->children().count(), 1);
|
|
QVERIFY(comp->parent() == entity.data());
|
|
|
|
// WHEN
|
|
spy.events.clear();
|
|
entity->removeComponent(comp);
|
|
|
|
// THEN
|
|
QVERIFY(entity->components().count() == 0);
|
|
QVERIFY(comp->parent() == entity.data());
|
|
QVERIFY(entity->children().count() == 1);
|
|
QCOMPARE(spy.events.size(), 1);
|
|
QVERIFY(spy.events.first().wasLocked());
|
|
const auto event = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QComponentRemovedChange>();
|
|
QCOMPARE(event->type(), Qt3DCore::ComponentRemoved);
|
|
QCOMPARE(event->subjectId(), entity->id());
|
|
QCOMPARE(event->entityId(), entity->id());
|
|
QCOMPARE(event->componentId(), comp->id());
|
|
QCOMPARE(event->componentMetaObject(), comp->metaObject());
|
|
}
|
|
|
|
void tst_Nodes::changeCustomProperty()
|
|
{
|
|
// GIVEN
|
|
ObserverSpy spy;
|
|
QScopedPointer<MyQNode> node(new MyQNode());
|
|
node->setArbiterAndScene(&spy);
|
|
// WHEN
|
|
node->setCustomProperty(QStringLiteral("foo"));
|
|
// THEN
|
|
QCOMPARE(spy.events.size(), 1);
|
|
QVERIFY(spy.events.first().wasLocked());
|
|
Qt3DCore::QPropertyUpdatedChangePtr event = spy.events.takeFirst().change().dynamicCast<Qt3DCore::QPropertyUpdatedChange>();
|
|
QCOMPARE(event->type(), Qt3DCore::PropertyUpdated);
|
|
QCOMPARE(event->propertyName(), "customProperty");
|
|
QCOMPARE(event->value().toString(), QString("foo"));
|
|
}
|
|
|
|
void tst_Nodes::checkDestruction()
|
|
{
|
|
// GIVEN
|
|
QScopedPointer<MyQNode> root(new MyQNode());
|
|
Qt3DCore::QEntity *entity = new Qt3DCore::QEntity(root.data());
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
MyQComponent *comp1 = new MyQComponent();
|
|
MyQComponent *comp2 = new MyQComponent();
|
|
MyQComponent *comp3 = new MyQComponent();
|
|
|
|
entity->addComponent(comp1);
|
|
entity->addComponent(comp2);
|
|
entity->addComponent(comp3);
|
|
|
|
// THEN
|
|
QVERIFY(!root->children().isEmpty());
|
|
|
|
// WHEN
|
|
delete entity;
|
|
|
|
// THEN
|
|
QVERIFY(root->children().isEmpty());
|
|
}
|
|
|
|
QTEST_MAIN(tst_Nodes)
|
|
|
|
#include "tst_nodes.moc"
|