Revision control

++ // Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB).
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++
++ #include <QtTest/QTest>
++ #include <Qt3DRender/private/entity_p.h>
++ #include <Qt3DRender/private/entity_p_p.h>
++ #include <Qt3DRender/private/nodemanagers_p.h>
++ #include <Qt3DRender/private/managers_p.h>
++ #include <Qt3DRender/private/entityvisitor_p.h>
++ #include <Qt3DRender/private/entityaccumulator_p.h>
++
++ #include <Qt3DRender/QCameraLens>
++ #include <Qt3DCore/QTransform>
++
++ #include <Qt3DRender/QEnvironmentLight>
++ #include <Qt3DRender/QMesh>
++ #include <Qt3DRender/QMaterial>
++ #include <Qt3DRender/QLayer>
++ #include <Qt3DRender/QShaderData>
++ #include <Qt3DRender/QGeometryRenderer>
++ #include <Qt3DRender/QObjectPicker>
++ #include <Qt3DRender/QComputeCommand>
++ #include <Qt3DCore/QArmature>
++
++ #include "testrenderer.h"
++
++ typedef Qt3DCore::QNodeId (*UuidMethod)(Qt3DRender::Render::Entity *);
++ typedef QList<Qt3DCore::QNodeId> (*UuidListMethod)(Qt3DRender::Render::Entity *);
++
++ using namespace Qt3DCore;
++ using namespace Qt3DRender;
++ using namespace Qt3DRender::Render;
++
++ QNodeId transformUuid(Entity *entity) { return entity->componentUuid<Transform>(); }
++ QNodeId cameraLensUuid(Entity *entity) { return entity->componentUuid<CameraLens>(); }
++ QNodeId materialUuid(Entity *entity) { return entity->componentUuid<Material>(); }
++ QNodeId geometryRendererUuid(Entity *entity) { return entity->componentUuid<GeometryRenderer>(); }
++ QNodeId objectPickerUuid(Entity *entity) { return entity->componentUuid<ObjectPicker>(); }
++ QNodeId computeJobUuid(Entity *entity) { return entity->componentUuid<ComputeCommand>(); }
++ QNodeId armatureUuid(Entity *entity) { return entity->componentUuid<Armature>(); }
++
++ QList<QNodeId> layersUuid(Entity *entity) { return entity->componentsUuid<Layer>(); }
++ QList<QNodeId> shadersUuid(Entity *entity) { return entity->componentsUuid<ShaderData>(); }
++ QList<QNodeId> environmentLightsUuid(Entity *entity) { return entity->componentsUuid<EnvironmentLight>(); }
++
++ class CompleteVisitor : public EntityVisitor
++ {
++ public:
++ CompleteVisitor(NodeManagers *manager) : EntityVisitor(manager) { }
++
++ int count = 0;
++ Operation visit(Entity *) override { count++; return Continue; }
++ };
++
++ class EnabledVisitor : public EntityVisitor
++ {
++ public:
++ EnabledVisitor(NodeManagers *manager) : EntityVisitor(manager) { }
++
++ int count = 0;
++ Operation visit(Entity *e) override { count++; return e->isEnabled() ? Continue : Prune; }
++ };
++
++ class tst_RenderEntity : public QObject
++ {
++ Q_OBJECT
++ public:
++ tst_RenderEntity() {}
++ ~tst_RenderEntity() {}
++
++ private slots:
++
++ void checkInitialAndCleanUpState_data()
++ {
++ QTest::addColumn< QList<QComponent*> >("components");
++
++ QList<QComponent *> components = QList<QComponent *>()
++ << new Qt3DCore::QTransform
++ << new QGeometryRenderer
++ << new QCameraLens
++ << new QMaterial
++ << new QObjectPicker
++ << new QLayer
++ << new QShaderData
++ << new QComputeCommand
++ << new QEnvironmentLight
++ << new QArmature;
++
++ QTest::newRow("all components") << components;
++ }
++
++ void checkInitialAndCleanUpState()
++ {
++ // GIVEN
++ QFETCH(const QList<QComponent*>, components);
++
++ TestRenderer renderer;
++ NodeManagers nodeManagers;
++ Qt3DRender::Render::Entity entity;
++ entity.setRenderer(&renderer);
++ entity.setNodeManagers(&nodeManagers);
++
++ // THEN
++ QCOMPARE(renderer.dirtyBits(), 0);
++
++ // THEN
++ QVERIFY(entity.componentUuid<Transform>().isNull());
++ QVERIFY(entity.componentUuid<CameraLens>().isNull());
++ QVERIFY(entity.componentUuid<Material>().isNull());
++ QVERIFY(entity.componentUuid<GeometryRenderer>().isNull());
++ QVERIFY(entity.componentUuid<ObjectPicker>().isNull());
++ QVERIFY(entity.componentUuid<ComputeCommand>().isNull());
++ QVERIFY(entity.componentsUuid<Layer>().isEmpty());
++ QVERIFY(entity.componentsUuid<ShaderData>().isEmpty());
++ QVERIFY(entity.componentsUuid<EnvironmentLight>().isEmpty());
++ QVERIFY(entity.componentUuid<Armature>().isNull());
++ QVERIFY(!entity.isBoundingVolumeDirty());
++ QVERIFY(entity.childrenHandles().isEmpty());
++ QVERIFY(entity.layerIds().isEmpty());
++ QCOMPARE(entity.renderer(), &renderer);
++
++ QCOMPARE(renderer.dirtyBits(), 0);
++
++ // WHEN
++ for (QComponent *component : components)
++ EntityPrivate::get(&entity)->componentAdded(component);
++
++ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty);
++ renderer.resetDirty();
++
++ // THEN
++ QVERIFY(!entity.componentUuid<Transform>().isNull());
++ QVERIFY(!entity.componentUuid<CameraLens>().isNull());
++ QVERIFY(!entity.componentUuid<Material>().isNull());
++ QVERIFY(!entity.componentUuid<GeometryRenderer>().isNull());
++ QVERIFY(!entity.componentUuid<ObjectPicker>().isNull());
++ QVERIFY(!entity.componentUuid<ComputeCommand>().isNull());
++ QVERIFY(!entity.componentsUuid<Layer>().isEmpty());
++ QVERIFY(!entity.componentsUuid<ShaderData>().isEmpty());
++ QVERIFY(!entity.componentsUuid<EnvironmentLight>().isEmpty());
++ QVERIFY(!entity.componentUuid<Armature>().isNull());
++ QVERIFY(entity.isBoundingVolumeDirty());
++ QVERIFY(entity.childrenHandles().isEmpty());
++ QVERIFY(!entity.layerIds().isEmpty());
++ QCOMPARE(renderer.dirtyBits(), 0);
++ bool containsAll = entity.containsComponentsOfType<Transform,
++ CameraLens, Material, GeometryRenderer, ObjectPicker, ComputeCommand, Armature>();
++ QVERIFY(containsAll);
++
++ // WHEN
++ entity.cleanup();
++
++ // THEN
++ QVERIFY(entity.componentUuid<Transform>().isNull());
++ QVERIFY(entity.componentUuid<CameraLens>().isNull());
++ QVERIFY(entity.componentUuid<Material>().isNull());
++ QVERIFY(entity.componentUuid<GeometryRenderer>().isNull());
++ QVERIFY(entity.componentUuid<ObjectPicker>().isNull());
++ QVERIFY(entity.componentUuid<QComputeCommand>().isNull());
++ QVERIFY(entity.componentsUuid<Layer>().isEmpty());
++ QVERIFY(entity.componentsUuid<ShaderData>().isEmpty());
++ QVERIFY(entity.componentsUuid<EnvironmentLight>().isEmpty());
++ QVERIFY(entity.componentUuid<Armature>().isNull());
++ QVERIFY(!entity.isBoundingVolumeDirty());
++ QVERIFY(entity.childrenHandles().isEmpty());
++ QVERIFY(entity.layerIds().isEmpty());
++ containsAll = entity.containsComponentsOfType<Transform,
++ CameraLens, Material, GeometryRenderer, ObjectPicker, ComputeCommand, Armature>();
++ QVERIFY(!containsAll);
++
++ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty);
++ renderer.resetDirty();
++ }
++
++ void checkEntityReparenting()
++ {
++ // GIVEN
++ TestRenderer renderer;
++ NodeManagers nodeManagers;
++ Qt3DCore::QEntity frontendEntityA, frontendEntityB, frontendEntityC;
++
++ auto backendA = createEntity(renderer, nodeManagers, frontendEntityA);
++ auto backendB = createEntity(renderer, nodeManagers, frontendEntityB);
++ auto backendC = createEntity(renderer, nodeManagers, frontendEntityC);
++
++ // THEN
++ QVERIFY(backendA->parent() == nullptr);
++ QVERIFY(backendB->parent() == nullptr);
++ QVERIFY(backendC->parent() == nullptr);
++
++ QVERIFY(backendA->childrenHandles().isEmpty());
++ QVERIFY(backendB->childrenHandles().isEmpty());
++ QVERIFY(backendC->childrenHandles().isEmpty());
++
++ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty);
++ renderer.resetDirty();
++
++ // WHEN
++ auto sendParentChange = [&nodeManagers](const Qt3DCore::QEntity &entity) {
++ Entity *backendEntity = nodeManagers.renderNodesManager()->getOrCreateResource(entity.id());
++ backendEntity->syncFromFrontEnd(&entity, false);
++ };
++
++ // reparent B to A and C to B.
++ frontendEntityB.setParent(&frontendEntityA);
++ sendParentChange(frontendEntityB);
++ frontendEntityC.setParent(&frontendEntityB);
++ sendParentChange(frontendEntityC);
++
++ // THEN
++ QVERIFY(backendA->parent() == nullptr);
++ QVERIFY(backendB->parent() == backendA);
++ QVERIFY(backendC->parent() == backendB);
++
++ QCOMPARE(backendA->childrenHandles().size(), 1);
++ QCOMPARE(backendB->childrenHandles().size(), 1);
++ QVERIFY(backendC->childrenHandles().isEmpty());
++
++ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty);
++ renderer.resetDirty();
++
++ // WHEN - reparent C to A
++ frontendEntityC.setParent(&frontendEntityA);
++ sendParentChange(frontendEntityC);
++
++ // THEN
++ QVERIFY(backendA->parent() == nullptr);
++ QVERIFY(backendB->parent() == backendA);
++ QVERIFY(backendC->parent() == backendA);
++
++ QCOMPARE(backendA->childrenHandles().size(), 2);
++ QVERIFY(backendB->childrenHandles().isEmpty());
++ QVERIFY(backendC->childrenHandles().isEmpty());
++
++ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty);
++ renderer.resetDirty();
++
++ // WHEN - reparent B to null.
++ frontendEntityB.setParent(static_cast<Qt3DCore::QNode *>(nullptr));
++ sendParentChange(frontendEntityB);
++
++ // THEN
++ QVERIFY(backendA->parent() == nullptr);
++ QVERIFY(backendB->parent() == nullptr);
++ QVERIFY(backendC->parent() == backendA);
++
++ QCOMPARE(backendA->childrenHandles().size(), 1);
++ QVERIFY(!backendA->childrenHandles().contains(backendB->handle()));
++ QVERIFY(backendB->childrenHandles().isEmpty());
++ QVERIFY(backendC->childrenHandles().isEmpty());
++
++ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty);
++ renderer.resetDirty();
++ }
++
++ void checkEntityCleanup()
++ {
++ // GIVEN
++ TestRenderer renderer;
++ NodeManagers nodeManagers;
++ Qt3DCore::QEntity frontendEntityA, frontendEntityB, frontendEntityC;
++
++ auto backendA = createEntity(renderer, nodeManagers, frontendEntityA);
++ auto backendB = createEntity(renderer, nodeManagers, frontendEntityB);
++ auto backendC = createEntity(renderer, nodeManagers, frontendEntityC);
++
++ // WHEN
++ auto sendParentChange = [&nodeManagers](const Qt3DCore::QEntity &entity) {
++ Entity *backendEntity = nodeManagers.renderNodesManager()->getOrCreateResource(entity.id());
++ backendEntity->syncFromFrontEnd(&entity, false);
++ };
++
++ // reparent B and C to A.
++ frontendEntityB.setParent(&frontendEntityA);
++ sendParentChange(frontendEntityB);
++ frontendEntityC.setParent(&frontendEntityA);
++ sendParentChange(frontendEntityC);
++
++ // THEN
++ QVERIFY(backendA->parent() == nullptr);
++ QVERIFY(backendB->parent() == backendA);
++ QVERIFY(backendC->parent() == backendA);
++
++ QCOMPARE(backendA->childrenHandles().size(), 2);
++ QVERIFY(backendB->childrenHandles().isEmpty());
++ QVERIFY(backendC->childrenHandles().isEmpty());
++
++ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty);
++ renderer.resetDirty();
++
++ // WHEN - cleaning up a child
++ backendC->cleanup();
++
++ // THEN - the child's parent should be null and it
++ // should be removed from its parent's list of children
++ QVERIFY(backendA->parent() == nullptr);
++ QVERIFY(backendB->parent() == backendA);
++ QVERIFY(backendC->parent() == nullptr);
++
++ QCOMPARE(backendA->childrenHandles().size(), 1);
++ QVERIFY(!backendA->childrenHandles().contains(backendC->handle()));
++ QVERIFY(backendB->childrenHandles().isEmpty());
++ QVERIFY(backendC->childrenHandles().isEmpty());
++
++ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty);
++ renderer.resetDirty();
++
++ // WHEN (cleaning up parent)
++ backendA->cleanup();
++
++ // THEN (it's children's parent should be set to null)
++ QVERIFY(backendA->parent() == nullptr);
++ QVERIFY(backendB->parent() == nullptr);
++ QVERIFY(backendC->parent() == nullptr);
++
++ QVERIFY(backendA->childrenHandles().isEmpty());
++ QVERIFY(backendB->childrenHandles().isEmpty());
++ QVERIFY(backendC->childrenHandles().isEmpty());
++
++ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty);
++ renderer.resetDirty();
++
++ // WHEN
++ backendB->cleanup();
++
++ // THEN nothing should change
++ QVERIFY(backendA->childrenHandles().isEmpty());
++ QVERIFY(backendB->childrenHandles().isEmpty());
++ QVERIFY(backendC->childrenHandles().isEmpty());
++
++ QVERIFY(backendA->parent() == nullptr);
++ QVERIFY(backendB->parent() == nullptr);
++ QVERIFY(backendC->parent() == nullptr);
++
++ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty);
++ renderer.resetDirty();
++ }
++
++ void shouldHandleSingleComponentEvents_data()
++ {
++ QTest::addColumn<QComponent*>("component");
++ QTest::addColumn<void*>("functionPtr");
++
++ QComponent *component = new Qt3DCore::QTransform;
++ QTest::newRow("transform") << component << reinterpret_cast<void*>(transformUuid);
++
++ component = new QGeometryRenderer;
++ QTest::newRow("mesh") << component << reinterpret_cast<void*>(geometryRendererUuid);
++
++ component = new QCameraLens;
++ QTest::newRow("camera lens") << component << reinterpret_cast<void*>(cameraLensUuid);
++
++ component = new QMaterial;
++ QTest::newRow("material") << component << reinterpret_cast<void*>(materialUuid);
++
++ component = new QObjectPicker;
++ QTest::newRow("objectPicker") << component << reinterpret_cast<void*>(objectPickerUuid);
++
++ component = new QComputeCommand;
++ QTest::newRow("computeJob") << component << reinterpret_cast<void*>(computeJobUuid);
++
++ component = new QArmature;
++ QTest::newRow("armature") << component << reinterpret_cast<void*>(armatureUuid);
++ }
++
++ void shouldHandleSingleComponentEvents()
++ {
++ // GIVEN
++ QFETCH(QComponent*, component);
++ QFETCH(void*, functionPtr);
++ UuidMethod method = reinterpret_cast<UuidMethod>(functionPtr);
++
++ TestRenderer renderer;
++ Qt3DRender::Render::Entity entity;
++ Qt3DCore::QEntity dummyFrontendEntity;
++ entity.setRenderer(&renderer);
++
++ // THEN
++ QVERIFY(method(&entity).isNull());
++
++ // WHEN
++ EntityPrivate::get(&entity)->componentAdded(component);
++
++ // THEN
++ QCOMPARE(method(&entity), component->id());
++ QVERIFY(renderer.dirtyBits() != 0);
++
++ // WHEN
++ renderer.resetDirty();
++ EntityPrivate::get(&entity)->componentRemoved(component);
++
++ // THEN
++ QVERIFY(method(&entity).isNull());
++ QVERIFY(renderer.dirtyBits() != 0);
++
++ delete component;
++ }
++
++ void shouldHandleComponentsEvents_data()
++ {
++ QTest::addColumn< QList<QComponent*> >("components");
++ QTest::addColumn<void*>("functionPtr");
++
++ QList<QComponent*> components;
++
++ components.clear();
++ components << new QLayer << new QLayer << new QLayer;
++ QTest::newRow("layers") << components << reinterpret_cast<void*>(layersUuid);
++
++ components.clear();
++ components << new QShaderData << new QShaderData << new QShaderData;
++ QTest::newRow("shader data") << components << reinterpret_cast<void*>(shadersUuid);
++
++ components.clear();
++ components << new QEnvironmentLight << new QEnvironmentLight << new QEnvironmentLight;
++ QTest::newRow("environmentLights") << components << reinterpret_cast<void*>(environmentLightsUuid);
++ }
++
++ void shouldHandleComponentsEvents()
++ {
++ // GIVEN
++ QFETCH(const QList<QComponent*>, components);
++ QFETCH(void*, functionPtr);
++ UuidListMethod method = reinterpret_cast<UuidListMethod>(functionPtr);
++
++ TestRenderer renderer;
++ Qt3DRender::Render::Entity entity;
++ Qt3DCore::QEntity dummyFrontendEntity;
++ entity.setRenderer(&renderer);
++
++ // THEN
++ QVERIFY(method(&entity).isEmpty());
++
++ // WHEN
++ for (QComponent *component : components)
++ EntityPrivate::get(&entity)->componentAdded(component);
++
++ // THEN
++ QCOMPARE(method(&entity).size(), components.size());
++ for (QComponent *component : components) {
++ QVERIFY(method(&entity).contains(component->id()));
++ }
++ QVERIFY(renderer.dirtyBits() != 0);
++
++ // WHEN
++ renderer.resetDirty();
++ EntityPrivate::get(&entity)->componentRemoved(components.first());
++
++ // THEN
++ QCOMPARE(method(&entity).size(), components.size() - 1);
++ QVERIFY(!method(&entity).contains(components.first()->id()));
++ QVERIFY(renderer.dirtyBits() != 0);
++
++ qDeleteAll(components);
++ }
++
++ void traversal() {
++ // GIVEN
++ TestRenderer renderer;
++ NodeManagers nodeManagers;
++ Qt3DCore::QEntity frontendEntityA, frontendEntityB, frontendEntityC;
++
++ auto backendA = createEntity(renderer, nodeManagers, frontendEntityA);
++ createEntity(renderer, nodeManagers, frontendEntityB);
++ createEntity(renderer, nodeManagers, frontendEntityC);
++
++ auto sendParentChange = [&nodeManagers](const Qt3DCore::QEntity &entity) {
++ Entity *backendEntity = nodeManagers.renderNodesManager()->getOrCreateResource(entity.id());
++ backendEntity->syncFromFrontEnd(&entity, false);
++ };
++
++ // reparent B to A and C to B.
++ frontendEntityB.setParent(&frontendEntityA);
++ sendParentChange(frontendEntityB);
++ frontendEntityC.setParent(&frontendEntityB);
++ sendParentChange(frontendEntityC);
++
++ // WHEN
++ int visitCount = 0;
++ auto counter = [&visitCount](const Entity *) { ++visitCount; };
++ backendA->traverse(counter);
++
++ // THEN
++ QCOMPARE(visitCount, 3);
++ }
++
++ void visitor()
++ {
++ // GIVEN
++ TestRenderer renderer;
++ NodeManagers nodeManagers;
++ Qt3DCore::QEntity frontendEntityA, frontendEntityB, frontendEntityC;
++
++ frontendEntityA.setEnabled(false);
++ frontendEntityB.setEnabled(false);
++ frontendEntityC.setEnabled(false);
++
++ auto backendA = createEntity(renderer, nodeManagers, frontendEntityA);
++ createEntity(renderer, nodeManagers, frontendEntityB);
++ createEntity(renderer, nodeManagers, frontendEntityC);
++
++ auto sendParentChange = [&nodeManagers](const Qt3DCore::QEntity &entity) {
++ Entity *backendEntity = nodeManagers.renderNodesManager()->getOrCreateResource(entity.id());
++ backendEntity->syncFromFrontEnd(&entity, false);
++ };
++
++ // reparent B to A and C to B.
++ frontendEntityB.setParent(&frontendEntityA);
++ sendParentChange(frontendEntityB);
++ frontendEntityC.setParent(&frontendEntityB);
++ sendParentChange(frontendEntityC);
++
++ // WHEN
++ CompleteVisitor v1(&nodeManagers);
++ EnabledVisitor v2(&nodeManagers);
++ CompleteVisitor v3(&nodeManagers);
++ v3.setPruneDisabled(true);
++ v1.apply(backendA);
++ v2.apply(backendA);
++ v3.apply(backendA);
++
++ // THEN
++ QCOMPARE(v1.count, 3);
++ QCOMPARE(v2.count, 1); // nodes disabled but the first one is still visited before visitation finds out it's disabled
++ QCOMPARE(v3.count, 0); // nodes disabled
++ }
++
++ void accumulator()
++ {
++ // GIVEN
++ TestRenderer renderer;
++ NodeManagers nodeManagers;
++ Qt3DCore::QEntity frontendEntityA, frontendEntityB, frontendEntityC;
++
++ frontendEntityA.setEnabled(false);
++ frontendEntityB.setEnabled(false);
++ frontendEntityC.setEnabled(false);
++
++ auto backendA = createEntity(renderer, nodeManagers, frontendEntityA);
++ createEntity(renderer, nodeManagers, frontendEntityB);
++ createEntity(renderer, nodeManagers, frontendEntityC);
++
++ auto sendParentChange = [&nodeManagers](const Qt3DCore::QEntity &entity) {
++ Entity *backendEntity = nodeManagers.renderNodesManager()->getOrCreateResource(entity.id());
++ backendEntity->syncFromFrontEnd(&entity, false);
++ };
++
++ // reparent B to A and C to B.
++ frontendEntityB.setParent(&frontendEntityA);
++ sendParentChange(frontendEntityB);
++ frontendEntityC.setParent(&frontendEntityB);
++ sendParentChange(frontendEntityC);
++
++ // WHEN
++ EntityAccumulator v1(&nodeManagers);
++ EntityAccumulator v2([](Entity *e) { return e->isEnabled(); }, &nodeManagers);
++ const auto r1 = v1.apply(backendA);
++ const auto r2 = v2.apply(backendA);
++
++ // THEN
++ QCOMPARE(r1.size(), 3);
++ QCOMPARE(r2.size(), 0);
++ }
++
++ private:
++ Entity *createEntity(TestRenderer &renderer, NodeManagers &nodeManagers, const Qt3DCore::QEntity &frontEndEntity) {
++ HEntity renderNodeHandle = nodeManagers.renderNodesManager()->getOrAcquireHandle(frontEndEntity.id());
++ Entity *entity = nodeManagers.renderNodesManager()->data(renderNodeHandle);
++ entity->setNodeManagers(&nodeManagers);
++ entity->setHandle(renderNodeHandle);
++ entity->setRenderer(&renderer);
++ entity->syncFromFrontEnd(&frontEndEntity, true);
++ return entity;
++ }
++
++ };
++
++ QTEST_APPLESS_MAIN(tst_RenderEntity)
++
++ #include "tst_entity.moc"