PrefabInstanceSpawnerTests.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "VegetationTest.h"
  9. #include "VegetationMocks.h"
  10. #include <AzCore/Component/Entity.h>
  11. #include <AzTest/AzTest.h>
  12. #include <AzCore/UnitTest/Mocks/MockFileIOBase.h>
  13. #include <AzCore/UnitTest/TestTypes.h>
  14. #include <AzCore/Asset/AssetManagerBus.h>
  15. #include <AzCore/Asset/AssetManagerComponent.h>
  16. #include <AzCore/Asset/AssetManager.h>
  17. #include <AzCore/Jobs/JobManager.h>
  18. #include <AzCore/Jobs/JobContext.h>
  19. #include <AzCore/Memory/PoolAllocator.h>
  20. #include <AzFramework/Entity/GameEntityContextBus.h>
  21. #include <AzFramework/Spawnable/SpawnableEntitiesManager.h>
  22. #include <Tests/FileIOBaseTestTypes.h>
  23. #include <Mocks/MockSpawnableEntitiesInterface.h>
  24. #include <Vegetation/PrefabInstanceSpawner.h>
  25. #include <Vegetation/EmptyInstanceSpawner.h>
  26. #include <Vegetation/Ebuses/DescriptorNotificationBus.h>
  27. namespace UnitTest
  28. {
  29. // Mock VegetationSystemComponent is needed to reflect only the PrefabInstanceSpawner.
  30. class MockPrefabInstanceVegetationSystemComponent
  31. : public AZ::Component
  32. {
  33. public:
  34. AZ_COMPONENT(MockPrefabInstanceVegetationSystemComponent, "{5EC9AA35-2653-4326-853F-F2056F0DE36C}", AZ::Component);
  35. void Activate() override {}
  36. void Deactivate() override {}
  37. static void Reflect(AZ::ReflectContext* reflect)
  38. {
  39. Vegetation::InstanceSpawner::Reflect(reflect);
  40. Vegetation::PrefabInstanceSpawner::Reflect(reflect);
  41. Vegetation::EmptyInstanceSpawner::Reflect(reflect);
  42. }
  43. static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
  44. {
  45. provided.push_back(AZ_CRC_CE("VegetationSystemService"));
  46. }
  47. };
  48. class PrefabInstanceHandlerAndCatalog
  49. : public Vegetation::DescriptorNotificationBus::Handler
  50. , public AZ::Data::AssetCatalogRequestBus::Handler
  51. , public AZ::Data::AssetHandler
  52. , public AZ::Data::AssetCatalog
  53. {
  54. public:
  55. PrefabInstanceHandlerAndCatalog()
  56. {
  57. AZ::Data::AssetCatalogRequestBus::Handler::BusConnect();
  58. }
  59. ~PrefabInstanceHandlerAndCatalog()
  60. {
  61. AZ::Data::AssetCatalogRequestBus::Handler::BusDisconnect();
  62. }
  63. // Helper methods:
  64. // Set up a mock asset with the given name and id and direct the instance spawner to use it.
  65. void CreateAndSetMockAsset(Vegetation::PrefabInstanceSpawner& instanceSpawner, AZ::Data::AssetId assetId, AZStd::string assetPath)
  66. {
  67. // Save these off for use from our mock AssetCatalogRequestBus
  68. m_assetId = assetId;
  69. m_assetPath = assetPath;
  70. Vegetation::DescriptorNotificationBus::Handler::BusConnect(&instanceSpawner);
  71. // Tell the spawner to use this asset. Note that this also triggers a LoadAssets() call internally.
  72. instanceSpawner.SetSpawnableAssetPath(m_assetPath);
  73. // Our instance spawner should now have a valid asset reference.
  74. // It may or may not be loaded already by the time we get here,
  75. // depending on how quickly the Asset Processor job thread picks it up.
  76. EXPECT_FALSE(instanceSpawner.HasEmptyAssetReferences());
  77. // Since the asset load is going through the real AssetManager, there's a delay while a separate
  78. // job thread executes and actually loads our mock spawnable asset.
  79. // If our asset hasn't loaded successfully after 5 seconds, it's unlikely to succeed.
  80. // This choice of delay should be *reasonably* safe because it's all CPU-based processing,
  81. // no actual I/O occurs as a part of the test.
  82. constexpr int sleepMs = 10;
  83. constexpr int totalWaitTimeMs = 5000;
  84. int numRetries = totalWaitTimeMs / sleepMs;
  85. while ((m_numOnLoadedCalls < 1) && (numRetries >= 0))
  86. {
  87. AZ::Data::AssetManager::Instance().DispatchEvents();
  88. AZ::SystemTickBus::Broadcast(&AZ::SystemTickBus::Events::OnSystemTick);
  89. AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(sleepMs));
  90. numRetries--;
  91. }
  92. ASSERT_TRUE(m_numOnLoadedCalls == 1);
  93. EXPECT_TRUE(instanceSpawner.IsLoaded());
  94. EXPECT_TRUE(instanceSpawner.IsSpawnable());
  95. Vegetation::DescriptorNotificationBus::Handler::BusDisconnect();
  96. }
  97. // AssetHandler
  98. // Minimalist mocks to look like a Spawnable has been created/loaded/destroyed successfully
  99. AZ::Data::AssetPtr CreateAsset(const AZ::Data::AssetId& id, [[maybe_unused]] const AZ::Data::AssetType& type) override
  100. {
  101. AzFramework::Spawnable* spawnableAsset = new AzFramework::Spawnable(id);
  102. MockAssetData* temp = reinterpret_cast<MockAssetData*>(spawnableAsset);
  103. temp->SetStatus(AZ::Data::AssetData::AssetStatus::NotLoaded);
  104. return spawnableAsset;
  105. }
  106. void DestroyAsset(AZ::Data::AssetPtr ptr) override { delete ptr; }
  107. void GetHandledAssetTypes(AZStd::vector<AZ::Data::AssetType>& assetTypes) override
  108. {
  109. assetTypes.push_back(AZ::AzTypeInfo<AzFramework::Spawnable>::Uuid());
  110. }
  111. AZ::Data::AssetHandler::LoadResult LoadAssetData(
  112. const AZ::Data::Asset<AZ::Data::AssetData>& asset,
  113. AZStd::shared_ptr<AZ::Data::AssetDataStream> stream,
  114. [[maybe_unused]] const AZ::Data::AssetFilterCB& assetLoadFilterCB) override
  115. {
  116. MockAssetData* temp = reinterpret_cast<MockAssetData*>(asset.GetData());
  117. temp->SetStatus(AZ::Data::AssetData::AssetStatus::Ready);
  118. return AZ::Data::AssetHandler::LoadResult::LoadComplete;
  119. }
  120. // DescriptorNotificationBus
  121. // Keep track of whether or not the Spawner successfully loaded the asset and notified listeners
  122. void OnDescriptorAssetsLoaded() override { m_numOnLoadedCalls++; }
  123. // AssetCatalogRequestBus
  124. // Minimalist mocks to provide our desired asset path or asset id
  125. AZStd::string GetAssetPathById(const AZ::Data::AssetId& /*id*/) override { return m_assetPath; }
  126. AZ::Data::AssetId GetAssetIdByPath(const char* /*path*/, const AZ::Data::AssetType& /*typeToRegister*/, bool /*autoRegisterIfNotFound*/) override { return m_assetId; }
  127. AZ::Data::AssetInfo GetAssetInfoById(const AZ::Data::AssetId& /*id*/) override
  128. {
  129. AZ::Data::AssetInfo assetInfo;
  130. assetInfo.m_assetId = m_assetId;
  131. assetInfo.m_assetType = AZ::AzTypeInfo<AzFramework::Spawnable>::Uuid();
  132. assetInfo.m_relativePath = m_assetPath;
  133. return assetInfo;
  134. }
  135. // AssetCatalog
  136. // Minimalist mock to pretend like we've loaded a Spawnable asset
  137. AZ::Data::AssetStreamInfo GetStreamInfoForLoad(
  138. [[maybe_unused]] const AZ::Data::AssetId& id, const AZ::Data::AssetType& type) override
  139. {
  140. EXPECT_TRUE(type == AZ::AzTypeInfo<AzFramework::Spawnable>::Uuid());
  141. AZ::Data::AssetStreamInfo info;
  142. info.m_dataOffset = 0;
  143. info.m_streamName = m_assetPath;
  144. info.m_dataLen = 0;
  145. info.m_streamFlags = AZ::IO::OpenMode::ModeRead;
  146. return info;
  147. }
  148. AZStd::string m_assetPath;
  149. AZ::Data::AssetId m_assetId;
  150. int m_numOnLoadedCalls = 0;
  151. };
  152. // To test Prefab/Spawnable spawning, we need to mock up enough of the asset management system and the Spawnable
  153. // asset handling to pretend like we're loading/unloading Spawnables successfully.
  154. class PrefabInstanceSpawnerTests
  155. : public VegetationComponentTests
  156. {
  157. public:
  158. PrefabInstanceSpawnerTests()
  159. : m_restoreFileIO(m_fileIOMock)
  160. {
  161. AZ::IO::MockFileIOBase::InstallDefaultReturns(m_fileIOMock);
  162. AzFramework::MockSpawnableEntitiesInterface::InstallDefaultReturns(m_spawnableEntitiesInterfaceMock);
  163. }
  164. void SetUp() override
  165. {
  166. VegetationComponentTests::SetUp();
  167. // Create a real Asset Mananger, and point to ourselves as the handler for Spawnable assets.
  168. // Initialize the job manager with 1 thread for the AssetManager to use.
  169. AZ::JobManagerDesc jobDesc;
  170. AZ::JobManagerThreadDesc threadDesc;
  171. jobDesc.m_workerThreads.push_back(threadDesc);
  172. m_jobManager = aznew AZ::JobManager(jobDesc);
  173. m_jobContext = aznew AZ::JobContext(*m_jobManager);
  174. AZ::JobContext::SetGlobalContext(m_jobContext);
  175. AZ::Data::AssetManager::Descriptor descriptor;
  176. AZ::Data::AssetManager::Create(descriptor);
  177. m_testHandler = AZStd::make_unique<PrefabInstanceHandlerAndCatalog>();
  178. AZ::Data::AssetManager::Instance().RegisterHandler(m_testHandler.get(), AZ::AzTypeInfo<AzFramework::Spawnable>::Uuid());
  179. AZ::Data::AssetManager::Instance().RegisterCatalog(m_testHandler.get(), AZ::AzTypeInfo<AzFramework::Spawnable>::Uuid());
  180. }
  181. void TearDown() override
  182. {
  183. // Clear out the list of queued AssetBus Events before unregistering the AssetHandler
  184. // to make sure pending references to Asset<AssetData> instances are cleared
  185. AZ::Data::AssetManager::Instance().DispatchEvents();
  186. AZ::Data::AssetManager::Instance().UnregisterHandler(m_testHandler.get());
  187. AZ::Data::AssetManager::Instance().UnregisterCatalog(m_testHandler.get());
  188. AZ::Data::AssetManager::Destroy();
  189. m_testHandler.reset();
  190. AZ::JobContext::SetGlobalContext(nullptr);
  191. delete m_jobContext;
  192. delete m_jobManager;
  193. VegetationComponentTests::TearDown();
  194. }
  195. void RegisterComponentDescriptors() override
  196. {
  197. m_app.RegisterComponentDescriptor(MockPrefabInstanceVegetationSystemComponent::CreateDescriptor());
  198. }
  199. protected:
  200. AZStd::unique_ptr<PrefabInstanceHandlerAndCatalog> m_testHandler;
  201. private:
  202. AZ::JobManager* m_jobManager{ nullptr };
  203. AZ::JobContext* m_jobContext{ nullptr };
  204. SetRestoreFileIOBaseRAII m_restoreFileIO;
  205. ::testing::NiceMock<AZ::IO::MockFileIOBase> m_fileIOMock;
  206. ::testing::NiceMock<AzFramework::MockSpawnableEntitiesInterface> m_spawnableEntitiesInterfaceMock;
  207. };
  208. TEST_F(PrefabInstanceSpawnerTests, BasicInitializationTest)
  209. {
  210. // Basic test to make sure we can construct / destroy without errors.
  211. Vegetation::PrefabInstanceSpawner instanceSpawner;
  212. }
  213. TEST_F(PrefabInstanceSpawnerTests, DefaultSpawnersAreEqual)
  214. {
  215. // Two different instances of the default PrefabInstanceSpawner should be considered data-equivalent.
  216. Vegetation::PrefabInstanceSpawner instanceSpawner1;
  217. Vegetation::PrefabInstanceSpawner instanceSpawner2;
  218. EXPECT_TRUE(instanceSpawner1 == instanceSpawner2);
  219. }
  220. TEST_F(PrefabInstanceSpawnerTests, DifferentSpawnersAreNotEqual)
  221. {
  222. // Two spawners with different data should *not* be data-equivalent.
  223. Vegetation::PrefabInstanceSpawner instanceSpawner1;
  224. Vegetation::PrefabInstanceSpawner instanceSpawner2;
  225. // Give the second instance spawner a non-default asset reference.
  226. m_testHandler->CreateAndSetMockAsset(instanceSpawner2, AZ::Uuid::CreateRandom(), "test");
  227. // The test is written this way because only the == operator is overloaded.
  228. EXPECT_TRUE(!(instanceSpawner1 == instanceSpawner2));
  229. }
  230. TEST_F(PrefabInstanceSpawnerTests, LoadAndUnloadAssets)
  231. {
  232. // The spawner should successfully load/unload assets without errors.
  233. Vegetation::PrefabInstanceSpawner instanceSpawner;
  234. // Our instance spawner should be empty before we set the assets.
  235. EXPECT_TRUE(instanceSpawner.HasEmptyAssetReferences());
  236. // This will test the asset load.
  237. m_testHandler->CreateAndSetMockAsset(instanceSpawner, AZ::Uuid::CreateRandom(), "test");
  238. // Test the asset unload works too.
  239. m_testHandler->Vegetation::DescriptorNotificationBus::Handler::BusConnect(&instanceSpawner);
  240. instanceSpawner.UnloadAssets();
  241. EXPECT_FALSE(instanceSpawner.IsLoaded());
  242. EXPECT_FALSE(instanceSpawner.IsSpawnable());
  243. m_testHandler->Vegetation::DescriptorNotificationBus::Handler::BusDisconnect();
  244. }
  245. TEST_F(PrefabInstanceSpawnerTests, CreateAndDestroyInstance)
  246. {
  247. // The spawner should successfully create and destroy an instance without errors.
  248. Vegetation::PrefabInstanceSpawner instanceSpawner;
  249. m_testHandler->CreateAndSetMockAsset(instanceSpawner, AZ::Uuid::CreateRandom(), "test");
  250. instanceSpawner.OnRegisterUniqueDescriptor();
  251. Vegetation::InstanceData instanceData;
  252. Vegetation::InstancePtr instance = instanceSpawner.CreateInstance(instanceData);
  253. EXPECT_TRUE(instance);
  254. instanceSpawner.DestroyInstance(0, instance);
  255. instanceSpawner.OnReleaseUniqueDescriptor();
  256. }
  257. TEST_F(PrefabInstanceSpawnerTests, SpawnerRegisteredWithDescriptor)
  258. {
  259. // Validate that the Descriptor successfully gets PrefabInstanceSpawner registered with it,
  260. // as long as InstanceSpawner and PrefabInstanceSpawner have been reflected.
  261. MockPrefabInstanceVegetationSystemComponent* component = nullptr;
  262. auto entity = CreateEntity(&component);
  263. Vegetation::Descriptor descriptor;
  264. descriptor.RefreshSpawnerTypeList();
  265. auto spawnerTypes = descriptor.GetSpawnerTypeList();
  266. EXPECT_TRUE(spawnerTypes.size() > 0);
  267. const auto& prefabSpawnerEntry = AZStd::find(
  268. spawnerTypes.begin(), spawnerTypes.end(),
  269. AZStd::pair<AZ::TypeId, AZStd::string>(Vegetation::PrefabInstanceSpawner::RTTI_Type(), "PrefabInstanceSpawner"));
  270. EXPECT_NE(prefabSpawnerEntry, spawnerTypes.end());
  271. }
  272. TEST_F(PrefabInstanceSpawnerTests, DescriptorCreatesCorrectSpawner)
  273. {
  274. // Validate that the Descriptor successfully creates a new PrefabInstanceSpawner if we change
  275. // the spawner type on the Descriptor.
  276. MockPrefabInstanceVegetationSystemComponent* component = nullptr;
  277. auto entity = CreateEntity(&component);
  278. // We expect the Descriptor to start off with a Prefab spawner
  279. Vegetation::Descriptor descriptor;
  280. EXPECT_EQ(azrtti_typeid(*(descriptor.GetInstanceSpawner())),Vegetation::PrefabInstanceSpawner::RTTI_Type());
  281. // Change it to something other than a Prefab spawner and verify it changes
  282. descriptor.m_spawnerType = Vegetation::EmptyInstanceSpawner::RTTI_Type();
  283. descriptor.RefreshSpawnerTypeList();
  284. descriptor.SpawnerTypeChanged();
  285. EXPECT_NE(azrtti_typeid(*(descriptor.GetInstanceSpawner())), Vegetation::PrefabInstanceSpawner::RTTI_Type());
  286. // Change it back to a Prefab spawner and verify that we have the correct spawner type
  287. descriptor.m_spawnerType = Vegetation::PrefabInstanceSpawner::RTTI_Type();
  288. descriptor.RefreshSpawnerTypeList();
  289. descriptor.SpawnerTypeChanged();
  290. EXPECT_EQ(azrtti_typeid(*(descriptor.GetInstanceSpawner())), Vegetation::PrefabInstanceSpawner::RTTI_Type());
  291. }
  292. }