5fc4551ac0
* Enable relocation of the Project Game Release Layout Relocating the Project Game Release Layout to another directory on the file system failed due to the querying of the engine root failing due to the ComponentApplication::m_engineRoot not using the project path stored in the SettingsRegisry if the engine root cannot be detected Removed the ApplicationRequestBus GetEngineRoot function. The ComponentApplicationRequestBus has a function of the same name that returns the same path. Removed the deprecated GetAppRoot function. The path it returns has no defined value. It was not the engine root or the project root. Removed unused CFileUtil and CFileUtil_impl functions that were invoking the ApplicationREquestBus GetEngineRoot function. On the way to update the functions it was discovered that they aren't called Added a CalculateBranchToken overload that can populate a fixed_string to avoid heap allocations Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Protect against an empty list of artifacts to remove when generating the engine.pak Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>
1449 lines
70 KiB
C++
1449 lines
70 KiB
C++
/*
|
|
* 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 <AzCore/Component/ComponentApplicationBus.h>
|
|
#include <AzCore/Outcome/Outcome.h>
|
|
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
|
#include <AzCore/UnitTest/TestTypes.h>
|
|
#include <AzCore/UserSettings/UserSettingsComponent.h>
|
|
|
|
#include <AzToolsFramework/API/EntityCompositionNotificationBus.h>
|
|
#include <AzToolsFramework/API/EntityCompositionRequestBus.h>
|
|
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
|
#include <AzToolsFramework/Application/ToolsApplication.h>
|
|
#include <AzToolsFramework/Entity/EditorEntityActionComponent.h>
|
|
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
|
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
|
|
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
|
|
#include <AzToolsFramework/ToolsComponents/EditorDisabledCompositionBus.h>
|
|
#include <AzToolsFramework/ToolsComponents/EditorPendingCompositionBus.h>
|
|
#include <AzToolsFramework/ToolsComponents/EditorPendingCompositionComponent.h>
|
|
#include <AzToolsFramework/ToolsComponents/GenericComponentWrapper.h>
|
|
|
|
|
|
namespace UnitTest
|
|
{
|
|
using namespace AZ;
|
|
using namespace AzToolsFramework;
|
|
using namespace AZ::Data;
|
|
using namespace AZ::IO;
|
|
//
|
|
// Declaring several clothing-themed components for use in tests.
|
|
//
|
|
|
|
// Shoes require socks
|
|
class LeatherBootsComponent
|
|
: public AZ::Component
|
|
{
|
|
public:
|
|
AZ_COMPONENT(LeatherBootsComponent, "{C2852908-0FC6-4BF6-9907-E390840F9897}");
|
|
void Activate() override {}
|
|
void Deactivate() override {}
|
|
static void Reflect(AZ::ReflectContext* reflection)
|
|
{
|
|
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection))
|
|
{
|
|
serializeContext->Class<LeatherBootsComponent, AZ::Component>();
|
|
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
|
|
{
|
|
editContext->Class<LeatherBootsComponent>("Leather Boots", "")
|
|
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
|
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game"));
|
|
}
|
|
}
|
|
}
|
|
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
|
{
|
|
provided.push_back(AZ_CRC("ShoesService", 0xaa20aadf));
|
|
}
|
|
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
|
{
|
|
incompatible.push_back(AZ_CRC("ShoesService", 0xaa20aadf));
|
|
}
|
|
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
|
|
{
|
|
required.push_back(AZ_CRC("SocksService", 0x51e58440));
|
|
}
|
|
};
|
|
|
|
// Note that WoolSocksComponent is an "editor component".
|
|
// This is just to make sure we are testing with both "editor"
|
|
// and "non-editor" components.
|
|
class WoolSocksComponent
|
|
: public AzToolsFramework::Components::EditorComponentBase
|
|
{
|
|
public:
|
|
AZ_COMPONENT(WoolSocksComponent, "{6436A9A1-701E-4275-AF6F-82F53C7916C8}", EditorComponentBase);
|
|
void Activate() override {}
|
|
void Deactivate() override {}
|
|
static void Reflect(AZ::ReflectContext* reflection)
|
|
{
|
|
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection))
|
|
{
|
|
serializeContext->Class<WoolSocksComponent, EditorComponentBase>();
|
|
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
|
|
{
|
|
editContext->Class<WoolSocksComponent>("Wool Socks", "")
|
|
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
|
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game"));
|
|
}
|
|
}
|
|
}
|
|
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
|
{
|
|
provided.push_back(AZ_CRC("SocksService", 0x51e58440));
|
|
}
|
|
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
|
{
|
|
incompatible.push_back(AZ_CRC("SocksService", 0x51e58440));
|
|
}
|
|
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& /*required*/)
|
|
{
|
|
}
|
|
};
|
|
|
|
// Incompatible with socks
|
|
class HatesSocksComponent
|
|
: public AZ::Component
|
|
{
|
|
public:
|
|
AZ_COMPONENT(HatesSocksComponent, "{D359D446-A172-4854-8EA9-B95073FF5709}");
|
|
void Activate() override {}
|
|
void Deactivate() override {}
|
|
static void Reflect(AZ::ReflectContext* reflection)
|
|
{
|
|
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection))
|
|
{
|
|
serializeContext->Class<HatesSocksComponent, AZ::Component>();
|
|
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
|
|
{
|
|
editContext->Class<HatesSocksComponent>("Hates Socks", "")
|
|
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
|
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game"));
|
|
}
|
|
}
|
|
}
|
|
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& /*provided*/)
|
|
{
|
|
}
|
|
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
|
{
|
|
incompatible.push_back(AZ_CRC("SocksService", 0x51e58440));
|
|
}
|
|
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& /*required*/)
|
|
{
|
|
}
|
|
};
|
|
|
|
// Pants require underwear
|
|
class BlueJeansComponent
|
|
: public AZ::Component
|
|
{
|
|
public:
|
|
AZ_COMPONENT(BlueJeansComponent, "{AEA4D69E-F02B-4F6D-A793-8DEE0C0E54E3}");
|
|
void Activate() override {}
|
|
void Deactivate() override {}
|
|
static void Reflect(AZ::ReflectContext* reflection)
|
|
{
|
|
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection))
|
|
{
|
|
serializeContext->Class<BlueJeansComponent, AZ::Component>();
|
|
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
|
|
{
|
|
editContext->Class<BlueJeansComponent>("Blue Jeans", "")
|
|
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
|
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game"));
|
|
}
|
|
}
|
|
}
|
|
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
|
{
|
|
provided.push_back(AZ_CRC("TrousersService", 0x15edf105));
|
|
}
|
|
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
|
{
|
|
incompatible.push_back(AZ_CRC("TrousersService", 0x15edf105));
|
|
}
|
|
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
|
|
{
|
|
required.push_back(AZ_CRC("UnderwearService", 0x915ec03a));
|
|
}
|
|
};
|
|
|
|
// 1 of 2 underwear styles
|
|
class WhiteBriefsComponent
|
|
: public AZ::Component
|
|
{
|
|
public:
|
|
AZ_COMPONENT(WhiteBriefsComponent, "{8B095E11-082B-4EB1-A119-D1534323C956}");
|
|
void Activate() override {}
|
|
void Deactivate() override {}
|
|
static void Reflect(AZ::ReflectContext* reflection)
|
|
{
|
|
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection))
|
|
{
|
|
serializeContext->Class<WhiteBriefsComponent, AZ::Component>();
|
|
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
|
|
{
|
|
editContext->Class<WhiteBriefsComponent>("White Briefs", "")
|
|
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
|
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game"));
|
|
}
|
|
}
|
|
}
|
|
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
|
{
|
|
provided.push_back(AZ_CRC("UnderwearService", 0x915ec03a));
|
|
}
|
|
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
|
{
|
|
incompatible.push_back(AZ_CRC("UnderwearService", 0x915ec03a));
|
|
}
|
|
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& /*required*/)
|
|
{
|
|
}
|
|
};
|
|
|
|
// 2 of 2 underwear styles
|
|
class HeartBoxersComponent
|
|
: public AZ::Component
|
|
{
|
|
public:
|
|
AZ_COMPONENT(HeartBoxersComponent, "{06071955-CC65-4C32-A4D8-1125D827C10B}");
|
|
void Activate() override {}
|
|
void Deactivate() override {}
|
|
static void Reflect(AZ::ReflectContext* reflection)
|
|
{
|
|
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection))
|
|
{
|
|
serializeContext->Class<HeartBoxersComponent, AZ::Component>();
|
|
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
|
|
{
|
|
editContext->Class<HeartBoxersComponent>("Heart Boxers", "")
|
|
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
|
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game"));
|
|
}
|
|
}
|
|
}
|
|
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
|
{
|
|
provided.push_back(AZ_CRC("UnderwearService", 0x915ec03a));
|
|
}
|
|
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
|
{
|
|
incompatible.push_back(AZ_CRC("UnderwearService", 0x915ec03a));
|
|
}
|
|
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& /*required*/)
|
|
{
|
|
}
|
|
};
|
|
|
|
// Requires a belt (but no belt exists)
|
|
class KnifeSheathComponent
|
|
: public AZ::Component
|
|
{
|
|
public:
|
|
AZ_COMPONENT(KnifeSheathComponent, "{D99C3EF1-592F-4744-9D07-A5F2CE679870}");
|
|
void Activate() override {}
|
|
void Deactivate() override {}
|
|
static void Reflect(AZ::ReflectContext* reflection)
|
|
{
|
|
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection))
|
|
{
|
|
serializeContext->Class<KnifeSheathComponent, AZ::Component>();
|
|
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
|
|
{
|
|
editContext->Class<KnifeSheathComponent>("Knife Sheath", "")
|
|
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
|
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game"));
|
|
}
|
|
}
|
|
}
|
|
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& /*provided*/)
|
|
{
|
|
}
|
|
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& /*incompatible*/)
|
|
{
|
|
}
|
|
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
|
|
{
|
|
required.push_back(AZ_CRC("BeltService", 0xba4df957));
|
|
}
|
|
};
|
|
|
|
// Count components of given type
|
|
template <typename ComponentType>
|
|
size_t CountComponentsOnEntity(AZ::Entity* entity)
|
|
{
|
|
EXPECT_NE(nullptr, entity);
|
|
if (!entity)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
size_t count = 0;
|
|
for (const auto component : entity->GetComponents())
|
|
{
|
|
if (AzToolsFramework::GetUnderlyingComponentType(*component) == azrtti_typeid<ComponentType>())
|
|
{
|
|
++count;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
template <typename ComponentType>
|
|
size_t CountPendingComponentsOnEntity(AZ::Entity* entity)
|
|
{
|
|
EXPECT_NE(nullptr, entity);
|
|
if (!entity)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
AZ::Entity::ComponentArrayType pendingComponents;
|
|
AzToolsFramework::EditorPendingCompositionRequestBus::Event(entity->GetId(), &AzToolsFramework::EditorPendingCompositionRequestBus::Events::GetPendingComponents, pendingComponents);
|
|
|
|
size_t count = 0;
|
|
for (const auto pendingComponent : pendingComponents)
|
|
{
|
|
if (AzToolsFramework::GetUnderlyingComponentType(*pendingComponent) == azrtti_typeid<ComponentType>())
|
|
{
|
|
++count;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
template <typename ComponentType>
|
|
size_t CountDisabledComponentsOnEntity(AZ::Entity* entity)
|
|
{
|
|
EXPECT_NE(nullptr, entity);
|
|
if (!entity)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
AZ::Entity::ComponentArrayType disabledComponents;
|
|
AzToolsFramework::EditorDisabledCompositionRequestBus::Event(entity->GetId(), &AzToolsFramework::EditorDisabledCompositionRequestBus::Events::GetDisabledComponents, disabledComponents);
|
|
|
|
size_t count = 0;
|
|
for (const auto disabledComponent : disabledComponents)
|
|
{
|
|
if (AzToolsFramework::GetUnderlyingComponentType(*disabledComponent) == azrtti_typeid<ComponentType>())
|
|
{
|
|
++count;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
template <typename ComponentType>
|
|
AZStd::vector<AZ::Component*> GetComponentsForEntity(AZ::Entity* entity)
|
|
{
|
|
AZStd::vector<AZ::Component*> components;
|
|
AzToolsFramework::GetAllComponentsForEntity(entity, components);
|
|
|
|
auto itr = AZStd::remove_if(components.begin(), components.end(), [](AZ::Component* component) {return AzToolsFramework::GetUnderlyingComponentType(*component) != azrtti_typeid<ComponentType>();});
|
|
components.erase(itr, components.end());
|
|
|
|
return components;
|
|
}
|
|
|
|
bool CheckAllAreTrue(std::initializer_list<bool> booleans)
|
|
{
|
|
for (auto boolean : booleans)
|
|
{
|
|
if (!boolean)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template <typename... BooleanTypes>
|
|
bool CheckAllAreTrue(BooleanTypes... booleans)
|
|
{
|
|
return CheckAllAreTrue({booleans...});
|
|
}
|
|
|
|
bool DoesComponentListHaveComponent(const AZ::Entity::ComponentArrayType& componentList, const AZ::Uuid& componentType)
|
|
{
|
|
for (const auto component : componentList)
|
|
{
|
|
if (AzToolsFramework::GetUnderlyingComponentType(*component) == componentType)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <typename... AdditionalValidatedComponentTypes>
|
|
struct VerifyAdditionalValidatedComponents
|
|
{
|
|
static bool OnOutcomeForEntity(const AzToolsFramework::EntityCompositionRequestBus::Events::AddComponentsOutcome& outcome, AZ::Entity* entity)
|
|
{
|
|
auto iterEntityOutcome = outcome.GetValue().find(entity->GetId());
|
|
EXPECT_NE(iterEntityOutcome, outcome.GetValue().end());
|
|
return CheckAllAreTrue(DoesComponentListHaveComponent(iterEntityOutcome->second.m_additionalValidatedComponents, azrtti_typeid<AdditionalValidatedComponentTypes>())...);
|
|
}
|
|
};
|
|
|
|
template <typename... AddedPendingComponentTypes>
|
|
struct VerifyAddedPendingComponents
|
|
{
|
|
static bool OnOutcomeForEntity(const AzToolsFramework::EntityCompositionRequestBus::Events::AddComponentsOutcome& outcome, AZ::Entity* entity)
|
|
{
|
|
auto iterEntityOutcome = outcome.GetValue().find(entity->GetId());
|
|
EXPECT_NE(iterEntityOutcome, outcome.GetValue().end());
|
|
return CheckAllAreTrue(DoesComponentListHaveComponent(iterEntityOutcome->second.m_addedPendingComponents, azrtti_typeid<AddedPendingComponentTypes>())...);
|
|
}
|
|
};
|
|
|
|
template <typename... AddedValidComponentTypes>
|
|
struct VerifyAddedValidComponents
|
|
{
|
|
static bool OnOutcomeForEntity(const AzToolsFramework::EntityCompositionRequestBus::Events::AddComponentsOutcome& outcome, AZ::Entity* entity)
|
|
{
|
|
auto iterEntityOutcome = outcome.GetValue().find(entity->GetId());
|
|
EXPECT_NE(iterEntityOutcome, outcome.GetValue().end());
|
|
return CheckAllAreTrue(DoesComponentListHaveComponent(iterEntityOutcome->second.m_addedValidComponents, azrtti_typeid<AddedValidComponentTypes>())...);
|
|
}
|
|
|
|
template <typename... AdditionalValidatedComponentTypes>
|
|
struct AndAdditionalValidatedComponents
|
|
{
|
|
static bool OnOutcomeForEntity(const AzToolsFramework::EntityCompositionRequestBus::Events::AddComponentsOutcome& outcome, AZ::Entity* entity)
|
|
{
|
|
return CheckAllAreTrue(
|
|
VerifyAddedValidComponents<AddedValidComponentTypes...>::OnOutcomeForEntity(outcome, entity),
|
|
VerifyAdditionalValidatedComponents<AdditionalValidatedComponentTypes...>::OnOutcomeForEntity(outcome, entity)
|
|
);
|
|
}
|
|
};
|
|
|
|
template <typename... AddedPendingComponentTypes>
|
|
struct AndAddedPendingComponents
|
|
{
|
|
static bool OnOutcomeForEntity(const AzToolsFramework::EntityCompositionRequestBus::Events::AddComponentsOutcome& outcome, AZ::Entity* entity)
|
|
{
|
|
return CheckAllAreTrue(
|
|
VerifyAddedValidComponents<AddedValidComponentTypes...>::OnOutcomeForEntity(outcome, entity),
|
|
VerifyAddedPendingComponents<AddedPendingComponentTypes...>::OnOutcomeForEntity(outcome, entity)
|
|
);
|
|
}
|
|
|
|
template <typename... AdditionalValidatedComponentTypes>
|
|
struct AndAdditionalValidatedComponents
|
|
{
|
|
static bool OnOutcomeForEntity(const AzToolsFramework::EntityCompositionRequestBus::Events::AddComponentsOutcome& outcome, AZ::Entity* entity)
|
|
{
|
|
return CheckAllAreTrue(
|
|
VerifyAddedValidComponents<AddedValidComponentTypes...>::OnOutcomeForEntity(outcome, entity),
|
|
VerifyAddedPendingComponents<AddedPendingComponentTypes...>::OnOutcomeForEntity(outcome, entity),
|
|
VerifyAdditionalValidatedComponents<AdditionalValidatedComponentTypes...>::OnOutcomeForEntity(outcome, entity)
|
|
);
|
|
}
|
|
};
|
|
};
|
|
};
|
|
|
|
template <typename... InvalidatedComponentTypes>
|
|
struct VerifyRemovalInvalidatedComponents
|
|
{
|
|
static bool OnOutcomeForEntity(const AzToolsFramework::EntityCompositionRequestBus::Events::RemoveComponentsOutcome& outcome, AZ::Entity* entity)
|
|
{
|
|
auto iterEntityOutcome = outcome.GetValue().find(entity->GetId());
|
|
EXPECT_NE(iterEntityOutcome, outcome.GetValue().end());
|
|
return CheckAllAreTrue(DoesComponentListHaveComponent(iterEntityOutcome->second.m_invalidatedComponents, azrtti_typeid<InvalidatedComponentTypes>())...);
|
|
}
|
|
};
|
|
|
|
template <typename... ValidatedComponentTypes>
|
|
struct VerifyRemovalValidatedComponents
|
|
{
|
|
static bool OnOutcomeForEntity(const AzToolsFramework::EntityCompositionRequestBus::Events::RemoveComponentsOutcome& outcome, AZ::Entity* entity)
|
|
{
|
|
auto iterEntityOutcome = outcome.GetValue().find(entity->GetId());
|
|
EXPECT_NE(iterEntityOutcome, outcome.GetValue().end());
|
|
return CheckAllAreTrue(DoesComponentListHaveComponent(iterEntityOutcome->second.m_validatedComponents, azrtti_typeid<ValidatedComponentTypes>())...);
|
|
}
|
|
|
|
template <typename... InvalidatedComponentTypes>
|
|
struct AndInvalidatedComponents
|
|
{
|
|
static bool OnOutcomeForEntity(const AzToolsFramework::EntityCompositionRequestBus::Events::RemoveComponentsOutcome& outcome, AZ::Entity* entity)
|
|
{
|
|
return CheckAllAreTrue(
|
|
VerifyRemovalValidatedComponents<ValidatedComponentTypes...>::OnOutcomeForEntity(outcome, entity),
|
|
VerifyRemovalInvalidatedComponents<InvalidatedComponentTypes...>::OnOutcomeForEntity(outcome, entity)
|
|
);
|
|
}
|
|
};
|
|
};
|
|
|
|
class EntityComponentCounter
|
|
{
|
|
public:
|
|
void SetEntity(const AZ::Entity* entity)
|
|
{
|
|
m_entity = entity;
|
|
}
|
|
|
|
size_t GetCount() const
|
|
{
|
|
if (!m_entity)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return GetComponentCount() - m_lastResetCount;
|
|
}
|
|
|
|
size_t GetPendingCount() const
|
|
{
|
|
if (!m_entity)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return GetPendingComponentCount() - m_lastPendingResetCount;
|
|
}
|
|
|
|
size_t GetDisabledCount() const
|
|
{
|
|
if (!m_entity)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return GetDisabledComponentCount() - m_lastDisabledResetCount;
|
|
}
|
|
|
|
void Reset()
|
|
{
|
|
m_lastResetCount = GetComponentCount();
|
|
m_lastPendingResetCount = GetPendingComponentCount();
|
|
m_lastDisabledResetCount = GetDisabledComponentCount();
|
|
}
|
|
|
|
private:
|
|
|
|
size_t GetComponentCount() const
|
|
{
|
|
return m_entity->GetComponents().size();
|
|
}
|
|
|
|
size_t GetPendingComponentCount() const
|
|
{
|
|
AZ::Entity::ComponentArrayType pendingComponents;
|
|
AzToolsFramework::EditorPendingCompositionRequestBus::Event(m_entity->GetId(), &AzToolsFramework::EditorPendingCompositionRequestBus::Events::GetPendingComponents, pendingComponents);
|
|
return pendingComponents.size();
|
|
}
|
|
|
|
size_t GetDisabledComponentCount() const
|
|
{
|
|
AZ::Entity::ComponentArrayType disabledComponents;
|
|
AzToolsFramework::EditorDisabledCompositionRequestBus::Event(m_entity->GetId(), &AzToolsFramework::EditorDisabledCompositionRequestBus::Events::GetDisabledComponents, disabledComponents);
|
|
return disabledComponents.size();
|
|
}
|
|
|
|
size_t m_lastResetCount = 0;
|
|
size_t m_lastPendingResetCount = 0;
|
|
size_t m_lastDisabledResetCount = 0;
|
|
const AZ::Entity* m_entity;
|
|
};
|
|
|
|
class AddComponentsTest
|
|
: public AllocatorsTestFixture
|
|
{
|
|
public:
|
|
void SetUp() override
|
|
{
|
|
AllocatorsTestFixture::SetUp();
|
|
|
|
AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get();
|
|
auto projectPathKey =
|
|
AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path";
|
|
AZ::IO::FixedMaxPath enginePath;
|
|
registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
|
|
registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native());
|
|
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry);
|
|
|
|
AzFramework::Application::Descriptor descriptor;
|
|
descriptor.m_enableDrilling = false;
|
|
m_app.Start(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);
|
|
|
|
m_app.RegisterComponentDescriptor(LeatherBootsComponent::CreateDescriptor());
|
|
m_app.RegisterComponentDescriptor(WoolSocksComponent::CreateDescriptor());
|
|
m_app.RegisterComponentDescriptor(HatesSocksComponent::CreateDescriptor());
|
|
m_app.RegisterComponentDescriptor(BlueJeansComponent::CreateDescriptor());
|
|
m_app.RegisterComponentDescriptor(WhiteBriefsComponent::CreateDescriptor());
|
|
m_app.RegisterComponentDescriptor(HeartBoxersComponent::CreateDescriptor());
|
|
m_app.RegisterComponentDescriptor(KnifeSheathComponent::CreateDescriptor());
|
|
|
|
m_entity1 = new AZ::Entity("Entity1");
|
|
m_entity1Counter.SetEntity(m_entity1);
|
|
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(&AzToolsFramework::EditorEntityContextRequestBus::Events::AddRequiredComponents, *m_entity1);
|
|
m_entity1->Init();
|
|
m_entity1Counter.Reset();
|
|
|
|
m_entity2 = new AZ::Entity("Entity2");
|
|
m_entity2Counter.SetEntity(m_entity2);
|
|
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(&AzToolsFramework::EditorEntityContextRequestBus::Events::AddRequiredComponents, *m_entity2);
|
|
m_entity2->Init();
|
|
m_entity2Counter.Reset();
|
|
}
|
|
|
|
void TearDown() override
|
|
{
|
|
m_app.Stop();
|
|
|
|
AllocatorsTestFixture::TearDown();
|
|
}
|
|
|
|
AzToolsFramework::ToolsApplication m_app;
|
|
|
|
AZ::Entity* m_entity1 = nullptr;
|
|
EntityComponentCounter m_entity1Counter;
|
|
|
|
AZ::Entity* m_entity2 = nullptr;
|
|
EntityComponentCounter m_entity2Counter;
|
|
|
|
size_t m_initialComponentCount = 0;
|
|
};
|
|
|
|
TEST_F(AddComponentsTest, AddOneComponentToOneEntity)
|
|
{
|
|
auto outcome = AzToolsFramework::AddComponents<WoolSocksComponent>::ToEntities(m_entity1);
|
|
// Verify success
|
|
ASSERT_TRUE(outcome.IsSuccess());
|
|
|
|
// Check that the returned result was what we expected
|
|
ASSERT_TRUE(VerifyAddedValidComponents<WoolSocksComponent>::OnOutcomeForEntity(outcome, m_entity1));
|
|
|
|
// Check that we have the component added as expected
|
|
ASSERT_EQ(1, CountComponentsOnEntity<WoolSocksComponent>(m_entity1));
|
|
|
|
// We do separate count checks in case some other random components were added or something unexpected occurred
|
|
ASSERT_EQ(1, m_entity1Counter.GetCount());
|
|
|
|
// Verify nothing is pending
|
|
ASSERT_EQ(0, CountPendingComponentsOnEntity<WoolSocksComponent>(m_entity1));
|
|
ASSERT_EQ(0, m_entity1Counter.GetPendingCount());
|
|
|
|
// Verify nothing is disabled
|
|
ASSERT_EQ(0, CountDisabledComponentsOnEntity<WoolSocksComponent>(m_entity1));
|
|
ASSERT_EQ(0, m_entity1Counter.GetDisabledCount());
|
|
|
|
auto addComponentsResults = outcome.GetValue()[m_entity1->GetId()];
|
|
AzToolsFramework::DisableComponents(addComponentsResults.m_addedValidComponents);
|
|
ASSERT_EQ(0, CountComponentsOnEntity<WoolSocksComponent>(m_entity1));
|
|
ASSERT_EQ(0, m_entity1Counter.GetCount());
|
|
ASSERT_EQ(0, CountPendingComponentsOnEntity<WoolSocksComponent>(m_entity1));
|
|
ASSERT_EQ(0, m_entity1Counter.GetPendingCount());
|
|
ASSERT_EQ(1, CountDisabledComponentsOnEntity<WoolSocksComponent>(m_entity1));
|
|
ASSERT_EQ(1, m_entity1Counter.GetDisabledCount());
|
|
|
|
AzToolsFramework::EnableComponents(addComponentsResults.m_addedValidComponents);
|
|
ASSERT_EQ(1, CountComponentsOnEntity<WoolSocksComponent>(m_entity1));
|
|
ASSERT_EQ(1, m_entity1Counter.GetCount());
|
|
ASSERT_EQ(0, CountPendingComponentsOnEntity<WoolSocksComponent>(m_entity1));
|
|
ASSERT_EQ(0, m_entity1Counter.GetPendingCount());
|
|
ASSERT_EQ(0, CountDisabledComponentsOnEntity<WoolSocksComponent>(m_entity1));
|
|
ASSERT_EQ(0, m_entity1Counter.GetDisabledCount());
|
|
}
|
|
|
|
TEST_F(AddComponentsTest, AddOneComponentToMultipleEntities)
|
|
{
|
|
// have one entity activated, we must ensure that it is still activated after add operation
|
|
m_entity1->Activate();
|
|
|
|
// add a component to both the activated and inactive entities
|
|
auto outcome = AzToolsFramework::AddComponents<WoolSocksComponent>::ToEntities(m_entity1, m_entity2);
|
|
|
|
// Verify outcome
|
|
ASSERT_TRUE(outcome.IsSuccess());
|
|
ASSERT_TRUE(VerifyAddedValidComponents<WoolSocksComponent>::OnOutcomeForEntity(outcome, m_entity1));
|
|
ASSERT_TRUE(VerifyAddedValidComponents<WoolSocksComponent>::OnOutcomeForEntity(outcome, m_entity2));
|
|
|
|
// Should always be on entity, not pending since services are met
|
|
ASSERT_EQ(1, CountComponentsOnEntity<WoolSocksComponent>(m_entity1));
|
|
ASSERT_EQ(1, CountComponentsOnEntity<WoolSocksComponent>(m_entity2));
|
|
ASSERT_EQ(1, m_entity1Counter.GetCount());
|
|
ASSERT_EQ(1, m_entity2Counter.GetCount());
|
|
|
|
// Nothing is pending
|
|
ASSERT_EQ(0, CountPendingComponentsOnEntity<WoolSocksComponent>(m_entity1));
|
|
ASSERT_EQ(0, CountPendingComponentsOnEntity<WoolSocksComponent>(m_entity2));
|
|
ASSERT_EQ(0, m_entity1Counter.GetPendingCount());
|
|
ASSERT_EQ(0, m_entity2Counter.GetPendingCount());
|
|
|
|
// Nothing is disabled
|
|
ASSERT_EQ(0, CountDisabledComponentsOnEntity<WoolSocksComponent>(m_entity1));
|
|
ASSERT_EQ(0, CountDisabledComponentsOnEntity<WoolSocksComponent>(m_entity2));
|
|
ASSERT_EQ(0, m_entity1Counter.GetDisabledCount());
|
|
ASSERT_EQ(0, m_entity2Counter.GetDisabledCount());
|
|
|
|
// Still in original states
|
|
ASSERT_EQ(AZ::Entity::State::Active, m_entity1->GetState());
|
|
ASSERT_EQ(AZ::Entity::State::Init, m_entity2->GetState());
|
|
}
|
|
|
|
// Add a component which requires another component
|
|
TEST_F(AddComponentsTest, ComponentRequiresService)
|
|
{
|
|
auto outcome = AzToolsFramework::AddComponents<LeatherBootsComponent>::ToEntities(m_entity1);
|
|
|
|
// Verify outcome
|
|
ASSERT_TRUE(outcome.IsSuccess());
|
|
ASSERT_TRUE(VerifyAddedPendingComponents<LeatherBootsComponent>::OnOutcomeForEntity(outcome, m_entity1));
|
|
|
|
// This will be pending since it is missing a socks service
|
|
ASSERT_EQ(0, CountComponentsOnEntity<LeatherBootsComponent>(m_entity1));
|
|
ASSERT_EQ(0, m_entity1Counter.GetCount());
|
|
ASSERT_EQ(1, CountPendingComponentsOnEntity<LeatherBootsComponent>(m_entity1));
|
|
ASSERT_EQ(1, m_entity1Counter.GetPendingCount());
|
|
ASSERT_EQ(0, CountDisabledComponentsOnEntity<LeatherBootsComponent>(m_entity1));
|
|
ASSERT_EQ(0, m_entity1Counter.GetDisabledCount());
|
|
|
|
// Satisfy the pending component with wool socks
|
|
outcome = AzToolsFramework::AddComponents<WoolSocksComponent>::ToEntities(m_entity1);
|
|
ASSERT_TRUE(outcome.IsSuccess());
|
|
ASSERT_TRUE(VerifyAddedValidComponents<WoolSocksComponent>::AndAdditionalValidatedComponents<LeatherBootsComponent>::OnOutcomeForEntity(outcome, m_entity1));
|
|
|
|
// Should have both on the entity now and no pending
|
|
ASSERT_EQ(1, CountComponentsOnEntity<LeatherBootsComponent>(m_entity1));
|
|
ASSERT_EQ(1, CountComponentsOnEntity<WoolSocksComponent>(m_entity1));
|
|
ASSERT_EQ(2, m_entity1Counter.GetCount());
|
|
ASSERT_EQ(0, CountPendingComponentsOnEntity<LeatherBootsComponent>(m_entity1));
|
|
ASSERT_EQ(0, CountPendingComponentsOnEntity<WoolSocksComponent>(m_entity1));
|
|
ASSERT_EQ(0, m_entity1Counter.GetPendingCount());
|
|
ASSERT_EQ(0, CountDisabledComponentsOnEntity<LeatherBootsComponent>(m_entity1));
|
|
ASSERT_EQ(0, CountDisabledComponentsOnEntity<WoolSocksComponent>(m_entity1));
|
|
ASSERT_EQ(0, m_entity1Counter.GetDisabledCount());
|
|
|
|
// LeatherBootsComponent should be wrapped in a GenericComponentWrapper
|
|
// because it is not an "editor component".
|
|
auto* wrapper = m_entity1->FindComponent<AzToolsFramework::Components::GenericComponentWrapper>();
|
|
|
|
ASSERT_TRUE(wrapper && wrapper->GetTemplate());
|
|
ASSERT_EQ(azrtti_typeid<LeatherBootsComponent>(), azrtti_typeid(wrapper->GetTemplate()));
|
|
|
|
// Try adding a component which requires a service
|
|
// that no other component provides.
|
|
outcome = AzToolsFramework::AddComponents<KnifeSheathComponent>::ToEntities(m_entity2);
|
|
|
|
ASSERT_TRUE(outcome.IsSuccess());
|
|
ASSERT_TRUE(VerifyAddedPendingComponents<KnifeSheathComponent>::OnOutcomeForEntity(outcome, m_entity2));
|
|
|
|
// This one will always be pending, never on entity as it will never be satisfied
|
|
ASSERT_EQ(0, CountComponentsOnEntity<KnifeSheathComponent>(m_entity2));
|
|
ASSERT_EQ(0, m_entity2Counter.GetCount());
|
|
ASSERT_EQ(1, CountPendingComponentsOnEntity<KnifeSheathComponent>(m_entity2));
|
|
ASSERT_EQ(1, m_entity2Counter.GetPendingCount());
|
|
ASSERT_EQ(0, CountDisabledComponentsOnEntity<KnifeSheathComponent>(m_entity2));
|
|
ASSERT_EQ(0, m_entity2Counter.GetDisabledCount());
|
|
|
|
// Check pending status
|
|
auto component = outcome.GetValue()[m_entity2->GetId()].m_addedPendingComponents[0];
|
|
AzToolsFramework::EntityCompositionRequestBus::Events::PendingComponentInfo pendingComponentInfo;
|
|
AzToolsFramework::EntityCompositionRequestBus::BroadcastResult(pendingComponentInfo, &AzToolsFramework::EntityCompositionRequestBus::Events::GetPendingComponentInfo, component);
|
|
// Should have one missing service
|
|
ASSERT_EQ(pendingComponentInfo.m_missingRequiredServices.size(), 1);
|
|
ASSERT_EQ(pendingComponentInfo.m_validComponentsThatAreIncompatible.size(), 0);
|
|
ASSERT_EQ(pendingComponentInfo.m_pendingComponentsWithRequiredServices.size(), 0);
|
|
// And that missing service should be the BeltService
|
|
ASSERT_EQ(pendingComponentInfo.m_missingRequiredServices[0], AZ_CRC("BeltService"));
|
|
|
|
// Entity 1 should remain untouched
|
|
ASSERT_EQ(2, m_entity1Counter.GetCount());
|
|
ASSERT_EQ(0, m_entity1Counter.GetPendingCount());
|
|
ASSERT_EQ(0, m_entity1Counter.GetDisabledCount());
|
|
}
|
|
|
|
// Add a component (jeans) which requires a service (underwear),
|
|
// and there are two viable options (boxers or briefs).
|
|
TEST_F(AddComponentsTest, ComponentRequiresServiceWithTwoViableOptions)
|
|
{
|
|
auto outcome = AzToolsFramework::AddComponents<BlueJeansComponent>::ToEntities(m_entity1);
|
|
|
|
ASSERT_TRUE(outcome.IsSuccess());
|
|
ASSERT_TRUE(VerifyAddedPendingComponents<BlueJeansComponent>::OnOutcomeForEntity(outcome, m_entity1));
|
|
|
|
ASSERT_EQ(0, CountComponentsOnEntity<BlueJeansComponent>(m_entity1));
|
|
ASSERT_EQ(0, m_entity1Counter.GetCount());
|
|
ASSERT_EQ(1, CountPendingComponentsOnEntity<BlueJeansComponent>(m_entity1));
|
|
ASSERT_EQ(1, m_entity1Counter.GetPendingCount());
|
|
ASSERT_EQ(0, CountDisabledComponentsOnEntity<BlueJeansComponent>(m_entity1));
|
|
ASSERT_EQ(0, m_entity1Counter.GetDisabledCount());
|
|
|
|
// Check pending status
|
|
auto component = outcome.GetValue()[m_entity1->GetId()].m_addedPendingComponents[0];
|
|
AzToolsFramework::EntityCompositionRequestBus::Events::PendingComponentInfo pendingComponentInfo;
|
|
AzToolsFramework::EntityCompositionRequestBus::BroadcastResult(pendingComponentInfo, &AzToolsFramework::EntityCompositionRequestBus::Events::GetPendingComponentInfo, component);
|
|
// Should have one missing service
|
|
ASSERT_EQ(pendingComponentInfo.m_missingRequiredServices.size(), 1);
|
|
ASSERT_EQ(pendingComponentInfo.m_validComponentsThatAreIncompatible.size(), 0);
|
|
ASSERT_EQ(pendingComponentInfo.m_pendingComponentsWithRequiredServices.size(), 0);
|
|
// And that missing service should be the "UnderwearService"
|
|
ASSERT_EQ(pendingComponentInfo.m_missingRequiredServices[0], AZ_CRC("UnderwearService"));
|
|
|
|
outcome = AzToolsFramework::AddComponents<WhiteBriefsComponent>::ToEntities(m_entity1);
|
|
|
|
ASSERT_TRUE(outcome.IsSuccess());
|
|
ASSERT_TRUE(VerifyAddedValidComponents<WhiteBriefsComponent>::AndAdditionalValidatedComponents<BlueJeansComponent>::OnOutcomeForEntity(outcome, m_entity1));
|
|
|
|
// Save this for later checks
|
|
auto whiteBriefsComponent = outcome.GetValue()[m_entity1->GetId()].m_addedValidComponents[0];
|
|
|
|
ASSERT_EQ(1, CountComponentsOnEntity<BlueJeansComponent>(m_entity1));
|
|
ASSERT_EQ(1, CountComponentsOnEntity<WhiteBriefsComponent>(m_entity1));
|
|
ASSERT_EQ(2, m_entity1Counter.GetCount());
|
|
ASSERT_EQ(0, CountPendingComponentsOnEntity<BlueJeansComponent>(m_entity1));
|
|
ASSERT_EQ(0, CountPendingComponentsOnEntity<WhiteBriefsComponent>(m_entity1));
|
|
ASSERT_EQ(0, m_entity1Counter.GetPendingCount());
|
|
ASSERT_EQ(0, CountDisabledComponentsOnEntity<BlueJeansComponent>(m_entity1));
|
|
ASSERT_EQ(0, CountDisabledComponentsOnEntity<WhiteBriefsComponent>(m_entity1));
|
|
ASSERT_EQ(0, m_entity1Counter.GetDisabledCount());
|
|
|
|
// Now try adding the second kind of underwear
|
|
// (it should be pending because entity already has underwear)
|
|
outcome = AzToolsFramework::AddComponents<HeartBoxersComponent>::ToEntities(m_entity1);
|
|
ASSERT_TRUE(outcome.IsSuccess());
|
|
ASSERT_TRUE(VerifyAddedPendingComponents<HeartBoxersComponent>::OnOutcomeForEntity(outcome, m_entity1));
|
|
|
|
ASSERT_EQ(1, CountComponentsOnEntity<BlueJeansComponent>(m_entity1));
|
|
ASSERT_EQ(1, CountComponentsOnEntity<WhiteBriefsComponent>(m_entity1));
|
|
ASSERT_EQ(0, CountComponentsOnEntity<HeartBoxersComponent>(m_entity1));
|
|
ASSERT_EQ(2, m_entity1Counter.GetCount());
|
|
ASSERT_EQ(0, CountPendingComponentsOnEntity<BlueJeansComponent>(m_entity1));
|
|
ASSERT_EQ(0, CountPendingComponentsOnEntity<WhiteBriefsComponent>(m_entity1));
|
|
ASSERT_EQ(1, CountPendingComponentsOnEntity<HeartBoxersComponent>(m_entity1));
|
|
ASSERT_EQ(1, m_entity1Counter.GetPendingCount());
|
|
ASSERT_EQ(0, CountDisabledComponentsOnEntity<BlueJeansComponent>(m_entity1));
|
|
ASSERT_EQ(0, CountDisabledComponentsOnEntity<WhiteBriefsComponent>(m_entity1));
|
|
ASSERT_EQ(0, CountDisabledComponentsOnEntity<HeartBoxersComponent>(m_entity1));
|
|
ASSERT_EQ(0, m_entity1Counter.GetDisabledCount());
|
|
|
|
// Check pending status
|
|
component = outcome.GetValue()[m_entity1->GetId()].m_addedPendingComponents[0];
|
|
AzToolsFramework::EntityCompositionRequestBus::BroadcastResult(pendingComponentInfo, &AzToolsFramework::EntityCompositionRequestBus::Events::GetPendingComponentInfo, component);
|
|
// Should have one incompatible component
|
|
ASSERT_EQ(pendingComponentInfo.m_missingRequiredServices.size(), 0);
|
|
ASSERT_EQ(pendingComponentInfo.m_validComponentsThatAreIncompatible.size(), 1);
|
|
ASSERT_EQ(pendingComponentInfo.m_pendingComponentsWithRequiredServices.size(), 0);
|
|
// And that incompatible component should be the WhiteBriefsComponent from earlier
|
|
ASSERT_EQ(pendingComponentInfo.m_validComponentsThatAreIncompatible[0], whiteBriefsComponent);
|
|
|
|
// disable white briefs component, which should resolve heart briefs, and check container counts
|
|
AzToolsFramework::DisableComponents(whiteBriefsComponent);
|
|
ASSERT_EQ(1, CountComponentsOnEntity<BlueJeansComponent>(m_entity1));
|
|
ASSERT_EQ(0, CountComponentsOnEntity<WhiteBriefsComponent>(m_entity1));
|
|
ASSERT_EQ(1, CountComponentsOnEntity<HeartBoxersComponent>(m_entity1));
|
|
ASSERT_EQ(2, m_entity1Counter.GetCount());
|
|
ASSERT_EQ(0, CountPendingComponentsOnEntity<BlueJeansComponent>(m_entity1));
|
|
ASSERT_EQ(0, CountPendingComponentsOnEntity<WhiteBriefsComponent>(m_entity1));
|
|
ASSERT_EQ(0, CountPendingComponentsOnEntity<HeartBoxersComponent>(m_entity1));
|
|
ASSERT_EQ(0, m_entity1Counter.GetPendingCount());
|
|
ASSERT_EQ(0, CountDisabledComponentsOnEntity<BlueJeansComponent>(m_entity1));
|
|
ASSERT_EQ(1, CountDisabledComponentsOnEntity<WhiteBriefsComponent>(m_entity1));
|
|
ASSERT_EQ(0, CountDisabledComponentsOnEntity<HeartBoxersComponent>(m_entity1));
|
|
ASSERT_EQ(1, m_entity1Counter.GetDisabledCount());
|
|
|
|
// re-enable white briefs component which is now pending because it's re-added after heart boxers was resolved
|
|
AzToolsFramework::EnableComponents(whiteBriefsComponent);
|
|
ASSERT_EQ(1, CountComponentsOnEntity<BlueJeansComponent>(m_entity1));
|
|
ASSERT_EQ(0, CountComponentsOnEntity<WhiteBriefsComponent>(m_entity1));
|
|
ASSERT_EQ(1, CountComponentsOnEntity<HeartBoxersComponent>(m_entity1));
|
|
ASSERT_EQ(2, m_entity1Counter.GetCount());
|
|
ASSERT_EQ(0, CountPendingComponentsOnEntity<BlueJeansComponent>(m_entity1));
|
|
ASSERT_EQ(1, CountPendingComponentsOnEntity<WhiteBriefsComponent>(m_entity1));
|
|
ASSERT_EQ(0, CountPendingComponentsOnEntity<HeartBoxersComponent>(m_entity1));
|
|
ASSERT_EQ(1, m_entity1Counter.GetPendingCount());
|
|
ASSERT_EQ(0, CountDisabledComponentsOnEntity<BlueJeansComponent>(m_entity1));
|
|
ASSERT_EQ(0, CountDisabledComponentsOnEntity<WhiteBriefsComponent>(m_entity1));
|
|
ASSERT_EQ(0, CountDisabledComponentsOnEntity<HeartBoxersComponent>(m_entity1));
|
|
ASSERT_EQ(0, m_entity1Counter.GetDisabledCount());
|
|
|
|
// Try removing pending component (should be uneventful, but it is a branch internally)
|
|
auto removalOutcome = AzToolsFramework::RemoveComponents(component);
|
|
ASSERT_TRUE(removalOutcome.IsSuccess());
|
|
ASSERT_EQ(1, CountComponentsOnEntity<BlueJeansComponent>(m_entity1));
|
|
ASSERT_EQ(1, CountComponentsOnEntity<WhiteBriefsComponent>(m_entity1));
|
|
ASSERT_EQ(0, CountComponentsOnEntity<HeartBoxersComponent>(m_entity1));
|
|
ASSERT_EQ(2, m_entity1Counter.GetCount());
|
|
ASSERT_EQ(0, CountPendingComponentsOnEntity<BlueJeansComponent>(m_entity1));
|
|
ASSERT_EQ(0, CountPendingComponentsOnEntity<WhiteBriefsComponent>(m_entity1));
|
|
ASSERT_EQ(0, CountPendingComponentsOnEntity<HeartBoxersComponent>(m_entity1));
|
|
ASSERT_EQ(0, m_entity1Counter.GetPendingCount());
|
|
ASSERT_EQ(0, CountDisabledComponentsOnEntity<BlueJeansComponent>(m_entity1));
|
|
ASSERT_EQ(0, CountDisabledComponentsOnEntity<WhiteBriefsComponent>(m_entity1));
|
|
ASSERT_EQ(0, CountDisabledComponentsOnEntity<HeartBoxersComponent>(m_entity1));
|
|
ASSERT_EQ(0, m_entity1Counter.GetDisabledCount());
|
|
}
|
|
|
|
// Add a component to two entities, where the component requires a service,
|
|
// and one entity already has that service, but the other entity does not.
|
|
TEST_F(AddComponentsTest, TwoEntitiesWhereOneHasRequiredServiceAndOneDoesNot)
|
|
{
|
|
// entity1 already has socks
|
|
auto outcome = AzToolsFramework::AddComponents<WoolSocksComponent>::ToEntities(m_entity1);
|
|
ASSERT_TRUE(outcome.IsSuccess());
|
|
ASSERT_TRUE(VerifyAddedValidComponents<WoolSocksComponent>::OnOutcomeForEntity(outcome, m_entity1));
|
|
|
|
ASSERT_EQ(1, CountComponentsOnEntity<WoolSocksComponent>(m_entity1));
|
|
ASSERT_EQ(1, m_entity1Counter.GetCount());
|
|
ASSERT_EQ(0, CountPendingComponentsOnEntity<WoolSocksComponent>(m_entity1));
|
|
ASSERT_EQ(0, m_entity1Counter.GetPendingCount());
|
|
ASSERT_EQ(0, CountDisabledComponentsOnEntity<WoolSocksComponent>(m_entity1));
|
|
ASSERT_EQ(0, m_entity1Counter.GetDisabledCount());
|
|
|
|
outcome = AzToolsFramework::AddComponents<LeatherBootsComponent>::ToEntities(m_entity1, m_entity2);
|
|
ASSERT_TRUE(outcome.IsSuccess());
|
|
ASSERT_TRUE(VerifyAddedValidComponents<LeatherBootsComponent>::OnOutcomeForEntity(outcome, m_entity1));
|
|
ASSERT_TRUE(VerifyAddedPendingComponents<LeatherBootsComponent>::OnOutcomeForEntity(outcome, m_entity2));
|
|
|
|
ASSERT_EQ(1, CountComponentsOnEntity<WoolSocksComponent>(m_entity1));
|
|
ASSERT_EQ(1, CountComponentsOnEntity<LeatherBootsComponent>(m_entity1));
|
|
ASSERT_EQ(2, m_entity1Counter.GetCount());
|
|
|
|
ASSERT_EQ(0, CountPendingComponentsOnEntity<WoolSocksComponent>(m_entity1));
|
|
ASSERT_EQ(0, CountPendingComponentsOnEntity<LeatherBootsComponent>(m_entity1));
|
|
ASSERT_EQ(0, m_entity1Counter.GetPendingCount());
|
|
|
|
ASSERT_EQ(0, CountDisabledComponentsOnEntity<WoolSocksComponent>(m_entity1));
|
|
ASSERT_EQ(0, CountDisabledComponentsOnEntity<LeatherBootsComponent>(m_entity1));
|
|
ASSERT_EQ(0, m_entity1Counter.GetDisabledCount());
|
|
|
|
ASSERT_EQ(0, CountComponentsOnEntity<LeatherBootsComponent>(m_entity2));
|
|
ASSERT_EQ(0, CountComponentsOnEntity<WoolSocksComponent>(m_entity2));
|
|
ASSERT_EQ(0, m_entity2Counter.GetCount());
|
|
|
|
ASSERT_EQ(1, CountPendingComponentsOnEntity<LeatherBootsComponent>(m_entity2));
|
|
ASSERT_EQ(0, CountPendingComponentsOnEntity<WoolSocksComponent>(m_entity2));
|
|
ASSERT_EQ(1, m_entity2Counter.GetPendingCount());
|
|
|
|
ASSERT_EQ(0, CountDisabledComponentsOnEntity<LeatherBootsComponent>(m_entity2));
|
|
ASSERT_EQ(0, CountDisabledComponentsOnEntity<WoolSocksComponent>(m_entity2));
|
|
ASSERT_EQ(0, m_entity2Counter.GetDisabledCount());
|
|
}
|
|
|
|
// Test adding a component which requires a service,
|
|
// but all candidates which provide that service conflicts with some existing component.
|
|
TEST_F(AddComponentsTest, RequiredServiceConflictsWithExistingComponents)
|
|
{
|
|
auto outcome = AzToolsFramework::AddComponents<HatesSocksComponent>::ToEntities(m_entity1);
|
|
|
|
ASSERT_TRUE(outcome.IsSuccess());
|
|
ASSERT_TRUE(VerifyAddedValidComponents<HatesSocksComponent>::OnOutcomeForEntity(outcome, m_entity1));
|
|
|
|
// Save this for tests and removal later
|
|
AZ::Component* hatesSocksComponent = outcome.GetValue()[m_entity1->GetId()].m_addedValidComponents[0];
|
|
|
|
// Adding boots
|
|
outcome = AzToolsFramework::AddComponents<LeatherBootsComponent>::ToEntities(m_entity1, m_entity2);
|
|
|
|
ASSERT_TRUE(outcome.IsSuccess());
|
|
ASSERT_TRUE(VerifyAddedPendingComponents<LeatherBootsComponent>::OnOutcomeForEntity(outcome, m_entity1));
|
|
ASSERT_TRUE(VerifyAddedPendingComponents<LeatherBootsComponent>::OnOutcomeForEntity(outcome, m_entity2));
|
|
auto leatherBootsComponent = outcome.GetValue()[m_entity1->GetId()].m_addedPendingComponents[0];
|
|
|
|
// Add socks to make it valid, but incompatible with HatesSocks on entity 1
|
|
outcome = AzToolsFramework::AddComponents<WoolSocksComponent>::ToEntities(m_entity1, m_entity2);
|
|
ASSERT_TRUE(VerifyAddedPendingComponents<WoolSocksComponent>::OnOutcomeForEntity(outcome, m_entity1));
|
|
// Socks will work on entity 2 and leather boots should be valid now because of it
|
|
ASSERT_TRUE(VerifyAddedValidComponents<WoolSocksComponent>::AndAdditionalValidatedComponents<LeatherBootsComponent>::OnOutcomeForEntity(outcome, m_entity2));
|
|
|
|
// Save this component for later
|
|
auto woolSocksComponent = outcome.GetValue()[m_entity1->GetId()].m_addedPendingComponents[0];
|
|
|
|
// Check pending status
|
|
AzToolsFramework::EntityCompositionRequestBus::Events::PendingComponentInfo pendingComponentInfo;
|
|
|
|
// First check leather boots, it should indicate it is waiting on woolSocksComponent
|
|
AzToolsFramework::EntityCompositionRequestBus::BroadcastResult(pendingComponentInfo, &AzToolsFramework::EntityCompositionRequestBus::Events::GetPendingComponentInfo, leatherBootsComponent);
|
|
// Should have one pending component
|
|
ASSERT_EQ(pendingComponentInfo.m_missingRequiredServices.size(), 0);
|
|
ASSERT_EQ(pendingComponentInfo.m_validComponentsThatAreIncompatible.size(), 0);
|
|
ASSERT_EQ(pendingComponentInfo.m_pendingComponentsWithRequiredServices.size(), 1);
|
|
// And that pending component should be the woolSocksComponent
|
|
ASSERT_EQ(pendingComponentInfo.m_pendingComponentsWithRequiredServices[0], woolSocksComponent);
|
|
|
|
// Now check the wool socks, they should be incompatible with Hates Socks
|
|
AzToolsFramework::EntityCompositionRequestBus::BroadcastResult(pendingComponentInfo, &AzToolsFramework::EntityCompositionRequestBus::Events::GetPendingComponentInfo, woolSocksComponent);
|
|
// Should have one incompatible component
|
|
ASSERT_EQ(pendingComponentInfo.m_missingRequiredServices.size(), 0);
|
|
ASSERT_EQ(pendingComponentInfo.m_validComponentsThatAreIncompatible.size(), 1);
|
|
ASSERT_EQ(pendingComponentInfo.m_pendingComponentsWithRequiredServices.size(), 0);
|
|
// And that incompatible component should be the hatesSocksComponent
|
|
ASSERT_EQ(pendingComponentInfo.m_validComponentsThatAreIncompatible[0], hatesSocksComponent);
|
|
|
|
// Remove HatesSocks from entity 1 to valid the entire entity
|
|
auto removalOutcome = AzToolsFramework::RemoveComponents(hatesSocksComponent);
|
|
ASSERT_TRUE(removalOutcome.IsSuccess());
|
|
ASSERT_TRUE((VerifyRemovalValidatedComponents<WoolSocksComponent, LeatherBootsComponent>::OnOutcomeForEntity(removalOutcome, m_entity1)));
|
|
}
|
|
|
|
// Test adding, enabling, disabling several components
|
|
TEST_F(AddComponentsTest, EnableDisableConflictingServices)
|
|
{
|
|
auto outcome = AzToolsFramework::AddComponents<LeatherBootsComponent>::ToEntities(m_entity1);
|
|
ASSERT_TRUE(outcome.IsSuccess());
|
|
|
|
ASSERT_EQ(0, m_entity1Counter.GetCount());
|
|
ASSERT_EQ(1, m_entity1Counter.GetPendingCount());
|
|
ASSERT_EQ(0, m_entity1Counter.GetDisabledCount());
|
|
|
|
outcome = AzToolsFramework::AddComponents<HatesSocksComponent>::ToEntities(m_entity1);
|
|
ASSERT_TRUE(outcome.IsSuccess());
|
|
|
|
ASSERT_EQ(1, m_entity1Counter.GetCount());
|
|
ASSERT_EQ(1, m_entity1Counter.GetPendingCount());
|
|
ASSERT_EQ(0, m_entity1Counter.GetDisabledCount());
|
|
|
|
outcome = AzToolsFramework::AddComponents<BlueJeansComponent>::ToEntities(m_entity1);
|
|
ASSERT_TRUE(outcome.IsSuccess());
|
|
|
|
ASSERT_EQ(1, m_entity1Counter.GetCount());
|
|
ASSERT_EQ(2, m_entity1Counter.GetPendingCount());
|
|
ASSERT_EQ(0, m_entity1Counter.GetDisabledCount());
|
|
|
|
outcome = AzToolsFramework::AddComponents<WhiteBriefsComponent>::ToEntities(m_entity1);
|
|
ASSERT_TRUE(outcome.IsSuccess());
|
|
|
|
ASSERT_EQ(3, m_entity1Counter.GetCount());
|
|
ASSERT_EQ(1, m_entity1Counter.GetPendingCount());
|
|
ASSERT_EQ(0, m_entity1Counter.GetDisabledCount());
|
|
|
|
outcome = AzToolsFramework::AddComponents<HeartBoxersComponent>::ToEntities(m_entity1);
|
|
ASSERT_TRUE(outcome.IsSuccess());
|
|
|
|
ASSERT_EQ(3, m_entity1Counter.GetCount());
|
|
ASSERT_EQ(2, m_entity1Counter.GetPendingCount());
|
|
ASSERT_EQ(0, m_entity1Counter.GetDisabledCount());
|
|
|
|
outcome = AzToolsFramework::AddComponents<KnifeSheathComponent>::ToEntities(m_entity1);
|
|
ASSERT_TRUE(outcome.IsSuccess());
|
|
|
|
ASSERT_EQ(3, m_entity1Counter.GetCount());
|
|
ASSERT_EQ(3, m_entity1Counter.GetPendingCount());
|
|
ASSERT_EQ(0, m_entity1Counter.GetDisabledCount());
|
|
|
|
outcome = AzToolsFramework::AddComponents<WoolSocksComponent>::ToEntities(m_entity1);
|
|
ASSERT_TRUE(outcome.IsSuccess());
|
|
|
|
ASSERT_EQ(3, m_entity1Counter.GetCount());
|
|
ASSERT_EQ(4, m_entity1Counter.GetPendingCount());
|
|
ASSERT_EQ(0, m_entity1Counter.GetDisabledCount());
|
|
|
|
AzToolsFramework::DisableComponents(GetComponentsForEntity<HatesSocksComponent>(m_entity1));
|
|
|
|
ASSERT_EQ(4, m_entity1Counter.GetCount());
|
|
ASSERT_EQ(2, m_entity1Counter.GetPendingCount());
|
|
ASSERT_EQ(1, m_entity1Counter.GetDisabledCount());
|
|
|
|
AzToolsFramework::DisableComponents(GetComponentsForEntity<HeartBoxersComponent>(m_entity1));
|
|
|
|
ASSERT_EQ(4, m_entity1Counter.GetCount());
|
|
ASSERT_EQ(1, m_entity1Counter.GetPendingCount());
|
|
ASSERT_EQ(2, m_entity1Counter.GetDisabledCount());
|
|
|
|
AzToolsFramework::DisableComponents(GetComponentsForEntity<KnifeSheathComponent>(m_entity1));
|
|
|
|
ASSERT_EQ(4, m_entity1Counter.GetCount());
|
|
ASSERT_EQ(0, m_entity1Counter.GetPendingCount());
|
|
ASSERT_EQ(3, m_entity1Counter.GetDisabledCount());
|
|
|
|
AzToolsFramework::DisableComponents(GetComponentsForEntity<WhiteBriefsComponent>(m_entity1));
|
|
|
|
ASSERT_EQ(2, m_entity1Counter.GetCount());
|
|
ASSERT_EQ(1, m_entity1Counter.GetPendingCount());
|
|
ASSERT_EQ(4, m_entity1Counter.GetDisabledCount());
|
|
|
|
AzToolsFramework::EnableComponents(GetComponentsForEntity<HeartBoxersComponent>(m_entity1));
|
|
|
|
ASSERT_EQ(4, m_entity1Counter.GetCount());
|
|
ASSERT_EQ(0, m_entity1Counter.GetPendingCount());
|
|
ASSERT_EQ(3, m_entity1Counter.GetDisabledCount());
|
|
}
|
|
|
|
// A reusable testing fixture that ensures basic application services are mocked.
|
|
// Provided:
|
|
// Basic Component descriptor functionality
|
|
// Memory
|
|
// Serialize (and Edit) contexts
|
|
class MockApplicationFixture
|
|
: public AllocatorsFixture
|
|
, public AZ::ComponentApplicationBus::Handler
|
|
{
|
|
public:
|
|
AZStd::unique_ptr<AZ::SerializeContext> m_serializeContext;
|
|
AZStd::vector<const ComponentDescriptor*> m_descriptors;
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// ComponentApplicationMessages
|
|
ComponentApplication* GetApplication() override { return nullptr; }
|
|
void RegisterComponentDescriptor(const ComponentDescriptor* descriptor) override
|
|
{
|
|
m_descriptors.push_back(descriptor);
|
|
descriptor->Reflect(m_serializeContext.get());
|
|
}
|
|
|
|
void UnregisterComponentDescriptor(const ComponentDescriptor*) override {}
|
|
void RegisterEntityAddedEventHandler(EntityAddedEvent::Handler&) override {}
|
|
void RegisterEntityRemovedEventHandler(EntityRemovedEvent::Handler&) override {}
|
|
void RegisterEntityActivatedEventHandler(EntityActivatedEvent::Handler&) override {}
|
|
void RegisterEntityDeactivatedEventHandler(EntityDeactivatedEvent::Handler&) override {}
|
|
void SignalEntityActivated(Entity*) override {}
|
|
void SignalEntityDeactivated(Entity*) override {}
|
|
bool AddEntity(Entity*) override { return true; }
|
|
bool RemoveEntity(Entity*) override { return true; }
|
|
bool DeleteEntity(const EntityId&) override { return true; }
|
|
Entity* FindEntity(const EntityId&) override { return nullptr; }
|
|
SerializeContext* GetSerializeContext() override { return m_serializeContext.get(); }
|
|
BehaviorContext* GetBehaviorContext() override { return nullptr; }
|
|
JsonRegistrationContext* GetJsonRegistrationContext() override { return nullptr; }
|
|
const char* GetEngineRoot() const override { return nullptr; }
|
|
const char* GetExecutableFolder() const override { return nullptr; }
|
|
void EnumerateEntities(const EntityCallback& /*callback*/) override {}
|
|
void QueryApplicationType(AZ::ApplicationTypeQuery& /*appType*/) const override {}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
MockApplicationFixture()
|
|
: AllocatorsFixture()
|
|
{
|
|
}
|
|
|
|
void SetUp() override
|
|
{
|
|
AllocatorsFixture::SetUp();
|
|
|
|
ComponentApplicationBus::Handler::BusConnect();
|
|
AZ::Interface<AZ::ComponentApplicationRequests>::Register(this);
|
|
m_serializeContext.reset(aznew AZ::SerializeContext(true, true));
|
|
|
|
Entity::Reflect(m_serializeContext.get());
|
|
}
|
|
|
|
void TearDown() override
|
|
{
|
|
for (auto descriptor : m_descriptors)
|
|
{
|
|
delete descriptor;
|
|
}
|
|
m_descriptors.set_capacity(0);
|
|
|
|
m_serializeContext.reset();
|
|
AZ::Interface<AZ::ComponentApplicationRequests>::Unregister(this);
|
|
ComponentApplicationBus::Handler::BusDisconnect();
|
|
|
|
AllocatorsFixture::TearDown();
|
|
}
|
|
};
|
|
|
|
// add scrubbing capability to the above fixture by adding some components to it.
|
|
class EntityTest_Scrubbing : public MockApplicationFixture
|
|
{
|
|
protected:
|
|
AZStd::unique_ptr<Entity> m_fakeSystemEntity;
|
|
|
|
public:
|
|
class VisibleComponent
|
|
: public AzToolsFramework::Components::EditorComponentBase
|
|
{
|
|
public:
|
|
AZ_COMPONENT(VisibleComponent, "{6CEC2D1E-08CF-4609-9BEE-BA9D32B4C223}", AzToolsFramework::Components::EditorComponentBase);
|
|
|
|
void Activate() override {}
|
|
void Deactivate() override {}
|
|
|
|
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
|
{
|
|
provided.push_back(AZ_CRC("ValidComponentService"));
|
|
}
|
|
|
|
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
|
{
|
|
incompatible.push_back(AZ_CRC("ValidComponentService"));
|
|
}
|
|
|
|
static void Reflect(ReflectContext* reflection)
|
|
{
|
|
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(reflection);
|
|
if (serializeContext)
|
|
{
|
|
serializeContext->Class<VisibleComponent, AzToolsFramework::Components::EditorComponentBase>();
|
|
|
|
AZ::EditContext* ec = serializeContext->GetEditContext();
|
|
ec->Class<VisibleComponent>("Visible Component", "A class that should show up in the property editor")
|
|
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
|
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Show)
|
|
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c));
|
|
}
|
|
}
|
|
};
|
|
|
|
class HiddenComponent
|
|
: public AzToolsFramework::Components::EditorComponentBase
|
|
{
|
|
public:
|
|
AZ_COMPONENT(HiddenComponent, "{E4D2AD8B-3930-46FC-837A-8DDFCA0FB1AF}", AzToolsFramework::Components::EditorComponentBase);
|
|
|
|
static Component* s_wasDeleted;
|
|
~HiddenComponent() override
|
|
{
|
|
s_wasDeleted = this;
|
|
}
|
|
|
|
void Activate() override {}
|
|
void Deactivate() override {}
|
|
|
|
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
|
{
|
|
provided.push_back(AZ_CRC("HiddenComponentService"));
|
|
}
|
|
|
|
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
|
{
|
|
incompatible.push_back(AZ_CRC("HiddenComponentService"));
|
|
}
|
|
|
|
static void Reflect(ReflectContext* reflection)
|
|
{
|
|
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(reflection);
|
|
if (serializeContext)
|
|
{
|
|
serializeContext->Class<HiddenComponent, AzToolsFramework::Components::EditorComponentBase>();
|
|
|
|
AZ::EditContext* ec = serializeContext->GetEditContext();
|
|
ec->Class<HiddenComponent>("Hidden Component", "A class that should not show up in the property editor")
|
|
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
|
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Hide)
|
|
->Attribute(AZ::Edit::Attributes::HideIcon, true)
|
|
->Attribute(AZ::Edit::Attributes::SliceFlags, AZ::Edit::SliceFlags::HideOnAdd | AZ::Edit::SliceFlags::PushWhenHidden);
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
void SetUp() override
|
|
{
|
|
MockApplicationFixture::SetUp();
|
|
RegisterComponentDescriptor(VisibleComponent::CreateDescriptor());
|
|
RegisterComponentDescriptor(HiddenComponent::CreateDescriptor());
|
|
RegisterComponentDescriptor(AzToolsFramework::Components::EditorPendingCompositionComponent::CreateDescriptor());
|
|
RegisterComponentDescriptor(AzToolsFramework::Components::EditorEntityActionComponent::CreateDescriptor());
|
|
|
|
m_fakeSystemEntity.reset(aznew Entity());
|
|
m_fakeSystemEntity->CreateComponent<AzToolsFramework::Components::EditorEntityActionComponent>();
|
|
m_fakeSystemEntity->Init();
|
|
m_fakeSystemEntity->Activate();
|
|
}
|
|
|
|
void TearDown() override
|
|
{
|
|
m_fakeSystemEntity->Deactivate();
|
|
m_fakeSystemEntity.reset();
|
|
MockApplicationFixture::TearDown();
|
|
}
|
|
};
|
|
|
|
Component* EntityTest_Scrubbing::HiddenComponent::s_wasDeleted = nullptr;
|
|
|
|
TEST_F(EntityTest_Scrubbing, ConflictingVisibleComponents_AreInvalidated)
|
|
{
|
|
// in this test we make sure that visible components (ones which show up on the UI)
|
|
// that conflict with each other are properly disabled and moved to the pending list during scrubbing.
|
|
|
|
// Component setup:
|
|
AZ::Entity newEntity;
|
|
|
|
AZ::Component* firstValidComponent = aznew VisibleComponent();
|
|
newEntity.AddComponent(firstValidComponent);
|
|
AZ::Component* conflictingVisibleComponent = aznew VisibleComponent();
|
|
newEntity.AddComponent(conflictingVisibleComponent);
|
|
|
|
EntityList entities;
|
|
entities.push_back(&newEntity);
|
|
EntityCompositionRequests::ScrubEntitiesOutcome resultValue = AZ::Failure<AZStd::string>("Didn't get called");
|
|
EntityCompositionRequestBus::BroadcastResult(resultValue, &EntityCompositionRequestBus::Events::ScrubEntities, entities);
|
|
|
|
ASSERT_TRUE(resultValue.IsSuccess());
|
|
ASSERT_EQ(resultValue.GetValue().size(), 1);
|
|
|
|
EntityCompositionRequests::ScrubEntityResults& resultForThisEntity = resultValue.GetValue()[entities[0]->GetId()];
|
|
|
|
EXPECT_EQ(resultForThisEntity.m_invalidatedComponents.size(), 1);
|
|
EXPECT_TRUE(AZStd::find(resultForThisEntity.m_invalidatedComponents.begin(), resultForThisEntity.m_invalidatedComponents.end(), conflictingVisibleComponent) != resultForThisEntity.m_invalidatedComponents.end());
|
|
|
|
// The "Validated components" array should be empty becuase it should only list previously invalid components that were somehow validated by the scrubbing.
|
|
EXPECT_EQ(resultForThisEntity.m_validatedComponents.size(), 0);
|
|
|
|
// make sure the valid visible one wasn't removed:
|
|
EXPECT_EQ(newEntity.FindComponent(azrtti_typeid<VisibleComponent>()), firstValidComponent);
|
|
}
|
|
|
|
TEST_F(EntityTest_Scrubbing, ConflictingHiddenComponents_AreDeleted)
|
|
{
|
|
// in this test we make sure that when an entity contains a conflicting hidden component
|
|
// the hidden component is deleted as part of thes scrubbing.
|
|
|
|
// Component setup:
|
|
AZ::Entity newEntity;
|
|
|
|
AZ::Component* validHiddenComponent = aznew HiddenComponent();
|
|
AZ::Component* conflictingHiddenComponent = aznew HiddenComponent();
|
|
newEntity.AddComponent(validHiddenComponent);
|
|
newEntity.AddComponent(conflictingHiddenComponent);
|
|
|
|
EntityTest_Scrubbing::HiddenComponent::s_wasDeleted = nullptr;
|
|
|
|
EntityList entities;
|
|
entities.push_back(&newEntity);
|
|
EntityCompositionRequests::ScrubEntitiesOutcome resultValue = AZ::Failure<AZStd::string>("Didn't get called");
|
|
EntityCompositionRequestBus::BroadcastResult(resultValue, &EntityCompositionRequestBus::Events::ScrubEntities, entities);
|
|
|
|
// we cannnot test anything further if the array is empty or it failed.
|
|
ASSERT_TRUE(resultValue.IsSuccess());
|
|
ASSERT_EQ(resultValue.GetValue().size(), 1);
|
|
|
|
EntityCompositionRequests::ScrubEntityResults& resultForThisEntity = resultValue.GetValue()[entities[0]->GetId()];
|
|
|
|
// we must NOT find the conflicting component - should have been deleted.
|
|
EXPECT_EQ(EntityTest_Scrubbing::HiddenComponent::s_wasDeleted, conflictingHiddenComponent);
|
|
|
|
// we must also not find it on the invalidated list, since it has been deleted.
|
|
EXPECT_EQ(resultForThisEntity.m_invalidatedComponents.size(), 0);
|
|
|
|
// The "Validated components" array should be empty becuase it should only list previously invalid components that were somehow validated by the scrubbing.
|
|
EXPECT_EQ(resultForThisEntity.m_validatedComponents.size(), 0);
|
|
// make sure the remaining component on the entity is the correct hidden component
|
|
EXPECT_EQ(newEntity.FindComponents(azrtti_typeid<HiddenComponent>()).size(), 1); // there should only be one of them.
|
|
EXPECT_EQ(newEntity.FindComponent(azrtti_typeid<HiddenComponent>()), validHiddenComponent);
|
|
|
|
}
|
|
|
|
TEST_F(EntityTest_Scrubbing, NonConflictingVisibleComponents_AreReinstated)
|
|
{
|
|
// in this test we make sure that if a pending component (inactive due to prior problems)
|
|
// no longer has those problems, it is made valid and active when scrubbing occurs.
|
|
|
|
// Component setup:
|
|
AZ::Entity newEntity;
|
|
|
|
AZ::Component* firstValidComponent = aznew VisibleComponent();
|
|
AZ::Component* conflictingVisibleComponent = aznew VisibleComponent();
|
|
newEntity.AddComponent(firstValidComponent);
|
|
newEntity.AddComponent(conflictingVisibleComponent);
|
|
|
|
EntityList entities;
|
|
entities.push_back(&newEntity);
|
|
{
|
|
EntityCompositionRequests::ScrubEntitiesOutcome resultValue = AZ::Failure<AZStd::string>("Didn't get called");
|
|
EntityCompositionRequestBus::BroadcastResult(resultValue, &EntityCompositionRequestBus::Events::ScrubEntities, entities);
|
|
|
|
ASSERT_TRUE(resultValue.IsSuccess());
|
|
ASSERT_EQ(resultValue.GetValue().size(), 1);
|
|
// note that the actual results of the above operation are already verified in another test. now we go further with this
|
|
// and actually delete the original component so that the second one can become valid.
|
|
}
|
|
|
|
newEntity.RemoveComponent(firstValidComponent);
|
|
delete firstValidComponent;
|
|
|
|
// now re-scrub and expect to see the previously disabled conflicting one become the active one:
|
|
{
|
|
EntityCompositionRequests::ScrubEntitiesOutcome resultValue = AZ::Failure<AZStd::string>("Didn't get called");
|
|
EntityCompositionRequestBus::BroadcastResult(resultValue, &EntityCompositionRequestBus::Events::ScrubEntities, entities);
|
|
ASSERT_TRUE(resultValue.IsSuccess());
|
|
ASSERT_EQ(resultValue.GetValue().size(), 1);
|
|
EntityCompositionRequests::ScrubEntityResults& resultForThisEntity = resultValue.GetValue()[entities[0]->GetId()];
|
|
|
|
// nothing should be invalidated
|
|
EXPECT_EQ(resultForThisEntity.m_invalidatedComponents.size(), 0);
|
|
// the visible component should now be activated.
|
|
ASSERT_EQ(resultForThisEntity.m_validatedComponents.size(), 1);
|
|
EXPECT_EQ(resultForThisEntity.m_validatedComponents[0], conflictingVisibleComponent);
|
|
|
|
// make sure its actually active, on the entity.
|
|
EXPECT_EQ(newEntity.FindComponent(azrtti_typeid<VisibleComponent>()), conflictingVisibleComponent);
|
|
|
|
}
|
|
}
|
|
|
|
TEST_F(EntityTest_Scrubbing, InactiveEntityWithInvalidComponents_AreValidatedByPendingComponents)
|
|
{
|
|
// This test takes an entity with known invalid component setup that has not been activated
|
|
// Adds pending components which will satisfy the invalid component setup
|
|
// And expects scrub entities to succeed in this case.
|
|
// Note - this is an edge case when deserializing a module entity or system entity from the app descriptor
|
|
RegisterComponentDescriptor(LeatherBootsComponent::CreateDescriptor());
|
|
RegisterComponentDescriptor(WoolSocksComponent::CreateDescriptor());
|
|
RegisterComponentDescriptor(AzToolsFramework::Components::GenericComponentWrapper::CreateDescriptor());
|
|
|
|
AZ::Entity* testEntity = aznew AZ::Entity("Test Scrubbing Entity");
|
|
testEntity->CreateComponent<AzToolsFramework::Components::EditorPendingCompositionComponent>();
|
|
testEntity->Init(); // init to kick off the pending composition request bus, but don't activate because we have invalid components
|
|
|
|
EntityList entities;
|
|
entities.push_back(testEntity);
|
|
|
|
// Manually add boots component that requires the socks component, to simulate this being read out of app descriptor
|
|
testEntity->CreateComponent<AzToolsFramework::Components::GenericComponentWrapper>(aznew LeatherBootsComponent());
|
|
EntityCompositionRequests::ScrubEntitiesOutcome scrubResults = AZ::Failure<AZStd::string>("Didn't get called");
|
|
EntityCompositionRequestBus::BroadcastResult(scrubResults, &EntityCompositionRequestBus::Events::ScrubEntities, entities);
|
|
ASSERT_TRUE(scrubResults.IsSuccess());
|
|
|
|
// If component is invalidated by scrubbing, it should now be in the pending set
|
|
AZ::Entity::ComponentArrayType pendingComponents;
|
|
AzToolsFramework::EditorPendingCompositionRequestBus::Event(testEntity->GetId(), &AzToolsFramework::EditorPendingCompositionRequests::GetPendingComponents, pendingComponents);
|
|
ASSERT_EQ(pendingComponents.size(), 1);
|
|
|
|
// the boots component should be flagged as invalid, since our entity was not activated
|
|
EntityCompositionRequests::ScrubEntityResults& resultForTestEntity = scrubResults.GetValue()[entities[0]->GetId()];
|
|
ASSERT_EQ(resultForTestEntity.m_invalidatedComponents.size(), 1);
|
|
|
|
// Don't actually want to keep the component in the pending set, so that we can validate the initial problem, so add it back onto the entity
|
|
pendingComponents.clear();
|
|
AZ::Component* invalidComponent = resultForTestEntity.m_invalidatedComponents[0];
|
|
AzToolsFramework::EditorPendingCompositionRequestBus::Event(testEntity->GetId(), &AzToolsFramework::EditorPendingCompositionRequests::RemovePendingComponent, invalidComponent);
|
|
AzToolsFramework::EditorPendingCompositionRequestBus::Event(testEntity->GetId(), &AzToolsFramework::EditorPendingCompositionRequests::GetPendingComponents, pendingComponents);
|
|
ASSERT_TRUE(pendingComponents.empty());
|
|
delete invalidComponent;
|
|
|
|
// now add a socks component to the pending set which will fulfill the boots' dependency
|
|
testEntity->CreateComponent<AzToolsFramework::Components::GenericComponentWrapper>(aznew LeatherBootsComponent());
|
|
AzToolsFramework::EditorPendingCompositionRequestBus::Event(testEntity->GetId(), &AzToolsFramework::EditorPendingCompositionRequests::AddPendingComponent, aznew WoolSocksComponent());
|
|
|
|
pendingComponents.clear();
|
|
AzToolsFramework::EditorPendingCompositionRequestBus::Event(testEntity->GetId(), &AzToolsFramework::EditorPendingCompositionRequests::GetPendingComponents, pendingComponents);
|
|
ASSERT_EQ(pendingComponents.size(), 1);
|
|
|
|
scrubResults = AZ::Failure<AZStd::string>("Didn't get called");
|
|
EntityCompositionRequestBus::BroadcastResult(scrubResults, &EntityCompositionRequestBus::Events::ScrubEntities, entities);
|
|
ASSERT_TRUE(scrubResults.IsSuccess());
|
|
|
|
resultForTestEntity = scrubResults.GetValue()[entities[0]->GetId()];
|
|
ASSERT_TRUE(resultForTestEntity.m_invalidatedComponents.empty());
|
|
|
|
// this should now succeed because the wool socks component is on the entity
|
|
testEntity->Activate();
|
|
ASSERT_EQ(testEntity->GetState(), Entity::State::Active);
|
|
|
|
delete testEntity;
|
|
}
|
|
} // namespace UnitTest
|