PrefabUndoLinkTests.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  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 <AzToolsFramework/Prefab/Undo/PrefabUndo.h>
  9. #include <AzToolsFramework/Prefab/Undo/PrefabUndoUpdateLink.h>
  10. #include <Prefab/PrefabTestComponent.h>
  11. #include <Prefab/PrefabTestDomUtils.h>
  12. #include <Prefab/PrefabTestUndoFixture.h>
  13. namespace UnitTest
  14. {
  15. using PrefabUndoLinkTests = PrefabTestUndoFixture;
  16. TEST_F(PrefabUndoLinkTests, PrefabUndoLink_Add)
  17. {
  18. AZStd::unique_ptr<Instance> firstInstance = nullptr;
  19. AZStd::unique_ptr<Instance> secondInstance = nullptr;
  20. TemplateId firstTemplateId = InvalidTemplateId;
  21. TemplateId secondTemplateId = InvalidTemplateId;
  22. SetupInstances(firstInstance, secondInstance, firstTemplateId, secondTemplateId);
  23. firstInstance->AddInstance(AZStd::move(secondInstance));
  24. AZStd::vector<InstanceAlias> aliases = firstInstance->GetNestedInstanceAliases(secondTemplateId);
  25. //parent prefab2 to prefab 1 by creating a link
  26. //capture the link addition in undo node
  27. PrefabUndoInstanceLink undoLink("Undo Link Add Node");
  28. undoLink.Capture(firstTemplateId, secondTemplateId, aliases[0]);
  29. undoLink.Redo();
  30. m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
  31. AZStd::unique_ptr<Instance> newInstance = m_prefabSystemComponent->InstantiatePrefab(firstTemplateId);
  32. AZStd::vector<InstanceAlias> instances = newInstance->GetNestedInstanceAliases(secondTemplateId);
  33. ASSERT_TRUE(instances.size() > 0);
  34. instances.clear();
  35. //undo the parenting
  36. undoLink.Undo();
  37. m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
  38. newInstance = m_prefabSystemComponent->InstantiatePrefab(firstTemplateId);
  39. instances = newInstance->GetNestedInstanceAliases(secondTemplateId);
  40. ASSERT_TRUE(instances.size() == 0);
  41. //undo the parenting
  42. undoLink.Redo();
  43. m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
  44. newInstance = m_prefabSystemComponent->InstantiatePrefab(firstTemplateId);
  45. instances = newInstance->GetNestedInstanceAliases(secondTemplateId);
  46. ASSERT_TRUE(instances.size() == 1);
  47. }
  48. TEST_F(PrefabUndoLinkTests, PrefabUndoLink_InitialPatchData_ContainerTarget_PatchSucceeds)
  49. {
  50. //create two instances
  51. AZStd::unique_ptr<Instance> nestedInstance = nullptr;
  52. AZStd::unique_ptr<Instance> rootInstance = nullptr;
  53. TemplateId nestedTemplateId = InvalidTemplateId;
  54. TemplateId rootTemplateId = InvalidTemplateId;
  55. SetupInstances(nestedInstance, rootInstance, nestedTemplateId, rootTemplateId);
  56. nestedInstance->ActivateContainerEntity();
  57. AZ::EntityId nestedContainerEntityId = nestedInstance->GetContainerEntityId();
  58. AZ::EntityId rootContainerEntityId = rootInstance->GetContainerEntityId();
  59. AZ::Entity* nestedContainerEntity = nullptr;
  60. AZ::ComponentApplicationBus::BroadcastResult(nestedContainerEntity, &AZ::ComponentApplicationBus::Events::FindEntity, nestedContainerEntityId);
  61. ASSERT_TRUE(nestedContainerEntity);
  62. //generate a patch to add a component to the nested instance
  63. PrefabDom initialEntityDom;
  64. m_instanceToTemplateInterface->GenerateEntityDomBySerializing(initialEntityDom, *nestedContainerEntity);
  65. nestedContainerEntity->Deactivate();
  66. PrefabTestComponent* nestedTestComponent = nestedContainerEntity->CreateComponent<PrefabTestComponent>();
  67. ASSERT_TRUE(nestedTestComponent);
  68. nestedContainerEntity->Activate();
  69. PrefabDom modifiedEntityDom;
  70. m_instanceToTemplateInterface->GenerateEntityDomBySerializing(modifiedEntityDom, *nestedContainerEntity);
  71. PrefabDom patch;
  72. m_instanceToTemplateInterface->GeneratePatch(patch, initialEntityDom, modifiedEntityDom);
  73. m_instanceToTemplateInterface->PrependEntityAliasPathToPatchPaths(patch, nestedContainerEntityId);
  74. //apply the patch
  75. PrefabDom& templateDomReference = m_prefabSystemComponent->FindTemplateDom(nestedTemplateId);
  76. [[maybe_unused]] AZ::JsonSerializationResult::ResultCode result =
  77. PrefabDomUtils::ApplyPatches(templateDomReference, templateDomReference.GetAllocator(), patch);
  78. AZ_Error("Prefab", result.GetOutcome() == AZ::JsonSerializationResult::Outcomes::Success,
  79. "Patch was not successfully applied");
  80. //nest the second instance under the first instance
  81. rootInstance->AddInstance(AZStd::move(nestedInstance));
  82. AZStd::vector<InstanceAlias> aliases = rootInstance->GetNestedInstanceAliases(nestedTemplateId);
  83. //create patch for nesting
  84. m_instanceToTemplateInterface->GenerateEntityDomBySerializing(initialEntityDom, *nestedContainerEntity);
  85. nestedTestComponent->m_entityIdProperty = rootContainerEntityId;
  86. m_instanceToTemplateInterface->GenerateEntityDomBySerializing(modifiedEntityDom, *nestedContainerEntity);
  87. m_instanceToTemplateInterface->GeneratePatch(patch, initialEntityDom, modifiedEntityDom);
  88. m_instanceToTemplateInterface->PrependEntityAliasPathToPatchPaths(patch, nestedContainerEntityId);
  89. //create an undo node to apply the patch and prep for undo
  90. PrefabUndoInstanceLink undoInstanceLinkNode("Undo Link Patch");
  91. undoInstanceLinkNode.Capture(rootTemplateId, nestedTemplateId, aliases[0], AZStd::move(patch), InvalidLinkId);
  92. undoInstanceLinkNode.Redo();
  93. m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
  94. //verify the application worked
  95. InstanceOptionalReference nestedInstanceRef = rootInstance->FindNestedInstance(aliases[0]);
  96. AZ::EntityId nestedContainerId = nestedInstanceRef->get().GetContainerEntityId();
  97. AZ::Entity* nestedContainer = nullptr;
  98. AZ::ComponentApplicationBus::BroadcastResult(nestedContainer, &AZ::ComponentApplicationBus::Events::FindEntity, nestedContainerId);
  99. ASSERT_TRUE(nestedContainer);
  100. PrefabTestComponent* nestedComponent = nestedContainer->FindComponent<PrefabTestComponent>();
  101. ASSERT_EQ(nestedComponent->m_entityIdProperty, rootInstance->GetContainerEntityId());
  102. }
  103. TEST_F(PrefabUndoLinkTests, PrefabUndoLink_InitialPatchData_UpdateLinkEntity_PatchSucceeds)
  104. {
  105. //create two instance
  106. AZStd::unique_ptr<Instance> nestedInstance = nullptr;
  107. AZStd::unique_ptr<Instance> rootInstance = nullptr;
  108. TemplateId nestedTemplateId = InvalidTemplateId;
  109. TemplateId rootTemplateId = InvalidTemplateId;
  110. SetupInstances(nestedInstance, rootInstance, nestedTemplateId, rootTemplateId);
  111. nestedInstance->ActivateContainerEntity();
  112. AZ::EntityId nestedContainerEntityId = nestedInstance->GetContainerEntityId();
  113. AZ::Entity* nestedContainerEntity = nullptr;
  114. AZ::ComponentApplicationBus::BroadcastResult(nestedContainerEntity, &AZ::ComponentApplicationBus::Events::FindEntity, nestedContainerEntityId);
  115. ASSERT_TRUE(nestedContainerEntity);
  116. //add a component for testing to the instance that will be nested
  117. PrefabDom initialEntityDom;
  118. m_instanceToTemplateInterface->GenerateEntityDomBySerializing(initialEntityDom, *nestedContainerEntity);
  119. nestedContainerEntity->Deactivate();
  120. PrefabTestComponent* nestedTestComponent = nestedContainerEntity->CreateComponent<PrefabTestComponent>();
  121. ASSERT_TRUE(nestedTestComponent);
  122. nestedContainerEntity->Activate();
  123. PrefabDom modifiedEntityDom;
  124. m_instanceToTemplateInterface->GenerateEntityDomBySerializing(modifiedEntityDom, *nestedContainerEntity);
  125. //create patch
  126. PrefabDom patch;
  127. m_instanceToTemplateInterface->GeneratePatch(patch, initialEntityDom, modifiedEntityDom);
  128. m_instanceToTemplateInterface->PrependEntityAliasPathToPatchPaths(patch, nestedContainerEntityId);
  129. m_instanceToTemplateInterface->PatchTemplate(patch, nestedTemplateId);
  130. m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
  131. //instantiate a new nested instance
  132. nestedInstance = m_prefabSystemComponent->InstantiatePrefab(
  133. nestedTemplateId, AZStd::nullopt,
  134. [](const AzToolsFramework::EntityList& entities)
  135. {
  136. AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
  137. &AzToolsFramework::EditorEntityContextRequests::HandleEntitiesAdded, entities);
  138. });
  139. nestedContainerEntityId = nestedInstance->GetContainerEntityId();
  140. AZ::ComponentApplicationBus::BroadcastResult(nestedContainerEntity, &AZ::ComponentApplicationBus::Events::FindEntity, nestedContainerEntityId);
  141. ASSERT_TRUE(nestedContainerEntity);
  142. //nest the second instance under the first instance
  143. m_instanceToTemplateInterface->GenerateEntityDomBySerializing(initialEntityDom, *nestedContainerEntity);
  144. rootInstance->AddInstance(AZStd::move(nestedInstance));
  145. m_instanceToTemplateInterface->GenerateEntityDomBySerializing(modifiedEntityDom, *nestedContainerEntity);
  146. //create patch
  147. PrefabDom linkPatch;
  148. m_instanceToTemplateInterface->GeneratePatch(linkPatch, initialEntityDom, modifiedEntityDom);
  149. m_instanceToTemplateInterface->PrependEntityAliasPathToPatchPaths(linkPatch, nestedContainerEntityId);
  150. AZStd::vector<InstanceAlias> aliases = rootInstance->GetNestedInstanceAliases(nestedTemplateId);
  151. //create an undo node to apply the patch and prep for undo
  152. PrefabUndoInstanceLink undoInstanceLinkNode("Undo Link Patch");
  153. undoInstanceLinkNode.Capture(rootTemplateId, nestedTemplateId, aliases[0], AZStd::move(linkPatch), InvalidLinkId);
  154. undoInstanceLinkNode.Redo();
  155. m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
  156. LinkId linkId = undoInstanceLinkNode.GetLinkId();
  157. rootInstance = m_prefabSystemComponent->InstantiatePrefab(
  158. rootTemplateId, AZStd::nullopt,
  159. [](const AzToolsFramework::EntityList& entities)
  160. {
  161. AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
  162. &AzToolsFramework::EditorEntityContextRequests::HandleEntitiesAdded, entities);
  163. });
  164. aliases = rootInstance->GetNestedInstanceAliases(nestedTemplateId);
  165. //verify the link was created
  166. InstanceOptionalReference nestedInstanceRef = rootInstance->FindNestedInstance(aliases[0]);
  167. nestedContainerEntityId = nestedInstanceRef->get().GetContainerEntityId();
  168. AZ::ComponentApplicationBus::BroadcastResult(nestedContainerEntity, &AZ::ComponentApplicationBus::Events::FindEntity, nestedContainerEntityId);
  169. ASSERT_TRUE(nestedContainerEntity);
  170. //update the property on the nested component
  171. m_instanceToTemplateInterface->GenerateEntityDomBySerializing(initialEntityDom, *nestedContainerEntity);
  172. nestedTestComponent = nestedContainerEntity->FindComponent<PrefabTestComponent>();
  173. nestedTestComponent->m_boolProperty = true;
  174. m_instanceToTemplateInterface->GenerateEntityDomBySerializing(modifiedEntityDom, *nestedContainerEntity);
  175. //create patch
  176. PrefabDom updatePatch;
  177. m_instanceToTemplateInterface->GeneratePatch(updatePatch, initialEntityDom, modifiedEntityDom);
  178. m_instanceToTemplateInterface->PrependEntityAliasPathToPatchPaths(updatePatch, nestedContainerEntityId);
  179. //create the update link undo/redo node
  180. PrefabUndoUpdateLink undoLinkUpdateNode("Undo Link Update");
  181. undoLinkUpdateNode.Capture(updatePatch, linkId);
  182. undoLinkUpdateNode.Redo();
  183. m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
  184. //verify the update worked
  185. rootInstance = m_prefabSystemComponent->InstantiatePrefab(
  186. rootTemplateId, AZStd::nullopt,
  187. [](const AzToolsFramework::EntityList& entities)
  188. {
  189. AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
  190. &AzToolsFramework::EditorEntityContextRequests::HandleEntitiesAdded, entities);
  191. });
  192. aliases = rootInstance->GetNestedInstanceAliases(nestedTemplateId);
  193. nestedInstanceRef = rootInstance->FindNestedInstance(aliases[0]);
  194. nestedContainerEntityId = nestedInstanceRef->get().GetContainerEntityId();
  195. AZ::ComponentApplicationBus::BroadcastResult(nestedContainerEntity, &AZ::ComponentApplicationBus::Events::FindEntity, nestedContainerEntityId);
  196. ASSERT_TRUE(nestedContainerEntity);
  197. nestedTestComponent = nestedContainerEntity->FindComponent<PrefabTestComponent>();
  198. ASSERT_EQ(nestedTestComponent->m_boolProperty, true);
  199. //undo the update
  200. undoLinkUpdateNode.Undo();
  201. m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
  202. //verify the undo update worked
  203. rootInstance = m_prefabSystemComponent->InstantiatePrefab(
  204. rootTemplateId, AZStd::nullopt,
  205. [](const AzToolsFramework::EntityList& entities)
  206. {
  207. AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
  208. &AzToolsFramework::EditorEntityContextRequests::HandleEntitiesAdded, entities);
  209. });
  210. aliases = rootInstance->GetNestedInstanceAliases(nestedTemplateId);
  211. nestedInstanceRef = rootInstance->FindNestedInstance(aliases[0]);
  212. nestedContainerEntityId = nestedInstanceRef->get().GetContainerEntityId();
  213. AZ::ComponentApplicationBus::BroadcastResult(nestedContainerEntity, &AZ::ComponentApplicationBus::Events::FindEntity, nestedContainerEntityId);
  214. ASSERT_TRUE(nestedContainerEntity);
  215. nestedTestComponent = nestedContainerEntity->FindComponent<PrefabTestComponent>();
  216. ASSERT_EQ(nestedTestComponent->m_boolProperty, false);
  217. //redo the update so we can test if updating previously changed values matter
  218. undoLinkUpdateNode.Redo();
  219. m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
  220. rootInstance = m_prefabSystemComponent->InstantiatePrefab(
  221. rootTemplateId, AZStd::nullopt,
  222. [](const AzToolsFramework::EntityList& entities)
  223. {
  224. AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
  225. &AzToolsFramework::EditorEntityContextRequests::HandleEntitiesAdded, entities);
  226. });
  227. aliases = rootInstance->GetNestedInstanceAliases(nestedTemplateId);
  228. nestedInstanceRef = rootInstance->FindNestedInstance(aliases[0]);
  229. nestedContainerEntityId = nestedInstanceRef->get().GetContainerEntityId();
  230. AZ::ComponentApplicationBus::BroadcastResult(nestedContainerEntity, &AZ::ComponentApplicationBus::Events::FindEntity, nestedContainerEntityId);
  231. ASSERT_TRUE(nestedContainerEntity);
  232. //update the property on the nested component
  233. m_instanceToTemplateInterface->GenerateEntityDomBySerializing(initialEntityDom, *nestedContainerEntity);
  234. nestedTestComponent = nestedContainerEntity->FindComponent<PrefabTestComponent>();
  235. nestedTestComponent->m_intProperty = 1;
  236. m_instanceToTemplateInterface->GenerateEntityDomBySerializing(modifiedEntityDom, *nestedContainerEntity);
  237. //create patch
  238. PrefabDom updatePatchIntField;
  239. m_instanceToTemplateInterface->GeneratePatch(updatePatchIntField, initialEntityDom, modifiedEntityDom);
  240. m_instanceToTemplateInterface->PrependEntityAliasPathToPatchPaths(updatePatchIntField, nestedContainerEntityId);
  241. //create the update link undo/redo node
  242. PrefabUndoUpdateLink undoIntFieldNode("Undo Link Update");
  243. undoIntFieldNode.Capture(updatePatchIntField, linkId);
  244. undoIntFieldNode.Redo();
  245. m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
  246. //verify the update worked
  247. rootInstance = m_prefabSystemComponent->InstantiatePrefab(
  248. rootTemplateId, AZStd::nullopt,
  249. [](const AzToolsFramework::EntityList& entities)
  250. {
  251. AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
  252. &AzToolsFramework::EditorEntityContextRequests::HandleEntitiesAdded, entities);
  253. });
  254. aliases = rootInstance->GetNestedInstanceAliases(nestedTemplateId);
  255. nestedInstanceRef = rootInstance->FindNestedInstance(aliases[0]);
  256. nestedContainerEntityId = nestedInstanceRef->get().GetContainerEntityId();
  257. AZ::ComponentApplicationBus::BroadcastResult(nestedContainerEntity, &AZ::ComponentApplicationBus::Events::FindEntity, nestedContainerEntityId);
  258. ASSERT_TRUE(nestedContainerEntity);
  259. nestedTestComponent = nestedContainerEntity->FindComponent<PrefabTestComponent>();
  260. ASSERT_EQ(nestedTestComponent->m_intProperty, 1);
  261. }
  262. } // namespace UnitTest