/* * Copyright (c) Contributors to the Open 3D Engine Project. * For complete copyright and license terms please see the LICENSE at the root of this distribution. * * SPDX-License-Identifier: Apache-2.0 OR MIT * */ #include #include #include "VegetationTest.h" #include "VegetationMocks.h" ////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace UnitTest { struct VegetationComponentTestsBasics : public VegetationComponentTests { void RegisterComponentDescriptors() override { m_app.RegisterComponentDescriptor(MockShapeServiceComponent::CreateDescriptor()); m_app.RegisterComponentDescriptor(MockVegetationAreaServiceComponent::CreateDescriptor()); m_app.RegisterComponentDescriptor(MockMeshServiceComponent::CreateDescriptor()); } template void CreateWith() { m_app.RegisterComponentDescriptor(Component::CreateDescriptor()); AZ::Entity entity; entity.CreateComponent(); entity.CreateComponent(); entity.Init(); ASSERT_TRUE(entity.GetState() == AZ::Entity::State::Init); entity.Activate(); ASSERT_TRUE(entity.GetState() == AZ::Entity::State::Active); entity.Deactivate(); ASSERT_TRUE(entity.GetState() == AZ::Entity::State::Init); } template bool IsComponentCompatible() { AZ::ComponentDescriptor::DependencyArrayType providedServicesA; ComponentA::GetProvidedServices(providedServicesA); AZ::ComponentDescriptor::DependencyArrayType incompatibleServicesB; ComponentB::GetIncompatibleServices(incompatibleServicesB); for (auto providedServiceA : providedServicesA) { for (auto incompatibleServiceB : incompatibleServicesB) { if (providedServiceA == incompatibleServiceB) { return false; } } } return true; } template bool AreComponentsCompatible() { return IsComponentCompatible() && IsComponentCompatible(); } bool BeginElementMinMaxTests( AZ::SerializeContext* sc, void* instance, const AZ::SerializeContext::ClassData* classData, const AZ::SerializeContext::ClassElement* classElement) { (void)instance; if (classElement) { // if we are a pointer, then we may be pointing to a derived type. if (classElement->m_flags & AZ::SerializeContext::ClassElement::FLG_POINTER) { // if dataAddress is a pointer in this case, cast it's value to a void* (or const void*) and dereference to get to the actual class. instance = *(void**)(instance); if (instance && classElement->m_azRtti) { AZ::Uuid actualClassId = classElement->m_azRtti->GetActualUuid(instance); if (actualClassId != classElement->m_typeId) { // we are pointing to derived type, adjust class data, uuid and pointer. classData = sc->FindClassData(actualClassId); if (classData) { instance = classElement->m_azRtti->Cast(instance, classData->m_azRtti->GetTypeId()); } } } } } //check editor data numeric elements for min/max attributes if (classElement && classElement->m_editData) { if (classElement->m_editData->m_elementId == AZ::Edit::UIHandlers::Default || classElement->m_editData->m_elementId == AZ::Edit::UIHandlers::Slider || classElement->m_editData->m_elementId == AZ::Edit::UIHandlers::SpinBox) { if (classElement->m_typeId == azrtti_typeid() || classElement->m_typeId == azrtti_typeid() || classElement->m_typeId == azrtti_typeid() || classElement->m_typeId == azrtti_typeid() || classElement->m_typeId == azrtti_typeid() || classElement->m_typeId == azrtti_typeid() || classElement->m_typeId == azrtti_typeid() || classElement->m_typeId == azrtti_typeid() || classElement->m_typeId == azrtti_typeid() || classElement->m_typeId == azrtti_typeid()) { EXPECT_TRUE(classElement->m_editData->FindAttribute(AZ::Edit::Attributes::Min) != nullptr); EXPECT_TRUE(classElement->m_editData->FindAttribute(AZ::Edit::Attributes::Max) != nullptr); } } } return true; } bool EndfElementMinMaxTests() { return true; } template void ValidateHasMinMaxRanges() { //create a serialize context with edit context enabled AZ::SerializeContext serializeContext(true, true); //must reflect AZ::Entity to register AZ::ComponentConfig so new serialize context asserts don't fail test AZ::Entity::Reflect(&serializeContext); //reflect and inspect the object T::Reflect(&serializeContext); T object; serializeContext.EnumerateObject(&object, AZStd::bind(&VegetationComponentTestsBasics::BeginElementMinMaxTests, this, &serializeContext, AZStd::placeholders::_1, AZStd::placeholders::_2, AZStd::placeholders::_3), AZStd::bind(&VegetationComponentTestsBasics::EndfElementMinMaxTests, this), AZ::SerializeContext::ENUM_ACCESS_FOR_READ); } }; TEST_F(VegetationComponentTestsBasics, VerifyCompatibility) { EXPECT_FALSE((AreComponentsCompatible())); EXPECT_FALSE((AreComponentsCompatible())); EXPECT_FALSE((AreComponentsCompatible())); EXPECT_FALSE((AreComponentsCompatible())); EXPECT_FALSE((AreComponentsCompatible())); EXPECT_FALSE((AreComponentsCompatible())); EXPECT_FALSE((AreComponentsCompatible())); EXPECT_FALSE((AreComponentsCompatible())); EXPECT_FALSE((AreComponentsCompatible())); EXPECT_FALSE((AreComponentsCompatible())); EXPECT_FALSE((AreComponentsCompatible())); EXPECT_FALSE((AreComponentsCompatible())); EXPECT_FALSE((AreComponentsCompatible())); EXPECT_FALSE((AreComponentsCompatible())); EXPECT_FALSE((AreComponentsCompatible())); EXPECT_FALSE((AreComponentsCompatible())); EXPECT_FALSE((AreComponentsCompatible())); EXPECT_FALSE((AreComponentsCompatible())); EXPECT_FALSE((AreComponentsCompatible())); EXPECT_FALSE((AreComponentsCompatible())); EXPECT_FALSE((AreComponentsCompatible())); EXPECT_FALSE((AreComponentsCompatible())); EXPECT_FALSE((AreComponentsCompatible())); EXPECT_FALSE((AreComponentsCompatible())); } TEST_F(VegetationComponentTestsBasics, CreateEach) { CreateWith(); CreateWith(); CreateWith(); CreateWith(); CreateWith(); CreateWith(); CreateWith(); CreateWith(); CreateWith(); CreateWith(); CreateWith(); CreateWith(); CreateWith(); CreateWith(); CreateWith(); CreateWith(); CreateWith(); CreateWith(); CreateWith(); } TEST_F(VegetationComponentTestsBasics, LevelSettingsComponent) { MockSystemConfigurationRequestBus mockSystemConfigurationRequestBus; struct TestLevelSettingsComponent : public Vegetation::LevelSettingsComponent { const Vegetation::LevelSettingsConfig& Config() const { return m_configuration; } }; // Provide a default configuration to the system component constexpr int defaultProcessTime = 7; Vegetation::InstanceSystemConfig defaultSystemConfig; defaultSystemConfig.m_maxInstanceProcessTimeMicroseconds = defaultProcessTime; mockSystemConfigurationRequestBus.UpdateSystemConfig(&defaultSystemConfig); const auto* instConfig = azrtti_cast(mockSystemConfigurationRequestBus.m_lastUpdated); ASSERT_TRUE(instConfig != nullptr); EXPECT_EQ(defaultProcessTime, instConfig->m_maxInstanceProcessTimeMicroseconds); { // Create a level settings component with a different config that should override the system component Vegetation::LevelSettingsComponent* component = nullptr; Vegetation::LevelSettingsConfig config; config.m_instanceSystemConfig.m_maxInstanceProcessTimeMicroseconds = 13; auto entity = CreateEntity(config, &component); const auto* instConfig2 = azrtti_cast(mockSystemConfigurationRequestBus.m_lastUpdated); ASSERT_TRUE(instConfig2 != nullptr); EXPECT_EQ(13, instConfig2->m_maxInstanceProcessTimeMicroseconds); TestLevelSettingsComponent* tester = static_cast(component); EXPECT_EQ(13, tester->Config().m_instanceSystemConfig.m_maxInstanceProcessTimeMicroseconds); } // Entity should be out of scope now (destroyed), so the default settings should be restored instConfig = azrtti_cast(mockSystemConfigurationRequestBus.m_lastUpdated); EXPECT_EQ(defaultProcessTime, instConfig->m_maxInstanceProcessTimeMicroseconds); } TEST_F(VegetationComponentTestsBasics, Components_HaveMinMaxRanges) { ValidateHasMinMaxRanges(); ValidateHasMinMaxRanges(); ValidateHasMinMaxRanges(); } } ////////////////////////////////////////////////////////////////////////// AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV);