/* * 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 "PythonPairTests.h" namespace UnitTest { ////////////////////////////////////////////////////////////////////////// // test class/structs struct PythonReflectionPairTypes { AZ_TYPE_INFO(PythonReflectionPairTypes, "{037C067F-7A03-47BE-A30E-124D8157EDA2}"); template struct PairOf { using PairType = AZStd::pair; PairType m_pair; explicit PairOf(const PairType& pair) { m_pair = pair; } explicit PairOf(const K& k, const V& v) { m_pair = PairType(k, v); } const PairType& ReturnPair() const { return m_pair; } void AcceptPair(const PairType& other) { m_pair = other; } void RegisterGenericType(AZ::SerializeContext& serializeContext) { serializeContext.RegisterGenericType(); } }; PairOf m_pairOfBoolToBool { false, true }; PairOf m_pairOfu8tou32 {1, 4}; PairOf m_pairOfu16toFloat {1, 0.4f}; PairOf m_pairOfStringTos32 {"1", -4}; PairOf m_pairOfStringToString {"one", "foo"}; PairOf m_pairOfStringToCustomType{ "foo", MyCustomType() }; void Reflect(AZ::ReflectContext* context) { if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) { m_pairOfBoolToBool.RegisterGenericType(*serializeContext); m_pairOfu8tou32.RegisterGenericType(*serializeContext); m_pairOfu16toFloat.RegisterGenericType(*serializeContext); m_pairOfStringTos32.RegisterGenericType(*serializeContext); m_pairOfStringToString.RegisterGenericType(*serializeContext); m_pairOfStringToCustomType.RegisterGenericType(*serializeContext); } 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.pair") ->Method("return_pair_of_boolToBool", [](PythonReflectionPairTypes* self) { return self->m_pairOfBoolToBool.ReturnPair(); }, nullptr, "") ->Method("accept_pair_of_boolToBool", [](PythonReflectionPairTypes* self, const PairOf::PairType& pair) { self->m_pairOfBoolToBool.AcceptPair(pair); }, nullptr, "") ->Method("return_pair_of_u8u32", [](PythonReflectionPairTypes* self) { return self->m_pairOfu8tou32.ReturnPair(); }, nullptr, "") ->Method("accept_pair_of_u8u32", [](PythonReflectionPairTypes* self, const PairOf::PairType& pair) { self->m_pairOfu8tou32.AcceptPair(pair); }, nullptr, "") ->Method("return_pair_of_u16toFloat", [](PythonReflectionPairTypes* self) { return self->m_pairOfu16toFloat.ReturnPair(); }, nullptr, "") ->Method("accept_pair_of_u16toFloat", [](PythonReflectionPairTypes* self, const PairOf::PairType& pair) { self->m_pairOfu16toFloat.AcceptPair(pair); }, nullptr, "") ->Method("return_pair_of_stringTos32", [](PythonReflectionPairTypes* self) { return self->m_pairOfStringTos32.ReturnPair(); }, nullptr, "") ->Method("accept_pair_of_stringTos32", [](PythonReflectionPairTypes* self, const PairOf::PairType& pair) { self->m_pairOfStringTos32.AcceptPair(pair); }, nullptr, "") ->Method("return_pair_of_stringToString", [](PythonReflectionPairTypes* self) { return self->m_pairOfStringToString.ReturnPair(); }, nullptr, "") ->Method("accept_pair_of_stringToString", [](PythonReflectionPairTypes* self, const PairOf::PairType& pair) { self->m_pairOfStringToString.AcceptPair(pair); }, nullptr, "") ->Method("return_pair_of_stringToCustomType", [](PythonReflectionPairTypes* self) { return self->m_pairOfStringToCustomType.ReturnPair(); }, nullptr, "") ->Method("accept_pair_of_stringToCustomType", [](PythonReflectionPairTypes* self, const PairOf::PairType& pair) { self->m_pairOfStringToCustomType.AcceptPair(pair); }, nullptr, "") ; } } }; ////////////////////////////////////////////////////////////////////////// // fixtures struct PythonReflectionPairTests : 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(PythonReflectionPairTests, SimpleTypes_Constructed) { enum class LogTypes { Skip = 0, PairTypeTest_ConstructBoolDefault, PairTypeTest_ConstructBoolParams, PairTypeTest_UseConstructed }; m_testSink.m_evaluateMessage = [](const char* window, const char* message) -> int { if (AzFramework::StringFunc::Equal(window, "python")) { if (AzFramework::StringFunc::StartsWith(message, "PairTypeTest_ConstructBoolDefault")) { return static_cast(LogTypes::PairTypeTest_ConstructBoolDefault); } else if (AzFramework::StringFunc::StartsWith(message, "PairTypeTest_ConstructBoolParams")) { return static_cast(LogTypes::PairTypeTest_ConstructBoolParams); } else if (AzFramework::StringFunc::StartsWith(message, "PairTypeTest_UseConstructed")) { return static_cast(LogTypes::PairTypeTest_UseConstructed); } } return static_cast(LogTypes::Skip); }; MyCustomType::Reflect(m_app.GetSerializeContext()); MyCustomType::Reflect(m_app.GetBehaviorContext()); PythonReflectionPairTypes pythonReflectionPairTypes; pythonReflectionPairTypes.Reflect(m_app.GetSerializeContext()); pythonReflectionPairTypes.Reflect(m_app.GetBehaviorContext()); AZ::Entity e; Activate(e); SimulateEditorBecomingInitialized(); try { pybind11::exec(R"( import azlmbr.test.pair import azlmbr.object import azlmbr.std test = azlmbr.object.create('PythonReflectionPairTypes') test_pair = azlmbr.object.create('AZStd::pair') if (test_pair): print ('PairTypeTest_ConstructBoolDefault') test_pair = azlmbr.object.construct('AZStd::pair', True, False) if (test_pair and test_pair.first == True and test_pair.second == False): print ('PairTypeTest_ConstructBoolParams') test_pair.first = False test_pair.second = True test.accept_pair_of_boolToBool(test_pair) result = test.return_pair_of_boolToBool() if (len(result) == 2 and result[0] == False and result[1] == True): print ('PairTypeTest_UseConstructed') )"); } catch (const std::exception& ex) { AZ_Warning("UnitTest", false, "Failed with Python exception of %s", ex.what()); FAIL(); } e.Deactivate(); EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast(LogTypes::PairTypeTest_ConstructBoolDefault)]); EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast(LogTypes::PairTypeTest_ConstructBoolParams)]); EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast(LogTypes::PairTypeTest_UseConstructed)]); } TEST_F(PythonReflectionPairTests, SimpleTypes_ConvertedCorrectly) { enum class LogTypes { Skip = 0, PairTypeTest_Input, PairTypeTest_Output, }; m_testSink.m_evaluateMessage = [](const char* window, const char* message) -> int { if (AzFramework::StringFunc::Equal(window, "python")) { if (AzFramework::StringFunc::StartsWith(message, "PairTypeTest_Input")) { return static_cast(LogTypes::PairTypeTest_Input); } else if (AzFramework::StringFunc::StartsWith(message, "PairTypeTest_Output")) { return static_cast(LogTypes::PairTypeTest_Output); } } return static_cast(LogTypes::Skip); }; MyCustomType::Reflect(m_app.GetSerializeContext()); MyCustomType::Reflect(m_app.GetBehaviorContext()); PythonReflectionPairTypes pythonReflectionPairTypes; pythonReflectionPairTypes.Reflect(m_app.GetSerializeContext()); pythonReflectionPairTypes.Reflect(m_app.GetBehaviorContext()); AZ::Entity e; Activate(e); SimulateEditorBecomingInitialized(); try { pybind11::exec(R"( import azlmbr.test.pair import azlmbr.object import azlmbr.std test = azlmbr.object.create('PythonReflectionPairTypes') result = test.return_pair_of_u8u32() if (len(result) == 2): print ('PairTypeTest_Output_u8u32') test.accept_pair_of_u8u32([42, 0]) result = test.return_pair_of_u8u32() if (len(result) == 2 and result[0] == 42 and result[1] == 0): print ('PairTypeTest_Input_u8u32_list') test.accept_pair_of_u8u32((1, 2)) result = test.return_pair_of_u8u32() if (len(result) == 2 and result[0] == 1 and result[1] == 2): print ('PairTypeTest_Input_u8u32') result = test.return_pair_of_u16toFloat() if (len(result) == 2): print ('PairTypeTest_Output_u16toFloat') test.accept_pair_of_u16toFloat((4, -0.01)) result = test.return_pair_of_u16toFloat() if (len(result) == 2 and result[0] == 4 and result[1] < 0): print ('PairTypeTest_Input_u16toFloat') result = test.return_pair_of_stringTos32() if (len(result) == 2): print ('PairTypeTest_Output_stringTos32') test.accept_pair_of_stringTos32(('abc', -1)) result = test.return_pair_of_stringTos32() if (len(result) == 2 and result[0] == 'abc' and result[1] == -1): print ('PairTypeTest_Input_stringTos32') result = test.return_pair_of_stringToString() if (len(result) == 2): print ('PairTypeTest_Output_stringToString') test.accept_pair_of_stringToString(('one', 'two')) result = test.return_pair_of_stringToString() if (len(result) == 2 and result[0] == 'one' and result[1] == 'two'): print ('PairTypeTest_Input_stringToString') )"); } catch (const std::exception& ex) { AZ_Warning("UnitTest", false, "Failed with Python exception of %s", ex.what()); FAIL(); } e.Deactivate(); EXPECT_EQ(5, m_testSink.m_evaluationMap[static_cast(LogTypes::PairTypeTest_Input)]); EXPECT_EQ(4, m_testSink.m_evaluationMap[static_cast(LogTypes::PairTypeTest_Output)]); } TEST_F(PythonReflectionPairTests, CustomTypes_ConvertedCorrectly) { enum class LogTypes { Skip = 0, PairCustomTypeTest_Input, PairCustomTypeTest_Output, }; m_testSink.m_evaluateMessage = [](const char* window, const char* message) -> int { if (AzFramework::StringFunc::Equal(window, "python")) { if (AzFramework::StringFunc::StartsWith(message, "PairCustomTypeTest_Input")) { return static_cast(LogTypes::PairCustomTypeTest_Input); } else if (AzFramework::StringFunc::StartsWith(message, "PairCustomTypeTest_Output")) { return static_cast(LogTypes::PairCustomTypeTest_Output); } } return static_cast(LogTypes::Skip); }; MyCustomType::Reflect(m_app.GetSerializeContext()); MyCustomType::Reflect(m_app.GetBehaviorContext()); PythonReflectionPairTypes pythonReflectionPairTypes; pythonReflectionPairTypes.Reflect(m_app.GetSerializeContext()); pythonReflectionPairTypes.Reflect(m_app.GetBehaviorContext()); AZ::Entity e; Activate(e); SimulateEditorBecomingInitialized(); try { pybind11::exec(R"( import azlmbr.test.pair import azlmbr.object import azlmbr.std test = azlmbr.object.create('PythonReflectionPairTypes') result = test.return_pair_of_stringToCustomType() if (len(result) == 2): print ('PairCustomTypeTest_Output_stringToCustomType') custom = azlmbr.object.create('MyCustomType') custom.set_data(42) test.accept_pair_of_stringToCustomType(('def', custom)) result = test.return_pair_of_stringToCustomType() if (len(result) == 2): if (result[0] == 'def' and result[1].get_data() == 42): print ('PairCustomTypeTest_Input_stringToCustomType_tuple') )"); } catch (const std::exception& ex) { AZ_Warning("UnitTest", false, "Failed with Python exception of %s", ex.what()); FAIL(); } e.Deactivate(); EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast(LogTypes::PairCustomTypeTest_Input)]); EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast(LogTypes::PairCustomTypeTest_Output)]); } TEST_F(PythonReflectionPairTests, UnsupportedTypes_ErrorLogged) { enum class LogTypes { Skip = 0, PairUnsupportedTypeTest_CannotConvert }; m_testSink.m_evaluateMessage = [](const char* window, const char* message) -> int { if (AzFramework::StringFunc::Equal(window, "python")) { if (AzFramework::StringFunc::StartsWith(message, "Cannot convert pair container for")) { return static_cast(LogTypes::PairUnsupportedTypeTest_CannotConvert); } } return static_cast(LogTypes::Skip); }; MyCustomType::Reflect(m_app.GetSerializeContext()); MyCustomType::Reflect(m_app.GetBehaviorContext()); PythonReflectionPairTypes pythonReflectionDictionaryTypes; pythonReflectionDictionaryTypes.Reflect(m_app.GetSerializeContext()); pythonReflectionDictionaryTypes.Reflect(m_app.GetBehaviorContext()); AZ::Entity e; Activate(e); SimulateEditorBecomingInitialized(); try { pybind11::exec(R"( import azlmbr.test.pair import azlmbr.object import azlmbr.std test = azlmbr.object.create('PythonReflectionPairTypes') test.accept_pair_of_u8u32([42, 0, 1]) test.accept_pair_of_u8u32({42, 0}) )"); } catch (const std::exception& ex) { AZ_Warning("UnitTest", false, "Failed with Python exception of %s", ex.what()); FAIL(); } e.Deactivate(); EXPECT_EQ(2, m_testSink.m_evaluationMap[static_cast(LogTypes::PairUnsupportedTypeTest_CannotConvert)]); } }