| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include <AzCore/base.h>
- #include <AzCore/Component/ComponentApplication.h>
- #include <AzCore/Component/Entity.h>
- #include <AzCore/Component/Component.h>
- #include <AzCore/Memory/AllocationRecords.h>
- #include <AzCore/Serialization/SerializeContext.h>
- #include <AzCore/Slice/SliceComponent.h>
- #include <AzCore/Slice/SliceAsset.h>
- #include <AzCore/Slice/SliceAssetHandler.h>
- #include <AzCore/Script/ScriptSystemComponent.h>
- #include <AzCore/Script/ScriptAsset.h>
- #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
- #include <AzCore/std/chrono/chrono.h>
- #include <AzCore/RTTI/TypeInfo.h>
- #include <AzCore/UnitTest/TestTypes.h>
- #include <AzCore/UserSettings/UserSettingsComponent.h>
- #include <AzToolsFramework/API/ToolsApplicationAPI.h>
- #include <AzToolsFramework/Entity/SliceEditorEntityOwnershipServiceBus.h>
- #include <AzToolsFramework/Slice/SliceCompilation.h>
- #include <AzToolsFramework/ToolsComponents/TransformComponent.h>
- #include <AzToolsFramework/ToolsComponents/EditorOnlyEntityComponent.h>
- #include <AzToolsFramework/ToolsComponents/EditorOnlyEntityComponentBus.h>
- #include <AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.hxx>
- #include "EntityTestbed.h"
- #include <AzCore/Serialization/Utils.h>
- #include <AzCore/IO/FileIO.h>
- namespace UnitTest
- {
- using namespace AZ;
- class SliceInteractiveWorkflowTest
- : public EntityTestbed
- , AZ::Data::AssetBus::MultiHandler
- {
- public:
- class TestComponent1
- : public AZ::Component
- {
- public:
- AZ_COMPONENT(TestComponent1, "{54BA51C3-41BD-4BB6-B1ED-7F6CEFAC2F9F}");
- void Init() override
- {
- }
- void Activate() override {}
- void Deactivate() override {}
- static void Reflect(ReflectContext* context)
- {
- auto* serialize = azrtti_cast<AZ::SerializeContext*>(context);
- if (serialize)
- {
- serialize->Class<TestComponent1, AZ::Component>()
- ->Version(1)
- ->Field("SomeFlag", &TestComponent1::m_someFlag)
- ;
- AZ::EditContext* ec = serialize->GetEditContext();
- if (ec)
- {
- ec->Class<TestComponent1>("Another component", "A component.")
- ->DataElement("CheckBox", &TestComponent1::m_someFlag, "SomeFlag", "");
- }
- }
- }
- bool m_someFlag = false;
- };
- class TestComponent
- : public AZ::Component
- {
- public:
- AZ_COMPONENT(TestComponent, "{F146074C-152E-483C-AD33-6D1945B4261A}");
- void Init() override
- {
- m_rootElement = aznew Entity("Blah");
- m_rootElement->CreateComponent<TestComponent1>();
- }
- void Activate() override {}
- void Deactivate() override {}
- static void Reflect(ReflectContext* context)
- {
- auto* serialize = azrtti_cast<AZ::SerializeContext*>(context);
- if (serialize)
- {
- serialize->Class<TestComponent, AZ::Component>()
- ->Version(1)
- ->Field("RootElement", &TestComponent::m_rootElement)
- ->Field("LastElement", &TestComponent::m_lastElementId)
- ->Field("DrawOrder", &TestComponent::m_drawOrder)
- ->Field("IsPixelAligned", &TestComponent::m_isPixelAligned)
- ;
- AZ::EditContext* ec = serialize->GetEditContext();
- if (ec)
- {
- ec->Class<TestComponent>("Ui Canvas", "A component.")
- ->DataElement("CheckBox", &TestComponent::m_isPixelAligned, "IsPixelAligned", "Is pixel aligned.");
- }
- }
- }
- AZ::Entity* m_rootElement = nullptr;
- unsigned int m_lastElementId = 0;
- int m_drawOrder = 0;
- bool m_isPixelAligned = false;
- };
- AZ::Data::AssetId m_instantiatingSliceAsset;
- AZStd::atomic_int m_stressLoadPending;
- AZStd::vector<AZ::Data::Asset<AZ::SliceAsset> > m_stressTestSliceAssets;
- enum
- {
- Stress_Descendents = 3,
- Stress_Generations = 5,
- };
- SliceInteractiveWorkflowTest()
- {
- }
- ~SliceInteractiveWorkflowTest() override
- {
- EntityTestbed::Destroy();
- }
- void OnSetup() override
- {
- auto* catalogBus = AZ::Data::AssetCatalogRequestBus::FindFirstHandler();
- if (catalogBus)
- {
- // Register asset types the asset DB should query our catalog for.
- catalogBus->AddAssetType(AZ::AzTypeInfo<AZ::SliceAsset>::Uuid());
- catalogBus->AddAssetType(AZ::AzTypeInfo<AZ::ScriptAsset>::Uuid());
- // Build the catalog (scan).
- catalogBus->AddExtension(".xml");
- catalogBus->AddExtension(".lua");
- }
- }
- void OnReflect(AZ::SerializeContext& context, AZ::Entity& systemEntity) override
- {
- (void)context;
- (void)systemEntity;
- TestComponent::Reflect(&context);
- TestComponent1::Reflect(&context);
- }
- void OnAddButtons(QHBoxLayout& layout) override
- {
- QPushButton* sliceSelected = new QPushButton(QString("New Slice"));
- QPushButton* sliceInherit = new QPushButton(QString("Inherit Slice"));
- QPushButton* sliceInstance = new QPushButton(QString("Instantiate Slice"));
- QPushButton* saveRoot = new QPushButton(QString("Save Root"));
- QPushButton* stressGen = new QPushButton(QString("Stress Gen"));
- QPushButton* stressLoad = new QPushButton(QString("Stress Load"));
- QPushButton* stressInst = new QPushButton(QString("Stress Inst"));
- QPushButton* stressAll = new QPushButton(QString("Stress All"));
- stressInst->setEnabled(false);
- layout.addWidget(sliceSelected);
- layout.addWidget(sliceInherit);
- layout.addWidget(sliceInstance);
- layout.addWidget(saveRoot);
- layout.addWidget(stressGen);
- layout.addWidget(stressLoad);
- layout.addWidget(stressInst);
- layout.addWidget(stressAll);
- m_qtApplication->connect(sliceSelected, &QPushButton::pressed, [ this ]() { this->CreateSlice(false); });
- m_qtApplication->connect(sliceInherit, &QPushButton::pressed, [this](){this->CreateSlice(true); });
- m_qtApplication->connect(sliceInstance, &QPushButton::pressed, [ this ]() { this->InstantiateSlice(); });
- m_qtApplication->connect(saveRoot, &QPushButton::pressed, [ this ]() { this->SaveRoot(); });
- m_qtApplication->connect(stressGen, &QPushButton::pressed, [ this ]() { this->StressGen(); });
- m_qtApplication->connect(stressLoad, &QPushButton::pressed, [ this, stressInst ]()
- {
- if (this->StressLoad())
- {
- stressInst->setEnabled(true);
- }
- });
- m_qtApplication->connect(stressInst, &QPushButton::pressed, [ this ]() { this->StressInst(); });
- m_qtApplication->connect(stressAll, &QPushButton::pressed,
- [ this ]()
- {
- this->StressGen();
- this->StressLoad();
- this->StressInst();
- }
- );
- }
- void OnEntityAdded(AZ::Entity& entity) override
- {
- (void)entity;
- entity.CreateComponent<TestComponent>();
- }
- void StressGenDrill(const AZ::Data::Asset<AZ::SliceAsset>& parent, size_t& nextSliceIndex, size_t generation, size_t& slicesCreated)
- {
- AZ::Data::Asset<AZ::SliceAsset> descendents[Stress_Descendents];
- for (size_t i = 0; i < Stress_Descendents; ++i)
- {
- AZ::Entity* entity = new AZ::Entity;
- auto* slice = entity->CreateComponent<AZ::SliceComponent>();
- {
- slice->AddSlice(parent);
- AZ::SliceComponent::EntityList entities;
- slice->GetEntities(entities);
- entities[0]->SetName(AZStd::string::format("Gen%zu_Descendent%zu_%zu", generation, i, nextSliceIndex));
- entities[1]->SetName(AZStd::string::format("Gen%zu_Descendent%zu_%zu", generation, i, nextSliceIndex + 1));
- //entities[0]->FindComponent<TestComponent>()->m_floatValue = float(nextSliceIndex) + 0.1f;
- //entities[0]->FindComponent<TestComponent>()->m_intValue = int(generation);
- //entities[1]->FindComponent<TestComponent>()->m_floatValue = float(nextSliceIndex) + 0.2f;
- }
- char assetFile[512];
- azsnprintf(assetFile, AZ_ARRAY_SIZE(assetFile), "GeneratedSlices/Gen%zu_Descendent%zu_%zu.xml", generation, i, nextSliceIndex++);
- AZ::Data::AssetId assetId;
- AZ::Data::AssetCatalogRequestBus::BroadcastResult(
- assetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, assetFile, azrtti_typeid<AZ::SliceAsset>(), true);
- AZ::Utils::SaveObjectToFile(assetFile, AZ::DataStream::ST_XML, entity);
- slicesCreated += 1;
- descendents[i].Create(assetId, false);
- descendents[i].Get()->SetData(entity, slice, false);
- }
- // Drill down on next generation of inheritence.
- if (generation + 1 < Stress_Generations)
- {
- for (size_t i = 0; i < Stress_Descendents; ++i)
- {
- StressGenDrill(descendents[i], nextSliceIndex, generation + 1, slicesCreated);
- }
- }
- }
- void StressGen()
- {
- ResetRoot();
- // Build a base slice containing two entities.
- AZ::Entity* e1 = new AZ::Entity();
- e1->SetName("Gen0_Left");
- //auto* c1 = e1->CreateComponent<TestComponent>();
- //c1->m_floatValue = 0.1f;
- AZ::Entity* e2 = new AZ::Entity();
- e2->SetName("Gen0_Right");
- //auto* c2 = e2->CreateComponent<TestComponent>();
- //c2->m_floatValue = 0.2f;
- AZ::Entity* root = new AZ::Entity();
- auto* slice = root->CreateComponent<AZ::SliceComponent>();
- slice->AddEntity(e1);
- slice->AddEntity(e2);
- AZ::Utils::SaveObjectToFile("GeneratedSlices/Gen0.xml", AZ::DataStream::ST_XML, root);
- // Build a deep binary tree, where we create two branches of each slice, each with a different
- // override from the parent.
- AZ::Data::AssetId assetId;
- AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath,
- "GeneratedSlices/Gen0.xml", azrtti_typeid<AZ::SliceAsset>(), true);
- AZ::Data::Asset<AZ::SliceAsset> baseSliceAsset;
- baseSliceAsset.Create(assetId, false);
- baseSliceAsset.Get()->SetData(root, slice);
- // Generate tree to Stress_Generations # of generations.
- size_t nextSliceIndex = 1;
- size_t slicesCreated = 1;
- (void)nextSliceIndex;
- (void)slicesCreated;
- StressGenDrill(baseSliceAsset, nextSliceIndex, 1, slicesCreated);
- AZ_TracePrintf("Debug", "Done generating %u assets\n", slicesCreated);
- }
- void StressLoadDrill(size_t& nextSliceIndex, size_t generation, AZStd::atomic_int& pending, size_t& assetsLoaded)
- {
- for (size_t i = 0; i < Stress_Descendents; ++i)
- {
- char assetFile[512];
- azsnprintf(assetFile, AZ_ARRAY_SIZE(assetFile), "GeneratedSlices/Gen%zu_Descendent%zu_%zu.xml", generation, i, nextSliceIndex++);
- AZ::Data::AssetId assetId;
- AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath,
- assetFile, azrtti_typeid<AZ::SliceAsset>(), true);
- if (assetId.IsValid())
- {
- ++pending;
- AZ::Data::AssetBus::MultiHandler::BusConnect(assetId);
- AZ::Data::Asset<AZ::SliceAsset> asset;
- if (!asset.Create(assetId, true))
- {
- AZ_Error("Debug", false, "Asset %s could not be created.", assetFile);
- --pending;
- }
- ++assetsLoaded;
- }
- else
- {
- AZ_Error("Debug", false, "Asset %s could not be found.", assetFile);
- }
- }
- if (generation + 1 < Stress_Generations)
- {
- for (size_t i = 0; i < Stress_Descendents; ++i)
- {
- StressLoadDrill(nextSliceIndex, generation + 1, pending, assetsLoaded);
- }
- }
- }
- void StressInstDrill(const AZ::Data::Asset<AZ::SliceAsset>& asset, size_t& nextSliceIndex, size_t generation, size_t& slicesInstantiated)
- {
- // Recurse...
- if (generation < Stress_Generations)
- {
- for (size_t i = 0; i < Stress_Descendents; ++i)
- {
- char assetFile[512];
- azsnprintf(assetFile, AZ_ARRAY_SIZE(assetFile), "GeneratedSlices/Gen%zu_Descendent%zu_%zu.xml", generation, i, nextSliceIndex++);
- AZ_Error("Debug", asset.IsReady(), "Asset %s not ready?", assetFile);
- StressInstDrill(asset, nextSliceIndex, generation + 1, slicesInstantiated);
- }
- }
- if (asset.IsReady())
- {
- AzToolsFramework::SliceEditorEntityOwnershipServiceRequestBus::Broadcast(
- &AzToolsFramework::SliceEditorEntityOwnershipServiceRequests::InstantiateEditorSlice,
- asset, AZ::Transform::CreateIdentity());
- ++slicesInstantiated;
- }
- }
- bool StressLoad()
- {
- m_instantiatingSliceAsset.SetInvalid();
- m_stressTestSliceAssets.clear();
- m_stressLoadPending = 0;
- ResetRoot();
- // Preload all slice assets.
- AZ::Data::AssetId rootAssetId;
- AZ::Data::AssetCatalogRequestBus::BroadcastResult(rootAssetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath,
- "GeneratedSlices/Gen0.xml", azrtti_typeid<AZ::SliceAsset>(), true);
- if (rootAssetId.IsValid())
- {
- AZ::Data::AssetBus::MultiHandler::BusConnect(rootAssetId);
- ++m_stressLoadPending;
- AZ::Data::Asset<AZ::SliceAsset> baseSliceAsset;
- if (!baseSliceAsset.Create(rootAssetId, true))
- {
- return false;
- }
- const AZStd::chrono::steady_clock::time_point startTime = AZStd::chrono::steady_clock::now();
- size_t nextIndex = 1;
- size_t assetsLoaded = 1;
- StressLoadDrill(nextIndex, 1, m_stressLoadPending, assetsLoaded);
- while (m_stressLoadPending > 0)
- {
- AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(10));
- AZ::TickBus::Broadcast(&AZ::TickBus::Events::OnTick, 0.3f, AZ::ScriptTimePoint());
- }
- const AZStd::chrono::steady_clock::time_point assetLoadFinishTime = AZStd::chrono::steady_clock::now();
- AZ_Printf("StressTest", "All Assets Loaded: %u assets, took %.2f ms\n", assetsLoaded,
- float(AZStd::chrono::duration_cast<AZStd::chrono::microseconds>(assetLoadFinishTime - startTime).count()) * 0.001f);
- return true;
- }
- return false;
- }
- bool StressInst()
- {
- ResetRoot();
- // Instantiate from the bottom generation up.
- {
- AZ::Data::AssetId assetId;
- AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath,
- "GeneratedSlices/Gen0.xml", azrtti_typeid<AZ::SliceAsset>(), true);
- AZ::Data::Asset<AZ::SliceAsset> baseSliceAsset;
- baseSliceAsset.Create(assetId, false);
- if (baseSliceAsset.IsReady())
- {
- size_t nextIndex = 1;
- size_t slices = 0;
- size_t liveAllocs = 0;
- [[maybe_unused]] size_t totalAllocs = 0;
- auto cb = [&liveAllocs](void*, const AZ::Debug::AllocationInfo&, unsigned char)
- {
- ++liveAllocs;
- return true;
- };
- AZ::AllocatorInstance<AZ::SystemAllocator>::Get().GetRecords()->EnumerateAllocations(cb);
- totalAllocs = AZ::AllocatorInstance<AZ::SystemAllocator>::Get().GetRecords()->RequestedAllocs();
- AZ_TracePrintf("StressTest", "Allocs Before Inst: %u live, %u total\n", liveAllocs, totalAllocs);
- const AZStd::chrono::steady_clock::time_point startTime = AZStd::chrono::steady_clock::now();
- StressInstDrill(baseSliceAsset, nextIndex, 1, slices);
- const AZStd::chrono::steady_clock::time_point instantiateFinishTime = AZStd::chrono::steady_clock::now();
- liveAllocs = 0;
- totalAllocs = 0;
- AZ::AllocatorInstance<AZ::SystemAllocator>::Get().GetRecords()->EnumerateAllocations(cb);
- totalAllocs = AZ::AllocatorInstance<AZ::SystemAllocator>::Get().GetRecords()->RequestedAllocs();
- AZ_TracePrintf("StressTest", "Allocs AfterInst: %u live, %u total\n", liveAllocs, totalAllocs);
- // 1023 slices, 2046 entities
- // Before -> After = Delta
- // (Live)|(Total) -> (Live)|(Total) = (Live)|(Total)
- // 28626 | 171792 -> 53157 | 533638 = 24531 | 361846
- // 38842 | 533654 -> 53157 | 716707 = 14315 | 183053
- // 38842 | 716723 -> 53157 | 899776 = 14315 | 183053
- AZ::SliceComponent* rootSlice;
- AzToolsFramework::SliceEditorEntityOwnershipServiceRequestBus::BroadcastResult(
- rootSlice, &AzToolsFramework::SliceEditorEntityOwnershipServiceRequestBus::Events::GetEditorRootSlice);
- AZ::SliceComponent::EntityList entities;
- entities.reserve(128);
- rootSlice->GetEntities(entities);
- AZ_Printf("StressTest", "All Assets Instantiated: %u slices, %u entities, took %.2f ms\n", slices, entities.size(),
- float(AZStd::chrono::duration_cast<AZStd::chrono::microseconds>(instantiateFinishTime - startTime).count()) * 0.001f);
- return true;
- }
- }
- return false;
- }
- void CreateSlice(bool inherit)
- {
- (void)inherit;
- static AZ::u32 sliceCounter = 1;
- AzToolsFramework::EntityIdList selected;
- AzToolsFramework::ToolsApplicationRequests::Bus::BroadcastResult(
- selected, &AzToolsFramework::ToolsApplicationRequests::Bus::Events::GetSelectedEntities);
- AZ::SliceComponent* rootSlice = nullptr;
- AzToolsFramework::SliceEditorEntityOwnershipServiceRequestBus::BroadcastResult(
- rootSlice, &AzToolsFramework::SliceEditorEntityOwnershipServiceRequestBus::Events::GetEditorRootSlice);
- AZ_Assert(rootSlice, "Failed to get root slice.");
- if (!selected.empty())
- {
- AZ::Entity newEntity(AZStd::string::format("Slice%u", sliceCounter).c_str());
- AZ::SliceComponent* newSlice = newEntity.CreateComponent<AZ::SliceComponent>();
- AZStd::vector<AZ::Entity*> reclaimFromSlice;
- AZStd::vector<AZ::SliceComponent::SliceInstanceAddress> sliceInstances;
- // Add all selected entities.
- for (AZ::EntityId id : selected)
- {
- AZ::Entity* entity = nullptr;
- AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, id);
- if (entity)
- {
- AZ::SliceComponent::SliceInstanceAddress sliceAddress = rootSlice->FindSlice(entity);
- if (sliceAddress.IsValid())
- {
- // This entity already belongs to a slice instance, so inherit that instance (the whole thing for now).
- if (sliceInstances.end() == AZStd::find(sliceInstances.begin(), sliceInstances.end(), sliceAddress))
- {
- sliceInstances.push_back(sliceAddress);
- }
- }
- else
- {
- // Otherwise add loose.
- newSlice->AddEntity(entity);
- reclaimFromSlice.push_back(entity);
- }
- }
- }
- for (AZ::SliceComponent::SliceInstanceAddress& info : sliceInstances)
- {
- info = newSlice->AddSliceInstance(info.GetReference(), info.GetInstance());
- }
- const QString saveAs = QFileDialog::getSaveFileName(nullptr,
- QString("Save As..."), QString("."), QString("Xml Files (*.xml)"));
- if (!saveAs.isEmpty())
- {
- AZ::Utils::SaveObjectToFile(saveAs.toUtf8().constData(), AZ::DataStream::ST_XML, &newEntity);
- }
- // Reclaim entities.
- for (AZ::Entity* entity : reclaimFromSlice)
- {
- newSlice->RemoveEntity(entity, false);
- }
- // Reclaim slices.
- for (AZ::SliceComponent::SliceInstanceAddress& info : sliceInstances)
- {
- rootSlice->AddSliceInstance(info.GetReference(), info.GetInstance());
- }
- ++sliceCounter;
- }
- }
- void InstantiateSlice()
- {
- const QString loadFrom = QFileDialog::getOpenFileName(nullptr,
- QString("Select Slice..."), QString("."), QString("Xml Files (*.xml)"));
- if (!loadFrom.isEmpty())
- {
- AZ::Data::AssetId assetId;
- AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath,
- loadFrom.toUtf8().constData(), azrtti_typeid<AZ::SliceAsset>(), true);
- AZ::Data::Asset<AZ::SliceAsset> baseSliceAsset;
- baseSliceAsset.Create(assetId, true);
- m_instantiatingSliceAsset = baseSliceAsset.GetId();
- AZ::Data::AssetBus::MultiHandler::BusConnect(assetId);
- }
- }
- void OnAssetError(AZ::Data::Asset<AZ::Data::AssetData> asset) override
- {
- AZ::Data::AssetBus::MultiHandler::BusDisconnect(asset.GetId());
- if (asset.GetId() == m_instantiatingSliceAsset)
- {
- }
- else
- {
- --m_stressLoadPending;
- }
- }
- void OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset) override
- {
- AZ::Data::AssetBus::MultiHandler::BusDisconnect(asset.GetId());
- if (asset.GetId() == m_instantiatingSliceAsset)
- {
- if (asset.Get() == nullptr)
- {
- return;
- }
- m_instantiatingSliceAsset.SetInvalid();
- // Just add the slice to the level slice.
- AZ::Data::Asset<AZ::SliceAsset> sliceAsset = asset;
- AzToolsFramework::SliceEditorEntityOwnershipServiceRequestBus::Broadcast(
- &AzToolsFramework::SliceEditorEntityOwnershipServiceRequests::InstantiateEditorSlice,
- asset, AZ::Transform::CreateIdentity());
- // Init everything in the slice.
- AZ::SliceComponent* rootSlice = nullptr;
- AzToolsFramework::SliceEditorEntityOwnershipServiceRequestBus::BroadcastResult(
- rootSlice, &AzToolsFramework::SliceEditorEntityOwnershipServiceRequestBus::Events::GetEditorRootSlice);
- AZ_Assert(rootSlice, "Failed to get root slice.");
- AZ::SliceComponent::EntityList entities;
- rootSlice->GetEntities(entities);
- for (AZ::Entity* entity : entities)
- {
- if (entity->GetState() == AZ::Entity::State::Constructed)
- {
- entity->Init();
- }
- }
- m_entityCounter += AZ::u32(entities.size());
- }
- else
- {
- m_stressTestSliceAssets.push_back(asset);
- --m_stressLoadPending;
- }
- }
- void run()
- {
- int argc = 0;
- char* argv = nullptr;
- Run(argc, &argv);
- }
- }; // class SliceInteractiveWorkflowTest
- TEST_F(SliceInteractiveWorkflowTest, DISABLED_Test)
- {
- run();
- }
- class MinimalEntityWorkflowTester
- : public EntityTestbed
- {
- public:
- void run()
- {
- int argc = 0;
- char* argv = nullptr;
- Run(argc, &argv);
- }
- void OnEntityAdded(AZ::Entity& entity) override
- {
- // Add your components.
- entity.CreateComponent<AzToolsFramework::Components::TransformComponent>();
- }
- };
- TEST_F(MinimalEntityWorkflowTester, DISABLED_Test)
- {
- run();
- }
- class SortTransformParentsBeforeChildrenTest
- : public LeakDetectionFixture
- {
- protected:
- AZStd::vector<AZ::Entity*> m_unsorted;
- AZStd::vector<AZ::Entity*> m_sorted;
- void TearDown() override
- {
- for (AZ::Entity* entity : m_unsorted)
- {
- delete entity;
- }
- m_unsorted.clear();
- m_sorted.clear();
- }
- // Entity IDs to use in tests
- AZ::EntityId E1 = AZ::EntityId(1);
- AZ::EntityId E2 = AZ::EntityId(2);
- AZ::EntityId E3 = AZ::EntityId(3);
- AZ::EntityId E4 = AZ::EntityId(4);
- AZ::EntityId E5 = AZ::EntityId(5);
- AZ::EntityId E6 = AZ::EntityId(6);
- AZ::EntityId MissingNo = AZ::EntityId(999);
- // add entity to m_unsorted
- void AddEntity(AZ::EntityId id, AZ::EntityId parentId = AZ::EntityId())
- {
- m_unsorted.push_back(aznew AZ::Entity(id));
- m_unsorted.back()->CreateComponent<AzFramework::TransformComponent>()->SetParent(parentId);
- }
- void SortAndSanityCheck()
- {
- m_sorted = m_unsorted;
- AzToolsFramework::SortTransformParentsBeforeChildren(m_sorted);
- // sanity check that all entries are still there
- EXPECT_TRUE(DoSameEntriesExistAfterSort());
- }
- bool DoSameEntriesExistAfterSort()
- {
- if (m_sorted.size() != m_unsorted.size())
- {
- return false;
- }
- for (AZ::Entity* entity : m_unsorted)
- {
- // compare counts in case multiple entries are identical (ex: 2 nullptrs)
- size_t unsortedCount = Count(entity, m_unsorted);
- size_t sortedCount = Count(entity, m_sorted);
- if (sortedCount < 1 || sortedCount != unsortedCount)
- {
- return false;
- }
- }
- return true;
- }
- int Count(const AZ::Entity* value, const AZStd::vector<AZ::Entity*>& entityList)
- {
- int count = 0;
- for (const AZ::Entity* entity : entityList)
- {
- if (entity == value)
- {
- ++count;
- }
- }
- return count;
- }
- bool IsChildAfterParent(AZ::EntityId childId, AZ::EntityId parentId)
- {
- int parentIndex = -1;
- int childIndex = -1;
- for (int i = 0; i < m_sorted.size(); ++i)
- {
- if (m_sorted[i] && (m_sorted[i]->GetId() == parentId) && (parentIndex == -1))
- {
- parentIndex = i;
- }
- if (m_sorted[i] && (m_sorted[i]->GetId() == childId) && (childIndex == -1))
- {
- childIndex = i;
- }
- }
- EXPECT_NE(childIndex, -1);
- EXPECT_NE(parentIndex, -1);
- return childIndex > parentIndex;
- }
- };
- TEST_F(SortTransformParentsBeforeChildrenTest, 0Entities_IsOk)
- {
- SortAndSanityCheck();
- }
- TEST_F(SortTransformParentsBeforeChildrenTest, 1Entity_IsOk)
- {
- AddEntity(E1);
- SortAndSanityCheck();
- }
- TEST_F(SortTransformParentsBeforeChildrenTest, ParentAndChild_SortsCorrectly)
- {
- AddEntity(E2, E1);
- AddEntity(E1);
- SortAndSanityCheck();
- EXPECT_TRUE(IsChildAfterParent(E2, E1));
- }
- TEST_F(SortTransformParentsBeforeChildrenTest, 6EntitiesWith2Roots_SortsCorrectly)
- {
- // Hierarchy looks like:
- // 1
- // + 2
- // + 3
- // 4
- // + 5
- // + 6
- // The entities are added in "randomish" order on purpose
- AddEntity(E3, E2);
- AddEntity(E1);
- AddEntity(E6, E4);
- AddEntity(E5, E4);
- AddEntity(E2, E1);
- AddEntity(E4);
- SortAndSanityCheck();
- EXPECT_TRUE(IsChildAfterParent(E2, E1));
- EXPECT_TRUE(IsChildAfterParent(E3, E2));
- EXPECT_TRUE(IsChildAfterParent(E5, E4));
- EXPECT_TRUE(IsChildAfterParent(E6, E4));
- }
- TEST_F(SortTransformParentsBeforeChildrenTest, ParentNotFound_ChildTreatedAsRoot)
- {
- AddEntity(E1);
- AddEntity(E2, E1);
- AddEntity(E3, MissingNo); // E3's parent not found
- AddEntity(E4, E3);
- SortAndSanityCheck();
- EXPECT_TRUE(IsChildAfterParent(E2, E1));
- EXPECT_TRUE(IsChildAfterParent(E4, E2));
- }
- TEST_F(SortTransformParentsBeforeChildrenTest, NullptrEntry_IsToleratedButNotSorted)
- {
- AddEntity(E2, E1);
- m_unsorted.push_back(nullptr);
- AddEntity(E1);
- SortAndSanityCheck();
- EXPECT_TRUE(IsChildAfterParent(E2, E1));
- }
- TEST_F(SortTransformParentsBeforeChildrenTest, DuplicateEntityId_IsToleratedButNotSorted)
- {
- AddEntity(E2, E1);
- AddEntity(E1);
- AddEntity(E1); // duplicate EntityId
- SortAndSanityCheck();
- EXPECT_TRUE(IsChildAfterParent(E2, E1));
- }
- TEST_F(SortTransformParentsBeforeChildrenTest, DuplicateEntityPtr_IsToleratedButNotSorted)
- {
- AddEntity(E2, E1);
- AddEntity(E1);
- m_unsorted.push_back(m_unsorted.back()); // duplicate Entity pointer
- SortAndSanityCheck();
- m_unsorted.pop_back(); // remove duplicate ptr so we don't double-delete during teardown
- EXPECT_TRUE(IsChildAfterParent(E2, E1));
- }
- TEST_F(SortTransformParentsBeforeChildrenTest, LoopingHierarchy_PicksAnyParentAsRoot)
- {
- // loop: E1 -> E2 -> E3 -> E1 -> ...
- AddEntity(E2, E1);
- AddEntity(E3, E2);
- AddEntity(E1, E3);
- SortAndSanityCheck();
- AZ::EntityId first = m_sorted.front()->GetId();
- if (first == E1)
- {
- EXPECT_TRUE(IsChildAfterParent(E2, E1));
- EXPECT_TRUE(IsChildAfterParent(E3, E2));
- }
- else if (first == E2)
- {
- EXPECT_TRUE(IsChildAfterParent(E3, E2));
- EXPECT_TRUE(IsChildAfterParent(E1, E3));
- }
- else // if (first == E3)
- {
- EXPECT_TRUE(IsChildAfterParent(E1, E3));
- EXPECT_TRUE(IsChildAfterParent(E2, E1));
- }
- }
- TEST_F(SortTransformParentsBeforeChildrenTest, EntityLackingTransformComponent_IsTreatedLikeItHasNoParent)
- {
- AddEntity(E2, E1);
- m_unsorted.push_back(aznew AZ::Entity(E1)); // E1 has no components
- SortAndSanityCheck();
- EXPECT_TRUE(IsChildAfterParent(E2, E1));
- }
- TEST_F(SortTransformParentsBeforeChildrenTest, EntityParentedToSelf_IsTreatedLikeItHasNoParent)
- {
- AddEntity(E2, E1);
- AddEntity(E1, E1); // parented to self
- SortAndSanityCheck();
- EXPECT_TRUE(IsChildAfterParent(E2, E1));
- }
- TEST_F(SortTransformParentsBeforeChildrenTest, EntityWithInvalidId_IsToleratedButNotSorted)
- {
- AddEntity(E2, E1);
- AddEntity(E1);
- AddEntity(AZ::EntityId()); // entity using invalid ID as its own ID
- SortAndSanityCheck();
- EXPECT_TRUE(IsChildAfterParent(E2, E1));
- }
- class TestExportRuntimeComponent
- : public AZ::Component
- {
- public:
- AZ_COMPONENT(TestExportRuntimeComponent, "{C984534F-C907-4968-B9D3-AF2A99CBD678}", AZ::Component);
- TestExportRuntimeComponent() {}
- TestExportRuntimeComponent(bool returnPointerToSelf, bool exportHandled) :
- m_returnPointerToSelf(returnPointerToSelf),
- m_exportHandled(exportHandled)
- {}
- void Activate() override {}
- void Deactivate() override {}
- static void Reflect(AZ::ReflectContext* context)
- {
- if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
- {
- serializeContext->Class<TestExportRuntimeComponent, AZ::Component>()
- ;
- if (AZ::EditContext* editContext = serializeContext->GetEditContext())
- {
- editContext->Class<TestExportRuntimeComponent>(
- "Test Export Runtime Component", "Validate different options for exporting runtime components")
- ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
- ->Attribute(AZ::Edit::Attributes::RuntimeExportCallback, &TestExportRuntimeComponent::ExportComponent)
- ;
- }
- }
- }
- AZ::ExportedComponent ExportComponent(AZ::Component* thisComponent, const AZ::PlatformTagSet& /*platformTags*/)
- {
- return AZ::ExportedComponent(m_returnPointerToSelf ? thisComponent : nullptr, false, m_exportHandled);
- }
- bool m_returnPointerToSelf = false;
- bool m_exportHandled = false;
- };
- class TestExportOtherRuntimeComponent
- : public AZ::Component
- {
- public:
- AZ_COMPONENT(TestExportOtherRuntimeComponent, "{7EEDCE0A-2D5F-4017-A20B-9224E52D75B8}");
- void Activate() override {}
- void Deactivate() override {}
- static void Reflect(ReflectContext* context)
- {
- auto* serialize = azrtti_cast<AZ::SerializeContext*>(context);
- if (serialize)
- {
- serialize->Class<TestExportOtherRuntimeComponent, AZ::Component>()
- ;
- }
- }
- };
- class SliceTestExportEditorComponent
- : public AzToolsFramework::Components::EditorComponentBase
- {
- public:
- AZ_COMPONENT(SliceTestExportEditorComponent, "{8FA877A2-38E6-49AD-B31E-71B86DC8BB03}", AzToolsFramework::Components::EditorComponentBase);
- enum ExportComponentType
- {
- EXPORT_EDITOR_COMPONENT,
- EXPORT_RUNTIME_COMPONENT,
- EXPORT_OTHER_RUNTIME_COMPONENT,
- EXPORT_NULL_COMPONENT
- };
- SliceTestExportEditorComponent() {}
- SliceTestExportEditorComponent(ExportComponentType exportType, bool exportHandled) :
- m_exportType(exportType),
- m_exportHandled(exportHandled)
- {}
- void Activate() override {}
- void Deactivate() override {}
- static void Reflect(AZ::ReflectContext* context)
- {
- if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
- {
- serializeContext->Class<SliceTestExportEditorComponent, AzToolsFramework::Components::EditorComponentBase>()
- ;
- if (AZ::EditContext* editContext = serializeContext->GetEditContext())
- {
- editContext->Class<SliceTestExportEditorComponent>(
- "Test Export Editor Component", "Validate different options for exporting editor components")
- ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
- ->Attribute(AZ::Edit::Attributes::RuntimeExportCallback, &SliceTestExportEditorComponent::ExportComponent)
- ;
- }
- }
- }
- AZ::ExportedComponent ExportComponent(AZ::Component* thisComponent, const AZ::PlatformTagSet& /*platformTags*/)
- {
- switch (m_exportType)
- {
- case EXPORT_EDITOR_COMPONENT:
- return AZ::ExportedComponent(thisComponent, false, m_exportHandled);
- case EXPORT_RUNTIME_COMPONENT:
- return AZ::ExportedComponent(aznew TestExportRuntimeComponent(true, true), true, m_exportHandled);
- case EXPORT_OTHER_RUNTIME_COMPONENT:
- return AZ::ExportedComponent(aznew TestExportOtherRuntimeComponent(), true, m_exportHandled);
- case EXPORT_NULL_COMPONENT:
- return AZ::ExportedComponent(nullptr, false, m_exportHandled);
- }
- return AZ::ExportedComponent();
- }
- void BuildGameEntity(AZ::Entity* gameEntity) override
- {
- gameEntity->CreateComponent<TestExportRuntimeComponent>(true, true);
- }
- ExportComponentType m_exportType = EXPORT_NULL_COMPONENT;
- bool m_exportHandled = false;
- };
- class SliceCompilerTest
- : public UnitTest::LeakDetectionFixture
- {
- protected:
- AzToolsFramework::ToolsApplication m_app;
- AZ::Data::Asset<AZ::SliceAsset> m_editorSliceAsset;
- AZ::SliceComponent* m_editorSliceComponent = nullptr;
- AZ::Data::Asset<AZ::SliceAsset> m_compiledSliceAsset;
- AZ::SliceComponent* m_compiledSliceComponent = nullptr;
- void SetUp() override
- {
- AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get();
- auto projectPathKey =
- AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path";
- AZ::IO::FixedMaxPath enginePath;
- registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
- registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native());
- AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry);
- AZ::ComponentApplication::StartupParameters startupParameters;
- startupParameters.m_loadSettingsRegistry = false;
- m_app.Start(AzFramework::Application::Descriptor(), startupParameters);
- // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
- // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
- // in the unit tests.
- AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
- m_app.RegisterComponentDescriptor(TestExportRuntimeComponent::CreateDescriptor());
- m_app.RegisterComponentDescriptor(TestExportOtherRuntimeComponent::CreateDescriptor());
- m_app.RegisterComponentDescriptor(SliceTestExportEditorComponent::CreateDescriptor());
- m_editorSliceAsset = Data::AssetManager::Instance().CreateAsset<SliceAsset>(Data::AssetId(Uuid::CreateRandom()));
- AZ::Entity* editorSliceEntity = aznew AZ::Entity();
- m_editorSliceComponent = editorSliceEntity->CreateComponent<AZ::SliceComponent>();
- m_editorSliceAsset.Get()->SetData(editorSliceEntity, m_editorSliceComponent);
- }
- void TearDown() override
- {
- m_compiledSliceComponent = nullptr;
- m_compiledSliceAsset.Release();
- m_editorSliceComponent = nullptr;
- m_editorSliceAsset.Release();
- m_app.Stop();
- }
- // create entity with a given parent in the editor slice
- void CreateEditorEntity(AZ::u64 id, const char* name, AZ::u64 parentId = (AZ::u64)AZ::EntityId())
- {
- AZ::Entity* entity = aznew AZ::Entity(AZ::EntityId(id), name);
- AzToolsFramework::Components::TransformComponent* transformComponent = entity->CreateComponent<AzToolsFramework::Components::TransformComponent>();
- transformComponent->SetParent(AZ::EntityId(parentId));
- m_editorSliceComponent->AddEntity(entity);
- }
- // create entity containing the EditorOnly component in the editor slice
- void CreateEditorOnlyEntity(const char* name, bool editorOnly)
- {
- AZ::Entity* entity = aznew AZ::Entity(name);
- entity->CreateComponent<AzToolsFramework::Components::TransformComponent>();
- entity->CreateComponent<AzToolsFramework::Components::EditorOnlyEntityComponent>();
- m_editorSliceComponent->AddEntity(entity);
- entity->Init();
- EXPECT_EQ(AZ::Entity::State::Init, entity->GetState());
- entity->Activate();
- EXPECT_EQ(AZ::Entity::State::Active, entity->GetState());
- AzToolsFramework::EditorOnlyEntityComponentRequestBus::Event(entity->GetId(), &AzToolsFramework::EditorOnlyEntityComponentRequests::SetIsEditorOnlyEntity, editorOnly);
- }
- // create entity containing the EditorOnly component in the editor slice
- void CreateTestExportRuntimeEntity(const char* name, bool returnPointerToSelf, bool exportHandled)
- {
- AZ::Entity* entity = aznew AZ::Entity(name);
- entity->CreateComponent<AzToolsFramework::Components::TransformComponent>();
- entity->CreateComponent<TestExportRuntimeComponent>(returnPointerToSelf, exportHandled);
- m_editorSliceComponent->AddEntity(entity);
- }
- // create entity containing the EditorOnly component in the editor slice
- void CreateTestExportEditorEntity(const char* name, SliceTestExportEditorComponent::ExportComponentType exportType, bool exportHandled)
- {
- AZ::Entity* entity = aznew AZ::Entity(name);
- entity->CreateComponent<AzToolsFramework::Components::TransformComponent>();
- entity->CreateComponent<SliceTestExportEditorComponent>(exportType, exportHandled);
- m_editorSliceComponent->AddEntity(entity);
- }
- // compile m_editorSliceAsset -> m_compiledSliceAsset
- bool CompileSlice(bool expectSuccess = true)
- {
- AzToolsFramework::WorldEditorOnlyEntityHandler worldEditorOnlyEntityHandler;
- AzToolsFramework::EditorOnlyEntityHandlers handlers =
- {
- &worldEditorOnlyEntityHandler
- };
- AzToolsFramework::SliceCompilationResult compileResult = AzToolsFramework::CompileEditorSlice(m_editorSliceAsset, AZ::PlatformTagSet(), *m_app.GetSerializeContext(), handlers);
- EXPECT_EQ(compileResult.IsSuccess(), expectSuccess);
- if (compileResult.IsSuccess())
- {
- m_compiledSliceAsset = AZStd::move(compileResult.GetValue());
- m_compiledSliceComponent = m_compiledSliceAsset.Get()->GetComponent();
- return true;
- }
- return false;
- }
- // check order of entities in compiled slice
- // reference entities by name, since they have different IDs in compiled slice
- bool IsChildAfterParent(const char* childName, const char* parentName)
- {
- AZStd::vector<AZ::Entity*> entities;
- m_compiledSliceComponent->GetEntities(entities);
- AZ::Entity* parent = nullptr;
- for (AZ::Entity* entity : entities)
- {
- const AZStd::string& name = entity->GetName();
- if (name == parentName)
- {
- parent = entity;
- }
- if (name == childName)
- {
- return parent != nullptr;
- }
- }
- return false;
- }
- // Locate and return an entity from the compiled slice
- AZ::Entity* GetCompiledEntity(const char* entityName)
- {
- AZStd::vector<AZ::Entity*> entities;
- m_compiledSliceComponent->GetEntities(entities);
- for (AZ::Entity* entity : entities)
- {
- const AZStd::string& name = entity->GetName();
- if (name == entityName)
- {
- return entity;
- }
- }
- return nullptr;
- }
- };
- TEST_F(SliceCompilerTest, EntitiesInCompiledSlice_SortedParentsBeforeChildren)
- {
- // Hieararchy looks like:
- // A
- // + B
- // + C
- // D
- // + E
- // + F
- CreateEditorEntity(0xB, "B", 0xA);
- CreateEditorEntity(0xE, "E", 0xD);
- CreateEditorEntity(0xD, "D");
- CreateEditorEntity(0xA, "A");
- CreateEditorEntity(0xF, "F", 0xD);
- CreateEditorEntity(0xC, "C", 0xB);
- if (!CompileSlice())
- {
- return;
- }
- EXPECT_TRUE(IsChildAfterParent("B", "A"));
- EXPECT_TRUE(IsChildAfterParent("C", "B"));
- EXPECT_TRUE(IsChildAfterParent("E", "D"));
- EXPECT_TRUE(IsChildAfterParent("F", "D"));
- }
- TEST_F(SliceCompilerTest, EditorOnlyEntity_OnlyRuntimeEntityExported)
- {
- // Create one entity that's flagged as Editor-Only, and one that's enabled for runtime.
- CreateEditorOnlyEntity("EditorOnly", true);
- CreateEditorOnlyEntity("EditorAndRuntime", false);
- if (!CompileSlice())
- {
- return;
- }
- // Expected result: Only the runtime entity exists in the exported slice.
- EXPECT_FALSE(GetCompiledEntity("EditorOnly"));
- EXPECT_TRUE(GetCompiledEntity("EditorAndRuntime"));
- }
- TEST_F(SliceCompilerTest, RuntimeExportCallback_RuntimeComponentExportedSuccessfully)
- {
- // Create a component that has a RuntimeExportCallback and successfully exports itself
- CreateTestExportRuntimeEntity("EntityWithRuntimeComponent", true, true);
- if (!CompileSlice())
- {
- return;
- }
- // Expected result: exported slice contains the component.
- AZ::Entity* entity = GetCompiledEntity("EntityWithRuntimeComponent");
- EXPECT_TRUE(entity);
- EXPECT_TRUE(entity->FindComponent<TestExportRuntimeComponent>());
- }
- TEST_F(SliceCompilerTest, RuntimeExportCallback_RuntimeComponentExportSuppressed)
- {
- // Create a component that has a RuntimeExportCallback and successfully suppresses itself from exporting
- CreateTestExportRuntimeEntity("EntityWithRuntimeComponent", false, true);
- if (!CompileSlice())
- {
- return;
- }
- // Expected result: exported slice does NOT contain the component.
- AZ::Entity* entity = GetCompiledEntity("EntityWithRuntimeComponent");
- EXPECT_TRUE(entity);
- EXPECT_FALSE(entity->FindComponent<TestExportRuntimeComponent>());
- }
- TEST_F(SliceCompilerTest, RuntimeExportCallback_RuntimeComponentExportUnhandled)
- {
- // Create a component that has a RuntimeExportCallback, returns a pointer to itself, but says it wasn't handled.
- CreateTestExportRuntimeEntity("EntityWithRuntimeComponent", true, false);
- if (!CompileSlice())
- {
- return;
- }
- // Expected result: exported slice contains the component, because the default behavior is "clone/add" for
- // runtime components.
- AZ::Entity* entity = GetCompiledEntity("EntityWithRuntimeComponent");
- EXPECT_TRUE(entity);
- EXPECT_TRUE(entity->FindComponent<TestExportRuntimeComponent>());
- }
- TEST_F(SliceCompilerTest, RuntimeExportCallback_RuntimeComponentExportSuppressedAndUnhandled)
- {
- // Create a component that has a RuntimeExportCallback and suppresses itself from exporting, but says it wasn't handled
- CreateTestExportRuntimeEntity("EntityWithRuntimeComponent", false, false);
- if (!CompileSlice())
- {
- return;
- }
- // Expected result: exported slice contains the component, because by saying it wasn't handled, it
- // should fall back on the default behavior of "clone/add" for runtime components.
- AZ::Entity* entity = GetCompiledEntity("EntityWithRuntimeComponent");
- EXPECT_TRUE(entity);
- EXPECT_TRUE(entity->FindComponent<TestExportRuntimeComponent>());
- }
- TEST_F(SliceCompilerTest, RuntimeExportCallback_EditorComponentExportedSuccessfully)
- {
- // Create an editor component that has a RuntimeExportCallback and successfully exports itself
- CreateTestExportEditorEntity("EntityWithEditorComponent", SliceTestExportEditorComponent::ExportComponentType::EXPORT_OTHER_RUNTIME_COMPONENT, true);
- if (!CompileSlice())
- {
- return;
- }
- // Expected result: exported slice contains the OtherRuntime component, exported from RuntimeExportCallback.
- // (A result of Runtime component means BuildGameEntity() ran instead)
- AZ::Entity* entity = GetCompiledEntity("EntityWithEditorComponent");
- EXPECT_TRUE(entity);
- EXPECT_FALSE(entity->FindComponent<SliceTestExportEditorComponent>());
- EXPECT_FALSE(entity->FindComponent<TestExportRuntimeComponent>());
- EXPECT_TRUE(entity->FindComponent<TestExportOtherRuntimeComponent>());
- }
- TEST_F(SliceCompilerTest, RuntimeExportCallback_EditorComponentExportSuppressed)
- {
- // Create an editor component that has a RuntimeExportCallback and successfully suppresses itself from exporting
- CreateTestExportEditorEntity("EntityWithEditorComponent", SliceTestExportEditorComponent::ExportComponentType::EXPORT_NULL_COMPONENT, true);
- if (!CompileSlice())
- {
- return;
- }
- // Expected result: exported slice does NOT contain either component.
- AZ::Entity* entity = GetCompiledEntity("EntityWithEditorComponent");
- EXPECT_TRUE(entity);
- EXPECT_FALSE(entity->FindComponent<SliceTestExportEditorComponent>());
- EXPECT_FALSE(entity->FindComponent<TestExportRuntimeComponent>());
- EXPECT_FALSE(entity->FindComponent<TestExportOtherRuntimeComponent>());
- }
- TEST_F(SliceCompilerTest, RuntimeExportCallback_EditorComponentExportUnhandledFallbackToBuildGameEntity)
- {
- // Create an editor component that has a RuntimeExportCallback, returns a pointer to itself, but says it wasn't handled.
- CreateTestExportEditorEntity("EntityWithEditorComponent", SliceTestExportEditorComponent::ExportComponentType::EXPORT_EDITOR_COMPONENT, false);
- if (!CompileSlice())
- {
- return;
- }
- // Expected result: exported slice contains the runtime component, because the fallback to BuildGameEntity()
- // produced a runtime component.
- AZ::Entity* entity = GetCompiledEntity("EntityWithEditorComponent");
- EXPECT_TRUE(entity);
- EXPECT_FALSE(entity->FindComponent<SliceTestExportEditorComponent>());
- EXPECT_TRUE(entity->FindComponent<TestExportRuntimeComponent>());
- EXPECT_FALSE(entity->FindComponent<TestExportOtherRuntimeComponent>());
- }
- TEST_F(SliceCompilerTest, RuntimeExportCallback_EditorComponentExportSuppressedAndUnhandledFallbackToBuildGameEntity)
- {
- // Create an editor component that has a RuntimeExportCallback and suppresses itself from exporting, but says it wasn't handled
- CreateTestExportEditorEntity("EntityWithEditorComponent", SliceTestExportEditorComponent::ExportComponentType::EXPORT_NULL_COMPONENT, false);
- if (!CompileSlice())
- {
- return;
- }
- // Expected result: exported slice contains the runtime component, because the fallback to BuildGameEntity()
- // produced a runtime component.
- AZ::Entity* entity = GetCompiledEntity("EntityWithEditorComponent");
- EXPECT_TRUE(entity);
- EXPECT_FALSE(entity->FindComponent<SliceTestExportEditorComponent>());
- EXPECT_TRUE(entity->FindComponent<TestExportRuntimeComponent>());
- EXPECT_FALSE(entity->FindComponent<TestExportOtherRuntimeComponent>());
- }
- TEST_F(SliceCompilerTest, RuntimeExportCallback_EditorComponentFailsToExportItself)
- {
- // Create an editor component that has a RuntimeExportCallback and suppresses itself from exporting, but says it wasn't handled
- CreateTestExportEditorEntity("EntityWithEditorComponent", SliceTestExportEditorComponent::ExportComponentType::EXPORT_EDITOR_COMPONENT, true);
- // We expect the slice compilation to fail, since an editor component is being exported as a game component
- CompileSlice(false);
- }
- } // namespace UnitTest
|