EventLoggerReflectUtilsTests.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  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 <AzCore/IO/ByteContainerStream.h>
  9. #include <AzCore/Metrics/EventLoggerFactoryImpl.h>
  10. #include <AzCore/Metrics/EventLoggerReflectUtils.h>
  11. #include <AzCore/Metrics/JsonTraceEventLogger.h>
  12. #include <AzCore/RTTI/BehaviorContext.h>
  13. #include <AzCore/UnitTest/TestTypes.h>
  14. namespace UnitTest::EventLoggerReflectUtilsTests
  15. {
  16. class EventLoggerReflectUtilsFixture
  17. : public LeakDetectionFixture
  18. {
  19. public:
  20. EventLoggerReflectUtilsFixture()
  21. {
  22. // Create an event logger factory
  23. m_eventLoggerFactory = AZStd::make_unique<AZ::Metrics::EventLoggerFactoryImpl>();
  24. // Register the event logger factory with the global interface
  25. AZ::Metrics::EventLoggerFactory::Register(m_eventLoggerFactory.get());
  26. // Create an byte container stream that allows event logger output to be logged in-memory
  27. auto metricsStream = AZStd::make_unique<AZ::IO::ByteContainerStream<AZStd::string>>(&m_metricsOutput);
  28. // Create the trace event logger that logs to the Google Trace Event format
  29. auto eventLogger = AZStd::make_unique<AZ::Metrics::JsonTraceEventLogger>(AZStd::move(metricsStream));
  30. // Register the Google Trace Event Logger with the Event Logger Factory
  31. EXPECT_TRUE(m_eventLoggerFactory->RegisterEventLogger(m_loggerId, AZStd::move(eventLogger)));
  32. m_behaviorContext = AZStd::make_unique<AZ::BehaviorContext>();
  33. AZ::Metrics::Reflect(m_behaviorContext.get());
  34. }
  35. ~EventLoggerReflectUtilsFixture()
  36. {
  37. AZ::Metrics::EventLoggerFactory::Unregister(m_eventLoggerFactory.get());
  38. }
  39. protected:
  40. AZStd::string m_metricsOutput;
  41. AZStd::unique_ptr<AZ::Metrics::IEventLoggerFactory> m_eventLoggerFactory;
  42. AZ::Metrics::EventLoggerId m_loggerId{ 1 };
  43. AZStd::unique_ptr<AZ::BehaviorContext> m_behaviorContext;
  44. };
  45. constexpr const char* EventValueClassName = "EventValue";
  46. constexpr const char* EventLoggerIdClassName = "EventLoggerId";
  47. constexpr const char* EventArgsClassName = "EventArgs";
  48. constexpr const char* EventPhaseClassName = "EventPhase";
  49. constexpr const char* InstantEventScopeClassName = "InstantEventScope";
  50. constexpr const char* EventLoggerFactoryClassName = "EventLoggerFactory";
  51. // EventArgs Member Method Names
  52. constexpr const char* EventArgsNameMember = "m_name";
  53. constexpr const char* EventArgsCategoryMember = "m_cat";
  54. constexpr const char* EventArgsIdMember = "m_id";
  55. constexpr const char* EventArgsAppendArgMethodName = "AppendArg";
  56. // EventFactory Member Method Names
  57. constexpr const char* EventLoggerFactoryIsRegisteredMethodName = "IsRegistered";
  58. // Global Method names
  59. constexpr const char* RecordEventMethodName = "RecordEvent";
  60. constexpr const char* CreateMetricsLoggerMethodName = "CreateMetricsLogger";
  61. constexpr const char* IsEventLoggerRegisteredMethodName = "IsEventLoggerRegistered";
  62. constexpr const char* GetGlobalEventLoggerFactoryMethodName = "GetGlobalEventLoggerFactory";
  63. TEST_F(EventLoggerReflectUtilsFixture, EventLogger_EventDataTypes_AreAccessibleInScript_Succeeds)
  64. {
  65. auto classIter = m_behaviorContext->m_classes.find(EventValueClassName);
  66. EXPECT_NE(m_behaviorContext->m_classes.end(), classIter);
  67. classIter = m_behaviorContext->m_classes.find(EventLoggerIdClassName);
  68. EXPECT_NE(m_behaviorContext->m_classes.end(), classIter);
  69. classIter = m_behaviorContext->m_classes.find(EventArgsClassName);
  70. EXPECT_NE(m_behaviorContext->m_classes.end(), classIter);
  71. classIter = m_behaviorContext->m_classes.find(EventPhaseClassName);
  72. EXPECT_NE(m_behaviorContext->m_classes.end(), classIter);
  73. classIter = m_behaviorContext->m_classes.find(InstantEventScopeClassName);
  74. EXPECT_NE(m_behaviorContext->m_classes.end(), classIter);
  75. }
  76. TEST_F(EventLoggerReflectUtilsFixture, EventLogger_EventDataTypes_CanBeCreated)
  77. {
  78. auto classIter = m_behaviorContext->m_classes.find(EventValueClassName);
  79. ASSERT_NE(m_behaviorContext->m_classes.end(), classIter);
  80. {
  81. AZ::BehaviorClass* eventValueClass = classIter->second;
  82. AZ::BehaviorClass::ScopedBehaviorObject eventValueInst = eventValueClass->CreateWithScope();
  83. EXPECT_TRUE(eventValueInst.IsValid());
  84. }
  85. classIter = m_behaviorContext->m_classes.find(EventLoggerIdClassName);
  86. ASSERT_NE(m_behaviorContext->m_classes.end(), classIter);
  87. {
  88. AZ::BehaviorClass* eventLoggerIdClass = classIter->second;
  89. AZ::BehaviorClass::ScopedBehaviorObject eventLoggerIdInst = eventLoggerIdClass->CreateWithScope();
  90. EXPECT_TRUE(eventLoggerIdInst.IsValid());
  91. }
  92. classIter = m_behaviorContext->m_classes.find(EventArgsClassName);
  93. ASSERT_NE(m_behaviorContext->m_classes.end(), classIter);
  94. {
  95. AZ::BehaviorClass* eventArgsClass = classIter->second;
  96. AZ::BehaviorClass::ScopedBehaviorObject eventArgsInst = eventArgsClass->CreateWithScope();
  97. EXPECT_TRUE(eventArgsInst.IsValid());
  98. }
  99. classIter = m_behaviorContext->m_classes.find(EventPhaseClassName);
  100. ASSERT_NE(m_behaviorContext->m_classes.end(), classIter);
  101. {
  102. AZ::BehaviorClass* eventPhaseClass = classIter->second;
  103. AZ::BehaviorClass::ScopedBehaviorObject eventPhaseInst = eventPhaseClass->CreateWithScope();
  104. EXPECT_TRUE(eventPhaseInst.IsValid());
  105. }
  106. classIter = m_behaviorContext->m_classes.find(InstantEventScopeClassName);
  107. ASSERT_NE(m_behaviorContext->m_classes.end(), classIter);
  108. {
  109. AZ::BehaviorClass* instantEventScopeClass = classIter->second;
  110. AZ::BehaviorClass::ScopedBehaviorObject instantEventScopeInst = instantEventScopeClass->CreateWithScope();
  111. EXPECT_TRUE(instantEventScopeInst.IsValid());
  112. }
  113. }
  114. TEST_F(EventLoggerReflectUtilsFixture, EventLogger_CanCreateFromIntOrString_Succeeds)
  115. {
  116. // Create an EventLoggerId instance in script that maps to Id 1
  117. auto classIter = m_behaviorContext->m_classes.find(EventLoggerIdClassName);
  118. ASSERT_NE(m_behaviorContext->m_classes.end(), classIter);
  119. {
  120. auto methodIter = classIter->second->m_methods.find("CreateIdFromInt");
  121. ASSERT_NE(classIter->second->m_methods.end(), methodIter);
  122. AZ::Metrics::EventLoggerId loggerId{ AZStd::numeric_limits<AZStd::underlying_type_t<AZ::Metrics::EventLoggerId>>::max() };
  123. EXPECT_TRUE(methodIter->second->InvokeResult(loggerId, 1U));
  124. EXPECT_EQ(1, static_cast<AZ::u32>(loggerId));
  125. }
  126. {
  127. auto methodIter = classIter->second->m_methods.find("CreateIdFromString");
  128. ASSERT_NE(classIter->second->m_methods.end(), methodIter);
  129. AZ::Metrics::EventLoggerId loggerId{ AZStd::numeric_limits<AZStd::underlying_type_t<AZ::Metrics::EventLoggerId>>::max() };
  130. EXPECT_TRUE(methodIter->second->InvokeResult(loggerId, AZStd::string_view("foo")));
  131. EXPECT_EQ(static_cast<AZ::u32>(AZStd::hash<AZStd::string_view>{}("foo")), static_cast<AZ::u32>(loggerId));
  132. }
  133. }
  134. TEST_F(EventLoggerReflectUtilsFixture, RecordEvent_CanRecordDurationEventInScript_Succeeds)
  135. {
  136. const AZ::BehaviorClass* eventArgsClass = m_behaviorContext->FindClassByReflectedName(EventArgsClassName);
  137. ASSERT_NE(nullptr, eventArgsClass);
  138. const AZ::BehaviorClass* eventValueClass = m_behaviorContext->FindClassByReflectedName(EventValueClassName);
  139. ASSERT_NE(nullptr, eventValueClass);
  140. auto eventArgsScopedObj = eventArgsClass->CreateWithScope();
  141. EXPECT_TRUE(eventArgsScopedObj.IsValid());
  142. // Stores arguments for behavior context calls
  143. AZStd::fixed_vector<AZ::BehaviorArgument, 8> behaviorArguments;
  144. // Use the bound behavior context properties and methods to configure the ScriptEventArgs
  145. {
  146. // Set `ScriptEventArgs::m_name` field
  147. const AZ::BehaviorMethod* nameSetter = eventArgsClass->FindSetterByReflectedName(EventArgsNameMember);
  148. ASSERT_NE(nullptr, nameSetter);
  149. AZStd::string eventName{ "Duration Event" };
  150. behaviorArguments.emplace_back(&eventArgsScopedObj.m_behaviorObject);
  151. behaviorArguments.emplace_back(&eventName);
  152. EXPECT_TRUE(nameSetter->Call(behaviorArguments, nullptr));
  153. }
  154. {
  155. // Set `ScriptEventArgs::m_cat` field
  156. const AZ::BehaviorMethod* nameSetter = eventArgsClass->FindSetterByReflectedName(EventArgsCategoryMember);
  157. ASSERT_NE(nullptr, nameSetter);
  158. behaviorArguments.clear();
  159. AZStd::string category{ "Test" };
  160. behaviorArguments.emplace_back(&eventArgsScopedObj.m_behaviorObject);
  161. behaviorArguments.emplace_back(&category);
  162. EXPECT_TRUE(nameSetter->Call(behaviorArguments, nullptr));
  163. }
  164. {
  165. // Set `ScriptEventArgs::m_id` field
  166. const AZ::BehaviorMethod* nameSetter = eventArgsClass->FindSetterByReflectedName(EventArgsIdMember);
  167. ASSERT_NE(nullptr, nameSetter);
  168. behaviorArguments.clear();
  169. AZStd::optional<AZStd::string> eventId{ "0" };
  170. behaviorArguments.emplace_back(&eventArgsScopedObj.m_behaviorObject);
  171. behaviorArguments.emplace_back(&eventId);
  172. EXPECT_TRUE(nameSetter->Call(behaviorArguments, nullptr));
  173. }
  174. {
  175. // Populate the ScriptEventArgs "args" object field
  176. const AZ::BehaviorMethod* appendArgMethod = eventArgsClass->FindMethodByReflectedName(EventArgsAppendArgMethodName);
  177. ASSERT_NE(nullptr, appendArgMethod);
  178. // Create an EventValue instance
  179. AZ::u64 intValue{ 20 };
  180. behaviorArguments.clear();
  181. behaviorArguments.emplace_back(&intValue);
  182. auto eventValueClassObj = eventValueClass->CreateWithScope(behaviorArguments);
  183. EXPECT_TRUE(eventValueClassObj.IsValid());
  184. AZStd::string argName{ "Entity Count" };
  185. behaviorArguments.clear();
  186. behaviorArguments.emplace_back(&eventArgsScopedObj.m_behaviorObject);
  187. behaviorArguments.emplace_back(&argName);
  188. // The AppendArg function accepts an EventValue instance by value, so it needs to be emplaced
  189. // with the BehaviorArgumentValueTypeTag
  190. behaviorArguments.emplace_back(AZ::BehaviorArgumentValueTypeTag, &eventValueClassObj.m_behaviorObject);
  191. EXPECT_TRUE(appendArgMethod->Call(behaviorArguments, nullptr));
  192. argName = "Entity Group";
  193. AZStd::string stringValue{ "Level" };
  194. behaviorArguments.clear();
  195. behaviorArguments.emplace_back(&stringValue);
  196. eventValueClassObj = eventValueClass->CreateWithScope(behaviorArguments);
  197. EXPECT_TRUE(eventValueClassObj.IsValid());
  198. // Invoke the AppendArg method this time with a string value
  199. behaviorArguments.clear();
  200. behaviorArguments.emplace_back(&eventArgsScopedObj.m_behaviorObject);
  201. behaviorArguments.emplace_back(&argName);
  202. behaviorArguments.emplace_back(AZ::BehaviorArgumentValueTypeTag, &eventValueClassObj.m_behaviorObject);
  203. EXPECT_TRUE(appendArgMethod->Call(behaviorArguments, nullptr));
  204. }
  205. // Finally invoke the RecordEvent function
  206. const AZ::BehaviorMethod* recordEventMethod = m_behaviorContext->FindMethodByReflectedName(RecordEventMethodName);
  207. ASSERT_NE(nullptr, recordEventMethod);
  208. constexpr auto durationBeginPhase = AZ::Metrics::EventPhase::DurationBegin;
  209. behaviorArguments.clear();
  210. behaviorArguments.emplace_back(&m_loggerId);
  211. behaviorArguments.emplace_back(&durationBeginPhase);
  212. behaviorArguments.emplace_back(AZ::BehaviorArgumentValueTypeTag, &eventArgsScopedObj.m_behaviorObject);
  213. // The return value argument needs storage
  214. bool returnStorage{};
  215. AZ::BehaviorArgument returnValue(&returnStorage);
  216. EXPECT_TRUE(recordEventMethod->Call(behaviorArguments, &returnValue));
  217. ASSERT_EQ(azrtti_typeid<bool>(), returnValue.m_typeId);
  218. bool* recordEventResult = returnValue.GetAsUnsafe<bool>();
  219. ASSERT_NE(nullptr, recordEventResult);
  220. EXPECT_TRUE(*recordEventResult);
  221. // Validate that the output contains an "Entity Count and an "Entity Group" argument
  222. EXPECT_TRUE(m_metricsOutput.contains("Entity Count"));
  223. EXPECT_TRUE(m_metricsOutput.contains("Entity Group"));
  224. }
  225. TEST_F(EventLoggerReflectUtilsFixture, RecordEvent_CanCreateMetricsLoggerInScript_Succeeds)
  226. {
  227. const AZ::BehaviorMethod* createLoggerMethod = m_behaviorContext->FindMethodByReflectedName(CreateMetricsLoggerMethodName);
  228. ASSERT_NE(nullptr, createLoggerMethod);
  229. constexpr AZ::Metrics::EventLoggerId scriptLoggerId{ 2 };
  230. AZ::Test::ScopedAutoTempDirectory tempDirectory;
  231. const auto loggerPath = tempDirectory.GetDirectoryAsFixedMaxPath() / "script_logger.json";
  232. constexpr AZStd::string_view loggerName = "ScriptLogger";
  233. bool createLoggerResult{};
  234. EXPECT_TRUE(createLoggerMethod->InvokeResult(createLoggerResult, scriptLoggerId, loggerPath.c_str(), loggerName));
  235. EXPECT_TRUE(createLoggerResult);
  236. // Validate that the event logger created during script is registered with the global event logger factory
  237. EXPECT_TRUE(m_eventLoggerFactory->IsRegistered(scriptLoggerId));
  238. // Also validate that the script IsEventLoggerRegistered function succeeds as well
  239. const AZ::BehaviorMethod* isEventLoggerRegisteredMethod = m_behaviorContext->FindMethodByReflectedName(
  240. IsEventLoggerRegisteredMethodName);
  241. ASSERT_NE(nullptr, isEventLoggerRegisteredMethod);
  242. bool isEventLoggerRegistered{};
  243. EXPECT_TRUE(isEventLoggerRegisteredMethod->InvokeResult(isEventLoggerRegistered, scriptLoggerId));
  244. EXPECT_TRUE(isEventLoggerRegistered);
  245. // Finally validate that the EventFactory IsRegistered member function succeeds as well
  246. const AZ::BehaviorMethod* getGlobalEventLoggerFactoryMethod = m_behaviorContext->FindMethodByReflectedName(
  247. GetGlobalEventLoggerFactoryMethodName);
  248. ASSERT_NE(nullptr, getGlobalEventLoggerFactoryMethod);
  249. AZ::Metrics::IEventLoggerFactory* eventLoggerFactory{};
  250. EXPECT_TRUE(getGlobalEventLoggerFactoryMethod->InvokeResult(eventLoggerFactory));
  251. // This event logger factory should be the same address as the member variable
  252. ASSERT_EQ(m_eventLoggerFactory.get(), eventLoggerFactory);
  253. // reset back to false;
  254. isEventLoggerRegistered = {};
  255. // Lookup the EventLoggerFactory BehaviorClass in order to lookup the IsRegistered member function
  256. const AZ::BehaviorClass* eventLoggerFactoryClass = m_behaviorContext->FindClassByReflectedName(EventLoggerFactoryClassName);
  257. ASSERT_NE(nullptr, eventLoggerFactoryClass);
  258. const AZ::BehaviorMethod* isRegisteredMethod = eventLoggerFactoryClass->FindMethodByReflectedName(
  259. EventLoggerFactoryIsRegisteredMethodName);
  260. ASSERT_NE(nullptr, isRegisteredMethod);
  261. EXPECT_TRUE(isRegisteredMethod->InvokeResult(isEventLoggerRegistered, eventLoggerFactory, scriptLoggerId));
  262. EXPECT_TRUE(isEventLoggerRegistered);
  263. }
  264. } // namespace UnitTest::EventLoggerReflectUtilsTests