/* * 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 #include "PythonTraceMessageSink.h" #include "PythonTestingUtility.h" #include #include #include #include #include #include #include namespace UnitTest { ////////////////////////////////////////////////////////////////////////// // test class/struts struct PythonReflectionTestDoPrint { AZ_TYPE_INFO(PythonReflectionTestDoPrint, "{CA1146E1-A2DF-4AE3-A712-5333CE60D65C}"); static const char* DoTest(const char* label) { return "proxy_do_test"; } static void DoPrint(const char* msg) { AZ_TracePrintf("python", msg); } void Reflect(AZ::ReflectContext* context) { if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) { behaviorContext->Class() ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) ->Attribute(AZ::Script::Attributes::Module, "legacy.test") ->Method("do_test", DoTest, nullptr, "Hook to perform a test with the legacy Python hook handler.") ->Method("do_print", DoPrint, nullptr, "Hook to perform a test print action to a console.") ; } } }; struct PythonReflectionTestSimple { AZ_TYPE_INFO(PythonReflectionTestSimple, "{03277B3D-DEC2-4113-9FCA-D37D527FCE77}"); static void DoWork() { AZ_TracePrintf("python", "PythonReflectionTestSimple_DoWork"); } void Reflect(AZ::ReflectContext* context) { if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) { behaviorContext->Class() ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) //AZ::Script::Attributes::Module, "default" //< this is optional; if not there then the BC is placed in the "azlmbr.default" module ->Method("do_work", DoWork, nullptr, "Test do work.") ; } } }; struct PythonReflectionAnotherModule { AZ_TYPE_INFO(PythonReflectionAnotherModule, "{F1935F7F-6A22-4CA3-BEE7-A2F8E8D5D35F}"); static void DoWork() { AZ_TracePrintf("python", "PythonReflectionAnotherModule_DoWork"); } static const AZ::VectorFloat& GetValue(const PythonReflectionAnotherModule& module) { return module.m_vectorFloat; } static void SetValue(PythonReflectionAnotherModule& module, const AZ::VectorFloat& value) { module.m_vectorFloat = value; } bool CompareWithValue(const AZ::VectorFloat& value) const { return (m_vectorFloat.IsClose(value)); } AZ::VectorFloat m_vectorFloat = { 0.0 }; void Reflect(AZ::ReflectContext* context) { if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) { behaviorContext->Class() ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) ->Attribute(AZ::Script::Attributes::Module, "legacy.test.subtest") // static globals ->Method("do_work", &PythonReflectionAnotherModule::DoWork, nullptr, "Test do work.") ->Method("get_value", &PythonReflectionAnotherModule::GetValue, nullptr, "") ->Method("set_value", &PythonReflectionAnotherModule::SetValue, nullptr, "") // instance ->Method("compare_with_value", &PythonReflectionAnotherModule::CompareWithValue, nullptr, "") ; } } }; struct PythonReflectionContainerSimpleTypes { AZ_TYPE_INFO(PythonReflectionContainerSimpleTypes, "{378AD363-467D-4285-BE40-4D1CB1A09A19}"); AZStd::vector m_floatValues{ 1.0, 2.2, 3.3, 4.4 }; AZStd::vector& ReturnVectorOfFloats() { return m_floatValues; } void AcceptVectorOfFloats(const AZStd::vector& values) { m_floatValues.assign(values.begin(), values.end()); } AZStd::vector m_doubleValues{ 1.0, 2.2, 3.3, 4.4 }; AZStd::vector& ReturnVectorOfDoubles() { return m_doubleValues; } void AcceptVectorOfDoubles(const AZStd::vector& values) { m_doubleValues.assign(values.begin(), values.end()); } template struct VectorOf { AZStd::vector m_values; explicit VectorOf(std::initializer_list list) { m_values = list; } const AZStd::vector& ReturnValues() const { return m_values; } void AcceptValues(const AZStd::vector& values) { m_values.assign(values.begin(), values.end()); } }; VectorOf m_s8ValueValues { 4, 5, 6, 7 }; VectorOf m_u8ValueValues { 4, 5, 6, 7 }; VectorOf m_s16ValueValues { 4, 5, 6, 7 }; VectorOf m_u16ValueValues { 4, 5, 6, 7 }; VectorOf m_s32ValueValues { 4, 5, 6, 7 }; VectorOf m_u32ValueValues { 4, 5, 6, 7 }; VectorOf m_s64ValueValues { 4, 5, 6, 7 }; VectorOf m_u64ValueValues { 4, 5, 6, 7 }; VectorOf m_boolValueValues { true, false, false }; void Reflect(AZ::ReflectContext* context) { if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) { serializeContext->RegisterGenericType>(); serializeContext->RegisterGenericType>(); serializeContext->RegisterGenericType>(); serializeContext->RegisterGenericType>(); serializeContext->RegisterGenericType>(); serializeContext->RegisterGenericType>(); serializeContext->RegisterGenericType>(); serializeContext->RegisterGenericType>(); serializeContext->RegisterGenericType>(); serializeContext->RegisterGenericType>(); serializeContext->RegisterGenericType>(); serializeContext->RegisterGenericType>(); } if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) { behaviorContext->Class("PythonReflectionContainerSimpleTypes") ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) ->Attribute(AZ::Script::Attributes::Module, "test.containers") ->Method("return_vector_of_floats", &PythonReflectionContainerSimpleTypes::ReturnVectorOfFloats, nullptr, "") ->Method("accept_vector_of_floats", &PythonReflectionContainerSimpleTypes::AcceptVectorOfFloats, nullptr, "") ->Method("return_vector_of_doubles", &PythonReflectionContainerSimpleTypes::ReturnVectorOfDoubles, nullptr, "") ->Method("accept_vector_of_doubles", &PythonReflectionContainerSimpleTypes::AcceptVectorOfDoubles, nullptr, "") ->Property("vector_of_s8", [](PythonReflectionContainerSimpleTypes* self) { return self->m_s8ValueValues.ReturnValues(); }, [](PythonReflectionContainerSimpleTypes* self, const AZStd::vector& values) { return self->m_s8ValueValues.AcceptValues(values); }) ->Property("vector_of_u8", [](PythonReflectionContainerSimpleTypes* self) { return self->m_u8ValueValues.ReturnValues(); }, [](PythonReflectionContainerSimpleTypes* self, const AZStd::vector& values) { return self->m_u8ValueValues.AcceptValues(values); }) ->Property("vector_of_s16", [](PythonReflectionContainerSimpleTypes* self) { return self->m_s16ValueValues.ReturnValues(); }, [](PythonReflectionContainerSimpleTypes* self, const AZStd::vector& values) { return self->m_s16ValueValues.AcceptValues(values); }) ->Property("vector_of_u16", [](PythonReflectionContainerSimpleTypes* self) { return self->m_u16ValueValues.ReturnValues(); }, [](PythonReflectionContainerSimpleTypes* self, const AZStd::vector& values) { return self->m_u16ValueValues.AcceptValues(values); }) ->Property("vector_of_s32", [](PythonReflectionContainerSimpleTypes* self) { return self->m_s32ValueValues.ReturnValues(); }, [](PythonReflectionContainerSimpleTypes* self, const AZStd::vector& values) { return self->m_s32ValueValues.AcceptValues(values); }) ->Property("vector_of_u32", [](PythonReflectionContainerSimpleTypes* self) { return self->m_u32ValueValues.ReturnValues(); }, [](PythonReflectionContainerSimpleTypes* self, const AZStd::vector& values) { return self->m_u32ValueValues.AcceptValues(values); }) ->Property("vector_of_s64", [](PythonReflectionContainerSimpleTypes* self) { return self->m_s64ValueValues.ReturnValues(); }, [](PythonReflectionContainerSimpleTypes* self, const AZStd::vector& values) { return self->m_s64ValueValues.AcceptValues(values); }) ->Property("vector_of_u64", [](PythonReflectionContainerSimpleTypes* self) { return self->m_u64ValueValues.ReturnValues(); }, [](PythonReflectionContainerSimpleTypes* self, const AZStd::vector& values) { return self->m_u64ValueValues.AcceptValues(values); }) ->Property("vector_of_bool", [](PythonReflectionContainerSimpleTypes* self) { return self->m_boolValueValues.ReturnValues(); }, [](PythonReflectionContainerSimpleTypes* self, const AZStd::vector& values) { return self->m_boolValueValues.AcceptValues(values); }) ; } } }; struct PythonReflectionStringTypes { AZ_TYPE_INFO(PythonReflectionStringTypes, "{A6BF24DB-50E2-435B-A896-0192D24974B1}"); static void RawPointerIn(const char* value) { AZ_TracePrintf("python", value); } static const char* RawPointerOut() { return "PythonReflectStringTypes_RawStringOut"; } static void StringViewIn(AZStd::string_view value) { AZ_TracePrintf("python", "%.*s", static_cast(value.size()), value.data()); } static AZStd::string_view StringViewOut() { return { "PythonReflectStringTypes_StringViewOut" }; } static void AZStdStringIn(const AZStd::string& value) { AZ_TracePrintf("python", "%s", value.c_str()); } static AZStd::string AZStdStringOut() { return { "PythonReflectStringTypes_AZStdStringOut" }; } static AZStd::vector OutputStringList() { AZStd::vector listOfStrings; listOfStrings.push_back("one"); listOfStrings.push_back("two"); listOfStrings.push_back("three"); return listOfStrings; } static bool InputStringList(AZStd::vector input) { if (input.size() == 3 && input[0] == "1" && input[1] == "2" && input[2] == "3") { return true; } return false; } static AZStd::vector OutputStringViewList() { AZStd::vector listOfStrings; listOfStrings.push_back("one"); listOfStrings.push_back("two"); listOfStrings.push_back("three"); return listOfStrings; } static bool InputStringViewList(AZStd::vector input) { if (input.size() == 3 && input[0] == "a" && input[1] == "b" && input[2] == "c") { return true; } return false; } static AZStd::string OutputEmptyString() { return AZStd::string(""); } static bool InputEmptyString(AZStd::string emptyString) { return emptyString.empty(); } void Reflect(AZ::ReflectContext* context) { if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) { behaviorContext->Class("Strings") ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) ->Attribute(AZ::Script::Attributes::Module, "test.strings") ->Method("raw_pointer_in", RawPointerIn, nullptr, "") ->Method("raw_pointer_out", RawPointerOut, nullptr, "") ->Method("string_view_in", StringViewIn, nullptr, "") ->Method("string_view_out", StringViewOut, nullptr, "") ->Method("azstd_string_in", AZStdStringIn, nullptr, "") ->Method("azstd_string_out", AZStdStringOut, nullptr, "") ->Method("output_string_list", OutputStringList, nullptr, "") ->Method("input_string_list", InputStringList, nullptr, "") ->Method("output_empty_string", OutputEmptyString, nullptr, "") ->Method("input_empty_string", InputEmptyString, nullptr, "") ->Method("output_string_view_list", OutputStringViewList, nullptr, "") ->Method("input_string_view_list", InputStringViewList, nullptr, "") ; } } }; struct FakeEntityIdType final { AZ_TYPE_INFO(FakeEntityIdType, "{33FF7076-50AD-42E7-9DFF-19FA5026264A}"); public: static const AZ::u64 InvalidEntityId = 0x00000000FFFFFFFFull; explicit FakeEntityIdType(AZ::u64 id = InvalidEntityId) : m_id(id) {} explicit operator AZ::u64() const { return m_id; } bool IsValid() const { return m_id != InvalidEntityId; } void SetInvalid() { m_id = InvalidEntityId; } AZStd::string ToString() const { return AZStd::string::format("[%llu]", m_id); } AZ::u64 m_id; static void Reflect(AZ::ReflectContext* context) { if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) { behaviorContext->Class("FakeEntityId") ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) ->Attribute(AZ::Script::Attributes::Module, "entity") ->Method("IsValid", &FakeEntityIdType::IsValid) ->Method("ToString", &FakeEntityIdType::ToString) ; } } }; struct PythonReflectionComplexContainer { AZ_TYPE_INFO(PythonReflectionComplexContainer, "{A1935F7F-6A22-4CA3-BEE7-A2F8E8D5D35F}"); AZStd::vector SendListOfIds() const { AZStd::vector fakeIds; fakeIds.push_back(FakeEntityIdType{ 101 }); fakeIds.push_back(FakeEntityIdType{ 202 }); fakeIds.push_back(FakeEntityIdType{ 303 }); return fakeIds; } void Reflect(AZ::ReflectContext* context) { FakeEntityIdType::Reflect(context); if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) { serializeContext->RegisterGenericType>(); } if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) { behaviorContext->Class("ComplexContainer") ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) ->Attribute(AZ::Script::Attributes::Module, "test") ->Method("send_list_of_ids", &PythonReflectionComplexContainer::SendListOfIds) ; } } }; struct PythonReflectionAny { AZ_TYPE_INFO(PythonReflectionAny, "{1B6617E1-C259-48A4-A337-232782024B5D}"); AZ::s32 m_intValue = 0; AZStd::any m_anyValue; AZ::Data::AssetId m_assetId; PythonReflectionAny() { m_assetId.m_guid = AZ::Uuid::CreateRandom(); } void ReportMutate(const AZStd::any& value) const { if (value.is()) { AZ_TracePrintf("python", "MutateAny"); } else if (value.is()) { AZ_TracePrintf("python", "MutateAssetId"); } } void ReportAccess() const { if (m_anyValue.is()) { AZ_TracePrintf("python", "AccessAny"); } else if (m_anyValue.is()) { AZ_TracePrintf("python", "AccessAssetId"); } } void MutateAnyRef(const AZStd::any& value) { ReportMutate(value); m_anyValue = value; } const AZStd::any& AccessAnyRef() const { ReportAccess(); return m_anyValue; } void MutateAnyValue(AZStd::any value) { ReportMutate(value); m_anyValue = value; } AZStd::any AccessAnyValue() const { ReportAccess(); return m_anyValue; } void MutateAnyPointer(const AZStd::any* value) { ReportMutate(*value); m_anyValue = *value; } bool CompareAssetIds(const AZ::Data::AssetId& lhs, const AZ::Data::AssetId& rhs) const { return lhs == rhs; } AZStd::any m_anySimple; void MutateAnySimple(const AZStd::any& value) { if (value.is()) { AZ_TracePrintf("python", "MutateAnySimple_double"); } else if (value.is()) { AZ_TracePrintf("python", "MutateAnySimple_s64"); } else if (value.is()) { AZ_TracePrintf("python", "MutateAnySimple_bool"); } else if (value.is()) { AZ_TracePrintf("python", "MutateAnySimple_string_view"); } m_anySimple = value; } const AZStd::any& AccessAnySimple() const { if (m_anySimple.is()) { AZ_TracePrintf("python", "AccessAnySimple_double"); } else if (m_anySimple.is()) { AZ_TracePrintf("python", "AccessAnySimple_s64"); } else if (m_anySimple.is()) { AZ_TracePrintf("python", "AccessAnySimple_bool"); } else if (m_anySimple.is()) { AZ_TracePrintf("python", "AccessAnySimple_string_view"); } return m_anySimple; } void Reflect(AZ::ReflectContext* context) { if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) { behaviorContext->Class() ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) ->Attribute(AZ::Script::Attributes::Module, "test") ->Method("mutate_any_ref", &PythonReflectionAny::MutateAnyRef, nullptr, "Mutate any value ref.") ->Method("access_any_ref", &PythonReflectionAny::AccessAnyRef, nullptr, "Access any value ref.") ->Method("mutate_any_pointer", &PythonReflectionAny::MutateAnyPointer, nullptr, "Mutate any value ptr.") ->Method("mutate_any_value", &PythonReflectionAny::MutateAnyValue, nullptr, "Mutate any value by value.") ->Method("access_any_value", &PythonReflectionAny::AccessAnyValue, nullptr, "Access any value by value.") ->Property("theInt", BehaviorValueProperty(&PythonReflectionAny::m_intValue)) ->Property("theAsset", BehaviorValueProperty(&PythonReflectionAny::m_assetId)) ->Method("compare_asset_ids", &PythonReflectionAny::CompareAssetIds) ->Method("mutate_any_simple", &PythonReflectionAny::MutateAnySimple) ->Method("access_any_simple", &PythonReflectionAny::AccessAnySimple) ; } } }; ////////////////////////////////////////////////////////////////////////// // fixtures struct PythonReflectionComponentTests : public PythonTestingFixture { PythonTraceMessageSink m_testSink; void SetUp() override { PythonTestingFixture::SetUp(); PythonTestingFixture::RegisterComponentDescriptors(); } void TearDown() override { // clearing up memory m_testSink = PythonTraceMessageSink(); PythonTestingFixture::TearDown(); } }; TEST_F(PythonReflectionComponentTests, InstallingPythonReflectionComponent) { AZ::Entity e; e.CreateComponent(); e.CreateComponent(); e.CreateComponent(); e.Init(); EXPECT_EQ(AZ::Entity::State::ES_INIT, e.GetState()); e.Activate(); EXPECT_EQ(AZ::Entity::State::ES_ACTIVE, e.GetState()); SimulateEditorBecomingInitialized(); e.Deactivate(); } TEST_F(PythonReflectionComponentTests, MakeSureTheAzlmbrModuleExists) { AZ::Entity e; Activate(e); SimulateEditorBecomingInitialized(); { auto m = pybind11::module::import("azlmbr"); EXPECT_TRUE(m); } e.Deactivate(); } TEST_F(PythonReflectionComponentTests, GetProxyCommand) { enum class LogTypes { Skip = 0, GotProxyFromPython }; m_testSink.m_evaluateMessage = [](const char* window, const char* message) -> int { if (AzFramework::StringFunc::Equal(window, "python")) { if (AzFramework::StringFunc::Equal(message, "proxy_do_test")) { return static_cast(LogTypes::GotProxyFromPython); } } return static_cast(LogTypes::Skip); }; PythonReflectionTestDoPrint handler; handler.Reflect(m_app.GetBehaviorContext()); AZ::Entity e; Activate(e); SimulateEditorBecomingInitialized(); try { pybind11::exec(R"( import azlmbr.legacy.test as test v = test.PythonReflectionTestDoPrint_do_test('does_not_exist') test.PythonReflectionTestDoPrint_do_print(str(v)) )"); } catch (const std::exception& e) { AZ_Warning("UnitTest", false, "Failed on GetProxyCommand with %s", e.what()); FAIL(); } e.Deactivate(); EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast(LogTypes::GotProxyFromPython)]); } // a non-named 'editor' flagged BehaviorClass methods end up in the 'azlmbr.default' namespace TEST_F(PythonReflectionComponentTests, DefaultModule) { enum class LogTypes { Skip = 0, PythonReflectionTestSimple_DoWork }; m_testSink.m_evaluateMessage = [](const char* window, const char* message) -> int { if (AzFramework::StringFunc::Equal(window, "python")) { if (AzFramework::StringFunc::Equal(message, "PythonReflectionTestSimple_DoWork")) { return static_cast(LogTypes::PythonReflectionTestSimple_DoWork); } } return static_cast(LogTypes::Skip); }; PythonReflectionTestSimple handler; handler.Reflect(m_app.GetBehaviorContext()); AZ::Entity e; Activate(e); SimulateEditorBecomingInitialized(); try { pybind11::exec(R"( import azlmbr.default azlmbr.default.PythonReflectionTestSimple_do_work() )"); } catch (const std::exception& e) { AZ_Warning("UnitTest", false, "Failed on DefaultModule with %s", e.what()); FAIL(); } e.Deactivate(); EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast(LogTypes::PythonReflectionTestSimple_DoWork)]); } // at least 3 deep in a sub module thing like test.that.space TEST_F(PythonReflectionComponentTests, AtLeast3DeepModules) { enum class LogTypes { Skip = 0, PythonReflectionAnotherModule_DoWork, PythonReflectionTestSimple_DoWork, PythonReflection_DoPrint, }; m_testSink.m_evaluateMessage = [](const char* window, const char* message) -> int { if (AzFramework::StringFunc::Equal(window, "python")) { if (AzFramework::StringFunc::Equal(message, "PythonReflectionTestSimple_DoWork")) { return static_cast(LogTypes::PythonReflectionTestSimple_DoWork); } else if (AzFramework::StringFunc::Equal(message, "PythonReflectionAnotherModule_DoWork")) { return static_cast(LogTypes::PythonReflectionAnotherModule_DoWork); } else if (AzFramework::StringFunc::Equal(message, "PythonReflection_DoPrint")) { return static_cast(LogTypes::PythonReflection_DoPrint); } } return static_cast(LogTypes::Skip); }; PythonReflectionTestSimple pythonReflectionTestSimple; pythonReflectionTestSimple.Reflect(m_app.GetBehaviorContext()); PythonReflectionTestDoPrint pythonReflectionTestDoPrint; pythonReflectionTestDoPrint.Reflect(m_app.GetBehaviorContext()); PythonReflectionAnotherModule pythonReflectionAnotherModule; pythonReflectionAnotherModule.Reflect(m_app.GetBehaviorContext()); AZ::Entity e; Activate(e); SimulateEditorBecomingInitialized(); try { pybind11::exec(R"( import azlmbr.default import azlmbr.legacy.test import azlmbr.legacy.test.subtest azlmbr.default.PythonReflectionTestSimple_do_work() azlmbr.legacy.test.PythonReflectionTestDoPrint_do_print('PythonReflection_DoPrint') azlmbr.legacy.test.subtest.PythonReflectionAnotherModule_do_work() )"); } catch (const std::exception& e) { AZ_Warning("UnitTest", false, "Failed on DefaultModule with %s", e.what()); FAIL(); } e.Deactivate(); EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast(LogTypes::PythonReflectionAnotherModule_DoWork)]); EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast(LogTypes::PythonReflectionTestSimple_DoWork)]); EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast(LogTypes::PythonReflection_DoPrint)]); } // Access Mutate Any types TEST_F(PythonReflectionComponentTests, AccessMutateAny) { enum class LogTypes { Skip = 0, MutateAny, AccessAny, MutateAssetId, AccessAssetId }; m_testSink.m_evaluateMessage = [](const char* window, const char* message) -> int { if (AzFramework::StringFunc::Equal(window, "python")) { if (AzFramework::StringFunc::StartsWith(message, "MutateAny")) { return aznumeric_cast(LogTypes::MutateAny); } else if (AzFramework::StringFunc::StartsWith(message, "AccessAny")) { return aznumeric_cast(LogTypes::AccessAny); } else if (AzFramework::StringFunc::StartsWith(message, "MutateAssetId")) { return aznumeric_cast(LogTypes::MutateAssetId); } else if (AzFramework::StringFunc::StartsWith(message, "AccessAssetId")) { return aznumeric_cast(LogTypes::AccessAssetId); } } return aznumeric_cast(LogTypes::Skip); }; PythonReflectionAny pythonReflectionAny; pythonReflectionAny.Reflect(m_app.GetBehaviorContext()); AZ::Entity e; Activate(e); SimulateEditorBecomingInitialized(); try { pybind11::exec(R"( import azlmbr.test as test testObject = test.PythonReflectionAny() # by reference testObject.theInt = 10 reflectAny = test.PythonReflectionAny() reflectAny.mutate_any_ref(testObject) value = reflectAny.access_any_ref() if( value.theInt == 10 ): print ('AccessAny') # by value testObject.theInt = testObject.theInt + 1 reflectAny = test.PythonReflectionAny() reflectAny.mutate_any_value(testObject) value = reflectAny.access_any_value() if( value.theInt == 11 ): print ('AccessAny') # access and mutate using an AssetId theAsset = testObject.theAsset reflectAny = test.PythonReflectionAny() reflectAny.mutate_any_ref(theAsset) theAsset = reflectAny.access_any_ref() if( reflectAny.compare_asset_ids(theAsset,testObject.theAsset) ): print ('MutateAssetId') )"); } catch (const std::exception& e) { AZ_Warning("UnitTest", false, "Failed with %s", e.what()); FAIL(); } e.Deactivate(); EXPECT_EQ(2, m_testSink.m_evaluationMap[aznumeric_cast(LogTypes::MutateAny)]); EXPECT_EQ(4, m_testSink.m_evaluationMap[aznumeric_cast(LogTypes::AccessAny)]); EXPECT_EQ(2, m_testSink.m_evaluationMap[aznumeric_cast(LogTypes::MutateAssetId)]); EXPECT_EQ(1, m_testSink.m_evaluationMap[aznumeric_cast(LogTypes::AccessAssetId)]); } TEST_F(PythonReflectionComponentTests, AccessMutateAnySimpleTypes) { enum class LogTypes { Skip = 0, MutateAnySimple, AccessAnySimple, }; m_testSink.m_evaluateMessage = [](const char* window, const char* message) -> int { if (AzFramework::StringFunc::Equal(window, "python")) { if (AzFramework::StringFunc::StartsWith(message, "MutateAnySimple")) { return aznumeric_cast(LogTypes::MutateAnySimple); } else if (AzFramework::StringFunc::StartsWith(message, "AccessAnySimple")) { return aznumeric_cast(LogTypes::AccessAnySimple); } } return aznumeric_cast(LogTypes::Skip); }; PythonReflectionAny pythonReflectionAny; pythonReflectionAny.Reflect(m_app.GetBehaviorContext()); AZ::Entity e; Activate(e); SimulateEditorBecomingInitialized(); try { pybind11::exec(R"( import azlmbr.test as test import math # access mutate float reflectAny = test.PythonReflectionAny() reflectAny.mutate_any_simple(float(10.0)) if( math.floor(reflectAny.access_any_simple()) == 10.0 ): print ('AccessAnySimple_double') # access mutate int reflectAny = test.PythonReflectionAny() reflectAny.mutate_any_simple(int(11)) if( reflectAny.access_any_simple() == 11 ): print ('AccessAnySimple_s64') # access mutate bool reflectAny = test.PythonReflectionAny() reflectAny.mutate_any_simple(False) if( reflectAny.access_any_simple() is not True ): print ('AccessAnySimple_bool') # access mutate string reflectAny = test.PythonReflectionAny() reflectAny.mutate_any_simple('a string value') if( reflectAny.access_any_simple() == 'a string value' ): print ('AccessAnySimple_string_view') )"); } catch (const std::exception& e) { AZ_Warning("UnitTest", false, "Failed with %s", e.what()); FAIL(); } e.Deactivate(); EXPECT_EQ(4, m_testSink.m_evaluationMap[aznumeric_cast(LogTypes::MutateAnySimple)]); EXPECT_EQ(8, m_testSink.m_evaluationMap[aznumeric_cast(LogTypes::AccessAnySimple)]); } // test for the Python container types like List, Dictionary, Set, and Tuple TEST_F(PythonReflectionComponentTests, ContainerTypes) { enum class LogTypes { Skip = 0, ContainerTypes_Input, ContainerTypes_Output, }; m_testSink.m_evaluateMessage = [](const char* window, const char* message) -> int { if (AzFramework::StringFunc::Equal(window, "python")) { if (AzFramework::StringFunc::StartsWith(message, "ContainerTypes_Input")) { return static_cast(LogTypes::ContainerTypes_Input); } else if (AzFramework::StringFunc::StartsWith(message, "ContainerTypes_Output")) { return static_cast(LogTypes::ContainerTypes_Output); } } return static_cast(LogTypes::Skip); }; PythonReflectionContainerSimpleTypes PythonReflectionContainerSimpleTypes; PythonReflectionContainerSimpleTypes.Reflect(m_app.GetSerializeContext()); PythonReflectionContainerSimpleTypes.Reflect(m_app.GetBehaviorContext()); AZ::Entity e; Activate(e); SimulateEditorBecomingInitialized(); try { pybind11::exec(R"( import azlmbr.test.containers import azlmbr.object def real_number_list_equals(list1, list2): for a, b in zip(list1, list2): if abs(a-b) > 0.0001: return False return True def test_vector_of_reals(test, label, get, put, values): list = test.invoke(get) if (real_number_list_equals(values, list)): print ('ContainerTypes_Output{}'.format(label)) list.reverse() test.invoke(put, list) list = test.invoke(get) values.reverse() if (real_number_list_equals(values, list)): print ('ContainerTypes_Input{}'.format(label)) def test_vector_of(test, label, values): list = test.get_property('vector_of_{}'.format(label)) if (list == values): print ('ContainerTypes_Output{}'.format(label)) list.reverse() test.set_property('vector_of_{}'.format(label), list) list = test.get_property('vector_of_{}'.format(label)) values.reverse() if (list == values): print ('ContainerTypes_Input{}'.format(label)) test = azlmbr.object.create('PythonReflectionContainerSimpleTypes') test_vector_of_reals(test, 'doubles', 'return_vector_of_doubles', 'accept_vector_of_doubles', [ 1.0, 2.2, 3.3, 4.4 ]) test_vector_of_reals(test, 'floats', 'return_vector_of_floats', 'accept_vector_of_floats', [ 1.0, 2.2, 3.3, 4.4 ]) test_vector_of(test, 'bool', [True,False,False]) test_vector_of(test, 's8', [4,5,6,7]) test_vector_of(test, 'u8', [4,5,6,7]) test_vector_of(test, 's16', [4,5,6,7]) test_vector_of(test, 'u16', [4,5,6,7]) test_vector_of(test, 's32', [4,5,6,7]) test_vector_of(test, 'u32', [4,5,6,7]) test_vector_of(test, 's64', [4,5,6,7]) test_vector_of(test, 'u64', [4,5,6,7]) )"); } catch (const std::exception& e) { AZ_Warning("UnitTest", false, "Failed with Python exception of %s", e.what()); FAIL(); } e.Deactivate(); EXPECT_EQ(11, m_testSink.m_evaluationMap[static_cast(LogTypes::ContainerTypes_Input)]); EXPECT_EQ(11, m_testSink.m_evaluationMap[static_cast(LogTypes::ContainerTypes_Output)]); } // all accepted types tested with this test class struct PythonReflectionTypesTester { AZ_TYPE_INFO(PythonReflectionTestDoPrint, "{CA1146E2-A2DF-4AE3-A712-5333CE60D65C}"); static AZ::u32 s_returned; static AZ::u32 s_accepted; static AZ::u32 s_successCount; static AZStd::any s_theValue; template static T ReturnValue() { ++s_returned; return AZStd::any_cast(s_theValue); } template static void AcceptValue(T value) { s_theValue = value; ++s_accepted; } static void SignalSuccess() { ++s_successCount; } void Reflect(AZ::ReflectContext* context) { PythonReflectionTypesTester::s_accepted = 0; PythonReflectionTypesTester::s_returned = 0; PythonReflectionTypesTester::s_successCount = 0; PythonReflectionTypesTester::s_theValue = AZStd::any(); if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) { behaviorContext->Class("TypeTests") ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation) ->Attribute(AZ::Script::Attributes::Module, "test.types") ->Method("signal_success", &SignalSuccess, nullptr, "") ->Method("return_bool", &ReturnValue, nullptr, "") ->Method("accept_bool", &AcceptValue, nullptr, "") ->Method("return_char", &ReturnValue, nullptr, "") ->Method("accept_char", &AcceptValue, nullptr, "") ->Method("return_float", &ReturnValue, nullptr, "") ->Method("accept_float", &AcceptValue, nullptr, "") ->Method("return_double", &ReturnValue, nullptr, "") ->Method("accept_double", &AcceptValue, nullptr, "") ->Method("return_s8", &ReturnValue, nullptr, "") ->Method("accept_s8", &AcceptValue, nullptr, "") ->Method("return_u8", &ReturnValue, nullptr, "") ->Method("accept_u8", &AcceptValue, nullptr, "") ->Method("return_s16", &ReturnValue, nullptr, "") ->Method("accept_s16", &AcceptValue, nullptr, "") ->Method("return_u16", &ReturnValue, nullptr, "") ->Method("accept_u16", &AcceptValue, nullptr, "") ->Method("return_s32", &ReturnValue, nullptr, "") ->Method("accept_s32", &AcceptValue, nullptr, "") ->Method("return_u32", &ReturnValue, nullptr, "") ->Method("accept_u32", &AcceptValue, nullptr, "") ->Method("return_s64", &ReturnValue, nullptr, "") ->Method("accept_s64", &AcceptValue, nullptr, "") ->Method("return_u64", &ReturnValue, nullptr, "") ->Method("accept_u64", &AcceptValue, nullptr, "") ->Method("return_vectorFloat", &ReturnValue, nullptr, "") ->Method("accept_vectorFloat", &AcceptValue, nullptr, "") ->Method("return_vector4", &ReturnValue, nullptr, "") ->Method("accept_vector4", &AcceptValue, nullptr, "") ; } } }; AZ::u32 PythonReflectionTypesTester::s_accepted = 0; AZ::u32 PythonReflectionTypesTester::s_returned = 0; AZ::u32 PythonReflectionTypesTester::s_successCount = 0; AZStd::any PythonReflectionTypesTester::s_theValue; TEST_F(PythonReflectionComponentTests, PythonReflectionTypes) { PythonReflectionTypesTester pythonReflectionTypesTester; pythonReflectionTypesTester.Reflect(m_app.GetBehaviorContext()); AZ::Entity e; Activate(e); SimulateEditorBecomingInitialized(); try { int testCount = 0; pybind11::exec(R"(import azlmbr.test.types)"); pybind11::exec(R"( azlmbr.test.types.TypeTests_accept_bool(False) azlmbr.test.types.TypeTests_return_bool() )"); ++testCount; EXPECT_EQ(testCount, PythonReflectionTypesTester::s_accepted); EXPECT_EQ(testCount, PythonReflectionTypesTester::s_returned); pybind11::exec(R"( azlmbr.test.types.TypeTests_accept_char(chr(97)) azlmbr.test.types.TypeTests_return_char() )"); ++testCount; EXPECT_EQ(testCount, PythonReflectionTypesTester::s_accepted); EXPECT_EQ(testCount, PythonReflectionTypesTester::s_returned); pybind11::exec(R"( azlmbr.test.types.TypeTests_accept_float(0.01) azlmbr.test.types.TypeTests_return_float() )"); ++testCount; EXPECT_EQ(testCount, PythonReflectionTypesTester::s_accepted); EXPECT_EQ(testCount, PythonReflectionTypesTester::s_returned); pybind11::exec(R"( azlmbr.test.types.TypeTests_accept_double(0.1234) azlmbr.test.types.TypeTests_return_double() )"); ++testCount; EXPECT_EQ(testCount, PythonReflectionTypesTester::s_accepted); EXPECT_EQ(testCount, PythonReflectionTypesTester::s_returned); const char* intTypes[]{ "s8", "u8", "s16", "u16", "s32", "u32", "s64", "u64" }; for (const char* intType : intTypes) { AZStd::string intTypeTest = AZStd::string::format("azlmbr.test.types.TypeTests_accept_%s(1) \nazlmbr.test.types.TypeTests_return_%s() \n", intType, intType); pybind11::exec(intTypeTest.c_str()); ++testCount; EXPECT_EQ(testCount, PythonReflectionTypesTester::s_accepted); EXPECT_EQ(testCount, PythonReflectionTypesTester::s_returned); } pybind11::exec(R"( azlmbr.test.types.TypeTests_accept_vectorFloat(19.897) azlmbr.test.types.TypeTests_return_vectorFloat() )"); ++testCount; EXPECT_EQ(testCount, PythonReflectionTypesTester::s_accepted); EXPECT_EQ(testCount, PythonReflectionTypesTester::s_returned); } catch (const std::exception& e) { AZ_Warning("UnitTest", false, "Failed on with Python exception %s", e.what()); FAIL(); } e.Deactivate(); } TEST_F(PythonReflectionComponentTests, PythonReflectionTypeVectorFloat) { PythonReflectionTypesTester pythonReflectionTypesTester; pythonReflectionTypesTester.Reflect(m_app.GetBehaviorContext()); AZ::Entity e; Activate(e); SimulateEditorBecomingInitialized(); try { int testCount = 0; pybind11::exec(R"(import azlmbr.test.types)"); pybind11::exec(R"(import azlmbr.math)"); pybind11::exec(R"( azlmbr.test.types.TypeTests_accept_vectorFloat(19.897) azlmbr.test.types.TypeTests_return_vectorFloat() azlmbr.test.types.TypeTests_signal_success() )"); ++testCount; EXPECT_EQ(testCount, PythonReflectionTypesTester::s_accepted); EXPECT_EQ(testCount, PythonReflectionTypesTester::s_returned); EXPECT_EQ(testCount, PythonReflectionTypesTester::s_successCount); pybind11::exec(R"( testValue = 1.234567 azlmbr.test.types.TypeTests_accept_vectorFloat(testValue) result = azlmbr.test.types.TypeTests_return_vectorFloat() result = float(result) if (azlmbr.math.Math_IsClose(testValue, result)): azlmbr.test.types.TypeTests_signal_success() )"); ++testCount; EXPECT_EQ(testCount, PythonReflectionTypesTester::s_accepted); EXPECT_EQ(testCount, PythonReflectionTypesTester::s_returned); EXPECT_EQ(testCount, PythonReflectionTypesTester::s_successCount); // test returning a Vector4 pybind11::exec(R"( testVec4 = azlmbr.math.Vector4(0.1, 0.2, 0.3, 0.4) azlmbr.test.types.TypeTests_accept_vector4(testVec4) result = azlmbr.test.types.TypeTests_return_vector4() resultX = float(result.x) testX = float(testVec4.x) if (azlmbr.math.Math_IsClose(resultX, testX)): azlmbr.test.types.TypeTests_signal_success() )"); ++testCount; EXPECT_EQ(testCount, PythonReflectionTypesTester::s_accepted); EXPECT_EQ(testCount, PythonReflectionTypesTester::s_returned); EXPECT_EQ(testCount, PythonReflectionTypesTester::s_successCount); // test casting VectorFloat members pybind11::exec(R"( testVec4 = azlmbr.math.Vector4(1.1, 2.2, 3.3, 4.4) testX = int(float(testVec4.x)) testY = int(float(testVec4.y)) if (testX == 1 and testY == 2): azlmbr.test.types.TypeTests_signal_success() )"); ++testCount; EXPECT_EQ(testCount, PythonReflectionTypesTester::s_successCount); } catch (const std::exception& e) { AZ_Warning("UnitTest", false, "Failed on with Python exception %s", e.what()); FAIL(); } e.Deactivate(); } TEST_F(PythonReflectionComponentTests, PythonReflectStringTypes) { if (AZ::SerializeContext* serializeContext = m_app.GetSerializeContext()) { serializeContext->RegisterGenericType>(); serializeContext->RegisterGenericType>(); } PythonReflectionStringTypes pythonReflectionStringTypes; pythonReflectionStringTypes.Reflect(m_app.GetBehaviorContext()); AZ::Entity e; Activate(e); SimulateEditorBecomingInitialized(); enum class LogTypes { Skip = 0, PythonReflectStringTypes_RawStringIn, PythonReflectStringTypes_RawStringOut, PythonReflectStringTypes_StringViewIn, PythonReflectStringTypes_StringViewOut, PythonReflectStringTypes_AZStdStringIn, PythonReflectStringTypes_AZStdStringOut, PythonReflectStringTypes_AZStdVectorStringOut, PythonReflectStringTypes_AZStdVectorStringIn, PythonReflectStringTypes_EmptyStringOut, PythonReflectStringTypes_EmptyStringIn, PythonReflectStringTypes_AZStdVectorStringViewOut, PythonReflectStringTypes_AZStdVectorStringViewIn }; m_testSink.m_evaluateMessage = [](const char* window, const char* message) -> int { if (AzFramework::StringFunc::Equal(window, "python")) { if (AzFramework::StringFunc::Equal(message, "PythonReflectStringTypes_RawStringIn")) { return static_cast(LogTypes::PythonReflectStringTypes_RawStringIn); } else if (AzFramework::StringFunc::Equal(message, "PythonReflectStringTypes_RawStringOut")) { return static_cast(LogTypes::PythonReflectStringTypes_RawStringOut); } else if (AzFramework::StringFunc::Equal(message, "PythonReflectStringTypes_StringViewIn")) { return static_cast(LogTypes::PythonReflectStringTypes_StringViewIn); } else if (AzFramework::StringFunc::Equal(message, "PythonReflectStringTypes_StringViewOut")) { return static_cast(LogTypes::PythonReflectStringTypes_StringViewOut); } else if (AzFramework::StringFunc::Equal(message, "PythonReflectStringTypes_AZStdStringIn")) { return static_cast(LogTypes::PythonReflectStringTypes_AZStdStringIn); } else if (AzFramework::StringFunc::Equal(message, "PythonReflectStringTypes_AZStdStringOut")) { return static_cast(LogTypes::PythonReflectStringTypes_AZStdStringOut); } else if (AzFramework::StringFunc::Equal(message, "PythonReflectStringTypes_AZStdVectorStringOut")) { return static_cast(LogTypes::PythonReflectStringTypes_AZStdVectorStringOut); } else if (AzFramework::StringFunc::Equal(message, "PythonReflectStringTypes_AZStdVectorStringIn")) { return static_cast(LogTypes::PythonReflectStringTypes_AZStdVectorStringIn); } else if (AzFramework::StringFunc::Equal(message, "PythonReflectStringTypes_EmptyStringOut")) { return static_cast(LogTypes::PythonReflectStringTypes_EmptyStringOut); } else if (AzFramework::StringFunc::Equal(message, "PythonReflectStringTypes_EmptyStringIn")) { return static_cast(LogTypes::PythonReflectStringTypes_EmptyStringIn); } else if (AzFramework::StringFunc::Equal(message, "PythonReflectStringTypes_AZStdVectorStringViewOut")) { return static_cast(LogTypes::PythonReflectStringTypes_AZStdVectorStringViewOut); } else if (AzFramework::StringFunc::Equal(message, "PythonReflectStringTypes_AZStdVectorStringViewIn")) { return static_cast(LogTypes::PythonReflectStringTypes_AZStdVectorStringViewIn); } } return static_cast(LogTypes::Skip); }; try { pybind11::exec(R"( import azlmbr.test.strings azlmbr.test.strings.Strings_raw_pointer_in('PythonReflectStringTypes_RawStringIn') print (azlmbr.test.strings.Strings_raw_pointer_out()) azlmbr.test.strings.Strings_string_view_in('PythonReflectStringTypes_StringViewIn') print (azlmbr.test.strings.Strings_string_view_out()) azlmbr.test.strings.Strings_azstd_string_in('PythonReflectStringTypes_AZStdStringIn') print (azlmbr.test.strings.Strings_azstd_string_out()) stringList = azlmbr.test.strings.Strings_output_string_list() if (stringList[0] == 'one' and stringList[1] == 'two' and stringList[2] == 'three'): print ('PythonReflectStringTypes_AZStdVectorStringOut') newStringList = ['1','2','3'] if (azlmbr.test.strings.Strings_input_string_list(newStringList) == True): print ('PythonReflectStringTypes_AZStdVectorStringIn') stringList = azlmbr.test.strings.Strings_output_string_view_list() if (stringList[0] == 'one' and stringList[1] == 'two' and stringList[2] == 'three'): print ('PythonReflectStringTypes_AZStdVectorStringViewOut') newStringList = ['a','b','c'] if (azlmbr.test.strings.Strings_input_string_view_list(newStringList) == True): print ('PythonReflectStringTypes_AZStdVectorStringViewIn') emptyString = azlmbr.test.strings.Strings_output_empty_string() if (isinstance(emptyString, str) and len(emptyString) == 0): print ('PythonReflectStringTypes_EmptyStringOut') emptyString = '' if (azlmbr.test.strings.Strings_input_empty_string(emptyString)): print ('PythonReflectStringTypes_EmptyStringIn') )"); } catch (const std::exception& e) { AZ_Warning("UnitTest", false, "Failed on with Python exception %s", e.what()); FAIL(); } e.Deactivate(); EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast(LogTypes::PythonReflectStringTypes_RawStringIn)]); EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast(LogTypes::PythonReflectStringTypes_RawStringOut)]); EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast(LogTypes::PythonReflectStringTypes_StringViewIn)]); EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast(LogTypes::PythonReflectStringTypes_StringViewOut)]); EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast(LogTypes::PythonReflectStringTypes_AZStdStringIn)]); EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast(LogTypes::PythonReflectStringTypes_AZStdStringOut)]); EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast(LogTypes::PythonReflectStringTypes_AZStdVectorStringOut)]); EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast(LogTypes::PythonReflectStringTypes_AZStdVectorStringIn)]); EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast(LogTypes::PythonReflectStringTypes_AZStdVectorStringViewOut)]); EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast(LogTypes::PythonReflectStringTypes_AZStdVectorStringViewIn)]); EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast(LogTypes::PythonReflectStringTypes_EmptyStringOut)]); EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast(LogTypes::PythonReflectStringTypes_EmptyStringIn)]); } TEST_F(PythonReflectionComponentTests, MathReflectionTests) { enum class LogTypes { Skip = 0, MathColor, MathStaticMembers }; m_testSink.m_evaluateMessage = [](const char* window, const char* message) -> int { if (AzFramework::StringFunc::Equal(window, "python")) { if (AzFramework::StringFunc::Equal(message, "MathColor")) { return static_cast(LogTypes::MathColor); } else if (AzFramework::StringFunc::Equal(message, "MathStaticMembers")) { return static_cast(LogTypes::MathStaticMembers); } } return static_cast(LogTypes::Skip); }; PythonReflectionAnotherModule pythonReflectionAnotherModule; pythonReflectionAnotherModule.Reflect(m_app.GetBehaviorContext()); AZ::Entity e; Activate(e); SimulateEditorBecomingInitialized(); try { pybind11::exec(R"( import azlmbr.math as math import azlmbr.object # testing math type Color color = azlmbr.object.create('Color') if( color is not None ): print ('MathColor') color = azlmbr.object.construct('Color', 0.15, 0.25, 0.35, 0.45) if( color is not None ): print ('MathColor') if( math.Math_IsClose(color.r, 0.15) == True): print ('MathColor') if( math.Math_IsClose(color.g, 0.25) == True): print ('MathColor') if( math.Math_IsClose(color.b, 0.35) == True): print ('MathColor') if( math.Math_IsClose(color.a, 0.45) == True): print ('MathColor') color.r = 0.51 color.g = 0.52 color.b = 0.53 color.a = 0.54 if( math.Math_IsClose(color.r, 0.51) == True): print ('MathColor') if( math.Math_IsClose(color.g, 0.52) == True): print ('MathColor') if( math.Math_IsClose(color.b, 0.53) == True): print ('MathColor') if( math.Math_IsClose(color.a, 0.54) == True): print ('MathColor') # testing a math type member like function set import azlmbr.legacy.test.subtest as subtest tester = subtest.PythonReflectionAnotherModule() vectorFloatLhs = tester.get_value() + 1.0 tester.set_value(vectorFloatLhs) if( tester.compare_with_value(vectorFloatLhs) ): print ('MathStaticMembers') # testing the Vector3 math type member like functions vec3 = azlmbr.object.create('Vector3') vec3.x = 0.0 vec3.y = 0.0 vec3.z = 0.0 if( vec3.ToString() == '(x=0.0000000,y=0.0000000,z=0.0000000)'): print ('MathStaticMembers') # testing the Uuid math type member like functions uuidString = '{E866B520-D667-48A2-82F6-6AEBE1EC9C58}' uuid = azlmbr.math.Uuid_CreateString(uuidString, 0) if( uuid.ToString() == uuidString): print ('MathStaticMembers') )"); } catch (const std::exception& e) { AZStd::string failReason = AZStd::string::format("Failed on DefaultModule with %s", e.what()); GTEST_FATAL_FAILURE_(failReason.c_str()); } e.Deactivate(); EXPECT_EQ(10, m_testSink.m_evaluationMap[static_cast(LogTypes::MathColor)]); EXPECT_EQ(3, m_testSink.m_evaluationMap[static_cast(LogTypes::MathStaticMembers)]); } TEST_F(PythonReflectionComponentTests, ComplexContainers) { PythonReflectionComplexContainer pythonReflectionComplexContainer; pythonReflectionComplexContainer.Reflect(m_app.GetBehaviorContext()); pythonReflectionComplexContainer.Reflect(m_app.GetSerializeContext()); enum class LogTypes { Skip = 0, GotListOfIds }; m_testSink.m_evaluateMessage = [](const char* window, const char* message) -> int { if (AzFramework::StringFunc::Equal(window, "python")) { if (AzFramework::StringFunc::Equal(message, "GotListOfIds")) { return static_cast(LogTypes::GotListOfIds); } } return static_cast(LogTypes::Skip); }; AZ::Entity e; Activate(e); SimulateEditorBecomingInitialized(); try { pybind11::exec(R"( import azlmbr.test import azlmbr.object container = azlmbr.object.create('ComplexContainer') entityIdList = container.send_list_of_ids() if( len(entityIdList) == 3): print ('GotListOfIds') if( entityIdList[0].ToString() == '[101]'): print ('GotListOfIds') if( entityIdList[1].ToString() == '[202]'): print ('GotListOfIds') if( entityIdList[2].ToString() == '[303]'): print ('GotListOfIds') )"); } catch (const std::exception& e) { AZ_Warning("UnitTest", false, "Failed on DefaultModule with %s", e.what()); FAIL(); } e.Deactivate(); EXPECT_EQ(4, m_testSink.m_evaluationMap[static_cast(LogTypes::GotListOfIds)]); } TEST_F(PythonReflectionComponentTests, ProjectPaths) { enum class LogTypes { Skip = 0, EngrootIs, DevrootIs, pathResolvedTo, }; m_testSink.m_evaluateMessage = [](const char* window, const char* message) -> int { if (AzFramework::StringFunc::Equal(window, "python")) { AZStd::string_view m(message); if (AzFramework::StringFunc::StartsWith(message, "engroot is ")) { return static_cast(LogTypes::EngrootIs); } else if (AzFramework::StringFunc::StartsWith(message, "devroot is ")) { return static_cast(LogTypes::DevrootIs); } else if (AzFramework::StringFunc::StartsWith(message, "path resolved to ")) { return static_cast(LogTypes::pathResolvedTo); } } return static_cast(LogTypes::Skip); }; AZ::Entity e; e.CreateComponent(); e.CreateComponent(); e.CreateComponent(); e.Init(); e.Activate(); SimulateEditorBecomingInitialized(); try { pybind11::exec(R"( import azlmbr.paths if (len(azlmbr.paths.engroot) != 0): print ('engroot is {}'.format(azlmbr.paths.engroot)) if (len(azlmbr.paths.devroot) != 0): print ('devroot is {}'.format(azlmbr.paths.devroot)) path = azlmbr.paths.resolve_path('@engroot@/engineassets/texturemsg/defaultsolids.mtl') if (path.find('@engroot@') == -1): print ('path resolved to {}'.format(path)) )"); } catch (const std::exception& e) { AZ_Warning("UnitTest", false, "Failed on with Python exception: %s", e.what()); FAIL(); } e.Deactivate(); EXPECT_EQ(m_testSink.m_evaluationMap[(int)LogTypes::EngrootIs], 1); EXPECT_EQ(m_testSink.m_evaluationMap[(int)LogTypes::DevrootIs], 1); EXPECT_EQ(m_testSink.m_evaluationMap[(int)LogTypes::pathResolvedTo], 1); } }