/**************************************************************************** ** ** 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 #include #include #include #include #include using namespace Qt3DCore; class tst_Entity : public QObject { Q_OBJECT public: tst_Entity() : QObject() { qRegisterMetaType(); } ~tst_Entity() {} private slots: void constructionDestruction(); void addComponentSingleParentSingleAggregation(); void addComponentSingleParentSeveralAggregations(); void addComponentsSeveralParentsSingleAggregations(); void addComponentsSeveralParentsSeveralAggregations(); void retrieveSingleComponent(); void removeComponentSingleParentSingleAggregation(); void removeComponentSingleParentSeveralAggregations(); void removeComponentsSeveralParentsSingleAggreation(); void removeComponentsSeveralParentsSeveralAggregations(); void addSeveralTimesSameComponent(); void removeSeveralTimesSameComponent(); void checkCloning_data(); void checkCloning(); void checkComponentBookkeeping(); }; class MyQComponent : public Qt3DCore::QComponent { Q_OBJECT public: explicit MyQComponent(Qt3DCore::QNode *parent = nullptr) : QComponent(parent) {} }; class MyQ2Component : public Qt3DCore::QComponent { Q_OBJECT public: explicit MyQ2Component(Qt3DCore::QNode *parent = 0) : QComponent(parent) {} }; class MyEntity : public Qt3DCore::QEntity { public: explicit MyEntity(Qt3DCore::QNode *parent = nullptr) : QEntity(parent) {} }; void tst_Entity::constructionDestruction() { // GIVEN QEntity *entity = nullptr; // WHEN entity = new QEntity; // THEN QVERIFY(entity != nullptr); delete entity; // GIVEN QScopedPointer entity2(new QEntity); // WHEN entity2.reset(nullptr); // THEN // this should not crash } void tst_Entity::addComponentSingleParentSingleAggregation() { // GIVEN QScopedPointer entity(new QEntity()); MyQComponent *comp = new MyQComponent(entity.data()); QCoreApplication::processEvents(); // THEN QVERIFY(comp->parent() == entity.data()); QCOMPARE(entity->components().size(), 0); QCOMPARE(entity->children().size(), 1); QCOMPARE(comp->entities().size(), 0); // WHEN entity->addComponent(comp); // THEN QVERIFY(comp->parent() == entity.data()); QCOMPARE(entity->components().size(), 1); QCOMPARE(entity->children().size(), 1); QCOMPARE(comp->entities().size(), 1); } void tst_Entity::addComponentSingleParentSeveralAggregations() { // GIVEN QScopedPointer entity1(new QEntity()); QScopedPointer entity2(new QEntity()); MyQComponent *comp1 = new MyQComponent(entity1.data()); MyQComponent *comp2 = new MyQComponent(entity1.data()); MyQComponent *comp3 = new MyQComponent(entity1.data()); QCoreApplication::processEvents(); // THEN QVERIFY(comp1->parent() == entity1.data()); QVERIFY(comp2->parent() == entity1.data()); QVERIFY(comp3->parent() == entity1.data()); QCOMPARE(entity1->components().size(), 0); QCOMPARE(entity2->components().size(), 0); QCOMPARE(entity1->children().size(), 3); QCOMPARE(entity2->children().size(), 0); QCOMPARE(comp1->entities().size(), 0); QCOMPARE(comp2->entities().size(), 0); QCOMPARE(comp3->entities().size(), 0); // WHEN entity1->addComponent(comp1); entity1->addComponent(comp2); entity1->addComponent(comp3); entity2->addComponent(comp1); entity2->addComponent(comp2); entity2->addComponent(comp3); // THEN QVERIFY(comp1->parent() == entity1.data()); QVERIFY(comp2->parent() == entity1.data()); QVERIFY(comp3->parent() == entity1.data()); QCOMPARE(entity1->components().size(), 3); QCOMPARE(entity2->components().size(), 3); QCOMPARE(entity1->children().size(), 3); QCOMPARE(entity2->children().size(), 0); QCOMPARE(comp1->entities().size(), 2); QCOMPARE(comp2->entities().size(), 2); QCOMPARE(comp3->entities().size(), 2); } void tst_Entity::addComponentsSeveralParentsSingleAggregations() { // GIVEN QScopedPointer entity1(new QEntity()); QScopedPointer entity2(new QEntity()); MyQComponent *comp1 = new MyQComponent(entity1.data()); MyQComponent *comp2 = new MyQComponent(entity1.data()); MyQComponent *comp3 = new MyQComponent(entity2.data()); QCoreApplication::processEvents(); // THEN QVERIFY(comp1->parent() == entity1.data()); QVERIFY(comp2->parent() == entity1.data()); QVERIFY(comp3->parent() == entity2.data()); QCOMPARE(entity1->components().size(), 0); QCOMPARE(entity2->components().size(), 0); QCOMPARE(entity1->children().size(), 2); QCOMPARE(entity2->children().size(), 1); QCOMPARE(comp1->entities().size(), 0); QCOMPARE(comp2->entities().size(), 0); QCOMPARE(comp3->entities().size(), 0); // WHEN entity1->addComponent(comp1); entity1->addComponent(comp2); entity2->addComponent(comp3); // THEN QVERIFY(comp1->parent() == entity1.data()); QVERIFY(comp2->parent() == entity1.data()); QVERIFY(comp3->parent() == entity2.data()); QCOMPARE(entity1->components().size(), 2); QCOMPARE(entity2->components().size(), 1); QCOMPARE(entity1->children().size(), 2); QCOMPARE(entity2->children().size(), 1); QCOMPARE(comp1->entities().size(), 1); QCOMPARE(comp2->entities().size(), 1); QCOMPARE(comp3->entities().size(), 1); } void tst_Entity::addComponentsSeveralParentsSeveralAggregations() { // GIVEN QScopedPointer entity1(new QEntity()); QScopedPointer entity2(new QEntity()); MyQComponent *comp1 = new MyQComponent(entity1.data()); MyQComponent *comp2 = new MyQComponent(entity1.data()); MyQComponent *comp3 = new MyQComponent(entity2.data()); QCoreApplication::processEvents(); // THEN QVERIFY(comp1->parent() == entity1.data()); QVERIFY(comp2->parent() == entity1.data()); QVERIFY(comp3->parent() == entity2.data()); QCOMPARE(entity1->components().size(), 0); QCOMPARE(entity2->components().size(), 0); QCOMPARE(entity1->children().size(), 2); QCOMPARE(entity2->children().size(), 1); QCOMPARE(comp1->entities().size(), 0); QCOMPARE(comp2->entities().size(), 0); QCOMPARE(comp3->entities().size(), 0); // WHEN entity1->addComponent(comp1); entity1->addComponent(comp2); entity1->addComponent(comp3); entity2->addComponent(comp1); entity2->addComponent(comp2); entity2->addComponent(comp3); // THEN QVERIFY(comp1->parent() == entity1.data()); QVERIFY(comp2->parent() == entity1.data()); QVERIFY(comp3->parent() == entity2.data()); QCOMPARE(entity1->components().size(), 3); QCOMPARE(entity2->components().size(), 3); QCOMPARE(entity1->children().size(), 2); QCOMPARE(entity2->children().size(), 1); QCOMPARE(comp1->entities().size(), 2); QCOMPARE(comp2->entities().size(), 2); QCOMPARE(comp3->entities().size(), 2); } void tst_Entity::removeComponentSingleParentSingleAggregation() { // GIVEN QScopedPointer entity(new QEntity()); MyQComponent *comp = new MyQComponent(entity.data()); QCoreApplication::processEvents(); entity->addComponent(comp); // THEN QVERIFY(comp->parent() == entity.data()); QCOMPARE(entity->components().size(), 1); QCOMPARE(entity->children().size(), 1); QCOMPARE(comp->entities().size(), 1); // WHEN entity->removeComponent(comp); // THEN QVERIFY(comp->parent() == entity.data()); QCOMPARE(entity->components().size(), 0); QCOMPARE(entity->children().size(), 1); QCOMPARE(comp->entities().size(), 0); } void tst_Entity::removeComponentSingleParentSeveralAggregations() { // GIVEN QScopedPointer entity1(new QEntity()); QScopedPointer entity2(new QEntity()); MyQComponent *comp1 = new MyQComponent(entity1.data()); MyQComponent *comp2 = new MyQComponent(entity1.data()); MyQComponent *comp3 = new MyQComponent(entity1.data()); QCoreApplication::processEvents(); entity1->addComponent(comp1); entity1->addComponent(comp2); entity1->addComponent(comp3); entity2->addComponent(comp1); entity2->addComponent(comp2); entity2->addComponent(comp3); // THEN QVERIFY(comp1->parent() == entity1.data()); QVERIFY(comp2->parent() == entity1.data()); QVERIFY(comp3->parent() == entity1.data()); QCOMPARE(entity1->components().size(), 3); QCOMPARE(entity2->components().size(), 3); QCOMPARE(entity1->children().size(), 3); QCOMPARE(entity2->children().size(), 0); QCOMPARE(comp1->entities().size(), 2); QCOMPARE(comp2->entities().size(), 2); QCOMPARE(comp3->entities().size(), 2); // WHEN entity1->removeComponent(comp1); entity1->removeComponent(comp2); entity1->removeComponent(comp3); // THEN QVERIFY(comp1->parent() == entity1.data()); QVERIFY(comp2->parent() == entity1.data()); QVERIFY(comp3->parent() == entity1.data()); QCOMPARE(entity1->components().size(), 0); QCOMPARE(entity2->components().size(), 3); QCOMPARE(entity1->children().size(), 3); QCOMPARE(entity2->children().size(), 0); QCOMPARE(comp1->entities().size(), 1); QCOMPARE(comp2->entities().size(), 1); QCOMPARE(comp3->entities().size(), 1); // WHEN entity2->removeComponent(comp1); entity2->removeComponent(comp2); entity2->removeComponent(comp3); // THEN QVERIFY(comp1->parent() == entity1.data()); QVERIFY(comp2->parent() == entity1.data()); QVERIFY(comp3->parent() == entity1.data()); QCOMPARE(entity1->components().size(), 0); QCOMPARE(entity2->components().size(), 0); QCOMPARE(entity1->children().size(), 3); QCOMPARE(entity2->children().size(), 0); QCOMPARE(comp1->entities().size(), 0); QCOMPARE(comp2->entities().size(), 0); QCOMPARE(comp3->entities().size(), 0); } void tst_Entity::removeComponentsSeveralParentsSingleAggreation() { // GIVEN QScopedPointer entity1(new QEntity()); QScopedPointer entity2(new QEntity()); MyQComponent *comp1 = new MyQComponent(entity1.data()); MyQComponent *comp2 = new MyQComponent(entity1.data()); MyQComponent *comp3 = new MyQComponent(entity2.data()); QCoreApplication::processEvents(); // WHEN entity1->addComponent(comp1); entity1->addComponent(comp2); entity2->addComponent(comp3); // THEN QVERIFY(comp1->parent() == entity1.data()); QVERIFY(comp2->parent() == entity1.data()); QVERIFY(comp3->parent() == entity2.data()); QCOMPARE(entity1->components().size(), 2); QCOMPARE(entity2->components().size(), 1); QCOMPARE(entity1->children().size(), 2); QCOMPARE(entity2->children().size(), 1); QCOMPARE(comp1->entities().size(), 1); QCOMPARE(comp2->entities().size(), 1); QCOMPARE(comp3->entities().size(), 1); // WHEN entity1->removeComponent(comp1); entity1->removeComponent(comp2); entity2->removeComponent(comp3); // THEN QVERIFY(comp1->parent() == entity1.data()); QVERIFY(comp2->parent() == entity1.data()); QVERIFY(comp3->parent() == entity2.data()); QCOMPARE(entity1->components().size(), 0); QCOMPARE(entity2->components().size(), 0); QCOMPARE(entity1->children().size(), 2); QCOMPARE(entity2->children().size(), 1); QCOMPARE(comp1->entities().size(), 0); QCOMPARE(comp2->entities().size(), 0); QCOMPARE(comp3->entities().size(), 0); } void tst_Entity::removeComponentsSeveralParentsSeveralAggregations() { // GIVEN QScopedPointer entity1(new QEntity()); QScopedPointer entity2(new QEntity()); MyQComponent *comp1 = new MyQComponent(entity1.data()); MyQComponent *comp2 = new MyQComponent(entity1.data()); MyQComponent *comp3 = new MyQComponent(entity2.data()); QCoreApplication::processEvents(); // WHEN entity1->addComponent(comp1); entity1->addComponent(comp2); entity1->addComponent(comp3); entity2->addComponent(comp1); entity2->addComponent(comp2); entity2->addComponent(comp3); // THEN QVERIFY(comp1->parent() == entity1.data()); QVERIFY(comp2->parent() == entity1.data()); QVERIFY(comp3->parent() == entity2.data()); QCOMPARE(entity1->components().size(), 3); QCOMPARE(entity2->components().size(), 3); QCOMPARE(entity1->children().size(), 2); QCOMPARE(entity2->children().size(), 1); QCOMPARE(comp1->entities().size(), 2); QCOMPARE(comp2->entities().size(), 2); QCOMPARE(comp3->entities().size(), 2); // WHEN entity1->removeComponent(comp1); entity1->removeComponent(comp2); entity1->removeComponent(comp3); // THEN QVERIFY(comp1->parent() == entity1.data()); QVERIFY(comp2->parent() == entity1.data()); QVERIFY(comp3->parent() == entity2.data()); QCOMPARE(entity1->components().size(), 0); QCOMPARE(entity2->components().size(), 3); QCOMPARE(entity1->children().size(), 2); QCOMPARE(entity2->children().size(), 1); QCOMPARE(comp1->entities().size(), 1); QCOMPARE(comp2->entities().size(), 1); QCOMPARE(comp3->entities().size(), 1); // WHEN entity2->removeComponent(comp1); entity2->removeComponent(comp2); entity2->removeComponent(comp3); // THEN QVERIFY(comp1->parent() == entity1.data()); QVERIFY(comp2->parent() == entity1.data()); QVERIFY(comp3->parent() == entity2.data()); QCOMPARE(entity1->components().size(), 0); QCOMPARE(entity2->components().size(), 0); QCOMPARE(entity1->children().size(), 2); QCOMPARE(entity2->children().size(), 1); QCOMPARE(comp1->entities().size(), 0); QCOMPARE(comp2->entities().size(), 0); QCOMPARE(comp3->entities().size(), 0); } void tst_Entity::retrieveSingleComponent() { // GIVEN QScopedPointer entity1(new QEntity()); MyQComponent *comp1 = new MyQComponent(entity1.data()); MyQComponent *comp2 = new MyQComponent(entity1.data()); QCoreApplication::processEvents(); entity1->addComponent(comp1); entity1->addComponent(comp2); // WHEN QVector myQComponentsInEntity = entity1->componentsOfType(); QVector myQ2ComponentsInEntity = entity1->componentsOfType(); // THEN QVERIFY(myQComponentsInEntity.size() == 2); QVERIFY(myQComponentsInEntity[0] == comp1); QVERIFY(myQComponentsInEntity[1] == comp2); QVERIFY(myQ2ComponentsInEntity.size() == 0); } void tst_Entity::addSeveralTimesSameComponent() { // GIVEN QScopedPointer entity(new QEntity()); MyQComponent *comp = new MyQComponent(entity.data()); QCoreApplication::processEvents(); entity->addComponent(comp); // THEN QVERIFY(comp->parent() == entity.data()); QCOMPARE(entity->components().size(), 1); QCOMPARE(entity->children().size(), 1); QCOMPARE(comp->entities().size(), 1); // WHEN entity->addComponent(comp); // THEN QVERIFY(comp->parent() == entity.data()); QCOMPARE(entity->components().size(), 1); QCOMPARE(entity->children().size(), 1); QCOMPARE(comp->entities().size(), 1); } void tst_Entity::removeSeveralTimesSameComponent() { // GIVEN QScopedPointer entity(new QEntity()); MyQComponent *comp = new MyQComponent(entity.data()); QCoreApplication::processEvents(); entity->addComponent(comp); entity->removeComponent(comp); // THEN QVERIFY(comp->parent() == entity.data()); QCOMPARE(entity->components().size(), 0); QCOMPARE(entity->children().size(), 1); QCOMPARE(comp->entities().size(), 0); // WHEN entity->removeComponent(comp); // THEN QVERIFY(comp->parent() == entity.data()); QCOMPARE(entity->components().size(), 0); QCOMPARE(entity->children().size(), 1); QCOMPARE(comp->entities().size(), 0); } void tst_Entity::checkCloning_data() { QTest::addColumn("entity"); QTest::addColumn>("childEntityIds"); QTest::addColumn("creationChangeCount"); { QTest::newRow("defaultConstructed") << new MyEntity() << QVector() << 1; } { Qt3DCore::QEntity *entityWithComponents = new MyEntity(); Qt3DCore::QComponent *component1 = new MyQComponent(); Qt3DCore::QComponent *component2 = new MyQComponent(); Qt3DCore::QComponent *component3 = new MyQComponent(); entityWithComponents->addComponent(component1); entityWithComponents->addComponent(component2); entityWithComponents->addComponent(component3); QTest::newRow("entityWithComponents") << entityWithComponents << QVector() << 4; } { Qt3DCore::QEntity *entityWithChildren = new MyEntity(); Qt3DCore::QEntity *child1 = new MyEntity(entityWithChildren); Qt3DCore::QEntity *child2 = new MyEntity(entityWithChildren); QVector childIds = {child1->id(), child2->id()}; QTest::newRow("entityWithChildren") << entityWithChildren << childIds << 3; } { Qt3DCore::QEntity *entityWithNestedChildren = new MyEntity(); Qt3DCore::QEntity *child = new MyEntity(entityWithNestedChildren); Qt3DCore::QNode *dummy = new Qt3DCore::QNode(entityWithNestedChildren); Qt3DCore::QEntity *grandChild = new MyEntity(entityWithNestedChildren); QVector childIds = {child->id(), grandChild->id()}; QTest::newRow("entityWithNestedChildren") << entityWithNestedChildren << childIds << 4; Q_UNUSED(dummy); } } void tst_Entity::checkCloning() { // GIVEN QFETCH(Qt3DCore::QEntity *, entity); QFETCH(QVector, childEntityIds); QFETCH(int, creationChangeCount); // WHEN Qt3DCore::QNodeCreatedChangeGenerator creationChangeGenerator(entity); QVector creationChanges = creationChangeGenerator.creationChanges(); // THEN QCOMPARE(creationChanges.size(), creationChangeCount); const Qt3DCore::QNodeCreatedChangePtr creationChangeData = qSharedPointerCast>(creationChanges.first()); const Qt3DCore::QEntityData &cloneData = creationChangeData->data; // THEN QCOMPARE(creationChangeData->subjectId(), entity->id()); QCOMPARE(creationChangeData->isNodeEnabled(), entity->isEnabled()); QCOMPARE(creationChangeData->metaObject(), entity->metaObject()); QCOMPARE(creationChangeData->parentId(), entity->parentNode() ? entity->parentNode()->id() : Qt3DCore::QNodeId()); QCOMPARE(cloneData.parentEntityId, entity->parentEntity() ? entity->parentEntity()->id() : Qt3DCore::QNodeId()); QCOMPARE(cloneData.childEntityIds, childEntityIds); QCOMPARE(cloneData.componentIdsAndTypes.size(), entity->components().size()); const QVector &components = entity->components(); for (int i = 0, m = components.size(); i < m; ++i) { QCOMPARE(cloneData.componentIdsAndTypes.at(i).id, components.at(i)->id()); QCOMPARE(cloneData.componentIdsAndTypes.at(i).type, components.at(i)->metaObject()); } } void tst_Entity::checkComponentBookkeeping() { // GIVEN QScopedPointer rootEntity(new Qt3DCore::QEntity); { // WHEN QScopedPointer comp(new MyQComponent(rootEntity.data())); rootEntity->addComponent(comp.data()); // THEN QCOMPARE(comp->parent(), rootEntity.data()); QCOMPARE(rootEntity->components().size(), 1); } // THEN (Should not crash and comp should be automatically removed) QVERIFY(rootEntity->components().empty()); { // WHEN QScopedPointer someOtherEntity(new Qt3DCore::QEntity); QScopedPointer comp(new MyQComponent(someOtherEntity.data())); rootEntity->addComponent(comp.data()); // THEN QCOMPARE(comp->parent(), someOtherEntity.data()); QCOMPARE(rootEntity->components().size(), 1); // WHEN int sigCount = 0; QObject *sigSender = comp.data(); connect(comp.data(), &QComponent::removedFromEntity, [&sigCount, sigSender](QEntity *) { QComponent *c = qobject_cast(sigSender); if (sigSender && c) sigCount++; // test the sender is still a QComponent when signal is emitted }); comp.reset(); rootEntity.reset(); // THEN (Should not crash when the comp is destroyed (tests for failed removal of destruction helper) QCOMPARE(sigCount, 1); } } Qt3DCore::QNodeId parentEntityId(Qt3DCore::QEntity *entity) { Qt3DCore::QEntityPrivate *d = static_cast(Qt3DCore::QNodePrivate::get(entity)); return d->parentEntityId(); } QTEST_MAIN(tst_Entity) #include "tst_qentity.moc"