| /**************************************************************************** |
| ** |
| ** 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/QtTest> |
| #include <Qt3DCore/QAbstractAspect> |
| #include <Qt3DCore/qaspectengine.h> |
| #include <Qt3DCore/qentity.h> |
| #include <Qt3DCore/qtransform.h> |
| |
| using namespace Qt3DCore; |
| |
| class PrintRootAspect : public QAbstractAspect |
| { |
| Q_OBJECT |
| public: |
| explicit PrintRootAspect(QObject *parent = 0) |
| : QAbstractAspect(parent) |
| , m_rootEntityId() |
| { |
| qDebug() << Q_FUNC_INFO; |
| } |
| |
| private: |
| void onRegistered() override |
| { |
| qDebug() << Q_FUNC_INFO; |
| } |
| |
| void onEngineStartup() override |
| { |
| qDebug() << Q_FUNC_INFO; |
| m_rootEntityId = rootEntityId(); |
| } |
| |
| void onEngineShutdown() override |
| { |
| qDebug() << Q_FUNC_INFO; |
| } |
| |
| QVector<QAspectJobPtr> jobsToExecute(qint64) override \ |
| { |
| if (m_rootEntityId) |
| qDebug() << Q_FUNC_INFO << m_rootEntityId; |
| return QVector<QAspectJobPtr>(); |
| } |
| |
| QNodeId m_rootEntityId; |
| }; |
| |
| #define FAKE_ASPECT(ClassName) \ |
| class ClassName : public QAbstractAspect \ |
| { \ |
| Q_OBJECT \ |
| public: \ |
| explicit ClassName(QObject *parent = 0) \ |
| : QAbstractAspect(parent) {} \ |
| \ |
| private: \ |
| void onRegistered() override {} \ |
| void onEngineStartup() override {} \ |
| void onEngineShutdown() override {} \ |
| \ |
| QVector<QAspectJobPtr> jobsToExecute(qint64) override \ |
| { \ |
| return QVector<QAspectJobPtr>(); \ |
| } \ |
| \ |
| QVariant executeCommand(const QStringList &args) override \ |
| { \ |
| if (args.size() >= 2 && args.first() == QLatin1String("echo")) { \ |
| QStringList list = args; \ |
| list.removeFirst(); \ |
| return QString("%1 said '%2'").arg(metaObject()->className()).arg(list.join(QLatin1Char(' '))); \ |
| } \ |
| \ |
| return QVariant(); \ |
| } \ |
| }; |
| |
| FAKE_ASPECT(FakeAspect) |
| FAKE_ASPECT(FakeAspect2) |
| FAKE_ASPECT(FakeAspect3) |
| |
| QT3D_REGISTER_ASPECT("fake", FakeAspect) |
| QT3D_REGISTER_ASPECT("otherfake", FakeAspect2) |
| |
| |
| class tst_QAspectEngine : public QObject |
| { |
| Q_OBJECT |
| public: |
| tst_QAspectEngine() : QObject() {} |
| ~tst_QAspectEngine() {} |
| |
| private Q_SLOTS: |
| // TODO: Add more QAspectEngine tests |
| |
| void constructionDestruction() |
| { |
| QAspectEngine *engine = new QAspectEngine; |
| QVERIFY(engine->rootEntity() == nullptr); |
| delete engine; |
| } |
| |
| void setRootEntity() |
| { |
| QAspectEngine *engine = new QAspectEngine; |
| |
| QEntity *e = new QEntity; |
| e->setObjectName("root"); |
| engine->setRootEntity(QEntityPtr(e)); |
| |
| QSharedPointer<QEntity> root = engine->rootEntity(); |
| QVERIFY(root == e); |
| QVERIFY(root->objectName() == "root"); |
| root = QSharedPointer<QEntity>(); |
| QVERIFY(engine->rootEntity()->objectName() == "root"); |
| |
| delete engine; |
| } |
| |
| void shouldNotCrashInNormalStartupShutdownSequence() |
| { |
| #ifdef Q_OS_MACOS |
| QSKIP("Test frequently times out. See QTBUG-80660."); |
| #endif |
| |
| // GIVEN |
| // An initialized aspect engine... |
| QAspectEngine engine; |
| // ...and a simple aspect |
| PrintRootAspect *aspect = new PrintRootAspect; |
| |
| // WHEN |
| // We register the aspect |
| engine.registerAspect(aspect); |
| |
| // THEN |
| const auto registeredAspects = engine.aspects(); |
| QCOMPARE(registeredAspects.size(), 1); |
| QCOMPARE(registeredAspects.first(), aspect); |
| |
| // WHEN |
| QEntityPtr entity(new QEntity); |
| entity->setObjectName("RootEntity"); |
| // we set a scene root entity |
| engine.setRootEntity(entity); |
| |
| QEventLoop eventLoop; |
| QTimer::singleShot(1000, &eventLoop, SLOT(quit())); |
| eventLoop.exec(); |
| |
| // THEN |
| // we don't crash and... |
| const auto rootEntity = engine.rootEntity(); |
| QCOMPARE(rootEntity, entity); |
| |
| // WHEN |
| // we set an empty/null scene root... |
| engine.setRootEntity(QEntityPtr()); |
| QTimer::singleShot(600, &eventLoop, SLOT(quit())); |
| |
| // ...and allow events to process... |
| eventLoop.exec(); |
| |
| // THEN |
| // ... we don't crash. |
| |
| // TODO: Add more tests to check for |
| // * re-setting a scene |
| // * deregistering aspects |
| // * destroying the aspect engine |
| } |
| |
| void shouldNotCrashOnShutdownWhenComponentIsCreatedWithParentBeforeItsEntity() |
| { |
| // GIVEN |
| QEntity *root = new QEntity; |
| // A component parented to an entity... |
| QComponent *component = new Qt3DCore::QTransform(root); |
| // ... created *before* the entity it will be added to. |
| QEntity *entity = new QEntity(root); |
| entity->addComponent(component); |
| |
| // An initialized engine (so that the arbiter has been fed) |
| QAspectEngine engine; |
| |
| // WHEN |
| engine.setRootEntity(QEntityPtr(root)); |
| |
| // THEN |
| // Nothing particular happen on exit, especially no crash |
| } |
| |
| void shouldRegisterAspectsByName() |
| { |
| // GIVEN |
| QAspectEngine engine; |
| |
| // THEN |
| QVERIFY(engine.aspects().isEmpty()); |
| |
| // WHEN |
| engine.registerAspect("fake"); |
| |
| // THEN |
| QCOMPARE(engine.aspects().size(), 1); |
| QVERIFY(qobject_cast<FakeAspect*>(engine.aspects().first())); |
| } |
| |
| void shouldListLoadedAspects() |
| { |
| // GIVEN |
| QAspectEngine engine; |
| |
| // THEN |
| QCOMPARE(engine.executeCommand("list aspects").toString(), |
| QString("No loaded aspect")); |
| |
| // WHEN |
| engine.registerAspect("fake"); |
| |
| // THEN |
| QCOMPARE(engine.executeCommand("list aspects").toString(), |
| QString("fake")); |
| |
| // WHEN |
| engine.registerAspect("otherfake"); |
| |
| // THEN |
| QCOMPARE(engine.executeCommand("list aspects").toString(), |
| QString("fake\notherfake")); |
| |
| // WHEN |
| engine.registerAspect(new FakeAspect3); |
| |
| // THEN |
| QCOMPARE(engine.executeCommand("list aspects").toString(), |
| QString("fake\notherfake\n<unnamed>")); |
| } |
| |
| void shouldDelegateCommandsToAspects() |
| { |
| // GIVEN |
| QAspectEngine engine; |
| engine.registerAspect("fake"); |
| engine.registerAspect("otherfake"); |
| engine.registerAspect(new FakeAspect3); |
| |
| // WHEN |
| QVariant output = engine.executeCommand("fake echo foo bar"); |
| |
| // THEN |
| QVERIFY(output.isValid()); |
| QCOMPARE(output.toString(), QString("FakeAspect said 'foo bar'")); |
| |
| // WHEN |
| output = engine.executeCommand("otherfake echo fizz buzz"); |
| |
| // THEN |
| QVERIFY(output.isValid()); |
| QCOMPARE(output.toString(), QString("FakeAspect2 said 'fizz buzz'")); |
| |
| // WHEN |
| output = engine.executeCommand("mehfake echo fizz buzz"); |
| |
| // THEN |
| QVERIFY(!output.isValid()); |
| |
| // WHEN |
| output = engine.executeCommand("fake mooh fizz buzz"); |
| |
| // THEN |
| QVERIFY(!output.isValid()); |
| } |
| }; |
| |
| QTEST_MAIN(tst_QAspectEngine) |
| |
| #include "tst_qaspectengine.moc" |