PythonPairTests.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <EditorPythonBindings/PythonCommon.h>
  9. #include <pybind11/embed.h>
  10. #include <pybind11/pybind11.h>
  11. #include "PythonTraceMessageSink.h"
  12. #include "PythonTestingUtility.h"
  13. #include <Source/PythonSystemComponent.h>
  14. #include <Source/PythonReflectionComponent.h>
  15. #include <Source/PythonMarshalComponent.h>
  16. #include <Source/PythonProxyObject.h>
  17. #include <AzCore/RTTI/BehaviorContext.h>
  18. #include <AzFramework/StringFunc/StringFunc.h>
  19. #include "PythonPairTests.h"
  20. namespace UnitTest
  21. {
  22. //////////////////////////////////////////////////////////////////////////
  23. // test class/structs
  24. struct PythonReflectionPairTypes
  25. {
  26. AZ_TYPE_INFO(PythonReflectionPairTypes, "{037C067F-7A03-47BE-A30E-124D8157EDA2}");
  27. template <typename K, typename V>
  28. struct PairOf
  29. {
  30. using PairType = AZStd::pair<K, V>;
  31. PairType m_pair;
  32. explicit PairOf(const PairType& pair)
  33. {
  34. m_pair = pair;
  35. }
  36. explicit PairOf(const K& k, const V& v)
  37. {
  38. m_pair = PairType(k, v);
  39. }
  40. const PairType& ReturnPair() const
  41. {
  42. return m_pair;
  43. }
  44. void AcceptPair(const PairType& other)
  45. {
  46. m_pair = other;
  47. }
  48. void RegisterGenericType(AZ::SerializeContext& serializeContext)
  49. {
  50. serializeContext.RegisterGenericType<PairType>();
  51. }
  52. };
  53. PairOf<bool, bool> m_pairOfBoolToBool { false, true };
  54. PairOf<AZ::u8, AZ::u32> m_pairOfu8tou32 {1, 4};
  55. PairOf<AZ::u16, float> m_pairOfu16toFloat {1, 0.4f};
  56. PairOf<AZStd::string, AZ::s32> m_pairOfStringTos32 {"1", -4};
  57. PairOf<AZStd::string, AZStd::string> m_pairOfStringToString {"one", "foo"};
  58. PairOf<AZStd::string, MyCustomType> m_pairOfStringToCustomType{ "foo", MyCustomType() };
  59. void Reflect(AZ::ReflectContext* context)
  60. {
  61. if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  62. {
  63. m_pairOfBoolToBool.RegisterGenericType(*serializeContext);
  64. m_pairOfu8tou32.RegisterGenericType(*serializeContext);
  65. m_pairOfu16toFloat.RegisterGenericType(*serializeContext);
  66. m_pairOfStringTos32.RegisterGenericType(*serializeContext);
  67. m_pairOfStringToString.RegisterGenericType(*serializeContext);
  68. m_pairOfStringToCustomType.RegisterGenericType(*serializeContext);
  69. }
  70. if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  71. {
  72. behaviorContext->Class<PythonReflectionPairTypes>()
  73. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
  74. ->Attribute(AZ::Script::Attributes::Module, "test.pair")
  75. ->Method("return_pair_of_boolToBool", [](PythonReflectionPairTypes* self) { return self->m_pairOfBoolToBool.ReturnPair(); }, nullptr, "")
  76. ->Method("accept_pair_of_boolToBool", [](PythonReflectionPairTypes* self, const PairOf<bool, bool>::PairType& pair) { self->m_pairOfBoolToBool.AcceptPair(pair); }, nullptr, "")
  77. ->Method("return_pair_of_u8u32", [](PythonReflectionPairTypes* self) { return self->m_pairOfu8tou32.ReturnPair(); }, nullptr, "")
  78. ->Method("accept_pair_of_u8u32", [](PythonReflectionPairTypes* self, const PairOf<AZ::u8, AZ::u32>::PairType& pair) { self->m_pairOfu8tou32.AcceptPair(pair); }, nullptr, "")
  79. ->Method("return_pair_of_u16toFloat", [](PythonReflectionPairTypes* self) { return self->m_pairOfu16toFloat.ReturnPair(); }, nullptr, "")
  80. ->Method("accept_pair_of_u16toFloat", [](PythonReflectionPairTypes* self, const PairOf<AZ::u16, float>::PairType& pair) { self->m_pairOfu16toFloat.AcceptPair(pair); }, nullptr, "")
  81. ->Method("return_pair_of_stringTos32", [](PythonReflectionPairTypes* self) { return self->m_pairOfStringTos32.ReturnPair(); }, nullptr, "")
  82. ->Method("accept_pair_of_stringTos32", [](PythonReflectionPairTypes* self, const PairOf<AZStd::string, AZ::s32>::PairType& pair) { self->m_pairOfStringTos32.AcceptPair(pair); }, nullptr, "")
  83. ->Method("return_pair_of_stringToString", [](PythonReflectionPairTypes* self) { return self->m_pairOfStringToString.ReturnPair(); }, nullptr, "")
  84. ->Method("accept_pair_of_stringToString", [](PythonReflectionPairTypes* self, const PairOf<AZStd::string, AZStd::string>::PairType& pair) { self->m_pairOfStringToString.AcceptPair(pair); }, nullptr, "")
  85. ->Method("return_pair_of_stringToCustomType", [](PythonReflectionPairTypes* self) { return self->m_pairOfStringToCustomType.ReturnPair(); }, nullptr, "")
  86. ->Method("accept_pair_of_stringToCustomType", [](PythonReflectionPairTypes* self, const PairOf<AZStd::string, MyCustomType>::PairType& pair) { self->m_pairOfStringToCustomType.AcceptPair(pair); }, nullptr, "")
  87. ;
  88. }
  89. }
  90. };
  91. //////////////////////////////////////////////////////////////////////////
  92. // fixtures
  93. struct PythonReflectionPairTests
  94. : public PythonTestingFixture
  95. {
  96. PythonTraceMessageSink m_testSink;
  97. void SetUp() override
  98. {
  99. PythonTestingFixture::SetUp();
  100. PythonTestingFixture::RegisterComponentDescriptors();
  101. }
  102. void TearDown() override
  103. {
  104. // clearing up memory
  105. m_testSink.CleanUp();
  106. PythonTestingFixture::TearDown();
  107. }
  108. };
  109. TEST_F(PythonReflectionPairTests, SimpleTypes_Constructed)
  110. {
  111. enum class LogTypes
  112. {
  113. Skip = 0,
  114. PairTypeTest_ConstructBoolDefault,
  115. PairTypeTest_ConstructBoolParams,
  116. PairTypeTest_UseConstructed
  117. };
  118. m_testSink.m_evaluateMessage = [](const char* window, const char* message) -> int
  119. {
  120. if (AzFramework::StringFunc::Equal(window, "python"))
  121. {
  122. if (AzFramework::StringFunc::StartsWith(message, "PairTypeTest_ConstructBoolDefault"))
  123. {
  124. return static_cast<int>(LogTypes::PairTypeTest_ConstructBoolDefault);
  125. }
  126. else if (AzFramework::StringFunc::StartsWith(message, "PairTypeTest_ConstructBoolParams"))
  127. {
  128. return static_cast<int>(LogTypes::PairTypeTest_ConstructBoolParams);
  129. }
  130. else if (AzFramework::StringFunc::StartsWith(message, "PairTypeTest_UseConstructed"))
  131. {
  132. return static_cast<int>(LogTypes::PairTypeTest_UseConstructed);
  133. }
  134. }
  135. return static_cast<int>(LogTypes::Skip);
  136. };
  137. MyCustomType::Reflect(m_app.GetSerializeContext());
  138. MyCustomType::Reflect(m_app.GetBehaviorContext());
  139. PythonReflectionPairTypes pythonReflectionPairTypes;
  140. pythonReflectionPairTypes.Reflect(m_app.GetSerializeContext());
  141. pythonReflectionPairTypes.Reflect(m_app.GetBehaviorContext());
  142. AZ::Entity e;
  143. Activate(e);
  144. SimulateEditorBecomingInitialized();
  145. try
  146. {
  147. pybind11::exec(R"(
  148. import azlmbr.test.pair
  149. import azlmbr.object
  150. import azlmbr.std
  151. test = azlmbr.object.create('PythonReflectionPairTypes')
  152. test_pair = azlmbr.object.create('AZStd::pair<bool, bool>')
  153. if (test_pair):
  154. print ('PairTypeTest_ConstructBoolDefault')
  155. test_pair = azlmbr.object.construct('AZStd::pair<bool, bool>', True, False)
  156. if (test_pair and test_pair.first == True and test_pair.second == False):
  157. print ('PairTypeTest_ConstructBoolParams')
  158. test_pair.first = False
  159. test_pair.second = True
  160. test.accept_pair_of_boolToBool(test_pair)
  161. # Test out the pair as a single return value that's a list
  162. result = test.return_pair_of_boolToBool()
  163. if (len(result) == 2 and result[0] == False and result[1] == True):
  164. print ('PairTypeTest_UseConstructed')
  165. # Test out the pair as two comma-separated return values extracted from the list
  166. a, b = test.return_pair_of_boolToBool()
  167. if (a == False and b == True):
  168. print ('PairTypeTest_UseConstructed')
  169. )");
  170. }
  171. catch ([[maybe_unused]] const std::exception& ex)
  172. {
  173. AZ_Warning("UnitTest", false, "Failed with Python exception of %s", ex.what());
  174. FAIL();
  175. }
  176. e.Deactivate();
  177. EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast<int>(LogTypes::PairTypeTest_ConstructBoolDefault)]);
  178. EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast<int>(LogTypes::PairTypeTest_ConstructBoolParams)]);
  179. EXPECT_EQ(2, m_testSink.m_evaluationMap[static_cast<int>(LogTypes::PairTypeTest_UseConstructed)]);
  180. }
  181. TEST_F(PythonReflectionPairTests, SimpleTypes_ConvertedCorrectly)
  182. {
  183. enum class LogTypes
  184. {
  185. Skip = 0,
  186. PairTypeTest_Input,
  187. PairTypeTest_Output,
  188. };
  189. m_testSink.m_evaluateMessage = [](const char* window, const char* message) -> int
  190. {
  191. if (AzFramework::StringFunc::Equal(window, "python"))
  192. {
  193. if (AzFramework::StringFunc::StartsWith(message, "PairTypeTest_Input"))
  194. {
  195. return static_cast<int>(LogTypes::PairTypeTest_Input);
  196. }
  197. else if (AzFramework::StringFunc::StartsWith(message, "PairTypeTest_Output"))
  198. {
  199. return static_cast<int>(LogTypes::PairTypeTest_Output);
  200. }
  201. }
  202. return static_cast<int>(LogTypes::Skip);
  203. };
  204. MyCustomType::Reflect(m_app.GetSerializeContext());
  205. MyCustomType::Reflect(m_app.GetBehaviorContext());
  206. PythonReflectionPairTypes pythonReflectionPairTypes;
  207. pythonReflectionPairTypes.Reflect(m_app.GetSerializeContext());
  208. pythonReflectionPairTypes.Reflect(m_app.GetBehaviorContext());
  209. AZ::Entity e;
  210. Activate(e);
  211. SimulateEditorBecomingInitialized();
  212. try
  213. {
  214. pybind11::exec(R"(
  215. import azlmbr.test.pair
  216. import azlmbr.object
  217. import azlmbr.std
  218. test = azlmbr.object.create('PythonReflectionPairTypes')
  219. result = test.return_pair_of_u8u32()
  220. if (len(result) == 2):
  221. print ('PairTypeTest_Output_u8u32')
  222. test.accept_pair_of_u8u32([42, 0])
  223. result = test.return_pair_of_u8u32()
  224. if (len(result) == 2 and result[0] == 42 and result[1] == 0):
  225. print ('PairTypeTest_Input_u8u32_list')
  226. test.accept_pair_of_u8u32((1, 2))
  227. result = test.return_pair_of_u8u32()
  228. if (len(result) == 2 and result[0] == 1 and result[1] == 2):
  229. print ('PairTypeTest_Input_u8u32')
  230. result = test.return_pair_of_u16toFloat()
  231. if (len(result) == 2):
  232. print ('PairTypeTest_Output_u16toFloat')
  233. test.accept_pair_of_u16toFloat((4, -0.01))
  234. result = test.return_pair_of_u16toFloat()
  235. if (len(result) == 2 and result[0] == 4 and result[1] < 0):
  236. print ('PairTypeTest_Input_u16toFloat')
  237. result = test.return_pair_of_stringTos32()
  238. if (len(result) == 2):
  239. print ('PairTypeTest_Output_stringTos32')
  240. test.accept_pair_of_stringTos32(('abc', -1))
  241. result = test.return_pair_of_stringTos32()
  242. if (len(result) == 2 and result[0] == 'abc' and result[1] == -1):
  243. print ('PairTypeTest_Input_stringTos32')
  244. result = test.return_pair_of_stringToString()
  245. if (len(result) == 2):
  246. print ('PairTypeTest_Output_stringToString')
  247. test.accept_pair_of_stringToString(('one', 'two'))
  248. result = test.return_pair_of_stringToString()
  249. if (len(result) == 2 and result[0] == 'one' and result[1] == 'two'):
  250. print ('PairTypeTest_Input_stringToString')
  251. )");
  252. }
  253. catch ([[maybe_unused]] const std::exception& ex)
  254. {
  255. AZ_Warning("UnitTest", false, "Failed with Python exception of %s", ex.what());
  256. FAIL();
  257. }
  258. e.Deactivate();
  259. EXPECT_EQ(5, m_testSink.m_evaluationMap[static_cast<int>(LogTypes::PairTypeTest_Input)]);
  260. EXPECT_EQ(4, m_testSink.m_evaluationMap[static_cast<int>(LogTypes::PairTypeTest_Output)]);
  261. }
  262. TEST_F(PythonReflectionPairTests, CustomTypes_ConvertedCorrectly)
  263. {
  264. enum class LogTypes
  265. {
  266. Skip = 0,
  267. PairCustomTypeTest_Input,
  268. PairCustomTypeTest_Output,
  269. };
  270. m_testSink.m_evaluateMessage = [](const char* window, const char* message) -> int
  271. {
  272. if (AzFramework::StringFunc::Equal(window, "python"))
  273. {
  274. if (AzFramework::StringFunc::StartsWith(message, "PairCustomTypeTest_Input"))
  275. {
  276. return static_cast<int>(LogTypes::PairCustomTypeTest_Input);
  277. }
  278. else if (AzFramework::StringFunc::StartsWith(message, "PairCustomTypeTest_Output"))
  279. {
  280. return static_cast<int>(LogTypes::PairCustomTypeTest_Output);
  281. }
  282. }
  283. return static_cast<int>(LogTypes::Skip);
  284. };
  285. MyCustomType::Reflect(m_app.GetSerializeContext());
  286. MyCustomType::Reflect(m_app.GetBehaviorContext());
  287. PythonReflectionPairTypes pythonReflectionPairTypes;
  288. pythonReflectionPairTypes.Reflect(m_app.GetSerializeContext());
  289. pythonReflectionPairTypes.Reflect(m_app.GetBehaviorContext());
  290. AZ::Entity e;
  291. Activate(e);
  292. SimulateEditorBecomingInitialized();
  293. try
  294. {
  295. pybind11::exec(R"(
  296. import azlmbr.test.pair
  297. import azlmbr.object
  298. import azlmbr.std
  299. test = azlmbr.object.create('PythonReflectionPairTypes')
  300. result = test.return_pair_of_stringToCustomType()
  301. if (len(result) == 2):
  302. print ('PairCustomTypeTest_Output_stringToCustomType')
  303. custom = azlmbr.object.create('MyCustomType')
  304. custom.set_data(42)
  305. test.accept_pair_of_stringToCustomType(('def', custom))
  306. result = test.return_pair_of_stringToCustomType()
  307. if (len(result) == 2):
  308. if (result[0] == 'def' and result[1].get_data() == 42):
  309. print ('PairCustomTypeTest_Input_stringToCustomType_tuple')
  310. )");
  311. }
  312. catch ([[maybe_unused]] const std::exception& ex)
  313. {
  314. AZ_Warning("UnitTest", false, "Failed with Python exception of %s", ex.what());
  315. FAIL();
  316. }
  317. e.Deactivate();
  318. EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast<int>(LogTypes::PairCustomTypeTest_Input)]);
  319. EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast<int>(LogTypes::PairCustomTypeTest_Output)]);
  320. }
  321. TEST_F(PythonReflectionPairTests, UnsupportedTypes_ErrorLogged)
  322. {
  323. enum class LogTypes
  324. {
  325. Skip = 0,
  326. PairUnsupportedTypeTest_CannotConvert
  327. };
  328. m_testSink.m_evaluateMessage = [](const char* window, const char* message) -> int
  329. {
  330. if (AzFramework::StringFunc::Equal(window, "python"))
  331. {
  332. if (AzFramework::StringFunc::StartsWith(message, "Cannot convert pair container for"))
  333. {
  334. return static_cast<int>(LogTypes::PairUnsupportedTypeTest_CannotConvert);
  335. }
  336. }
  337. return static_cast<int>(LogTypes::Skip);
  338. };
  339. MyCustomType::Reflect(m_app.GetSerializeContext());
  340. MyCustomType::Reflect(m_app.GetBehaviorContext());
  341. PythonReflectionPairTypes pythonReflectionDictionaryTypes;
  342. pythonReflectionDictionaryTypes.Reflect(m_app.GetSerializeContext());
  343. pythonReflectionDictionaryTypes.Reflect(m_app.GetBehaviorContext());
  344. AZ::Entity e;
  345. Activate(e);
  346. SimulateEditorBecomingInitialized();
  347. try
  348. {
  349. pybind11::exec(R"(
  350. import azlmbr.test.pair
  351. import azlmbr.object
  352. import azlmbr.std
  353. test = azlmbr.object.create('PythonReflectionPairTypes')
  354. test.accept_pair_of_u8u32([42, 0, 1])
  355. test.accept_pair_of_u8u32({42, 0})
  356. )");
  357. }
  358. catch ([[maybe_unused]] const std::exception& ex)
  359. {
  360. AZ_Warning("UnitTest", false, "Failed with Python exception of %s", ex.what());
  361. FAIL();
  362. }
  363. e.Deactivate();
  364. EXPECT_EQ(2, m_testSink.m_evaluationMap[static_cast<int>(LogTypes::PairUnsupportedTypeTest_CannotConvert)]);
  365. }
  366. }