LYN-7279 + LYN-7192 | Focus Mode - Container unit tests + Clear container entity open state on new level load (#4558)

* Change SetContainerOpenState to SetContainerOpen.

Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>

* Introduce Clear function to avoid retaining all lingering open states when switching contexts/loading a new level.

Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>

* Minor FocusMode fixture refactors to support ContainerEntity tests

Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>

* Introduce tests for the ContainerEntity API

Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>

* Add include to fix issue with EntityContextId not being defined.

Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>

* Minor comment fixes. Moved environment clear functions to TearDown function of test fixture.

Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>

* Use default editor context id in ContainerEntitySystemComponent

Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>

* Revert previous change as the EditorEntityContextId would not be initialized correctly on ContainerEntitySystemComponent Activate.

Signed-off-by: Danilo Aimini <82231674+AMZN-daimini@users.noreply.github.com>
This commit is contained in:
Danilo Aimini
2021-10-12 13:34:51 -07:00
committed by GitHub
parent c38c9739da
commit 6318247b3d
12 changed files with 575 additions and 130 deletions
@@ -0,0 +1,99 @@
/*
* 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 <Tests/FocusMode/EditorFocusModeSelectionFixture.h>
namespace AzToolsFramework
{
TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionTests_FindHighestSelectableEntityWithNoContainers)
{
// When no containers are in the way, the function will just return the entityId of the entity that was clicked.
// Click on Car Entity
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
// Verify the correct entity is selected
auto selectedEntitiesAfter = GetSelectedEntities();
EXPECT_EQ(selectedEntitiesAfter.size(), 1);
EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CarEntityName]);
}
TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionTests_FindHighestSelectableEntityWithClosedContainer)
{
// If a closed container is an ancestor of the queried entity, the closed container is selected.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]); // Containers are closed by default
// Click on Car Entity
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
// Verify the correct entity is selected
auto selectedEntitiesAfter = GetSelectedEntities();
EXPECT_EQ(selectedEntitiesAfter.size(), 1);
EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[StreetEntityName]);
// Restore default state for other tests.
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
}
TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionTests_FindHighestSelectableEntityWithOpenContainer)
{
// If a closed container is an ancestor of the queried entity, the closed container is selected.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]);
m_containerEntityInterface->SetContainerOpen(m_entityMap[StreetEntityName], true);
// Click on Car Entity
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
// Verify the correct entity is selected
auto selectedEntitiesAfter = GetSelectedEntities();
EXPECT_EQ(selectedEntitiesAfter.size(), 1);
EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CarEntityName]);
// Restore default state for other tests.
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
}
TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionTests_FindHighestSelectableEntityWithMultipleClosedContainers)
{
// If multiple closed containers are ancestors of the queried entity, the highest closed container is selected.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]);
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CityEntityName]);
// Click on Car Entity
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
// Verify the correct entity is selected
auto selectedEntitiesAfter = GetSelectedEntities();
EXPECT_EQ(selectedEntitiesAfter.size(), 1);
EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CityEntityName]);
// Restore default state for other tests.
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]);
}
TEST_F(EditorFocusModeSelectionFixture, ContainerEntitySelectionTests_FindHighestSelectableEntityWithMultipleContainers)
{
// If multiple containers are ancestors of the queried entity, the highest closed container is selected.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]);
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CityEntityName]);
m_containerEntityInterface->SetContainerOpen(m_entityMap[CityEntityName], true);
// Click on Car Entity
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
// Verify the correct entity is selected
auto selectedEntitiesAfter = GetSelectedEntities();
EXPECT_EQ(selectedEntitiesAfter.size(), 1);
EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[StreetEntityName]);
// Restore default state for other tests.
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]);
}
}
@@ -0,0 +1,293 @@
/*
* 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 <Tests/FocusMode/EditorFocusModeFixture.h>
namespace AzToolsFramework
{
TEST_F(EditorFocusModeFixture, ContainerEntityTests_Register)
{
// Registering an entity is successful.
auto outcome = m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CarEntityName]);
EXPECT_TRUE(outcome.IsSuccess());
// Restore default state for other tests.
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CarEntityName]);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_RegisterTwice)
{
// Registering an entity twice fails.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CarEntityName]);
auto outcome = m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CarEntityName]);
EXPECT_FALSE(outcome.IsSuccess());
// Restore default state for other tests.
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CarEntityName]);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_Unregister)
{
// Unregistering a container entity is successful.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CarEntityName]);
auto outcome = m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CarEntityName]);
EXPECT_TRUE(outcome.IsSuccess());
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_UnregisterRegularEntity)
{
// Unregistering an entity that was not previously registered fails.
auto outcome = m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CarEntityName]);
EXPECT_FALSE(outcome.IsSuccess());
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_UnregisterTwice)
{
// Unregistering a container entity twice fails.
auto outcome = m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CarEntityName]);
EXPECT_FALSE(outcome.IsSuccess());
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOnRegularEntity)
{
// If a regular entity is passed, IsContainer returns false.
// Note that we use a different entity than the tests above to validate a completely new EntityId.
bool isContainer = m_containerEntityInterface->IsContainer(m_entityMap[SportsCarEntityName]);
EXPECT_FALSE(isContainer);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOnRegisteredContainer)
{
// If a container entity is passed, IsContainer returns true.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
bool isContainer = m_containerEntityInterface->IsContainer(m_entityMap[SportsCarEntityName]);
EXPECT_TRUE(isContainer);
// Restore default state for other tests.
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOnUnRegisteredContainer)
{
// If an entity that was previously a container but was then unregistered is passed, IsContainer returns false.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
bool isContainer = m_containerEntityInterface->IsContainer(m_entityMap[SportsCarEntityName]);
EXPECT_FALSE(isContainer);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_SetContainerOpenOnRegularEntity)
{
// Setting a regular entity to open should return a failure.
auto outcome = m_containerEntityInterface->SetContainerOpen(m_entityMap[StreetEntityName], true);
EXPECT_FALSE(outcome.IsSuccess());
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_SetContainerOpen)
{
// Set a container entity to open, and verify the operation was successful.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]);
auto outcome = m_containerEntityInterface->SetContainerOpen(m_entityMap[StreetEntityName], true);
EXPECT_TRUE(outcome.IsSuccess());
// Restore default state for other tests.
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_SetContainerOpenTwice)
{
// Set a container entity to open twice, and verify that does not cause a failure (as intended).
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]);
m_containerEntityInterface->SetContainerOpen(m_entityMap[StreetEntityName], true);
auto outcome = m_containerEntityInterface->SetContainerOpen(m_entityMap[StreetEntityName], true);
EXPECT_TRUE(outcome.IsSuccess());
// Restore default state for other tests.
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_SetContainerClosed)
{
// Set a container entity to closed, and verify the operation was successful.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]);
auto outcome = m_containerEntityInterface->SetContainerOpen(m_entityMap[StreetEntityName], true);
EXPECT_TRUE(outcome.IsSuccess());
// Restore default state for other tests.
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOpenOnRegularEntity)
{
// Query open state on a regular entity, and verify it returns true.
// Open containers behave exactly as regular entities, so this is the expected return value.
bool isOpen = m_containerEntityInterface->IsContainerOpen(m_entityMap[CityEntityName]);
EXPECT_TRUE(isOpen);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOpenOnDefaultContainerEntity)
{
// Query open state on a newly registered container entity, and verify it returns false.
// Containers are registered closed by default.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CityEntityName]);
bool isOpen = m_containerEntityInterface->IsContainerOpen(m_entityMap[CityEntityName]);
EXPECT_FALSE(isOpen);
// Restore default state for other tests.
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOpenOnOpenContainerEntity)
{
// Query open state on a container entity that was opened, and verify it returns true.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CityEntityName]);
m_containerEntityInterface->SetContainerOpen(m_entityMap[CityEntityName], true);
bool isOpen = m_containerEntityInterface->IsContainerOpen(m_entityMap[CityEntityName]);
EXPECT_TRUE(isOpen);
// Restore default state for other tests.
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_IsContainerOpenOnClosedContainerEntity)
{
// Query open state on a container entity that was opened and then closed, and verify it returns false.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CityEntityName]);
m_containerEntityInterface->SetContainerOpen(m_entityMap[CityEntityName], true);
m_containerEntityInterface->SetContainerOpen(m_entityMap[CityEntityName], false);
bool isOpen = m_containerEntityInterface->IsContainerOpen(m_entityMap[CityEntityName]);
EXPECT_FALSE(isOpen);
// Restore default state for other tests.
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_ContainerOpenStateIsPreserved)
{
// Register an entity as container, open it, then unregister it.
// When the entity is registered again, the open state should be preserved.
// This behavior is necessary for the system to work alongside Prefab propagation refreshes.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CityEntityName]);
m_containerEntityInterface->SetContainerOpen(m_entityMap[CityEntityName], true);
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]);
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[CityEntityName]);
bool isOpen = m_containerEntityInterface->IsContainerOpen(m_entityMap[CityEntityName]);
EXPECT_TRUE(isOpen);
// Restore default state for other tests.
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[CityEntityName]);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_ClearSucceeds)
{
// The Clear function works if no container is registered.
auto outcome = m_containerEntityInterface->Clear(m_editorEntityContextId);
EXPECT_TRUE(outcome.IsSuccess());
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_ClearFailsIfContainersAreStillRegistered)
{
// The Clear function fails if a container is registered.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[Passenger1EntityName]);
auto outcome = m_containerEntityInterface->Clear(m_editorEntityContextId);
EXPECT_FALSE(outcome.IsSuccess());
// Restore default state for other tests.
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[Passenger1EntityName]);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_ClearSucceedsIfContainersAreUnregistered)
{
// The Clear function fails if a container is registered.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[Passenger1EntityName]);
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[Passenger1EntityName]);
auto outcome = m_containerEntityInterface->Clear(m_editorEntityContextId);
EXPECT_TRUE(outcome.IsSuccess());
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_ClearDeletesPreservedOpenStates)
{
// Register an entity as container, open it, unregister it, then call clear.
// When the entity is registered again, the open state should not be preserved.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[Passenger1EntityName]);
m_containerEntityInterface->SetContainerOpen(m_entityMap[Passenger1EntityName], true);
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[Passenger1EntityName]);
m_containerEntityInterface->Clear(m_editorEntityContextId);
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[Passenger1EntityName]);
bool isOpen = m_containerEntityInterface->IsContainerOpen(m_entityMap[Passenger1EntityName]);
EXPECT_FALSE(isOpen);
// Restore default state for other tests.
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[Passenger1EntityName]);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_FindHighestSelectableEntityWithNoContainers)
{
// When no containers are in the way, the function will just return the entityId that was passed to it.
AZ::EntityId selectedEntityId = m_containerEntityInterface->FindHighestSelectableEntity(m_entityMap[Passenger2EntityName]);
EXPECT_EQ(selectedEntityId, m_entityMap[Passenger2EntityName]);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_FindHighestSelectableEntityWithClosedContainer)
{
// If a closed container is an ancestor of the queried entity, the closed container is selected.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[SportsCarEntityName]); // Containers are closed by default
AZ::EntityId selectedEntityId = m_containerEntityInterface->FindHighestSelectableEntity(m_entityMap[Passenger2EntityName]);
EXPECT_EQ(selectedEntityId, m_entityMap[SportsCarEntityName]);
// Restore default state for other tests.
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_FindHighestSelectableEntityWithOpenContainer)
{
// If an open container is an ancestor of the queried entity, it is ignored.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
m_containerEntityInterface->SetContainerOpen(m_entityMap[SportsCarEntityName], true);
AZ::EntityId selectedEntityId = m_containerEntityInterface->FindHighestSelectableEntity(m_entityMap[Passenger2EntityName]);
EXPECT_EQ(selectedEntityId, m_entityMap[Passenger2EntityName]);
// Restore default state for other tests.
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_FindHighestSelectableEntityWithMultipleClosedContainers)
{
// If multiple closed containers are ancestors of the queried entity, the highest closed container is selected.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]);
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
AZ::EntityId selectedEntityId = m_containerEntityInterface->FindHighestSelectableEntity(m_entityMap[Passenger2EntityName]);
EXPECT_EQ(selectedEntityId, m_entityMap[StreetEntityName]);
// Restore default state for other tests.
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
}
TEST_F(EditorFocusModeFixture, ContainerEntityTests_FindHighestSelectableEntityWithMultipleContainers)
{
// If multiple containers are ancestors of the queried entity, the highest closed container is selected.
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[StreetEntityName]);
m_containerEntityInterface->RegisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
m_containerEntityInterface->SetContainerOpen(m_entityMap[StreetEntityName], true);
AZ::EntityId selectedEntityId = m_containerEntityInterface->FindHighestSelectableEntity(m_entityMap[Passenger2EntityName]);
EXPECT_EQ(selectedEntityId, m_entityMap[SportsCarEntityName]);
// Restore default state for other tests.
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
}
}
@@ -14,6 +14,20 @@
namespace AzToolsFramework
{
void ClearSelectedEntities()
{
AzToolsFramework::ToolsApplicationRequestBus::Broadcast(
&AzToolsFramework::ToolsApplicationRequestBus::Events::SetSelectedEntities, AzToolsFramework::EntityIdList());
}
AzToolsFramework::EntityIdList EditorFocusModeFixture::GetSelectedEntities()
{
AzToolsFramework::EntityIdList selectedEntities;
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
selectedEntities, &AzToolsFramework::ToolsApplicationRequestBus::Events::GetSelectedEntities);
return selectedEntities;
}
void EditorFocusModeFixture::SetUpEditorFixtureImpl()
{
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
@@ -21,6 +35,9 @@ namespace AzToolsFramework
// in the unit tests.
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
m_containerEntityInterface = AZ::Interface<ContainerEntityInterface>::Get();
ASSERT_TRUE(m_containerEntityInterface != nullptr);
m_focusModeInterface = AZ::Interface<FocusModeInterface>::Get();
ASSERT_TRUE(m_focusModeInterface != nullptr);
@@ -31,6 +48,24 @@ namespace AzToolsFramework
m_editorEntityContextId, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetEditorEntityContextId);
GenerateTestHierarchy();
// Clear the focus, disabling focus mode
m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId);
// Clear selection
ClearSelectedEntities();
}
void EditorFocusModeFixture::TearDownEditorFixtureImpl()
{
// Clear Container Entity preserved open states
m_containerEntityInterface->Clear(m_editorEntityContextId);
// Clear the focus, disabling focus mode
m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId);
// Clear selection
ClearSelectedEntities();
}
void EditorFocusModeFixture::GenerateTestHierarchy()
@@ -59,7 +94,7 @@ namespace AzToolsFramework
entity->Activate();
// Move the CarEntity so it's out of the way.
AZ::TransformBus::Event(m_entityMap[CarEntityName], &AZ::TransformBus::Events::SetWorldTranslation, CarEntityPosition);
AZ::TransformBus::Event(m_entityMap[CarEntityName], &AZ::TransformBus::Events::SetWorldTranslation, WorldCarEntityPosition);
// Setup the camera so the Car entity is in view.
AzFramework::SetCameraTransform(
@@ -14,6 +14,7 @@
#include <AzTest/AzTest.h>
#include <AzToolsFramework/ContainerEntity/ContainerEntityInterface.h>
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
@@ -24,16 +25,20 @@ namespace AzToolsFramework
{
protected:
void SetUpEditorFixtureImpl() override;
void TearDownEditorFixtureImpl() override;
void GenerateTestHierarchy();
AZ::EntityId CreateEditorEntity(const char* name, AZ::EntityId parentId);
AZStd::unordered_map<AZStd::string, AZ::EntityId> m_entityMap;
ContainerEntityInterface* m_containerEntityInterface = nullptr;
FocusModeInterface* m_focusModeInterface = nullptr;
public:
AzFramework::EntityContextId m_editorEntityContextId = AzFramework::EntityContextId::CreateNull();
AzToolsFramework::EntityIdList GetSelectedEntities();
AzFramework::EntityContextId m_editorEntityContextId = AzFramework::EntityContextId::CreateNull();
AzFramework::CameraState m_cameraState;
inline static const AZ::Vector3 CameraPosition = AZ::Vector3(10.0f, 15.0f, 10.0f);
@@ -45,6 +50,7 @@ namespace AzToolsFramework
inline static const char* Passenger1EntityName = "Passenger1";
inline static const char* Passenger2EntityName = "Passenger2";
inline static AZ::Vector3 CarEntityPosition = AZ::Vector3(5.0f, 15.0f, 0.0f);
inline static AZ::Vector3 WorldCarEntityPosition = AZ::Vector3(5.0f, 15.0f, 0.0f);
};
}
@@ -0,0 +1,43 @@
/*
* 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
*
*/
#pragma once
#include <Tests/FocusMode/EditorFocusModeFixture.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/std/string/string.h>
#include <AzFramework/Viewport/ViewportScreen.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFramework.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkTestHelpers.h>
#include <AzManipulatorTestFramework/DirectManipulatorViewportInteraction.h>
#include <AzManipulatorTestFramework/ImmediateModeActionDispatcher.h>
#include <AzManipulatorTestFramework/IndirectManipulatorViewportInteraction.h>
#include <AzToolsFramework/Component/EditorComponentAPIBus.h>
#include <AzToolsFramework/Manipulators/LinearManipulator.h>
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
#include <AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h>
namespace AzToolsFramework
{
class EditorFocusModeSelectionFixture : public UnitTest::IndirectCallManipulatorViewportInteractionFixtureMixin<EditorFocusModeFixture>
{
public:
void ClickAtWorldPositionOnViewport(const AZ::Vector3& worldPosition)
{
// Calculate the world position in screen space
const auto carScreenPosition = AzFramework::WorldToScreen(worldPosition, m_cameraState);
// Click the entity in the viewport
m_actionDispatcher->CameraState(m_cameraState)->MousePosition(carScreenPosition)->MouseLButtonDown()->MouseLButtonUp();
}
};
} // namespace AzToolsFramework
@@ -6,64 +6,14 @@
*
*/
#include <Tests/FocusMode/EditorFocusModeFixture.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/std/string/string.h>
#include <AzFramework/Viewport/ViewportScreen.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFramework.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkTestHelpers.h>
#include <AzManipulatorTestFramework/DirectManipulatorViewportInteraction.h>
#include <AzManipulatorTestFramework/ImmediateModeActionDispatcher.h>
#include <AzManipulatorTestFramework/IndirectManipulatorViewportInteraction.h>
#include <AzToolsFramework/Component/EditorComponentAPIBus.h>
#include <AzToolsFramework/Manipulators/LinearManipulator.h>
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
#include <AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h>
#include <Tests/FocusMode/EditorFocusModeSelectionFixture.h>
namespace AzToolsFramework
{
class EditorFocusModeSelectionFixture
: public UnitTest::IndirectCallManipulatorViewportInteractionFixtureMixin<EditorFocusModeFixture>
{
public:
void ClickAtWorldPositionOnViewport(const AZ::Vector3& worldPosition)
{
// Calculate the world position in screen space
const auto carScreenPosition = AzFramework::WorldToScreen(worldPosition, m_cameraState);
// Click the entity in the viewport
m_actionDispatcher->CameraState(m_cameraState)->MousePosition(carScreenPosition)->MouseLButtonDown()->MouseLButtonUp();
}
};
void ClearSelectedEntities()
{
AzToolsFramework::ToolsApplicationRequestBus::Broadcast(
&AzToolsFramework::ToolsApplicationRequestBus::Events::SetSelectedEntities, AzToolsFramework::EntityIdList());
}
AzToolsFramework::EntityIdList GetSelectedEntities()
{
AzToolsFramework::EntityIdList selectedEntities;
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
selectedEntities, &AzToolsFramework::ToolsApplicationRequestBus::Events::GetSelectedEntities);
return selectedEntities;
}
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnLevel)
{
// Clear the focus, disabling focus mode
m_focusModeInterface->ClearFocusRoot(AzFramework::EntityContextId::CreateNull());
// Clear selection
ClearSelectedEntities();
// Click on Car Entity
ClickAtWorldPositionOnViewport(CarEntityPosition);
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
// Verify entity is selected
auto selectedEntitiesAfter = GetSelectedEntities();
@@ -75,73 +25,53 @@ namespace AzToolsFramework
{
// Set the focus on the Street Entity (parent of the test entity)
m_focusModeInterface->SetFocusRoot(m_entityMap[StreetEntityName]);
// Clear selection
ClearSelectedEntities();
// Click on Car Entity
ClickAtWorldPositionOnViewport(CarEntityPosition);
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
// Verify entity is selected
auto selectedEntitiesAfter = GetSelectedEntities();
EXPECT_EQ(selectedEntitiesAfter.size(), 1);
EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CarEntityName]);
// Clear the focus, disabling focus mode
m_focusModeInterface->ClearFocusRoot(AzFramework::EntityContextId::CreateNull());
}
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnItself)
{
// Set the focus on the Car Entity (test entity)
m_focusModeInterface->SetFocusRoot(m_entityMap[CarEntityName]);
// Clear selection
ClearSelectedEntities();
// Click on Car Entity
ClickAtWorldPositionOnViewport(CarEntityPosition);
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
// Verify entity is selected
auto selectedEntitiesAfter = GetSelectedEntities();
EXPECT_EQ(selectedEntitiesAfter.size(), 1);
EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CarEntityName]);
// Clear the focus, disabling focus mode
m_focusModeInterface->ClearFocusRoot(AzFramework::EntityContextId::CreateNull());
}
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnSibling)
{
// Set the focus on the SportsCar Entity (sibling of the test entity)
m_focusModeInterface->SetFocusRoot(m_entityMap[SportsCarEntityName]);
// Clear selection
ClearSelectedEntities();
// Click on Car Entity
ClickAtWorldPositionOnViewport(CarEntityPosition);
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
// Verify entity is selected
auto selectedEntitiesAfter = GetSelectedEntities();
EXPECT_EQ(selectedEntitiesAfter.size(), 0);
// Clear the focus, disabling focus mode
m_focusModeInterface->ClearFocusRoot(AzFramework::EntityContextId::CreateNull());
}
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnDescendant)
{
// Set the focus on the Passenger1 Entity (child of the entity)
m_focusModeInterface->SetFocusRoot(m_entityMap[Passenger1EntityName]);
// Clear selection
ClearSelectedEntities();
// Click on Car Entity
ClickAtWorldPositionOnViewport(CarEntityPosition);
ClickAtWorldPositionOnViewport(WorldCarEntityPosition);
// Verify entity is selected
auto selectedEntitiesAfter = GetSelectedEntities();
EXPECT_EQ(selectedEntitiesAfter.size(), 0);
// Clear the focus, disabling focus mode
m_focusModeInterface->ClearFocusRoot(AzFramework::EntityContextId::CreateNull());
}
}
@@ -33,55 +33,40 @@ namespace AzToolsFramework
TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_AncestorsDescendants)
{
// When the focus is set to an entity, all its descendants are in the focus subtree while the ancestors aren't.
{
m_focusModeInterface->SetFocusRoot(m_entityMap[StreetEntityName]);
m_focusModeInterface->SetFocusRoot(m_entityMap[StreetEntityName]);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), false);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true);
}
// Restore default expected focus.
m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), false);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true);
}
TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_Siblings)
{
// If the root entity has siblings, they are also outside of the focus subtree.
{
m_focusModeInterface->SetFocusRoot(m_entityMap[CarEntityName]);
m_focusModeInterface->SetFocusRoot(m_entityMap[CarEntityName]);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), false);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), false);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), false);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), false);
}
// Restore default expected focus.
m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), false);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), false);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), false);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), false);
}
TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_Leaf)
{
// If the root is a leaf, then the focus subtree will consists of just that entity.
{
m_focusModeInterface->SetFocusRoot(m_entityMap[Passenger2EntityName]);
m_focusModeInterface->SetFocusRoot(m_entityMap[Passenger2EntityName]);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), false);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), false);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), false);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), false);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), false);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true);
}
// Restore default expected focus.
m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), false);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), false);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), false);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), false);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), false);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true);
}
TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_Clear)
@@ -90,15 +75,13 @@ namespace AzToolsFramework
m_focusModeInterface->SetFocusRoot(m_entityMap[StreetEntityName]);
// When the focus is cleared, the whole level is in the focus subtree; so we expect all entities to return true.
{
m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId);
m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true);
}
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), true);
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true);
}
}
@@ -37,8 +37,11 @@ set(FILES
EntityTestbed.h
FileFunc.cpp
FingerprintingTests.cpp
FocusMode/ContainerEntitySelectionTests.cpp
FocusMode/ContainerEntityTests.cpp
FocusMode/EditorFocusModeFixture.cpp
FocusMode/EditorFocusModeFixture.h
FocusMode/EditorFocusModeSelectionFixture.h
FocusMode/EditorFocusModeSelectionTests.cpp
FocusMode/EditorFocusModeTests.cpp
GenericComponentWrapperTest.cpp