/* * 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 #include #include namespace UnitTest { enum class TestEnum { One = 1, Two = 2 }; } // give the enum values types namespace AZ { AZ_TYPE_INFO_SPECIALIZE(UnitTest::TestEnum, "{F8EBD52B-D805-4A47-82CA-41E1DC176BCD}") } namespace UnitTest { using Counter0 = CreationCounter<16, 0>; struct TypingStruct { AZ_TYPE_INFO(TypingStruct, "{89D2E524-9F90-49E9-8620-0DA8FF308222}") const char* m_stringValue = ""; }; class BehaviorClassTest : public ScopedAllocatorSetupFixture { public: void SetUp() override { Counter0::Reset(); m_context.Class("Counter0") ->Property("val", static_cast(&Counter0::val), nullptr); m_context.Class("TypingStruct") ->Property("stringValue", BehaviorValueGetter(&TypingStruct::m_stringValue), BehaviorValueSetter(&TypingStruct::m_stringValue)) ; m_counter0Class = m_context.m_typeToClassMap[azrtti_typeid()]; m_typingClass = m_context.m_typeToClassMap[azrtti_typeid()]; } AZ::BehaviorContext m_context; AZ::BehaviorClass* m_counter0Class; AZ::BehaviorClass* m_typingClass; }; class BehaviorContextTestFixture : public ScopedAllocatorSetupFixture { public: AZ::BehaviorContext m_behaviorContext; }; TEST_F(BehaviorClassTest, BehaviorClass_Typing_ConstCharStar) { auto findIter = m_typingClass->m_properties.find("stringValue"); EXPECT_TRUE(findIter != m_typingClass->m_properties.end()); if (findIter != m_typingClass->m_properties.end()) { EXPECT_EQ(AZ::BehaviorParameter::TR_STRING, findIter->second->m_getter->GetResult()->m_traits & AZ::BehaviorParameter::TR_STRING); } } TEST_F(BehaviorClassTest, BehaviorClass_Create_WasCreated) { EXPECT_EQ(0, Counter0::s_count); auto instance1 = m_counter0Class->Create(); EXPECT_TRUE(instance1.IsValid()); EXPECT_EQ(1, Counter0::s_count); EXPECT_EQ(0, Counter0::s_copied); EXPECT_EQ(0, Counter0::s_moved); m_counter0Class->Destroy(instance1); } TEST_F(BehaviorClassTest, BehaviorClass_CopyValid_WasCopied) { EXPECT_EQ(0, Counter0::s_count); auto instance1 = m_counter0Class->Create(); EXPECT_TRUE(instance1.IsValid()); EXPECT_EQ(1, Counter0::s_count); EXPECT_EQ(0, Counter0::s_copied); EXPECT_EQ(0, Counter0::s_moved); auto instance2 = m_counter0Class->Clone(instance1); EXPECT_TRUE(instance2.IsValid()); EXPECT_EQ(2, Counter0::s_count); EXPECT_EQ(1, Counter0::s_copied); EXPECT_EQ(0, Counter0::s_moved); m_counter0Class->Destroy(instance2); m_counter0Class->Destroy(instance1); } TEST_F(BehaviorClassTest, BehaviorClass_CopyInvalid_WasNoop) { EXPECT_EQ(0, Counter0::s_count); auto instance1 = m_counter0Class->Clone(AZ::BehaviorObject()); EXPECT_FALSE(instance1.IsValid()); EXPECT_EQ(0, Counter0::s_count); EXPECT_EQ(0, Counter0::s_copied); EXPECT_EQ(0, Counter0::s_moved); m_counter0Class->Destroy(instance1); } TEST_F(BehaviorClassTest, BehaviorClass_Move_WasMoved) { EXPECT_EQ(0, Counter0::s_count); auto instance1 = m_counter0Class->Create(); EXPECT_TRUE(instance1.IsValid()); EXPECT_EQ(1, Counter0::s_count); EXPECT_EQ(0, Counter0::s_copied); EXPECT_EQ(0, Counter0::s_moved); auto instance2 = m_counter0Class->Move(AZStd::move(instance1)); EXPECT_TRUE(instance2.IsValid()); EXPECT_EQ(1, Counter0::s_count); EXPECT_EQ(0, Counter0::s_copied); EXPECT_EQ(1, Counter0::s_moved); m_counter0Class->Destroy(instance2); } TEST_F(BehaviorClassTest, BehaviorClass_MoveInvalid_WasNoop) { EXPECT_EQ(0, Counter0::s_count); auto instance1 = m_counter0Class->Move(AZ::BehaviorObject()); EXPECT_FALSE(instance1.IsValid()); EXPECT_EQ(0, Counter0::s_count); EXPECT_EQ(0, Counter0::s_copied); EXPECT_EQ(0, Counter0::s_moved); m_counter0Class->Destroy(instance1); } class ClassWithConstMethod { public: AZ_TYPE_INFO(ClassWithConstMethod, "{39235130-3339-41F6-AC70-0D6EF6B5145D}"); void ConstMethod() const { } }; using BehaviorContextConstTest = AllocatorsFixture; TEST_F(BehaviorContextConstTest, BehaviorContext_BindConstMethods_Compiles) { AZ::BehaviorContext bc; bc.Class() ->Method("ConstMethod", &ClassWithConstMethod::ConstMethod) ; } class EBusWithConstEvent : public AZ::EBusTraits { public: virtual void ConstEvent() const = 0; }; using EBusWithConstEventBus = AZ::EBus; TEST_F(BehaviorContextConstTest, BehaviorContext_BindConstEvents_Compiles) { AZ::BehaviorContext bc; bc.EBus("EBusWithConstEventBus") ->Event("ConstEvent", &EBusWithConstEvent::ConstEvent) ; } void MethodAcceptingTemplate(const AZStd::string&) { } TEST_F(BehaviorContextTestFixture, OnDemandReflection_Unreflect_IsRemoved) { // Test reflecting with OnDemandReflection m_behaviorContext.Method("TestTemplatedOnDemandReflection", &MethodAcceptingTemplate); EXPECT_TRUE(m_behaviorContext.IsOnDemandTypeReflected(azrtti_typeid())); EXPECT_NE(m_behaviorContext.m_typeToClassMap.find(azrtti_typeid()), m_behaviorContext.m_typeToClassMap.end()); // Test unreflecting OnDemandReflection m_behaviorContext.EnableRemoveReflection(); m_behaviorContext.Method("TestTemplatedOnDemandReflection", &MethodAcceptingTemplate); m_behaviorContext.DisableRemoveReflection(); EXPECT_FALSE(m_behaviorContext.IsOnDemandTypeReflected(azrtti_typeid())); EXPECT_EQ(m_behaviorContext.m_typeToClassMap.find(azrtti_typeid()), m_behaviorContext.m_typeToClassMap.end()); } // Used for on demand reflection to pick up the vector and string types AZStd::string globalMethodContainers(const AZStd::vector& value) { return value[0]; } TEST_F(BehaviorContextTestFixture, ContainerMethods) { AZ::BehaviorContext behaviorContext; behaviorContext.Method("containerMethod", &globalMethodContainers); auto containerType = azrtti_typeid>(); EXPECT_TRUE(behaviorContext.IsOnDemandTypeReflected(containerType)); AZ::BehaviorMethod* insertMethod = nullptr; AZ::BehaviorMethod* sizeMethod = nullptr; AZ::BehaviorMethod* assignAtMethod = nullptr; const auto classIter(behaviorContext.m_typeToClassMap.find(containerType)); EXPECT_FALSE(classIter == behaviorContext.m_typeToClassMap.end()); const AZ::BehaviorClass* behaviorClass = classIter->second; if (behaviorClass) { auto methodIt = behaviorClass->m_methods.find("Insert"); EXPECT_TRUE(methodIt != behaviorClass->m_methods.end()); if (methodIt != behaviorClass->m_methods.end()) { insertMethod = methodIt->second; } methodIt = behaviorClass->m_methods.find("Size"); EXPECT_TRUE(methodIt != behaviorClass->m_methods.end()); if (methodIt != behaviorClass->m_methods.end()) { sizeMethod = methodIt->second; } methodIt = behaviorClass->m_methods.find("AssignAt"); EXPECT_TRUE(methodIt != behaviorClass->m_methods.end()); if (methodIt != behaviorClass->m_methods.end()) { assignAtMethod = methodIt->second; } } EXPECT_TRUE(insertMethod != nullptr); EXPECT_TRUE(sizeMethod != nullptr); EXPECT_TRUE(assignAtMethod != nullptr); if (insertMethod && sizeMethod && assignAtMethod) { const int MaxParameterCount = 40; AZStd::vector container; // Insert { AZStd::array params; AZ::BehaviorValueParameter* paramFirst(params.begin()); AZ::BehaviorValueParameter* paramIter = paramFirst; AZ::Outcome insertOutcome; AZ::BehaviorValueParameter result(&insertOutcome); paramIter->Set(&container); ++paramIter; // Index AZ::BehaviorValueParameter indexParameter; AZ::u64 index = 0; indexParameter.Set(&index); paramIter->Set(indexParameter); ++paramIter; // Value to insert AZ::BehaviorValueParameter strParameter; AZStd::string str = "Hello"; strParameter.Set(&str); paramIter->Set(strParameter); ++paramIter; insertMethod->Call(paramFirst, static_cast(params.size()), &result); EXPECT_FALSE(insertOutcome.IsSuccess()); } // Size { AZStd::array params; AZ::BehaviorValueParameter* paramFirst(params.begin()); AZ::BehaviorValueParameter* paramIter = paramFirst; paramIter->Set(&container); ++paramIter; int containerSize = 0; AZ::BehaviorValueParameter result(&containerSize); sizeMethod->Call(paramFirst, static_cast(params.size()), &result); int* sizePtr = result.GetAsUnsafe(); EXPECT_TRUE(sizePtr != nullptr); EXPECT_EQ(*sizePtr, 1); } // AssignAt { AZStd::array params; AZ::BehaviorValueParameter* paramFirst(params.begin()); AZ::BehaviorValueParameter* paramIter = paramFirst; paramIter->Set(&container); ++paramIter; // Index AZ::BehaviorValueParameter indexParameter; AZ::u64 index = 4; indexParameter.Set(&index); paramIter->Set(indexParameter); ++paramIter; // Value to insert AZ::BehaviorValueParameter strParameter; AZStd::string str = "Hello"; strParameter.Set(&str); paramIter->Set(strParameter); ++paramIter; assignAtMethod->Call(paramFirst, static_cast(params.size())); } // Size { AZStd::array params; AZ::BehaviorValueParameter* paramFirst(params.begin()); AZ::BehaviorValueParameter* paramIter = paramFirst; paramIter->Set(&container); ++paramIter; int containerSize = 0; AZ::BehaviorValueParameter result(&containerSize); sizeMethod->Call(paramFirst, static_cast(params.size()), &result); int* sizePtr = result.GetAsUnsafe(); EXPECT_TRUE(sizePtr != nullptr); EXPECT_EQ(*sizePtr, 5); } } } class EBusWithAZStdVectorEvent : public AZ::EBusTraits { public: virtual AZStd::string MoveContainer(const AZStd::vector&) = 0; virtual AZStd::pair ExtractPair(const AZStd::unordered_map&) = 0; }; using EBusWithAZStdVectorEventBus = AZ::EBus; class BehaviorEBusWithAZStdVectorHandler : public EBusWithAZStdVectorEventBus::Handler , public AZ::BehaviorEBusHandler { public: AZ_EBUS_BEHAVIOR_BINDER(BehaviorEBusWithAZStdVectorHandler, "{B372D229-AE1F-411D-882E-0984AA3DC6F7}", AZ::SystemAllocator , MoveContainer , ExtractPair ); // User code AZStd::string MoveContainer(const AZStd::vector& lvalueContainer) override { // you can get the index yourself or use the FN_xxx enum FN_OnEvent AZStd::string result{}; CallResult(result, FN_MoveContainer, lvalueContainer); return result; } AZStd::pair ExtractPair(const AZStd::unordered_map& stringMap) override { AZStd::pair result{}; CallResult(result, FN_ExtractPair, stringMap); return result; } }; TEST_F(BehaviorContextTestFixture, EBusHandlers_Events_OnDemandReflection_Parameters) { // Test reflecting with EBus handler with an event that accepts an AZStd::vector m_behaviorContext.EBus("EBusWithAZStdVectorEventBus") ->Handler(); // Validate that OnDemandReflection for function parameters works const AZ::Uuid vectorIntTypeid = AZ::AzTypeInfo>::Uuid(); auto vectorIntClassIt = m_behaviorContext.m_typeToClassMap.find(vectorIntTypeid); EXPECT_NE(m_behaviorContext.m_typeToClassMap.end(), vectorIntClassIt); EXPECT_TRUE(m_behaviorContext.IsOnDemandTypeReflected(vectorIntTypeid)); // Validate that OnDemandReflection for the return value works const AZ::Uuid stringTypeid = AZ::AzTypeInfo::Uuid(); auto stringClassIt = m_behaviorContext.m_typeToClassMap.find(stringTypeid); EXPECT_NE(m_behaviorContext.m_typeToClassMap.end(), stringClassIt); EXPECT_TRUE(m_behaviorContext.IsOnDemandTypeReflected(stringTypeid)); // Validate that OnDemandReflection works for all handler member functions const AZ::Uuid stringToStringMapTypeid = AZ::AzTypeInfo>::Uuid(); auto stringToStringMapClassIt = m_behaviorContext.m_typeToClassMap.find(stringToStringMapTypeid); EXPECT_NE(m_behaviorContext.m_typeToClassMap.end(), stringToStringMapClassIt); EXPECT_TRUE(m_behaviorContext.IsOnDemandTypeReflected(stringToStringMapTypeid)); const AZ::Uuid pairStringStringTypeid = AZ::AzTypeInfo>::Uuid(); auto pairStringsTringTypeid = m_behaviorContext.m_typeToClassMap.find(pairStringStringTypeid); EXPECT_NE(m_behaviorContext.m_typeToClassMap.end(), pairStringsTringTypeid); EXPECT_TRUE(m_behaviorContext.IsOnDemandTypeReflected(pairStringStringTypeid)); } void FuncWithAcceptsVectorWithPointerValueTypeByRef(AZStd::vector&) { } void FuncWithAcceptsVectorWithPointerValueTypeByConstRef(const AZStd::vector&) { } void FuncWithAcceptsVectorWithPointerValueTypeByPointer(AZStd::vector*) { } void FuncWithAcceptsVectorWithPointerValueTypeByConstPointer(const AZStd::vector*) { } void FuncWithAcceptsVectorWithPointerValueTypeByConst(const AZStd::vector) { } TEST_F(BehaviorContextTestFixture, MethodReflectionWithRefParam_DoesNotCauseAssert_WhenBoundToScriptContext) { m_behaviorContext.Method("MethodWithVectorParam", &FuncWithAcceptsVectorWithPointerValueTypeByRef); AZ::ScriptContext scriptContext; AZ_TEST_START_TRACE_SUPPRESSION; scriptContext.BindTo(&m_behaviorContext); AZ_TEST_STOP_TRACE_SUPPRESSION(0); } TEST_F(BehaviorContextTestFixture, MethodReflectionWithConstRefParam_DoesNotCauseAssert_WhenBoundToScriptContext) { m_behaviorContext.Method("MethodWithVectorParam", &FuncWithAcceptsVectorWithPointerValueTypeByConstRef); AZ::ScriptContext scriptContext; AZ_TEST_START_TRACE_SUPPRESSION; scriptContext.BindTo(&m_behaviorContext); AZ_TEST_STOP_TRACE_SUPPRESSION(0); } TEST_F(BehaviorContextTestFixture, MethodReflectionWithPointerParam_DoesNotCauseAssert_WhenBoundToScriptContext) { m_behaviorContext.Method("MethodWithVectorParam", &FuncWithAcceptsVectorWithPointerValueTypeByPointer); AZ::ScriptContext scriptContext; AZ_TEST_START_TRACE_SUPPRESSION; scriptContext.BindTo(&m_behaviorContext); AZ_TEST_STOP_TRACE_SUPPRESSION(0); } TEST_F(BehaviorContextTestFixture, MethodReflectionWithConstPointerParam_DoesNotCauseAssert_WhenBoundToScriptContext) { m_behaviorContext.Method("MethodWithVectorParam", &FuncWithAcceptsVectorWithPointerValueTypeByConstPointer); AZ::ScriptContext scriptContext; AZ_TEST_START_TRACE_SUPPRESSION; scriptContext.BindTo(&m_behaviorContext); AZ_TEST_STOP_TRACE_SUPPRESSION(0); } TEST_F(BehaviorContextTestFixture, MethodReflectionWithConstParam_DoesNotCauseAssert_WhenBoundToScriptContext) { m_behaviorContext.Method("MethodWithVectorParam", &FuncWithAcceptsVectorWithPointerValueTypeByConst); AZ::ScriptContext scriptContext; AZ_TEST_START_TRACE_SUPPRESSION; scriptContext.BindTo(&m_behaviorContext); AZ_TEST_STOP_TRACE_SUPPRESSION(0); } class ClassWithEnumClass { public: AZ_TYPE_INFO(ClassWithEnumClass, "{DF867F22-00B6-4D8B-9967-B17E3CBA6AFC}"); TestEnum m_testEnumValue = TestEnum::Two; }; TEST_F(BehaviorContextTestFixture, DISABLED_ClassWithEnumClass_CanAccessEnumClass_WhenBound) { m_behaviorContext.Class("ClassWithEnumClass") ->Property("TestEnumValue", BehaviorValueProperty(&ClassWithEnumClass::m_testEnumValue)) ; AZ::BehaviorClass* behaviorClass = m_behaviorContext.m_classes["ClassWithEnumClass"]; AZ::BehaviorProperty* behaviorProperty = behaviorClass->m_properties["TestEnumValue"]; AZ::BehaviorObject instance = behaviorClass->Create(); // read the property that stores a class enum value { TestEnum enumValue = TestEnum::One; EXPECT_TRUE(behaviorProperty->m_getter->InvokeResult(enumValue, instance)); EXPECT_EQ(TestEnum::Two, enumValue); } // now set the property to One and validate it { TestEnum enumValue = TestEnum::Two; EXPECT_TRUE(behaviorProperty->m_setter->Invoke(instance, TestEnum::One)); EXPECT_TRUE(behaviorProperty->m_getter->InvokeResult(enumValue, instance)); EXPECT_EQ(TestEnum::One, enumValue); } behaviorClass->Destroy(instance); } }