UiDynamicScrollBoxComponentTest.cpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  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 "LyShineTest.h"
  9. #include "UiGameEntityContext.h"
  10. #include "UiDynamicScrollBoxComponent.h"
  11. #include "UiScrollBoxComponent.h"
  12. #include "UiElementComponent.h"
  13. #include "UiTransform2dComponent.h"
  14. #include "UiCanvasComponent.h"
  15. #include "Mocks/UiDynamicScrollBoxDataBusHandlerMock.h"
  16. #include <AzFramework/Application/Application.h>
  17. #include <AzFramework/Entity/GameEntityContextComponent.h>
  18. #include <AzFramework/Asset/AssetSystemComponent.h>
  19. #include <AzCore/Component/Entity.h>
  20. #include <AzCore/Asset/AssetManagerComponent.h>
  21. #include <AzCore/IO/Streamer/StreamerComponent.h>
  22. #include <AzCore/Jobs/JobManagerComponent.h>
  23. #include <AzCore/Slice/SliceSystemComponent.h>
  24. namespace UnitTest
  25. {
  26. // Simplified version of AzFramework::Application
  27. class UiDynamicScrollBoxTestApplication
  28. : public AzFramework::Application
  29. {
  30. void Reflect(AZ::ReflectContext* context) override
  31. {
  32. AzFramework::Application::Reflect(context);
  33. UiSerialize::ReflectUiTypes(context); //< needed to serialize ui Anchor and Offset
  34. }
  35. // override and only include system components required for tests.
  36. AZ::ComponentTypeList GetRequiredSystemComponents() const override
  37. {
  38. return AZ::ComponentTypeList{
  39. azrtti_typeid<AZ::AssetManagerComponent>(),
  40. azrtti_typeid<AZ::JobManagerComponent>(),
  41. azrtti_typeid<AZ::StreamerComponent>(),
  42. azrtti_typeid<AZ::SliceSystemComponent>(),
  43. azrtti_typeid<AzFramework::GameEntityContextComponent>(),
  44. azrtti_typeid<AzFramework::AssetSystem::AssetSystemComponent>(),
  45. };
  46. }
  47. void RegisterCoreComponents() override
  48. {
  49. AzFramework::Application::RegisterCoreComponents();
  50. RegisterComponentDescriptor(UiTransform2dComponent::CreateDescriptor());
  51. RegisterComponentDescriptor(UiElementComponent::CreateDescriptor());
  52. RegisterComponentDescriptor(UiScrollBoxComponent::CreateDescriptor());
  53. RegisterComponentDescriptor(UiDynamicScrollBoxComponent::CreateDescriptor());
  54. RegisterComponentDescriptor(UiDynamicScrollBoxDataBusHandlerMock::CreateDescriptor());
  55. RegisterComponentDescriptor(UiCanvasComponent::CreateDescriptor());
  56. }
  57. };
  58. class UiDynamicScrollBoxComponentTest
  59. : public UnitTest::LeakDetectionFixture
  60. {
  61. protected:
  62. void SetUp() override
  63. {
  64. // start application
  65. AZ::ComponentApplication::Descriptor appDescriptor;
  66. appDescriptor.m_useExistingAllocator = true;
  67. m_application = aznew UiDynamicScrollBoxTestApplication();
  68. AZ::ComponentApplication::StartupParameters startupParameters;
  69. startupParameters.m_loadSettingsRegistry = false;
  70. m_application->Start(appDescriptor, startupParameters);
  71. }
  72. void TearDown() override
  73. {
  74. m_application->Stop();
  75. delete m_application;
  76. m_application = nullptr;
  77. }
  78. static int FindDescendantCount(const AZ::Entity* entity)
  79. {
  80. LyShine::EntityArray children = entity->FindComponent<UiElementComponent>()->GetChildElements();
  81. int numDescendants = static_cast<int>(children.size());
  82. for(const AZ::Entity* child : children)
  83. {
  84. numDescendants += FindDescendantCount(child);
  85. }
  86. return numDescendants;
  87. }
  88. static int FindCanvasElementCount(UiCanvasComponent* uiCanvasComponent)
  89. {
  90. const LyShine::EntityArray childEntities = uiCanvasComponent->GetChildElements();
  91. int numCanvasElements = static_cast<int>(childEntities.size());
  92. for (const AZ::Entity* childEntity : childEntities)
  93. {
  94. numCanvasElements += FindDescendantCount(childEntity);
  95. }
  96. return numCanvasElements;
  97. }
  98. static AZStd::tuple<UiCanvasComponent*, UiScrollBoxComponent*, UiDynamicScrollBoxComponent*, AZ::Entity*> CreateUiCanvasWithScrollBox()
  99. {
  100. // create a canvas
  101. UiGameEntityContext* entityContext = new UiGameEntityContext(); //< UiCanvasComponent takes ownership of this pointer and will free this when exiting
  102. UiCanvasComponent* uiCanvasComponent = UiCanvasComponent::CreateCanvasInternal(entityContext, false);
  103. // add scroll box to the canvas
  104. AZ::Entity* uiScrollBoxEntity = uiCanvasComponent->CreateChildElement("Ui Scroll Box");
  105. uiScrollBoxEntity->Deactivate(); //< deactivate so that we can add components
  106. uiScrollBoxEntity->CreateComponent<UiTransform2dComponent>(); //< required by UiScrollBoxComponent
  107. auto uiScrollBoxComponent = uiScrollBoxEntity->CreateComponent<UiScrollBoxComponent>();
  108. auto uiDynamicScrollBoxComponent = uiScrollBoxEntity->CreateComponent<UiDynamicScrollBoxComponent>();
  109. uiScrollBoxEntity->Activate();
  110. // create the content entity (the parent container for the scroll box items)
  111. AZ::Entity* contentEntity = uiScrollBoxEntity->FindComponent<UiElementComponent>()->CreateChildElement("Content");
  112. contentEntity->Deactivate(); // deactivate to add component
  113. auto contentTransform = contentEntity->CreateComponent<UiTransform2dComponent>();
  114. contentEntity->Activate();
  115. // give the content a size otherwise scroll box items won't be spawned (fill the whole canvas)
  116. contentTransform->SetOffsets(UiTransform2dInterface::Offsets(0.0f, 0.0f, 0.0f, 0.0f));
  117. contentTransform->SetAnchors(UiTransform2dInterface::Anchors(0.0f, 0.0f, 1.0f, 1.0f), false, false);
  118. uiScrollBoxComponent->SetContentEntity(contentEntity->GetId());
  119. return AZStd::make_tuple(uiCanvasComponent, uiScrollBoxComponent, uiDynamicScrollBoxComponent, contentEntity);
  120. }
  121. private:
  122. UiDynamicScrollBoxTestApplication* m_application = nullptr;
  123. };
  124. TEST_F(UiDynamicScrollBoxComponentTest, UiDynamicScrollBoxComponent_WillClonePrototype_FT)
  125. {
  126. auto[uiCanvasComponent, uiScrollBoxComponent, uiDynamicScrollBoxComponent, contentEntity] = CreateUiCanvasWithScrollBox();
  127. AZ::Entity* uiScrollBoxEntity = uiScrollBoxComponent->GetEntity();
  128. // Main test here: Make a scroll box with 3 items and make sure the 3 items are actually spawned
  129. const int numScrollBoxItems = 3;
  130. uiScrollBoxEntity->Deactivate();
  131. const auto uiDynamicScrollBoxDataBusHandlerMock = new testing::NiceMock<UiDynamicScrollBoxDataBusHandlerMock>();
  132. uiScrollBoxEntity->AddComponent(uiDynamicScrollBoxDataBusHandlerMock);
  133. uiScrollBoxEntity->Activate();
  134. ON_CALL(*uiDynamicScrollBoxDataBusHandlerMock, GetNumElements()).WillByDefault(testing::Return(numScrollBoxItems));
  135. // create a prototype element and make it a child of the scroll box's content container
  136. AZ::Entity* prototype = contentEntity->FindComponent<UiElementComponent>()->CreateChildElement("Prototype");
  137. prototype->Deactivate(); //< deactivate before adding components
  138. auto prototypeTransform = prototype->CreateComponent<UiTransform2dComponent>();
  139. prototype->Activate();
  140. // Give the prototype some area (1x1px) because scroll boxes won't clone zero-sized (invisible) prototypes
  141. prototypeTransform->SetLocalWidth(1.0f);
  142. prototypeTransform->SetLocalHeight(1.0f);
  143. prototypeTransform->SetAnchors(UiTransform2dInterface::Anchors(0.5f, 0.5f, 0.5f, 0.5f), false, false);
  144. uiDynamicScrollBoxComponent->SetPrototypeElement(prototype->GetId());
  145. // We requested 3 scroll box items, so we expect 5 element in total:
  146. // (1) scroll box, (1) content entity, and (3) prototype clones. (the original prototype is deactivated and won't be counted)
  147. uiDynamicScrollBoxComponent->RefreshContent();
  148. EXPECT_EQ(FindCanvasElementCount(uiCanvasComponent), 5);
  149. // clean up the canvas
  150. delete uiCanvasComponent->GetEntity();
  151. }
  152. TEST_F(UiDynamicScrollBoxComponentTest, UiDynamicScrollBoxComponent_WillNotCloneInvalidPrototype_FT)
  153. {
  154. auto[uiCanvasComponent, uiScrollBoxComponent, uiDynamicScrollBoxComponent, contentEntity] = CreateUiCanvasWithScrollBox();
  155. AZ::Entity* uiScrollBoxEntity = uiScrollBoxComponent->GetEntity();
  156. // Main test here: Set the prototype to the scroll box itself causing a circular dependency.
  157. // We tell the scroll box to clone 3 bad elements, none of which should actually be cloned.
  158. const int numScrollBoxItems = 3;
  159. uiScrollBoxEntity->Deactivate(); //< deactivate before adding components
  160. const auto uiDynamicScrollBoxDataBusHandlerMock = new testing::NiceMock<UiDynamicScrollBoxDataBusHandlerMock>();
  161. uiScrollBoxEntity->AddComponent(uiDynamicScrollBoxDataBusHandlerMock);
  162. uiScrollBoxEntity->Activate();
  163. ON_CALL(*uiDynamicScrollBoxDataBusHandlerMock, GetNumElements()).WillByDefault(testing::Return(numScrollBoxItems));
  164. uiDynamicScrollBoxComponent->SetPrototypeElement(uiScrollBoxEntity->GetId());
  165. // We requested 3 scroll box items, but they are invalid so we expect that only 2 entity exist on the canvas
  166. // (1) scroll box and (1) content entity
  167. uiDynamicScrollBoxComponent->RefreshContent();
  168. EXPECT_EQ(FindCanvasElementCount(uiCanvasComponent), 2);
  169. // clean up the canvas
  170. delete uiCanvasComponent->GetEntity();
  171. }
  172. } //namespace UnitTest