Module.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  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/Component/ComponentApplication.h>
  9. #include <AzCore/Module/Module.h>
  10. #include <AzCore/PlatformIncl.h>
  11. #include <AzCore/Module/ModuleManagerBus.h>
  12. #include <AzCore/Memory/AllocationRecords.h>
  13. #include <AzCore/UnitTest/TestTypes.h>
  14. #include "ModuleTestBus.h"
  15. #if !AZ_UNIT_TEST_SKIP_DLL_TEST
  16. #if AZ_TRAIT_TEST_SUPPORT_DLOPEN
  17. #include <dlfcn.h>
  18. #endif
  19. using namespace AZ;
  20. using ::testing::Return;
  21. using ::testing::StrEq;
  22. using ::testing::Matcher;
  23. namespace UnitTest
  24. {
  25. static const AZ::Uuid AZCoreTestsDLLModuleId{ "{99C6BF95-847F-4EEE-BB60-9B26D02FF577}" };
  26. class SystemComponentRequests
  27. : public AZ::EBusTraits
  28. {
  29. public:
  30. virtual bool IsConnected() = 0;
  31. };
  32. using SystemComponentRequestBus = AZ::EBus<SystemComponentRequests>;
  33. class SystemComponentFromModule
  34. : public AZ::Component
  35. , protected SystemComponentRequestBus::Handler
  36. {
  37. public:
  38. AZ_COMPONENT(SystemComponentFromModule, "{7CDDF71F-4D9E-41B0-8F82-4FFA86513809}")
  39. void Activate() override
  40. {
  41. SystemComponentRequestBus::Handler::BusConnect();
  42. }
  43. void Deactivate() override
  44. {
  45. SystemComponentRequestBus::Handler::BusDisconnect();
  46. }
  47. static void Reflect(AZ::ReflectContext* reflectContext)
  48. {
  49. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext))
  50. {
  51. serializeContext->Class<SystemComponentFromModule, AZ::Component>()
  52. ;
  53. }
  54. }
  55. protected:
  56. bool IsConnected() override
  57. {
  58. return true;
  59. }
  60. };
  61. class StaticModule
  62. : public Module
  63. , public ModuleTestRequestBus::Handler
  64. {
  65. public:
  66. AZ_CLASS_ALLOCATOR(StaticModule, AZ::SystemAllocator)
  67. static bool s_loaded;
  68. static bool s_reflected;
  69. StaticModule()
  70. {
  71. s_loaded = true;
  72. ModuleTestRequestBus::Handler::BusConnect();
  73. m_descriptors.insert(m_descriptors.end(), {
  74. SystemComponentFromModule::CreateDescriptor(),
  75. });
  76. }
  77. ~StaticModule() override
  78. {
  79. ModuleTestRequestBus::Handler::BusDisconnect();
  80. s_loaded = false;
  81. }
  82. //void Reflect(ReflectContext*) override
  83. //{
  84. // s_reflected = true;
  85. //}
  86. AZ::ComponentTypeList GetRequiredSystemComponents() const override
  87. {
  88. return AZ::ComponentTypeList{
  89. azrtti_typeid<SystemComponentFromModule>(),
  90. };
  91. }
  92. const char* GetModuleName() override
  93. {
  94. return "StaticModule";
  95. }
  96. };
  97. bool StaticModule::s_loaded = false;
  98. bool StaticModule::s_reflected = false;
  99. void AZCreateStaticModules(AZStd::vector<AZ::Module*>& modulesOut)
  100. {
  101. modulesOut.push_back(new UnitTest::StaticModule());
  102. }
  103. class ModuleManager : public UnitTest::LeakDetectionFixture
  104. {
  105. };
  106. #if AZ_TRAIT_DISABLE_FAILED_MODULE_TESTS
  107. TEST_F(ModuleManager, DISABLED_Test)
  108. #else
  109. TEST_F(ModuleManager, Test)
  110. #endif // AZ_TRAIT_DISABLE_FAILED_MODULE_TESTS
  111. {
  112. {
  113. ComponentApplication app;
  114. // Create application descriptor
  115. ComponentApplication::Descriptor appDesc;
  116. appDesc.m_memoryBlocksByteSize = 10 * 1024 * 1024;
  117. appDesc.m_recordingMode = Debug::AllocationRecords::Mode::RECORD_FULL;
  118. // AZCoreTestDLL will load as a dynamic module
  119. DynamicModuleDescriptor& dynamicModuleDescriptor = appDesc.m_modules.emplace_back();
  120. dynamicModuleDescriptor.m_dynamicLibraryPath = "AzCoreTestDLL";
  121. // StaticModule will load via AZCreateStaticModule(...)
  122. // Start up application
  123. ComponentApplication::StartupParameters startupParams;
  124. startupParams.m_createStaticModulesCallback = AZCreateStaticModules;
  125. startupParams.m_loadSettingsRegistry = false;
  126. Entity* systemEntity = app.Create(appDesc, startupParams);
  127. EXPECT_NE(nullptr, systemEntity);
  128. systemEntity->Init();
  129. systemEntity->Activate();
  130. // Check that StaticModule was loaded and reflected
  131. EXPECT_TRUE(StaticModule::s_loaded);
  132. // AZ_TEST_ASSERT(StaticModule::s_reflected);
  133. { // Query both modules via the ModuleTestRequestBus
  134. EBusAggregateResults<const char*> moduleNames;
  135. ModuleTestRequestBus::BroadcastResult(moduleNames, &ModuleTestRequestBus::Events::GetModuleName);
  136. EXPECT_TRUE(moduleNames.values.size() == 2);
  137. bool foundStaticModule = false;
  138. bool foundDynamicModule = false;
  139. for (const char* moduleName : moduleNames.values)
  140. {
  141. if (strcmp(moduleName, "DllModule") == 0)
  142. {
  143. foundDynamicModule = true;
  144. }
  145. if (strcmp(moduleName, "StaticModule") == 0)
  146. {
  147. foundStaticModule = true;
  148. }
  149. }
  150. EXPECT_TRUE(foundDynamicModule);
  151. EXPECT_TRUE(foundStaticModule);
  152. }
  153. // Check that system component from module was added
  154. bool isComponentAround = false;
  155. SystemComponentRequestBus::BroadcastResult(isComponentAround, &SystemComponentRequestBus::Events::IsConnected);
  156. EXPECT_TRUE(isComponentAround);
  157. {
  158. // Find the dynamic module
  159. const ModuleData* systemLoadedModule = nullptr;
  160. ModuleManagerRequestBus::Broadcast(&ModuleManagerRequestBus::Events::EnumerateModules, [&systemLoadedModule](const ModuleData& moduleData) {
  161. if (azrtti_typeid(moduleData.GetModule()) == AZCoreTestsDLLModuleId)
  162. {
  163. systemLoadedModule = &moduleData;
  164. return false;
  165. }
  166. else
  167. {
  168. return true;
  169. }
  170. });
  171. ASSERT_NE(nullptr, systemLoadedModule);
  172. ModuleManagerRequests::LoadModuleOutcome loadResult = AZ::Failure(AZStd::string("Failed to connect to ModuleManagerRequestBus"));
  173. // Load the module
  174. ModuleManagerRequestBus::BroadcastResult(loadResult, &ModuleManagerRequestBus::Events::LoadDynamicModule, "AzCoreTestDLL", ModuleInitializationSteps::ActivateEntity, true);
  175. ASSERT_TRUE(loadResult.IsSuccess());
  176. // Capture the handle
  177. AZStd::shared_ptr<ModuleData> moduleHandle = AZStd::move(loadResult.GetValue());
  178. // Validate that the pointer is the same as the one the system loaded
  179. EXPECT_EQ(systemLoadedModule, moduleHandle.get());
  180. // Load the module again
  181. ModuleManagerRequestBus::BroadcastResult(loadResult, &ModuleManagerRequestBus::Events::LoadDynamicModule, "AzCoreTestDLL", ModuleInitializationSteps::ActivateEntity, true);
  182. ASSERT_TRUE(loadResult.IsSuccess());
  183. // Validate that the pointers from the load calls are the same
  184. EXPECT_EQ(moduleHandle.get(), loadResult.GetValue().get());
  185. }
  186. // shut down application (deletes Modules, unloads DLLs)
  187. app.Destroy();
  188. }
  189. EXPECT_FALSE(StaticModule::s_loaded);
  190. bool isComponentAround = false;
  191. SystemComponentRequestBus::BroadcastResult(isComponentAround, &SystemComponentRequestBus::Events::IsConnected);
  192. EXPECT_FALSE(isComponentAround);
  193. }
  194. #if AZ_TRAIT_DISABLE_FAILED_MODULE_TESTS
  195. TEST_F(ModuleManager, DISABLED_SequentialLoadTest)
  196. #else
  197. TEST_F(ModuleManager, SequentialLoadTest)
  198. #endif
  199. {
  200. {
  201. ComponentApplication app;
  202. // Start up application
  203. ComponentApplication::Descriptor appDesc;
  204. ComponentApplication::StartupParameters startupParams;
  205. startupParams.m_loadSettingsRegistry = false;
  206. Entity* systemEntity = app.Create(appDesc, startupParams);
  207. EXPECT_NE(nullptr, systemEntity);
  208. systemEntity->Init();
  209. systemEntity->Activate();
  210. {
  211. // this scope exists to clear memory before app is destroyed.
  212. ModuleManagerRequests::LoadModuleOutcome loadResult = AZ::Failure(AZStd::string("Failed to connect to ModuleManagerRequestBus"));
  213. // Create the module
  214. ModuleManagerRequestBus::BroadcastResult(loadResult, &ModuleManagerRequestBus::Events::LoadDynamicModule, "AzCoreTestDLL", ModuleInitializationSteps::None, true);
  215. ASSERT_TRUE(loadResult.IsSuccess());
  216. // Find the dynamic module
  217. const ModuleData* systemLoadedModule = nullptr;
  218. ModuleManagerRequestBus::Broadcast(&ModuleManagerRequestBus::Events::EnumerateModules, [&systemLoadedModule](const ModuleData& moduleData)
  219. {
  220. // Because the module was loaded with ModuleInitializationSteps::None, it should be the only one that doesn't have a module class
  221. if (!moduleData.GetModule())
  222. {
  223. systemLoadedModule = &moduleData;
  224. return false;
  225. }
  226. else
  227. {
  228. return true;
  229. }
  230. });
  231. // Test that the module exists, but is empty
  232. ASSERT_NE(nullptr, systemLoadedModule);
  233. EXPECT_EQ(nullptr, systemLoadedModule->GetDynamicModuleHandle());
  234. EXPECT_EQ(nullptr, systemLoadedModule->GetModule());
  235. EXPECT_EQ(nullptr, systemLoadedModule->GetEntity());
  236. // Capture the handle
  237. AZStd::shared_ptr<ModuleData> moduleHandle = AZStd::move(loadResult.GetValue());
  238. // Validate that the pointer is the same as the one the system loaded
  239. EXPECT_EQ(systemLoadedModule, moduleHandle.get());
  240. // Load the module
  241. ModuleManagerRequestBus::BroadcastResult(loadResult, &ModuleManagerRequestBus::Events::LoadDynamicModule, "AzCoreTestDLL", ModuleInitializationSteps::Load, true);
  242. ASSERT_TRUE(loadResult.IsSuccess());
  243. // Validate that the pointers from the load calls are the same
  244. EXPECT_EQ(moduleHandle.get(), loadResult.GetValue().get());
  245. EXPECT_NE(nullptr, systemLoadedModule->GetDynamicModuleHandle());
  246. EXPECT_EQ(nullptr, systemLoadedModule->GetModule());
  247. EXPECT_EQ(nullptr, systemLoadedModule->GetEntity());
  248. // Create the module class
  249. ModuleManagerRequestBus::BroadcastResult(loadResult, &ModuleManagerRequestBus::Events::LoadDynamicModule, "AzCoreTestDLL", ModuleInitializationSteps::CreateClass, true);
  250. ASSERT_TRUE(loadResult.IsSuccess());
  251. EXPECT_EQ(moduleHandle.get(), loadResult.GetValue().get());
  252. EXPECT_NE(nullptr, systemLoadedModule->GetDynamicModuleHandle());
  253. EXPECT_NE(nullptr, systemLoadedModule->GetModule());
  254. EXPECT_EQ(nullptr, systemLoadedModule->GetEntity());
  255. // Activate the system entity
  256. ModuleManagerRequestBus::BroadcastResult(loadResult, &ModuleManagerRequestBus::Events::LoadDynamicModule, "AzCoreTestDLL", ModuleInitializationSteps::ActivateEntity, true);
  257. ASSERT_TRUE(loadResult.IsSuccess());
  258. EXPECT_EQ(moduleHandle.get(), loadResult.GetValue().get());
  259. EXPECT_NE(nullptr, systemLoadedModule->GetDynamicModuleHandle());
  260. EXPECT_NE(nullptr, systemLoadedModule->GetModule());
  261. EXPECT_NE(nullptr, systemLoadedModule->GetEntity());
  262. }
  263. // shut down application (deletes Modules, unloads DLLs)
  264. app.Destroy();
  265. }
  266. }
  267. // the following tests only run on the following platforms which support module loading and unloading
  268. // as these platforms expand we can always use traits to include the ones that can do so:
  269. #if AZ_TRAIT_TEST_SUPPORT_MODULE_LOADING
  270. // this class just attaches to the PrintF stream and watches for a specific message
  271. // to appear.
  272. class PrintFCollector : public AZ::Debug::TraceMessageBus::Handler
  273. {
  274. public:
  275. PrintFCollector(const char* stringToWatchFor)
  276. :m_stringToWatchFor(stringToWatchFor)
  277. {
  278. BusConnect();
  279. }
  280. bool OnPrintf(const char* window, const char* message) override
  281. {
  282. if (
  283. ((window)&&(strstr(window, m_stringToWatchFor.c_str()))) ||
  284. ((message)&&(strstr(message, m_stringToWatchFor.c_str())))
  285. )
  286. {
  287. m_foundWhatWeWereWatchingFor = true;
  288. }
  289. return false;
  290. }
  291. ~PrintFCollector() override
  292. {
  293. BusDisconnect();
  294. }
  295. bool m_foundWhatWeWereWatchingFor = false;
  296. AZ::OSString m_stringToWatchFor;
  297. };
  298. TEST_F(ModuleManager, OwnerInitializesAndDeinitializesTest)
  299. {
  300. // in this test, we make sure that a module is always initialized even if the operating
  301. // system previously loaded it (due to static linkage or other reason)
  302. // and that when it is initialized in this manner, it is also deinitialized when the owner
  303. // unloads it (even if the operating system still has a handle to it).
  304. // note that the above test already tests repeated loads and unloads, so there is no
  305. // need to test that here.
  306. ComponentApplication app;
  307. // Start up application
  308. ComponentApplication::Descriptor appDesc;
  309. ComponentApplication::StartupParameters startupParams;
  310. startupParams.m_loadSettingsRegistry = false;
  311. Entity* systemEntity = app.Create(appDesc, startupParams);
  312. ASSERT_NE(nullptr, systemEntity);
  313. systemEntity->Init();
  314. systemEntity->Activate();
  315. // we open a scope here to make sure any heap allocations made by local variables during this test
  316. // are destroyed before we try to stop the app.
  317. {
  318. // we will use the fact that DynamicModuleHandle resolves paths to operating system specific
  319. // paths without actually calling Load(), and capture the final name it uses to load modules so that we
  320. // can manually load it ourselves.
  321. AZ::OSString finalPath;
  322. {
  323. auto handle = DynamicModuleHandle::Create("AzCoreTestDLL");
  324. finalPath = handle->GetFilename();
  325. }
  326. // now that we know the true name of the module in a way that it could be loaded by the operating system,
  327. // we need to actually load the module using the operating system loader so that its "already loaded" by OS.
  328. {
  329. #if AZ_TRAIT_TEST_SUPPORT_LOADLIBRARY
  330. // expect the module to not currently be loaded.
  331. EXPECT_EQ(nullptr, GetModuleHandleA(finalPath.c_str()));
  332. HMODULE mod = ::LoadLibraryA(finalPath.c_str());
  333. ASSERT_NE(nullptr, mod);
  334. #elif AZ_TRAIT_TEST_SUPPORT_DLOPEN
  335. void* pHandle = dlopen(finalPath.c_str(), RTLD_NOW);
  336. ASSERT_NE(nullptr, pHandle);
  337. #endif
  338. // now that the operating system has an open handle to it, we load it using the
  339. // AZ functions, and make sure that the AZ library correctly attaches even though
  340. // the OS already has it open:
  341. PrintFCollector watchForDestruction("UninitializeDynamicModule called");
  342. PrintFCollector watchForCreation("InitializeDynamicModule called");
  343. {
  344. auto handle = DynamicModuleHandle::Create("AzCoreTestDLL");
  345. handle->Load(AZ::DynamicModuleHandle::LoadFlags::InitFuncRequired);
  346. EXPECT_TRUE(watchForCreation.m_foundWhatWeWereWatchingFor); // should not destroy until we leave scope.
  347. // steal the file path (which will be resolved with per-platform extensions like DLL or SO.
  348. EXPECT_FALSE(watchForDestruction.m_foundWhatWeWereWatchingFor); // should not destroy until we leave scope.
  349. PrintFCollector watchForCreationSecondTime("InitializeDynamicModule called");
  350. auto handle2 = DynamicModuleHandle::Create("AzCoreTestDLL");
  351. handle2->Load(AZ::DynamicModuleHandle::LoadFlags::InitFuncRequired);
  352. // this should NOT have initialized it again:
  353. EXPECT_FALSE(watchForCreationSecondTime.m_foundWhatWeWereWatchingFor); // should not destroy until we leave scope.
  354. }
  355. EXPECT_TRUE(watchForDestruction.m_foundWhatWeWereWatchingFor); // we have left scope, destroy should have occurred.
  356. // drop the operating systems attachment to the module:
  357. #if AZ_TRAIT_TEST_SUPPORT_LOADLIBRARY
  358. ::FreeLibrary(mod);
  359. #elif AZ_TRAIT_TEST_SUPPORT_DLOPEN
  360. dlclose(pHandle);
  361. #endif // platform switch statement
  362. }
  363. }
  364. // shut down application (deletes Modules, unloads DLLs)
  365. app.Destroy();
  366. }
  367. #endif // AZ_TRAIT_TEST_SUPPORT_MODULE_LOADING
  368. } // namespace UnitTest
  369. #endif // !AZ_UNIT_TEST_SKIP_DLL_TEST