2
0

PythonProxyBusTests.cpp 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012
  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 "PythonTestingUtility.h"
  9. #include "PythonTraceMessageSink.h"
  10. #include <EditorPythonBindings/PythonCommon.h>
  11. #include <pybind11/embed.h>
  12. #include <pybind11/pybind11.h>
  13. #include <Source/PythonSystemComponent.h>
  14. #include <Source/PythonReflectionComponent.h>
  15. #include <Source/PythonProxyBus.h>
  16. #include <Source/PythonProxyObject.h>
  17. #include <AzCore/RTTI/BehaviorContext.h>
  18. #include <AzFramework/StringFunc/StringFunc.h>
  19. namespace UnitTest
  20. {
  21. //////////////////////////////////////////////////////////////////////////
  22. // test class/struts
  23. class FakeComponentId
  24. {
  25. public:
  26. AZ_TYPE_INFO(FakeComponentId, "{A0A9A069-9C3D-465A-B7AD-0D6CC803990A}");
  27. AZ_CLASS_ALLOCATOR(FakeComponentId, AZ::SystemAllocator);
  28. FakeComponentId() = default;
  29. bool operator==(const FakeComponentId& rhs) const { return m_id == rhs.m_id; }
  30. bool IsValid() const { return m_id != AZ::InvalidComponentId; }
  31. AZStd::string ToString() const { return AZStd::string::format("[%llu]", m_id); }
  32. void Set(AZ::u64 id)
  33. {
  34. m_id = id;
  35. }
  36. AZ::ComponentId m_id = AZ::InvalidComponentId;
  37. static void Reflect(AZ::ReflectContext* context)
  38. {
  39. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  40. {
  41. serializeContext->Class<FakeComponentId>()
  42. ->Version(1)
  43. ->Field("ComponentId", &FakeComponentId::m_id)
  44. ;
  45. serializeContext->RegisterGenericType<AZStd::vector<FakeComponentId>>();
  46. }
  47. if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  48. {
  49. behaviorContext->Class<FakeComponentId>("FakeComponentId")
  50. ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value)
  51. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
  52. ->Attribute(AZ::Script::Attributes::Module, "entity")
  53. ->Constructor()
  54. ->Method("IsValid", &FakeComponentId::IsValid)
  55. ->Method("Equal", &FakeComponentId::operator==)
  56. ->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::Equal)
  57. ->Method("ToString", &FakeComponentId::ToString)
  58. ->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::ToString)
  59. ->Method("Set", &FakeComponentId::Set)
  60. ;
  61. }
  62. }
  63. };
  64. struct PythonTestBroadcastRequests
  65. : AZ::EBusTraits
  66. {
  67. static const bool EnableEventQueue = true;
  68. virtual AZ::u32 GetBits() = 0;
  69. virtual void SetBits(AZ::u32 value) = 0;
  70. virtual void Ping() = 0;
  71. virtual void AcceptProxyList(const AZStd::vector<FakeComponentId>& componentIds) = 0;
  72. };
  73. using PythonTestBroadcastRequestBus = AZ::EBus<PythonTestBroadcastRequests>;
  74. struct PythonTestBroadcastRequestsHandler final
  75. : public PythonTestBroadcastRequestBus::Handler
  76. {
  77. PythonTestBroadcastRequestsHandler()
  78. {
  79. PythonTestBroadcastRequestBus::Handler::BusConnect();
  80. }
  81. virtual ~PythonTestBroadcastRequestsHandler()
  82. {
  83. PythonTestBroadcastRequestBus::Handler::BusDisconnect();
  84. }
  85. AZ::u32 m_bits = 0;
  86. AZ::u32 GetBits() override
  87. {
  88. return m_bits;
  89. }
  90. void SetBits(AZ::u32 value) override
  91. {
  92. m_bits |= value;
  93. }
  94. AZ::u64 m_pingCount = 0;
  95. void Ping() override
  96. {
  97. ++m_pingCount;
  98. }
  99. void AcceptProxyList(const AZStd::vector<FakeComponentId>& componentIds) override
  100. {
  101. AZStd::vector<AZ::Component*> components;
  102. for (auto componentId : componentIds)
  103. {
  104. if (componentId.IsValid())
  105. {
  106. AZ_Printf("python", "BasicRequests_AcceptProxyList:%s", componentId.ToString().c_str());
  107. }
  108. else
  109. {
  110. AZ_Warning("python", false, "AcceptProxyList failed - found invalid componentId.");
  111. }
  112. }
  113. }
  114. void Reflect(AZ::ReflectContext* context)
  115. {
  116. FakeComponentId::Reflect(context);
  117. if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  118. {
  119. behaviorContext->EBus<PythonTestBroadcastRequestBus>("PythonTestBroadcastRequestBus")
  120. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
  121. ->Event("SetBits", &PythonTestBroadcastRequestBus::Events::SetBits)
  122. ->Event("GetBits", &PythonTestBroadcastRequestBus::Events::GetBits)
  123. ->Event("Ping", &PythonTestBroadcastRequestBus::Events::Ping)
  124. ->Event("AcceptProxyList", &PythonTestBroadcastRequestBus::Events::AcceptProxyList)
  125. ;
  126. }
  127. }
  128. };
  129. //
  130. struct PythonTestEventRequests
  131. : AZ::EBusTraits
  132. {
  133. static const bool EnableEventQueue = true;
  134. static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
  135. using BusIdType = AZ::u32;
  136. virtual AZ::s32 Add(AZ::s32 a, AZ::s32 b) = 0;
  137. virtual void Pong() = 0;
  138. };
  139. using PythonTestEventRequestBus = AZ::EBus<PythonTestEventRequests>;
  140. struct PythonTestEventRequestsHandler final
  141. : public PythonTestEventRequestBus::Handler
  142. {
  143. PythonTestEventRequestsHandler()
  144. {
  145. PythonTestEventRequestBus::Handler::BusConnect(101);
  146. }
  147. virtual ~PythonTestEventRequestsHandler()
  148. {
  149. PythonTestEventRequestBus::Handler::BusDisconnect();
  150. }
  151. AZ::s32 Add(AZ::s32 a, AZ::s32 b) override
  152. {
  153. return a + b;
  154. }
  155. AZ::u64 m_pongCount = 0;
  156. void Pong() override
  157. {
  158. ++m_pongCount;
  159. }
  160. void Reflect(AZ::ReflectContext* context)
  161. {
  162. if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  163. {
  164. behaviorContext->EBus<PythonTestEventRequestBus>("PythonTestEventRequestBus")
  165. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
  166. ->Attribute(AZ::Script::Attributes::Module, "test")
  167. ->Event("Add", &PythonTestEventRequestBus::Events::Add)
  168. ->Event("Pong", &PythonTestEventRequestBus::Events::Pong)
  169. ;
  170. }
  171. }
  172. };
  173. // an example of an EBus Notification bus using a single address & BusIdType=NullBusId
  174. struct PythonTestSingleAddressNotifications
  175. : AZ::EBusTraits
  176. {
  177. static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
  178. using MutexType = AZStd::mutex;
  179. virtual ~PythonTestSingleAddressNotifications() = default;
  180. virtual void OnPing(AZ::u64 count) = 0;
  181. virtual void OnPong(AZ::u64 count) = 0;
  182. virtual void MultipleInputs(AZ::u64 one, AZ::s8 two, AZStd::string_view three) = 0;
  183. virtual AZStd::string OnAddFish(AZStd::string_view value) = 0;
  184. virtual void OnFire() = 0;
  185. };
  186. using PythonTestSingleAddressNotificationBus = AZ::EBus<PythonTestSingleAddressNotifications>;
  187. struct PythonTestNotificationHandler final
  188. : public PythonTestSingleAddressNotificationBus::Handler
  189. , public AZ::BehaviorEBusHandler
  190. {
  191. AZ_EBUS_BEHAVIOR_BINDER(PythonTestNotificationHandler, "{97052D15-A4E8-461B-B065-91D16E31C4F7}", AZ::SystemAllocator,
  192. OnPing, OnPong, MultipleInputs, OnAddFish, OnFire);
  193. virtual ~PythonTestNotificationHandler() = default;
  194. void OnPing(AZ::u64 count) override
  195. {
  196. Call(FN_OnPing, count);
  197. }
  198. void OnPong(AZ::u64 count) override
  199. {
  200. Call(FN_OnPong, count);
  201. }
  202. void MultipleInputs(AZ::u64 one, AZ::s8 two, AZStd::string_view three) override
  203. {
  204. Call(FN_MultipleInputs, one, two, three);
  205. }
  206. AZStd::string OnAddFish(AZStd::string_view value) override
  207. {
  208. AZStd::string result;
  209. CallResult(result, FN_OnAddFish, value);
  210. return result;
  211. }
  212. void OnFire() override
  213. {
  214. Call(FN_OnFire);
  215. }
  216. static AZ::u64 s_pongCount;
  217. static AZ::u64 s_pingCount;
  218. static void DoPing()
  219. {
  220. // notify the listeners about Ping
  221. ++s_pingCount;
  222. PythonTestSingleAddressNotificationBus::Broadcast(&PythonTestSingleAddressNotificationBus::Events::OnPing, s_pingCount);
  223. }
  224. static void DoPong()
  225. {
  226. // notify the listeners about Pong
  227. ++s_pongCount;
  228. PythonTestSingleAddressNotificationBus::Broadcast(&PythonTestSingleAddressNotificationBus::Events::OnPong, s_pongCount);
  229. }
  230. static AZStd::string DoAddFish(AZStd::string value)
  231. {
  232. AZStd::string result;
  233. PythonTestSingleAddressNotificationBus::BroadcastResult(result, &PythonTestSingleAddressNotificationBus::Events::OnAddFish, value);
  234. return result;
  235. }
  236. static void DoFire()
  237. {
  238. PythonTestSingleAddressNotificationBus::Broadcast(&PythonTestSingleAddressNotificationBus::Events::OnFire);
  239. }
  240. static void DoFiresInParallel(int value)
  241. {
  242. AZStd::vector<AZStd::thread> threads;
  243. threads.reserve(value);
  244. for (size_t i = 0; i < value; ++i)
  245. {
  246. threads.emplace_back(&DoFire);
  247. }
  248. for (AZStd::thread& thread : threads)
  249. {
  250. thread.join();
  251. }
  252. }
  253. static void Reset()
  254. {
  255. s_pingCount = 0;
  256. s_pongCount = 0;
  257. }
  258. void Reflect(AZ::ReflectContext* context)
  259. {
  260. if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  261. {
  262. behaviorContext->EBus<PythonTestSingleAddressNotificationBus>("PythonTestSingleAddressNotificationBus")
  263. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
  264. ->Attribute(AZ::Script::Attributes::Module, "test")
  265. ->Handler<PythonTestNotificationHandler>()
  266. ->Event("on_ping", &PythonTestSingleAddressNotificationBus::Events::OnPing)
  267. ->Event("on_pong", &PythonTestSingleAddressNotificationBus::Events::OnPong)
  268. ->Event("MultipleInputs", &PythonTestSingleAddressNotificationBus::Events::MultipleInputs)
  269. ->Event("OnAddFish", &PythonTestSingleAddressNotificationBus::Events::OnAddFish)
  270. ->Event("OnFire", &PythonTestSingleAddressNotificationBus::Events::OnFire)
  271. ;
  272. // for testing from Python to send out the events
  273. behaviorContext->Class<PythonTestNotificationHandler>()
  274. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
  275. ->Attribute(AZ::Script::Attributes::Module, "test")
  276. ->Method("do_ping", &PythonTestNotificationHandler::DoPing)
  277. ->Method("do_pong", &PythonTestNotificationHandler::DoPong)
  278. ->Method("do_add_fish", &PythonTestNotificationHandler::DoAddFish)
  279. ->Method("do_fire", &PythonTestNotificationHandler::DoFire)
  280. ->Method("do_fires_in_parallel", &PythonTestNotificationHandler::DoFiresInParallel)
  281. ;
  282. }
  283. }
  284. };
  285. AZ::u64 PythonTestNotificationHandler::s_pongCount = 0;
  286. AZ::u64 PythonTestNotificationHandler::s_pingCount = 0;
  287. // an example of an EBus Notification bus connecting to a bus by id
  288. struct PythonTestByIdNotifications
  289. : public AZ::EBusTraits
  290. {
  291. static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
  292. static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
  293. using BusIdType = AZ::s32;
  294. virtual void OnResult(AZ::s64 result) = 0;
  295. };
  296. using PythonTestByIdNotificationBus = AZ::EBus<PythonTestByIdNotifications>;
  297. struct PythonTestByIdNotificationsHandler final
  298. : public PythonTestByIdNotificationBus::Handler
  299. , public AZ::BehaviorEBusHandler
  300. {
  301. AZ_EBUS_BEHAVIOR_BINDER(PythonTestByIdNotificationsHandler, "{5F091D4B-86C4-4D25-B982-2ECAFD8AFF0F}", AZ::SystemAllocator, OnResult);
  302. virtual ~PythonTestByIdNotificationsHandler() = default;
  303. void OnResult(AZ::s64 result) override
  304. {
  305. Call(FN_OnResult, result);
  306. }
  307. void Reflect(AZ::ReflectContext* context)
  308. {
  309. if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  310. {
  311. behaviorContext->EBus<PythonTestByIdNotificationBus>("PythonTestByIdNotificationBus")
  312. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
  313. ->Handler<PythonTestByIdNotificationsHandler>()
  314. ->Event("OnResult", &PythonTestByIdNotificationBus::Events::OnResult)
  315. ;
  316. }
  317. }
  318. };
  319. //////////////////////////////////////////////////////////////////////////
  320. // fixture
  321. struct PythonBusProxyTests
  322. : public PythonTestingFixture
  323. {
  324. PythonTraceMessageSink m_testSink;
  325. void SetUp() override
  326. {
  327. PythonTestingFixture::SetUp();
  328. PythonTestingFixture::RegisterComponentDescriptors();
  329. }
  330. void TearDown() override
  331. {
  332. // clearing up memory
  333. m_testSink.CleanUp();
  334. PythonTestingFixture::TearDown();
  335. }
  336. };
  337. //////////////////////////////////////////////////////////////////////////
  338. // tests
  339. TEST_F(PythonBusProxyTests, ImportEbus)
  340. {
  341. enum class LogTypes
  342. {
  343. Skip = 0,
  344. BasicRequests_ImportEbus,
  345. BasicRequests_ImportEbusCount,
  346. BasicRequests_AcceptProxyList
  347. };
  348. m_testSink.m_evaluateMessage = [](const char* window, const char* message) -> int
  349. {
  350. if (AzFramework::StringFunc::Equal(window, "python"))
  351. {
  352. if (AzFramework::StringFunc::Equal(message, "BasicRequests_ImportEbus"))
  353. {
  354. return static_cast<int>(LogTypes::BasicRequests_ImportEbus);
  355. }
  356. else if (AzFramework::StringFunc::Equal(message, "BasicRequests_ImportEbusCount"))
  357. {
  358. return static_cast<int>(LogTypes::BasicRequests_ImportEbusCount);
  359. }
  360. else if (AzFramework::StringFunc::StartsWith(message, "BasicRequests_AcceptProxyList"))
  361. {
  362. return static_cast<int>(LogTypes::BasicRequests_AcceptProxyList);
  363. }
  364. }
  365. return static_cast<int>(LogTypes::Skip);
  366. };
  367. PythonTestBroadcastRequestsHandler pythonTestBroadcastRequestsHandler;
  368. pythonTestBroadcastRequestsHandler.Reflect(m_app.GetBehaviorContext());
  369. pythonTestBroadcastRequestsHandler.Reflect(m_app.GetSerializeContext());
  370. AZ::Entity e;
  371. Activate(e);
  372. SimulateEditorBecomingInitialized();
  373. try
  374. {
  375. pybind11::exec(R"(
  376. import azlmbr.bus
  377. import azlmbr.entity
  378. import azlmbr.object
  379. eventType = azlmbr.bus.Event
  380. if (eventType != None):
  381. print ('BasicRequests_ImportEbus')
  382. if len(azlmbr.bus.__dict__) > 0:
  383. print ('BasicRequests_ImportEbusCount')
  384. componentId101 = azlmbr.object.create('FakeComponentId')
  385. componentId101.Set(101)
  386. componentId102 = azlmbr.object.create('FakeComponentId')
  387. componentId102.Set(102)
  388. componentList = [componentId101, componentId102]
  389. azlmbr.bus.PythonTestBroadcastRequestBus(azlmbr.bus.Broadcast, 'AcceptProxyList', componentList)
  390. )");
  391. }
  392. catch ([[maybe_unused]] const std::exception& e)
  393. {
  394. AZ_Warning("UnitTest", false, "Failed on with Python exception: %s", e.what());
  395. FAIL();
  396. }
  397. e.Deactivate();
  398. EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast<int>(LogTypes::BasicRequests_ImportEbus)]);
  399. EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast<int>(LogTypes::BasicRequests_ImportEbusCount)]);
  400. EXPECT_EQ(2, m_testSink.m_evaluationMap[static_cast<int>(LogTypes::BasicRequests_AcceptProxyList)]);
  401. }
  402. TEST_F(PythonBusProxyTests, BroadcastRequests)
  403. {
  404. enum class LogTypes
  405. {
  406. Skip = 0,
  407. BroadcastRequests_SetBits,
  408. BroadcastRequests_GetBits
  409. };
  410. m_testSink.m_evaluateMessage = [](const char* window, const char* message) -> int
  411. {
  412. if (AzFramework::StringFunc::Equal(window, "python"))
  413. {
  414. if (AzFramework::StringFunc::Equal(message, "BroadcastRequests_SetBits"))
  415. {
  416. return static_cast<int>(LogTypes::BroadcastRequests_SetBits);
  417. }
  418. else if (AzFramework::StringFunc::Equal(message, "BroadcastRequests_GetBits"))
  419. {
  420. return static_cast<int>(LogTypes::BroadcastRequests_GetBits);
  421. }
  422. }
  423. return static_cast<int>(LogTypes::Skip);
  424. };
  425. PythonTestBroadcastRequestsHandler pythonTestBroadcastRequestsHandler;
  426. pythonTestBroadcastRequestsHandler.Reflect(m_app.GetBehaviorContext());
  427. AZ::Entity e;
  428. Activate(e);
  429. SimulateEditorBecomingInitialized();
  430. try
  431. {
  432. pybind11::exec(R"(
  433. import azlmbr.bus
  434. bits = azlmbr.bus.PythonTestBroadcastRequestBus(azlmbr.bus.Broadcast, 'GetBits')
  435. if (bits == 0):
  436. print ('BroadcastRequests_GetBits')
  437. azlmbr.bus.PythonTestBroadcastRequestBus(azlmbr.bus.Broadcast, 'SetBits', bits | 3)
  438. bits = azlmbr.bus.PythonTestBroadcastRequestBus(azlmbr.bus.Broadcast, 'GetBits')
  439. if (bits == 3):
  440. print ('BroadcastRequests_SetBits')
  441. )");
  442. }
  443. catch ([[maybe_unused]] const std::exception& e)
  444. {
  445. AZ_Warning("UnitTest", false, "Failed on with Python exception: %s", e.what());
  446. FAIL();
  447. }
  448. e.Deactivate();
  449. EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast<int>(LogTypes::BroadcastRequests_SetBits)]);
  450. EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast<int>(LogTypes::BroadcastRequests_GetBits)]);
  451. }
  452. TEST_F(PythonBusProxyTests, QueueBroadcastRequests)
  453. {
  454. PythonTestBroadcastRequestsHandler pythonTestBroadcastRequestsHandler;
  455. pythonTestBroadcastRequestsHandler.Reflect(m_app.GetBehaviorContext());
  456. AZ::Entity e;
  457. Activate(e);
  458. SimulateEditorBecomingInitialized();
  459. try
  460. {
  461. pybind11::exec(R"(
  462. import azlmbr.bus
  463. for i in range(2019):
  464. azlmbr.bus.PythonTestBroadcastRequestBus(azlmbr.bus.QueueBroadcast, 'Ping')
  465. )");
  466. }
  467. catch ([[maybe_unused]] const std::exception& e)
  468. {
  469. AZ_Warning("UnitTest", false, "Failed on with Python exception: %s", e.what());
  470. FAIL();
  471. }
  472. EXPECT_EQ(0, pythonTestBroadcastRequestsHandler.m_pingCount);
  473. PythonTestBroadcastRequestBus::ExecuteQueuedEvents();
  474. EXPECT_EQ(2019, pythonTestBroadcastRequestsHandler.m_pingCount);
  475. e.Deactivate();
  476. }
  477. TEST_F(PythonBusProxyTests, EventRequests)
  478. {
  479. enum class LogTypes
  480. {
  481. Skip = 0,
  482. EventRequests_Add
  483. };
  484. m_testSink.m_evaluateMessage = [](const char* window, const char* message) -> int
  485. {
  486. if (AzFramework::StringFunc::Equal(window, "python"))
  487. {
  488. if (AzFramework::StringFunc::Equal(message, "EventRequests_Add"))
  489. {
  490. return static_cast<int>(LogTypes::EventRequests_Add);
  491. }
  492. }
  493. return static_cast<int>(LogTypes::Skip);
  494. };
  495. PythonTestEventRequestsHandler pythonTestEventRequestsHandler;
  496. pythonTestEventRequestsHandler.Reflect(m_app.GetBehaviorContext());
  497. AZ::Entity e;
  498. Activate(e);
  499. SimulateEditorBecomingInitialized();
  500. try
  501. {
  502. pybind11::exec(R"(
  503. import azlmbr.bus
  504. import azlmbr.test
  505. address = 101
  506. answer = azlmbr.test.PythonTestEventRequestBus(azlmbr.bus.Event, 'Add', address, 40, 2)
  507. if (answer == 42):
  508. print ('EventRequests_Add')
  509. )");
  510. }
  511. catch ([[maybe_unused]] const std::exception& e)
  512. {
  513. AZ_Warning("UnitTest", false, "Failed on with Python exception: %s", e.what());
  514. FAIL();
  515. }
  516. e.Deactivate();
  517. EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast<int>(LogTypes::EventRequests_Add)]);
  518. }
  519. TEST_F(PythonBusProxyTests, QueueEventRequests)
  520. {
  521. PythonTestEventRequestsHandler pythonTestEventRequestsHandler;
  522. pythonTestEventRequestsHandler.Reflect(m_app.GetBehaviorContext());
  523. AZ::Entity e;
  524. Activate(e);
  525. SimulateEditorBecomingInitialized();
  526. try
  527. {
  528. pybind11::exec(R"(
  529. import azlmbr.bus
  530. import azlmbr.test
  531. address = 101
  532. for i in range(address * 2):
  533. azlmbr.test.PythonTestEventRequestBus(azlmbr.bus.QueueEvent, 'Pong', address)
  534. )");
  535. }
  536. catch ([[maybe_unused]] const std::exception& e)
  537. {
  538. AZ_Warning("UnitTest", false, "Failed on with Python exception: %s", e.what());
  539. FAIL();
  540. }
  541. EXPECT_EQ(0, pythonTestEventRequestsHandler.m_pongCount);
  542. PythonTestEventRequestBus::ExecuteQueuedEvents();
  543. EXPECT_EQ(202, pythonTestEventRequestsHandler.m_pongCount);
  544. e.Deactivate();
  545. }
  546. TEST_F(PythonBusProxyTests, SingleAddressNotifications)
  547. {
  548. PythonTestNotificationHandler pythonTestNotificationHandler;
  549. pythonTestNotificationHandler.Reflect(m_app.GetBehaviorContext());
  550. AZ::Entity e;
  551. Activate(e);
  552. SimulateEditorBecomingInitialized();
  553. enum class LogTypes
  554. {
  555. Skip = 0,
  556. Notifications_OnPing,
  557. Notifications_OnPong,
  558. Notifications_Match,
  559. Notifications_Multi,
  560. };
  561. m_testSink.m_evaluateMessage = [](const char* window, const char* message) -> int
  562. {
  563. if (AzFramework::StringFunc::Equal(window, "python"))
  564. {
  565. if (AzFramework::StringFunc::Equal(message, "Notifications_OnPing"))
  566. {
  567. return static_cast<int>(LogTypes::Notifications_OnPing);
  568. }
  569. else if (AzFramework::StringFunc::Equal(message, "Notifications_OnPong"))
  570. {
  571. return static_cast<int>(LogTypes::Notifications_OnPong);
  572. }
  573. else if (AzFramework::StringFunc::Equal(message, "Notifications_Match"))
  574. {
  575. return static_cast<int>(LogTypes::Notifications_Match);
  576. }
  577. else if (AzFramework::StringFunc::StartsWith(message, "Notifications_Multi"))
  578. {
  579. return static_cast<int>(LogTypes::Notifications_Multi);
  580. }
  581. }
  582. return static_cast<int>(LogTypes::Skip);
  583. };
  584. UnitTest::PythonTestNotificationHandler::Reset();
  585. try
  586. {
  587. pybind11::exec(R"(
  588. import azlmbr.bus
  589. import azlmbr.test
  590. pingCount = 0
  591. pongCount = 0
  592. def OnPing(parameters):
  593. global pingCount
  594. pingCount = parameters[0]
  595. print ('Notifications_OnPing')
  596. def OnPong(parameters):
  597. global pongCount
  598. pongCount = parameters[0]
  599. print ('Notifications_OnPong')
  600. def OnMultipleInputs(parameters):
  601. if(len(parameters) == 3):
  602. print ('Notifications_Multi1')
  603. if(parameters[0] == 1):
  604. print ('Notifications_Multi2')
  605. if(parameters[1] == 2):
  606. print ('Notifications_Multi3')
  607. if(parameters[2] == '3'):
  608. print ('Notifications_Multi4')
  609. handler = azlmbr.bus.NotificationHandler('PythonTestSingleAddressNotificationBus')
  610. handler.connect(None)
  611. handler.add_callback('OnPing', OnPing)
  612. handler.add_callback('OnPong', OnPong)
  613. handler.add_callback('MultipleInputs', OnMultipleInputs)
  614. azlmbr.test.PythonTestSingleAddressNotificationBus(azlmbr.bus.Broadcast, 'MultipleInputs', 1, 2, '3')
  615. for i in range(40):
  616. azlmbr.test.PythonTestNotificationHandler_do_ping()
  617. for i in range(2):
  618. azlmbr.test.PythonTestNotificationHandler_do_pong()
  619. if (pingCount == 40):
  620. print ('Notifications_Match')
  621. if (pongCount == 2):
  622. print ('Notifications_Match')
  623. if ((pingCount + pongCount) == 42):
  624. print ('Notifications_Match')
  625. handler.disconnect()
  626. def OnMultipleInputsAgain(parameters):
  627. if(len(parameters) == 3):
  628. print ('Notifications_Multi5')
  629. if(parameters[0] == 4):
  630. print ('Notifications_Multi6')
  631. if(parameters[1] == 5):
  632. print ('Notifications_Multi7')
  633. if(parameters[2] == 'six'):
  634. print ('Notifications_Multi8')
  635. handler = azlmbr.test.PythonTestSingleAddressNotificationBusHandler()
  636. handler.connect(None)
  637. handler.add_callback('MultipleInputs', OnMultipleInputsAgain)
  638. azlmbr.test.PythonTestSingleAddressNotificationBus(azlmbr.bus.Broadcast, 'MultipleInputs', 4, 5, 'six')
  639. handler.disconnect()
  640. )");
  641. }
  642. catch ([[maybe_unused]] const std::exception& e)
  643. {
  644. AZ_Warning("UnitTest", false, "Failed on with Python exception: %s", e.what());
  645. FAIL();
  646. }
  647. e.Deactivate();
  648. EXPECT_EQ(40, m_testSink.m_evaluationMap[static_cast<int>(LogTypes::Notifications_OnPing)]);
  649. EXPECT_EQ(2, m_testSink.m_evaluationMap[static_cast<int>(LogTypes::Notifications_OnPong)]);
  650. EXPECT_EQ(3, m_testSink.m_evaluationMap[static_cast<int>(LogTypes::Notifications_Match)]);
  651. EXPECT_EQ(8, m_testSink.m_evaluationMap[static_cast<int>(LogTypes::Notifications_Multi)]);
  652. }
  653. TEST_F(PythonBusProxyTests, NotificationsAtAddress)
  654. {
  655. PythonTestByIdNotificationsHandler pythonTestByIdNotificationsHandler;
  656. pythonTestByIdNotificationsHandler.Reflect(m_app.GetBehaviorContext());
  657. AZ::Entity e;
  658. Activate(e);
  659. SimulateEditorBecomingInitialized();
  660. enum class LogTypes
  661. {
  662. Skip = 0,
  663. AtAddress_Match
  664. };
  665. m_testSink.m_evaluateMessage = [](const char* window, const char* message) -> int
  666. {
  667. if (AzFramework::StringFunc::Equal(window, "python"))
  668. {
  669. if (AzFramework::StringFunc::Equal(message, "AtAddress_Match"))
  670. {
  671. return static_cast<int>(LogTypes::AtAddress_Match);
  672. }
  673. }
  674. return static_cast<int>(LogTypes::Skip);
  675. };
  676. try
  677. {
  678. pybind11::exec(R"(
  679. import azlmbr.bus
  680. import azlmbr.default
  681. answer = 0
  682. def OnResult(parameters):
  683. global answer
  684. answer = int(parameters[0])
  685. handler = azlmbr.bus.NotificationHandler('PythonTestByIdNotificationBus')
  686. handler.connect(101)
  687. handler.add_callback('OnResult', OnResult)
  688. address = 101
  689. result = 40 + 2
  690. azlmbr.bus.PythonTestByIdNotificationBus(azlmbr.bus.Event, 'OnResult', address, result)
  691. if (answer == 42):
  692. print ('AtAddress_Match')
  693. handler.disconnect()
  694. azlmbr.bus.PythonTestByIdNotificationBus(azlmbr.bus.Event, 'OnResult', address, 2)
  695. if (answer == 42):
  696. print ('AtAddress_Match')
  697. )");
  698. }
  699. catch ([[maybe_unused]] const std::exception& e)
  700. {
  701. AZ_Warning("UnitTest", false, "Failed on with Python exception: %s", e.what());
  702. FAIL();
  703. }
  704. e.Deactivate();
  705. EXPECT_EQ(2, m_testSink.m_evaluationMap[static_cast<int>(LogTypes::AtAddress_Match)]);
  706. }
  707. TEST_F(PythonBusProxyTests, SingleAddressNotifications_InParallel_Errors)
  708. {
  709. PythonTestNotificationHandler pythonTestNotificationHandler;
  710. pythonTestNotificationHandler.Reflect(m_app.GetBehaviorContext());
  711. AZ::Entity e;
  712. Activate(e);
  713. SimulateEditorBecomingInitialized();
  714. enum class LogTypes
  715. {
  716. Skip = 0,
  717. Notifications_OnFire,
  718. };
  719. m_testSink.m_evaluateMessage = [](const char* window, const char* message) -> int
  720. {
  721. if (AzFramework::StringFunc::Equal(window, "python"))
  722. {
  723. if (AzFramework::StringFunc::Equal(message, "Notifications_OnFire"))
  724. {
  725. return static_cast<int>(LogTypes::Notifications_OnFire);
  726. }
  727. }
  728. return static_cast<int>(LogTypes::Skip);
  729. };
  730. UnitTest::PythonTestNotificationHandler::Reset();
  731. const int numFiresInParallel = 220;
  732. const AZStd::string script = AZStd::string::format(
  733. "import azlmbr.bus\n"
  734. "import azlmbr.test\n"
  735. "\n"
  736. "def OnFire(parameters) :\n"
  737. " print('Notifications_OnFire')\n"
  738. "\n"
  739. "handler = azlmbr.bus.NotificationHandler('PythonTestSingleAddressNotificationBus')\n"
  740. "handler.connect(None)\n"
  741. "handler.add_callback('OnFire', OnFire)\n"
  742. "\n"
  743. "azlmbr.test.PythonTestNotificationHandler_do_fire()\n"
  744. "\n"
  745. "azlmbr.test.PythonTestNotificationHandler_do_fires_in_parallel(%d)\n"
  746. "\n"
  747. "handler.disconnect()\n",
  748. numFiresInParallel);
  749. AZ_TEST_START_TRACE_SUPPRESSION;
  750. AzToolsFramework::EditorPythonRunnerRequestBus::Broadcast(&AzToolsFramework::EditorPythonRunnerRequestBus::Events::ExecuteByString, script, false);
  751. AZ_TEST_STOP_TRACE_SUPPRESSION(numFiresInParallel); // Expect numFiresInParallel errors
  752. e.Deactivate();
  753. EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast<int>(LogTypes::Notifications_OnFire)]);
  754. }
  755. TEST_F(PythonBusProxyTests, NotificationsWithNoAddress)
  756. {
  757. PythonTestNotificationHandler pythonTestNotificationHandler;
  758. pythonTestNotificationHandler.Reflect(m_app.GetBehaviorContext());
  759. AZ::Entity e;
  760. Activate(e);
  761. SimulateEditorBecomingInitialized();
  762. enum class LogTypes
  763. {
  764. Skip = 0,
  765. NoAddressConnect
  766. };
  767. m_testSink.m_evaluateMessage = [](const char* window, const char* message) -> int
  768. {
  769. if (AzFramework::StringFunc::Equal(window, "python"))
  770. {
  771. if (AzFramework::StringFunc::Equal(message, "NoAddressConnect"))
  772. {
  773. return static_cast<int>(LogTypes::NoAddressConnect);
  774. }
  775. }
  776. return static_cast<int>(LogTypes::Skip);
  777. };
  778. try
  779. {
  780. pybind11::exec(R"(
  781. import azlmbr.bus
  782. import azlmbr.test
  783. def on_ping(args):
  784. print('NoAddressConnect')
  785. handler = azlmbr.test.PythonTestSingleAddressNotificationBusHandler()
  786. handler.connect()
  787. handler.add_callback('OnPing', on_ping)
  788. azlmbr.test.PythonTestNotificationHandler_do_ping()
  789. handler.disconnect()
  790. azlmbr.test.PythonTestNotificationHandler_do_ping()
  791. )");
  792. }
  793. catch ([[maybe_unused]] const std::exception& e)
  794. {
  795. AZ_Warning("UnitTest", false, "Failed on with Python exception: %s", e.what());
  796. FAIL();
  797. }
  798. e.Deactivate();
  799. EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast<int>(LogTypes::NoAddressConnect)]);
  800. }
  801. TEST_F(PythonBusProxyTests, NotificationsWithResult)
  802. {
  803. PythonTestNotificationHandler pythonTestNotificationHandler;
  804. pythonTestNotificationHandler.Reflect(m_app.GetBehaviorContext());
  805. AZ::Entity e;
  806. Activate(e);
  807. SimulateEditorBecomingInitialized();
  808. enum class LogTypes
  809. {
  810. Skip = 0,
  811. WithResult
  812. };
  813. m_testSink.m_evaluateMessage = [](const char* window, const char* message) -> int
  814. {
  815. if (AzFramework::StringFunc::Equal(window, "python"))
  816. {
  817. if (AzFramework::StringFunc::StartsWith(message, "WithResult"))
  818. {
  819. return aznumeric_cast<int>(LogTypes::WithResult);
  820. }
  821. }
  822. return aznumeric_cast<int>(LogTypes::Skip);
  823. };
  824. try
  825. {
  826. pybind11::exec(R"(
  827. import azlmbr.bus
  828. import azlmbr.test
  829. def on_add_fish(args):
  830. value = args[0] + 'fish'
  831. return value
  832. handler = azlmbr.test.PythonTestSingleAddressNotificationBusHandler()
  833. handler.connect()
  834. handler.add_callback('OnAddFish', on_add_fish)
  835. babblefish = azlmbr.test.PythonTestNotificationHandler_do_add_fish('babble')
  836. if (babblefish == 'babblefish'):
  837. print('WithResult_babblefish')
  838. handler.disconnect()
  839. )");
  840. }
  841. catch ([[maybe_unused]] const std::exception& e)
  842. {
  843. AZ_Error("UnitTest", false, "Failed on with Python exception: %s", e.what());
  844. }
  845. e.Deactivate();
  846. EXPECT_EQ(1, m_testSink.m_evaluationMap[static_cast<int>(LogTypes::WithResult)]);
  847. }}