/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ #include "Vegetation_precompiled.h" #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 #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::ES_INIT); entity.Activate(); ASSERT_TRUE(entity.GetState() == AZ::Entity::ES_ACTIVE); entity.Deactivate(); ASSERT_TRUE(entity.GetState() == AZ::Entity::ES_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())); 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(); 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* instConfig = azrtti_cast(mockSystemConfigurationRequestBus.m_lastUpdated); ASSERT_TRUE(instConfig != nullptr); EXPECT_EQ(13, instConfig->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, ReferenceShapeComponent_WithValidReference) { UnitTest::MockShape testShape; Vegetation::ReferenceShapeConfig config; config.m_shapeEntityId = testShape.m_entity.GetId(); Vegetation::ReferenceShapeComponent* component; auto entity = CreateEntity(config, &component); AZ::RandomDistributionType randomDistribution = AZ::RandomDistributionType::Normal; AZ::Vector3 randPos = AZ::Vector3::CreateOne(); LmbrCentral::ShapeComponentRequestsBus::EventResult(randPos, entity->GetId(), &LmbrCentral::ShapeComponentRequestsBus::Events::GenerateRandomPointInside, randomDistribution); EXPECT_EQ(AZ::Vector3::CreateZero(), randPos); testShape.m_aabb = AZ::Aabb::CreateFromPoint(AZ::Vector3(1.0f, 21.0f, 31.0f)); AZ::Aabb resultAABB; LmbrCentral::ShapeComponentRequestsBus::EventResult(resultAABB, entity->GetId(), &LmbrCentral::ShapeComponentRequestsBus::Events::GetEncompassingAabb); EXPECT_EQ(testShape.m_aabb, resultAABB); AZ::Crc32 resultCRC = {}; LmbrCentral::ShapeComponentRequestsBus::EventResult(resultCRC, entity->GetId(), &LmbrCentral::ShapeComponentRequestsBus::Events::GetShapeType); EXPECT_EQ(AZ_CRC("TestShape", 0x856ca50c), resultCRC); testShape.m_localBounds = AZ::Aabb::CreateFromPoint(AZ::Vector3(1.0f, 21.0f, 31.0f)); testShape.m_localTransform = AZ::Transform::CreateTranslation(testShape.m_localBounds.GetCenter()); AZ::Transform resultTransform = AZ::Transform::CreateIdentity(); AZ::Aabb resultBounds = AZ::Aabb::CreateNull(); LmbrCentral::ShapeComponentRequestsBus::Event(entity->GetId(), &LmbrCentral::ShapeComponentRequestsBus::Events::GetTransformAndLocalBounds, resultTransform, resultBounds); EXPECT_EQ(testShape.m_localTransform, resultTransform); EXPECT_EQ(testShape.m_localBounds, resultBounds); testShape.m_pointInside = true; bool resultPointInside = false; LmbrCentral::ShapeComponentRequestsBus::EventResult(resultPointInside, entity->GetId(), &LmbrCentral::ShapeComponentRequestsBus::Events::IsPointInside, AZ::Vector3::CreateZero()); EXPECT_EQ(testShape.m_pointInside, resultPointInside); testShape.m_distanceSquaredFromPoint = 456.0f; float resultdistanceSquaredFromPoint = 0; LmbrCentral::ShapeComponentRequestsBus::EventResult(resultdistanceSquaredFromPoint, entity->GetId(), &LmbrCentral::ShapeComponentRequestsBus::Events::DistanceSquaredFromPoint, AZ::Vector3::CreateZero()); EXPECT_EQ(testShape.m_distanceSquaredFromPoint, resultdistanceSquaredFromPoint); testShape.m_intersectRay = false; bool resultIntersectRay = false; LmbrCentral::ShapeComponentRequestsBus::EventResult(resultIntersectRay, entity->GetId(), &LmbrCentral::ShapeComponentRequestsBus::Events::IntersectRay, AZ::Vector3::CreateZero(), AZ::Vector3::CreateZero(), AZ::VectorFloat()); EXPECT_TRUE(testShape.m_intersectRay == resultIntersectRay); } TEST_F(VegetationComponentTestsBasics, ReferenceShapeComponent_WithInvalidReference) { Vegetation::ReferenceShapeConfig config; config.m_shapeEntityId = AZ::EntityId(); Vegetation::ReferenceShapeComponent* component; auto entity = CreateEntity(config, &component); AZ::RandomDistributionType randomDistribution = AZ::RandomDistributionType::Normal; AZ::Vector3 randPos = AZ::Vector3::CreateOne(); LmbrCentral::ShapeComponentRequestsBus::EventResult(randPos, entity->GetId(), &LmbrCentral::ShapeComponentRequestsBus::Events::GenerateRandomPointInside, randomDistribution); EXPECT_EQ(randPos, AZ::Vector3::CreateZero()); AZ::Aabb resultAABB; LmbrCentral::ShapeComponentRequestsBus::EventResult(resultAABB, entity->GetId(), &LmbrCentral::ShapeComponentRequestsBus::Events::GetEncompassingAabb); EXPECT_EQ(resultAABB, AZ::Aabb::CreateNull()); AZ::Crc32 resultCRC; LmbrCentral::ShapeComponentRequestsBus::EventResult(resultCRC, entity->GetId(), &LmbrCentral::ShapeComponentRequestsBus::Events::GetShapeType); EXPECT_EQ(resultCRC, AZ::Crc32(AZ::u32(0))); AZ::Transform resultTransform; AZ::Aabb resultBounds; LmbrCentral::ShapeComponentRequestsBus::Event(entity->GetId(), &LmbrCentral::ShapeComponentRequestsBus::Events::GetTransformAndLocalBounds, resultTransform, resultBounds); EXPECT_EQ(resultTransform, AZ::Transform::CreateIdentity()); EXPECT_EQ(resultBounds, AZ::Aabb::CreateNull()); bool resultPointInside = true; LmbrCentral::ShapeComponentRequestsBus::EventResult(resultPointInside, entity->GetId(), &LmbrCentral::ShapeComponentRequestsBus::Events::IsPointInside, AZ::Vector3::CreateZero()); EXPECT_EQ(resultPointInside, false); float resultdistanceSquaredFromPoint = 0; LmbrCentral::ShapeComponentRequestsBus::EventResult(resultdistanceSquaredFromPoint, entity->GetId(), &LmbrCentral::ShapeComponentRequestsBus::Events::DistanceSquaredFromPoint, AZ::Vector3::CreateZero()); EXPECT_EQ(resultdistanceSquaredFromPoint, FLT_MAX); bool resultIntersectRay = true; LmbrCentral::ShapeComponentRequestsBus::EventResult(resultIntersectRay, entity->GetId(), &LmbrCentral::ShapeComponentRequestsBus::Events::IntersectRay, AZ::Vector3::CreateZero(), AZ::Vector3::CreateZero(), AZ::VectorFloat()); EXPECT_EQ(resultIntersectRay, false); } TEST_F(VegetationComponentTestsBasics, Components_HaveMinMaxRanges) { ValidateHasMinMaxRanges(); ValidateHasMinMaxRanges(); ValidateHasMinMaxRanges(); } } ////////////////////////////////////////////////////////////////////////// AZ_UNIT_TEST_HOOK();