From 72110e40cfa4989afd20cb5f6e7598b73bb1fd18 Mon Sep 17 00:00:00 2001 From: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> Date: Fri, 24 Sep 2021 15:05:34 -0700 Subject: [PATCH] Add Unit tests for EditorFocusMode and PrefabFocus. Minor change to PrefabFocusHandler to avoid instantiating a new AZ::EntityId() for checks. Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com> --- .../Prefab/PrefabFocusHandler.cpp | 4 +- .../Tests/FocusMode/EditorFocusModeTests.cpp | 128 ++++++++++++ .../Prefab/PrefabFocus/PrefabFocusTests.cpp | 185 ++++++++++++++++++ .../Tests/aztoolsframeworktests_files.cmake | 2 + 4 files changed, 317 insertions(+), 2 deletions(-) create mode 100644 Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeTests.cpp create mode 100644 Code/Framework/AzToolsFramework/Tests/Prefab/PrefabFocus/PrefabFocusTests.cpp diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp index 4fd202a73d..1ad3e5c90a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabFocusHandler.cpp @@ -35,7 +35,7 @@ namespace AzToolsFramework::Prefab { InstanceOptionalReference focusedInstance; - if (entityId == AZ::EntityId()) + if (entityId == static_cast(AZ::EntityId::InvalidEntityId)) { PrefabEditorEntityOwnershipInterface* prefabEditorEntityOwnershipInterface = AZ::Interface::Get(); @@ -89,7 +89,7 @@ namespace AzToolsFramework::Prefab return false; } - if (entityId == AZ::EntityId()) + if (entityId == static_cast(AZ::EntityId::InvalidEntityId)) { return false; } diff --git a/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeTests.cpp b/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeTests.cpp new file mode 100644 index 0000000000..5972da58a7 --- /dev/null +++ b/Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeTests.cpp @@ -0,0 +1,128 @@ +/* + * 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 +#include +#include +#include +#include + +namespace AzToolsFramework +{ + class EditorFocusModeTests + : public ::testing::Test + { + protected: + void SetUp() override + { + m_app.Start(m_descriptor); + + // 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); + + GenerateTestHierarchy(); + } + + void GenerateTestHierarchy() + { + /* + * City + * |_ Street + * |_ Car + * | |_ Passenger + * |_ SportsCar + * |_ Passenger + */ + + m_entityMap["cityId"] = CreateEditorEntity("City", AZ::EntityId()); + m_entityMap["streetId"] = CreateEditorEntity("Street", m_entityMap["cityId"]); + m_entityMap["carId"] = CreateEditorEntity("Car", m_entityMap["streetId"]); + m_entityMap["passengerId1"] = CreateEditorEntity("Passenger", m_entityMap["carId"]); + m_entityMap["sportsCarId"] = CreateEditorEntity("SportsCar", m_entityMap["streetId"]); + m_entityMap["passengerId2"] = CreateEditorEntity("Passenger", m_entityMap["sportsCarId"]); + } + + AZ::EntityId CreateEditorEntity(const char* name, AZ::EntityId parentId) + { + AZ::Entity* entity = nullptr; + UnitTest::CreateDefaultEditorEntity(name, &entity); + + // Parent + AZ::TransformBus::Event(entity->GetId(), &AZ::TransformInterface::SetParent, parentId); + + return entity->GetId(); + } + + void TearDown() override + { + m_app.Stop(); + } + + UnitTest::ToolsTestApplication m_app{ "EditorFocusModeTests" }; + AZ::ComponentApplication::Descriptor m_descriptor; + AZStd::unordered_map m_entityMap; + }; + + TEST_F(EditorFocusModeTests, EditorFocusModeTests_SetFocus) + { + FocusModeInterface* focusModeInterface = AZ::Interface::Get(); + EXPECT_TRUE(focusModeInterface != nullptr); + + focusModeInterface->SetFocusRoot(m_entityMap["carId"]); + EXPECT_EQ(focusModeInterface->GetFocusRoot(), m_entityMap["carId"]); + + focusModeInterface->ClearFocusRoot(); + EXPECT_EQ(focusModeInterface->GetFocusRoot(), AZ::EntityId()); + } + + TEST_F(EditorFocusModeTests, EditorFocusModeTests_IsInFocusSubTree) + { + FocusModeInterface* focusModeInterface = AZ::Interface::Get(); + EXPECT_TRUE(focusModeInterface != nullptr); + + focusModeInterface->ClearFocusRoot(); + + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["cityId"]), true); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["streetId"]), true); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["carId"]), true); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId1"]), true); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["sportsCarId"]), true); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId2"]), true); + + focusModeInterface->SetFocusRoot(m_entityMap["streetId"]); + + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["cityId"]), false); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["streetId"]), true); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["carId"]), true); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId1"]), true); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["sportsCarId"]), true); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId2"]), true); + + focusModeInterface->SetFocusRoot(m_entityMap["carId"]); + + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["cityId"]), false); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["streetId"]), false); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["carId"]), true); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId1"]), true); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["sportsCarId"]), false); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId2"]), false); + + focusModeInterface->SetFocusRoot(m_entityMap["passengerId2"]); + + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["cityId"]), false); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["streetId"]), false); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["carId"]), false); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId1"]), false); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["sportsCarId"]), false); + EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId2"]), true); + + focusModeInterface->ClearFocusRoot(); + } +} diff --git a/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabFocus/PrefabFocusTests.cpp b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabFocus/PrefabFocusTests.cpp new file mode 100644 index 0000000000..a9f38425d5 --- /dev/null +++ b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabFocus/PrefabFocusTests.cpp @@ -0,0 +1,185 @@ +/* + * 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 +#include +#include +#include +#include + +namespace UnitTest +{ + class PrefabFocusTests + : public PrefabTestFixture + { + protected: + void GenerateTestHierarchy() + { + /* + * City (Prefab Container) + * |_ City + * |_ Street (Prefab Container) + * |_ Car (Prefab Container) + * | |_ Passenger + * |_ SportsCar (Prefab Container) + * |_ Passenger + */ + + m_entityMap["passenger1"] = CreateEntity("Passenger1"); + m_entityMap["passenger2"] = CreateEntity("Passenger2"); + m_entityMap["city"] = CreateEntity("City"); + + AzToolsFramework::EditorEntityContextRequestBus::Broadcast( + &AzToolsFramework::EditorEntityContextRequests::HandleEntitiesAdded, + AzToolsFramework::EntityList{ m_entityMap["passenger1"], m_entityMap["passenger2"], m_entityMap["city"] }); + + AZStd::unique_ptr carInstance = + m_prefabSystemComponent->CreatePrefab({ m_entityMap["passenger1"] }, {}, "test/car"); + ASSERT_TRUE(carInstance); + m_instanceMap["car"] = carInstance.get(); + + AZStd::unique_ptr sportsCarInstance = + m_prefabSystemComponent->CreatePrefab({ m_entityMap["passenger2"] }, {}, "test/sportsCar"); + ASSERT_TRUE(sportsCarInstance); + m_instanceMap["sportsCar"] = sportsCarInstance.get(); + + AZStd::unique_ptr streetInstance = + m_prefabSystemComponent->CreatePrefab({}, MakeInstanceList( AZStd::move(carInstance), AZStd::move(sportsCarInstance) ), "test/street"); + ASSERT_TRUE(streetInstance); + m_instanceMap["street"] = streetInstance.get(); + + m_rootInstance = + m_prefabSystemComponent->CreatePrefab({ m_entityMap["city"] }, MakeInstanceList(AZStd::move(streetInstance)), "test/city"); + ASSERT_TRUE(m_rootInstance); + m_instanceMap["city"] = m_rootInstance.get(); + } + + AZ::EntityId CreateEditorEntity(const char* name, AZ::EntityId parentId) + { + AZ::Entity* newEntity = CreateEntity(name); + AzToolsFramework::EditorEntityContextRequestBus::Broadcast( + &AzToolsFramework::EditorEntityContextRequests::HandleEntitiesAdded, AzToolsFramework::EntityList{ newEntity }); + + // Parent + AZ::TransformBus::Event(newEntity->GetId(), &AZ::TransformInterface::SetParent, parentId); + + return newEntity->GetId(); + } + + AZStd::unordered_map m_entityMap; + AZStd::unordered_map m_instanceMap; + + AZStd::unique_ptr m_rootInstance; + }; + + TEST_F(PrefabFocusTests, PrefabFocus_FocusOnOwningPrefab) + { + GenerateTestHierarchy(); + + PrefabFocusInterface* prefabFocusInterface = AZ::Interface::Get(); + EXPECT_TRUE(prefabFocusInterface != nullptr); + + // Verify FocusOnOwningPrefab works when passing the container entity of the root prefab. + { + prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap["city"]->GetContainerEntityId()); + EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(), m_instanceMap["city"]->GetTemplateId()); + + auto instance = prefabFocusInterface->GetFocusedPrefabInstance(); + EXPECT_TRUE(instance.has_value()); + EXPECT_EQ(&instance->get(), m_instanceMap["city"]); + } + + // Verify FocusOnOwningPrefab works when passing a nested entity of the root prefab. + { + prefabFocusInterface->FocusOnOwningPrefab(m_entityMap["city"]->GetId()); + EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(), m_instanceMap["city"]->GetTemplateId()); + + auto instance = prefabFocusInterface->GetFocusedPrefabInstance(); + EXPECT_TRUE(instance.has_value()); + EXPECT_EQ(&instance->get(), m_instanceMap["city"]); + } + + // Verify FocusOnOwningPrefab works when passing the container entity of a nested prefab. + { + prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap["car"]->GetContainerEntityId()); + EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(), m_instanceMap["car"]->GetTemplateId()); + + auto instance = prefabFocusInterface->GetFocusedPrefabInstance(); + EXPECT_TRUE(instance.has_value()); + EXPECT_EQ(&instance->get(), m_instanceMap["car"]); + } + + // Verify FocusOnOwningPrefab works when passing a nested entity of the a nested prefab. + { + prefabFocusInterface->FocusOnOwningPrefab(m_entityMap["passenger1"]->GetId()); + EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(), m_instanceMap["car"]->GetTemplateId()); + + auto instance = prefabFocusInterface->GetFocusedPrefabInstance(); + EXPECT_TRUE(instance.has_value()); + EXPECT_EQ(&instance->get(), m_instanceMap["car"]); + } + + // Verify FocusOnOwningPrefab points to the root prefab when the focus is cleared. + { + AzToolsFramework::PrefabEditorEntityOwnershipInterface* prefabEditorEntityOwnershipInterface = + AZ::Interface::Get(); + AzToolsFramework::Prefab::InstanceOptionalReference rootPrefabInstance = + prefabEditorEntityOwnershipInterface->GetRootPrefabInstance(); + EXPECT_TRUE(rootPrefabInstance.has_value()); + + prefabFocusInterface->FocusOnOwningPrefab(AZ::EntityId()); + EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(), rootPrefabInstance->get().GetTemplateId()); + + auto instance = prefabFocusInterface->GetFocusedPrefabInstance(); + EXPECT_TRUE(instance.has_value()); + EXPECT_EQ(&instance->get(), &rootPrefabInstance->get()); + } + + m_rootInstance.release(); + } + + TEST_F(PrefabFocusTests, PrefabFocus_IsOwningPrefabBeingFocused) + { + GenerateTestHierarchy(); + + PrefabFocusInterface* prefabFocusInterface = AZ::Interface::Get(); + EXPECT_TRUE(prefabFocusInterface != nullptr); + + // Verify IsOwningPrefabBeingFocused returns true for all entities in a focused prefab (container/nested) + { + prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap["city"]->GetContainerEntityId()); + + EXPECT_TRUE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap["city"]->GetContainerEntityId())); + EXPECT_TRUE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap["city"]->GetId())); + } + + // Verify IsOwningPrefabBeingFocused returns false for all entities not in a focused prefab (ancestors/descendants) + { + prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap["street"]->GetContainerEntityId()); + + EXPECT_TRUE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap["street"]->GetContainerEntityId())); + EXPECT_FALSE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap["city"]->GetContainerEntityId())); + EXPECT_FALSE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap["city"]->GetId())); + EXPECT_FALSE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap["car"]->GetContainerEntityId())); + EXPECT_FALSE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap["passenger1"]->GetId())); + } + + // Verify IsOwningPrefabBeingFocused returns false for all entities not in a focused prefab (siblings) + { + prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap["sportsCar"]->GetContainerEntityId()); + + EXPECT_TRUE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap["sportsCar"]->GetContainerEntityId())); + EXPECT_TRUE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap["passenger2"]->GetId())); + EXPECT_FALSE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap["car"]->GetContainerEntityId())); + EXPECT_FALSE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap["passenger1"]->GetId())); + } + + m_rootInstance.release(); + } + +} diff --git a/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake b/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake index 764afce266..ecc7d17ccc 100644 --- a/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake +++ b/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake @@ -34,6 +34,7 @@ set(FILES EntityTestbed.h FileFunc.cpp FingerprintingTests.cpp + FocusMode/EditorFocusModeTests.cpp GenericComponentWrapperTest.cpp InstanceDataHierarchy.cpp IntegerPrimtitiveTestConfig.h @@ -50,6 +51,7 @@ set(FILES Prefab/Benchmark/PrefabLoadBenchmarks.cpp Prefab/Benchmark/PrefabUpdateInstancesBenchmarks.cpp Prefab/Benchmark/SpawnableCreateBenchmarks.cpp + Prefab/PrefabFocus/PrefabFocusTests.cpp Prefab/MockPrefabFileIOActionValidator.cpp Prefab/MockPrefabFileIOActionValidator.h Prefab/PrefabDuplicateTests.cpp