PrefabBuilderTests.cpp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  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 "PrefabBuilderTests.h"
  9. #include <AzCore/Asset/AssetSerializer.h>
  10. #include <AzCore/Component/TransformBus.h>
  11. #include <AzCore/Memory/AllocationRecords.h>
  12. #include <AzCore/Serialization/Json/JsonSystemComponent.h>
  13. #include <AzCore/Serialization/Json/JsonUtils.h>
  14. #include <AzCore/Serialization/Json/RegistrationContext.h>
  15. #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
  16. #include <AzCore/UserSettings/UserSettingsComponent.h>
  17. #include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
  18. namespace UnitTest
  19. {
  20. AZ::Entity* CreateEntity(const char* entityName, AZStd::initializer_list<AZ::Component*> componentsToAdd = {})
  21. {
  22. // Circumvent the EntityContext system and generate a new entity with a transformcomponent
  23. AZ::Entity* newEntity = aznew AZ::Entity(entityName);
  24. newEntity->CreateComponent(AZ::TransformComponentTypeId);
  25. newEntity->Init();
  26. for (auto&& component : componentsToAdd)
  27. {
  28. newEntity->AddComponent(component);
  29. }
  30. newEntity->Activate();
  31. return newEntity;
  32. }
  33. TEST_F(PrefabBuilderTests, SourceDependencies)
  34. {
  35. static constexpr const char* ChildPrefabPath = "child.prefab";
  36. using namespace AzToolsFramework::Prefab;
  37. AZStd::vector<AZStd::unique_ptr<Instance>> childInstances;
  38. auto* prefabSystemComponentInterface = AZ::Interface<PrefabSystemComponentInterface>::Get();
  39. auto* prefabLoaderInterface = AZ::Interface<PrefabLoaderInterface>::Get();
  40. ASSERT_NE(prefabSystemComponentInterface, nullptr);
  41. ASSERT_NE(prefabLoaderInterface, nullptr);
  42. // Create a child entity and a prefab containing it
  43. childInstances.push_back(prefabSystemComponentInterface->CreatePrefab({CreateEntity("child")}, {}, ChildPrefabPath));
  44. // Create a parent entity and a prefab for it, pass in the child prefab for it to reference
  45. auto parentInstance = prefabSystemComponentInterface->CreatePrefab({CreateEntity("parent")}, AZStd::move(childInstances), "parent.prefab");
  46. AZStd::string serializedInstance;
  47. // Save to a string so we can load it as a PrefabDom and so that the nested instance becomes a Source file reference
  48. ASSERT_TRUE(prefabLoaderInterface->SaveTemplateToString(parentInstance->GetTemplateId(), serializedInstance));
  49. AZ::Outcome<PrefabDom, AZStd::string> readPrefabFileResult = AZ::JsonSerializationUtils::ReadJsonString(serializedInstance);
  50. ASSERT_TRUE(readPrefabFileResult.IsSuccess());
  51. // Now that we have a PrefabDom, test extracting the Source file reference as a Source Dependency
  52. auto sourceFileDependencies = AZ::Prefab::PrefabBuilderComponent::GetSourceDependencies(readPrefabFileResult.TakeValue());
  53. ASSERT_EQ(sourceFileDependencies.size(), 1);
  54. EXPECT_STREQ(sourceFileDependencies[0].m_sourceFileDependencyPath.c_str(), ChildPrefabPath);
  55. }
  56. TEST_F(PrefabBuilderTests, ProductDependencies)
  57. {
  58. constexpr const char* ChildPrefabPath = "child.prefab";
  59. using namespace AzToolsFramework::Prefab;
  60. AZ::Uuid TestAssetUuid("{7725567D-D420-46C2-B481-E0F79212CD34}");
  61. AZ::Data::AssetId TestAssetId(TestAssetUuid, 0);
  62. AZStd::vector<AZStd::unique_ptr<Instance>> childInstances;
  63. auto* prefabSystemComponentInterface = AZ::Interface<PrefabSystemComponentInterface>::Get();
  64. ASSERT_NE(prefabSystemComponentInterface, nullptr);
  65. auto* component = aznew TestComponent();
  66. AZ::Data::Asset<AZ::Data::AssetData> asset = AZ::Data::Asset<TestAsset>(TestAssetId, azrtti_typeid<TestAsset>());
  67. component->m_asset = asset;
  68. AZ::Entity* childEntity = CreateEntity("child", {component});
  69. // Create a child prefab with an entity that has an Asset<T> reference on it
  70. childInstances.push_back(prefabSystemComponentInterface->CreatePrefab(
  71. {childEntity}, {}, ChildPrefabPath, AZStd::unique_ptr<AZ::Entity>(CreateEntity("Container"))));
  72. // Create a parent prefab that has a nested instance reference to the child prefab
  73. auto parentInstance =
  74. prefabSystemComponentInterface->CreatePrefab({CreateEntity("parent")}, AZStd::move(childInstances), "parent.prefab", AZStd::unique_ptr<AZ::Entity>(CreateEntity("Container")));
  75. AZStd::string serializedInstance;
  76. TestPrefabBuilderComponent prefabBuilderComponent;
  77. prefabBuilderComponent.Activate();
  78. AZStd::vector<AssetBuilderSDK::JobProduct> jobProducts;
  79. // Make a copy of the template DOM, as the prefab system still owns the existing template
  80. AzToolsFramework::Prefab::PrefabDom prefabDom;
  81. prefabDom.CopyFrom(prefabSystemComponentInterface->FindTemplateDom(parentInstance->GetTemplateId()), prefabDom.GetAllocator(), false);
  82. ASSERT_TRUE(prefabBuilderComponent.ProcessPrefab(
  83. { AZ::Crc32("pc") }, "parent.prefab", "unused", AZ::Uuid(), AZStd::move(prefabDom), jobProducts));
  84. ASSERT_EQ(jobProducts.size(), 1);
  85. ASSERT_EQ(jobProducts[0].m_dependencies.size(), 1);
  86. ASSERT_EQ(jobProducts[0].m_dependencies[0].m_dependencyId, TestAssetId);
  87. prefabBuilderComponent.Deactivate();
  88. }
  89. AZStd::pair<size_t, size_t> GetFingerprint(AzToolsFramework::Prefab::PrefabDom& dom)
  90. {
  91. TestPrefabBuilderComponent prefabBuilderComponent;
  92. prefabBuilderComponent.Activate();
  93. size_t builderFingerprint = prefabBuilderComponent.CalculateBuilderFingerprint();
  94. size_t fingerprint = prefabBuilderComponent.CalculatePrefabFingerprint(dom);
  95. prefabBuilderComponent.Deactivate();
  96. return {fingerprint, builderFingerprint};
  97. }
  98. TEST_F(PrefabBuilderTests, FingerprintTest)
  99. {
  100. auto* prefabSystemComponentInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabSystemComponentInterface>::Get();
  101. ASSERT_NE(prefabSystemComponentInterface, nullptr);
  102. auto* component = aznew TestComponent();
  103. AZ::Entity* entity = CreateEntity("test", {component});
  104. auto parentInstance = prefabSystemComponentInterface->CreatePrefab(
  105. {entity}, {}, "test.prefab",
  106. AZStd::unique_ptr<AZ::Entity>(CreateEntity("Container")));
  107. AZStd::vector<AssetBuilderSDK::JobProduct> jobProducts;
  108. auto&& prefabDom = prefabSystemComponentInterface->FindTemplateDom(parentInstance->GetTemplateId());
  109. auto [v0Dom, v0Builder] = GetFingerprint(prefabDom);
  110. auto [sanityDom, sanityBuilder] = GetFingerprint(prefabDom);
  111. // Make sure the fingerprint is stable without changes
  112. ASSERT_EQ(v0Dom, sanityDom);
  113. ASSERT_EQ(v0Builder, sanityBuilder);
  114. AZ::SerializeContext* context = m_app.GetSerializeContext();
  115. // Unreflect VersionChangingData, change its version, and reflect it again
  116. context->EnableRemoveReflection();
  117. VersionChangingData::Reflect(context);
  118. VersionChangingData::m_version = 1;
  119. context->DisableRemoveReflection();
  120. VersionChangingData::Reflect(context);
  121. // Get the new fingerprint and check that it changed
  122. auto [v1Dom, v1Builder] = GetFingerprint(prefabDom);
  123. ASSERT_NE(v0Dom, v1Dom); // Verify the fingerprint for the object changed
  124. ASSERT_NE(v0Builder, v1Builder); // Verify the fingerprint for the entire builder changed
  125. }
  126. AZStd::unique_ptr<AZ::IO::GenericStream> TestPrefabBuilderComponent::GetOutputStream(const AZ::IO::Path&) const
  127. {
  128. return AZStd::make_unique<SelfContainedMemoryStream>();
  129. }
  130. void PrefabBuilderTests::SetUp()
  131. {
  132. AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get();
  133. auto projectPathKey =
  134. AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path";
  135. AZ::IO::FixedMaxPath enginePath;
  136. registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
  137. registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native());
  138. AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry);
  139. AZ::ComponentApplication::Descriptor desc;
  140. desc.m_recordingMode = AZ::Debug::AllocationRecords::Mode::RECORD_NO_RECORDS;
  141. m_app.Start(desc);
  142. m_app.CreateReflectionManager();
  143. m_testComponentDescriptor = AZStd::unique_ptr<AZ::ComponentDescriptor>{TestComponent::CreateDescriptor()};
  144. m_testComponentDescriptor->Reflect(m_app.GetSerializeContext());
  145. TestAsset::Reflect(m_app.GetSerializeContext());
  146. VersionChangingData::Reflect(m_app.GetSerializeContext());
  147. // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
  148. // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
  149. // in the unit tests.
  150. AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
  151. AZ::Data::AssetManager::Instance().RegisterHandler(&m_assetHandler, azrtti_typeid<TestAsset>());
  152. }
  153. void PrefabBuilderTests::TearDown()
  154. {
  155. AZ::Data::AssetManager::Instance().UnregisterHandler(&m_assetHandler);
  156. m_testComponentDescriptor = nullptr;
  157. m_app.Stop();
  158. }
  159. }
  160. AZ_TOOLS_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV);