BaseAssetManagerTest.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  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 <Tests/Asset/BaseAssetManagerTest.h>
  9. #include <AzCore/Asset/AssetSerializer.h>
  10. #include <AzCore/Console/IConsole.h>
  11. #include <AzCore/Interface/Interface.h>
  12. #include <AzCore/IO/SystemFile.h>
  13. #include <AzCore/IO/Streamer/Streamer.h>
  14. #include <AzCore/IO/FileIO.h>
  15. #include <AzCore/IO/GenericStreams.h>
  16. #include <AzCore/Math/Crc.h>
  17. #include <AzCore/Jobs/JobManager.h>
  18. #include <AzCore/Jobs/JobContext.h>
  19. #include <AzCore/Outcome/Outcome.h>
  20. #include <AzCore/Serialization/SerializeContext.h>
  21. #include <AzCore/Serialization/ObjectStream.h>
  22. #include <AzCore/Serialization/Utils.h>
  23. #include <AzCore/std/parallel/thread.h>
  24. #include <AzCore/std/functional.h>
  25. #include <AzCore/std/parallel/condition_variable.h>
  26. #include <AzCore/UnitTest/TestTypes.h>
  27. #include <AZTestShared/Utils/Utils.h>
  28. #include <Streamer/IStreamerMock.h>
  29. namespace UnitTest
  30. {
  31. /**
  32. * Find the current status of the reload
  33. */
  34. AZ::Data::AssetData::AssetStatus TestAssetManager::GetReloadStatus(const AssetId& assetId)
  35. {
  36. AZStd::lock_guard<AZStd::recursive_mutex> assetLock(m_assetMutex);
  37. auto reloadInfo = m_reloads.find(assetId);
  38. if (reloadInfo != m_reloads.end())
  39. {
  40. return reloadInfo->second.GetStatus();
  41. }
  42. return AZ::Data::AssetData::AssetStatus::NotLoaded;
  43. }
  44. const AZ::Data::AssetManager::OwnedAssetContainerMap& TestAssetManager::GetAssetContainers() const
  45. {
  46. return m_ownedAssetContainers;
  47. }
  48. const AssetManager::AssetMap& TestAssetManager::GetAssets() const
  49. {
  50. return m_assets;
  51. }
  52. void BaseAssetManagerTest::SetUp()
  53. {
  54. SerializeContextFixture::SetUp();
  55. SuppressTraceOutput(false);
  56. AZ::JobManagerDesc jobDesc;
  57. AZ::JobManagerThreadDesc threadDesc;
  58. for (size_t threadCount = 0; threadCount < GetNumJobManagerThreads(); threadCount++)
  59. {
  60. jobDesc.m_workerThreads.push_back(threadDesc);
  61. }
  62. m_jobManager = aznew AZ::JobManager(jobDesc);
  63. m_jobContext = aznew AZ::JobContext(*m_jobManager);
  64. AZ::JobContext::SetGlobalContext(m_jobContext);
  65. m_taskExecutor = aznew TaskExecutor();
  66. TaskExecutor::SetInstance(m_taskExecutor);
  67. m_prevFileIO = IO::FileIOBase::GetInstance();
  68. IO::FileIOBase::SetInstance(&m_fileIO);
  69. m_streamer = CreateStreamer();
  70. if (m_streamer)
  71. {
  72. Interface<IO::IStreamer>::Register(m_streamer);
  73. }
  74. }
  75. void BaseAssetManagerTest::TearDown()
  76. {
  77. if (m_streamer)
  78. {
  79. Interface<IO::IStreamer>::Unregister(m_streamer);
  80. }
  81. DestroyStreamer(m_streamer);
  82. // Clean up any temporary asset files created during the test.
  83. for (auto& assetName : m_assetsWritten)
  84. {
  85. DeleteAssetFromDisk(assetName);
  86. }
  87. // Make sure to clear the memory from the name storage before shutting down the allocator.
  88. m_assetsWritten.clear();
  89. m_assetsWritten.shrink_to_fit();
  90. IO::FileIOBase::SetInstance(m_prevFileIO);
  91. TaskExecutor::SetInstance(nullptr);
  92. delete m_taskExecutor;
  93. AZ::JobContext::SetGlobalContext(nullptr);
  94. delete m_jobContext;
  95. delete m_jobManager;
  96. // Reset back to default suppression settings to avoid affecting other tests
  97. SuppressTraceOutput(true);
  98. SerializeContextFixture::TearDown();
  99. }
  100. void BaseAssetManagerTest::SuppressTraceOutput(bool suppress)
  101. {
  102. UnitTest::TestRunner::Instance().m_suppressAsserts = suppress;
  103. UnitTest::TestRunner::Instance().m_suppressErrors = suppress;
  104. UnitTest::TestRunner::Instance().m_suppressWarnings = suppress;
  105. UnitTest::TestRunner::Instance().m_suppressPrintf = suppress;
  106. UnitTest::TestRunner::Instance().m_suppressOutput = suppress;
  107. }
  108. void BaseAssetManagerTest::WriteAssetToDisk(const AZStd::string& assetName, [[maybe_unused]] const AZStd::string& assetIdGuid)
  109. {
  110. AZ::IO::Path assetFileName = GetTestFolderPath() / assetName;
  111. AssetWithCustomData asset;
  112. EXPECT_TRUE(AZ::Utils::SaveObjectToFile(assetFileName.Native(), AZ::DataStream::ST_XML, &asset, m_serializeContext));
  113. // Keep track of every asset written so that we can remove it on teardown
  114. m_assetsWritten.emplace_back(AZStd::move(assetFileName).Native());
  115. }
  116. void BaseAssetManagerTest::DeleteAssetFromDisk(const AZStd::string& assetName)
  117. {
  118. AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
  119. if (fileIO->Exists(assetName.c_str()))
  120. {
  121. fileIO->Remove(assetName.c_str());
  122. }
  123. }
  124. void BaseAssetManagerTest::BlockUntilAssetJobsAreComplete()
  125. {
  126. auto maxTimeout = AZStd::chrono::steady_clock::now() + DefaultTimeoutSeconds;
  127. while (AssetManager::Instance().HasActiveJobsOrStreamerRequests())
  128. {
  129. if (AZStd::chrono::steady_clock::now() > maxTimeout)
  130. {
  131. break;
  132. }
  133. AZStd::this_thread::yield();
  134. }
  135. EXPECT_FALSE(AssetManager::Instance().HasActiveJobsOrStreamerRequests());
  136. }
  137. MemoryStreamerWrapper::MemoryStreamerWrapper()
  138. {
  139. using ::testing::_;
  140. using ::testing::NiceMock;
  141. using ::testing::Return;
  142. ON_CALL(m_mockStreamer, SuspendProcessing()).WillByDefault([this]()
  143. {
  144. m_suspended = true;
  145. });
  146. ON_CALL(m_mockStreamer, ResumeProcessing()).WillByDefault([this]()
  147. {
  148. AZStd::unique_lock lock(m_mutex);
  149. m_suspended = false;
  150. while (!m_processingQueue.empty())
  151. {
  152. FileRequestHandle requestHandle = m_processingQueue.front();
  153. m_processingQueue.pop();
  154. const auto& onCompleteCallback = GetReadRequest(requestHandle)->m_callback;
  155. if (onCompleteCallback)
  156. {
  157. onCompleteCallback(requestHandle);
  158. }
  159. }
  160. });
  161. ON_CALL(m_mockStreamer, Read(_, ::testing::An<IStreamerTypes::RequestMemoryAllocator&>(), _, _, _, _))
  162. .WillByDefault(
  163. [this](
  164. [[maybe_unused]] AZStd::string_view relativePath, IStreamerTypes::RequestMemoryAllocator& allocator, size_t size,
  165. AZStd::chrono::microseconds deadline, IStreamerTypes::Priority priority, [[maybe_unused]] size_t offset)
  166. {
  167. AZStd::unique_lock lock(m_mutex);
  168. ReadRequest request;
  169. // Save off the requested deadline and priority
  170. request.m_deadline = deadline;
  171. request.m_priority = priority;
  172. request.m_data = allocator.Allocate(size, size, 8);
  173. const auto* virtualFile = FindFile(relativePath);
  174. AZ_Assert(
  175. virtualFile->size() == size, "Streamer read request size did not match size of saved file: %d vs %d (%.*s)",
  176. virtualFile->size(), size,
  177. relativePath.size(), relativePath.data());
  178. AZ_Assert(size > 0, "Size is zero %.*s", relativePath.size(), relativePath.data());
  179. memcpy(request.m_data.m_address, virtualFile->data(), size);
  180. // Create a real file request result and return it
  181. request.m_request = m_context.GetNewExternalRequest();
  182. m_readRequests.push_back(request);
  183. return request.m_request;
  184. });
  185. ON_CALL(m_mockStreamer, SetRequestCompleteCallback(_, _))
  186. .WillByDefault([this](FileRequestPtr& request, AZ::IO::IStreamer::OnCompleteCallback callback) -> FileRequestPtr&
  187. {
  188. // Save off the callback just so that we can call it when the request is "done"
  189. AZStd::unique_lock lock(m_mutex);
  190. ReadRequest* readRequest = GetReadRequest(request);
  191. readRequest->m_callback = callback;
  192. return request;
  193. });
  194. ON_CALL(m_mockStreamer, QueueRequest(_))
  195. .WillByDefault([this](const auto& fileRequest)
  196. {
  197. if (!m_suspended)
  198. {
  199. decltype(ReadRequest::m_callback) onCompleteCallback;
  200. AZStd::unique_lock lock(m_mutex);
  201. ReadRequest* readRequest = GetReadRequest(fileRequest);
  202. onCompleteCallback = readRequest->m_callback;
  203. if (onCompleteCallback)
  204. {
  205. onCompleteCallback(fileRequest);
  206. m_readRequests.erase(readRequest);
  207. }
  208. }
  209. else
  210. {
  211. AZStd::unique_lock lock(m_mutex);
  212. m_processingQueue.push(fileRequest);
  213. }
  214. });
  215. ON_CALL(m_mockStreamer, GetRequestStatus(_))
  216. .WillByDefault([]([[maybe_unused]] FileRequestHandle request)
  217. {
  218. // Return whatever request status has been set in this class
  219. return IO::IStreamerTypes::RequestStatus::Completed;
  220. });
  221. ON_CALL(m_mockStreamer, GetReadRequestResult(_, _, _, _))
  222. .WillByDefault([this](
  223. [[maybe_unused]] FileRequestHandle request, void*& buffer, AZ::u64& numBytesRead,
  224. IStreamerTypes::ClaimMemory claimMemory)
  225. {
  226. // Make sure the requestor plans to free the data buffer we allocated.
  227. EXPECT_EQ(claimMemory, IStreamerTypes::ClaimMemory::Yes);
  228. AZStd::unique_lock lock(m_mutex);
  229. ReadRequest* readRequest = GetReadRequest(request);
  230. // Provide valid data buffer results.
  231. numBytesRead = readRequest->m_data.m_size;
  232. buffer = readRequest->m_data.m_address;
  233. return true;
  234. });
  235. ON_CALL(m_mockStreamer, RescheduleRequest(_, _, _))
  236. .WillByDefault([this](IO::FileRequestPtr target, AZStd::chrono::microseconds newDeadline, IO::IStreamerTypes::Priority newPriority)
  237. {
  238. AZStd::unique_lock lock(m_mutex);
  239. ReadRequest* readRequest = GetReadRequest(target);
  240. readRequest->m_deadline = newDeadline;
  241. readRequest->m_priority = newPriority;
  242. return target;
  243. });
  244. }
  245. ReadRequest* MemoryStreamerWrapper::GetReadRequest(FileRequestHandle request)
  246. {
  247. auto itr = AZStd::find_if(
  248. m_readRequests.begin(), m_readRequests.end(),
  249. [request](const ReadRequest& searchItem) -> bool
  250. {
  251. return (searchItem.m_request == request);
  252. });
  253. return itr;
  254. }
  255. AZStd::vector<char>* MemoryStreamerWrapper::FindFile(AZStd::string_view path)
  256. {
  257. auto itr = m_virtualFiles.find(path);
  258. if (itr == m_virtualFiles.end())
  259. {
  260. // Path didn't work as-is, does it have the test folder prefixed? If so try removing it
  261. AZ::IO::Path testFolderPath = GetTestFolderPath();
  262. if (AZ::IO::PathView(path).IsRelativeTo(testFolderPath))
  263. {
  264. AZ::IO::Path pathWithoutFolder = AZ::IO::PathView(path).LexicallyProximate(testFolderPath).String();
  265. itr = m_virtualFiles.find(pathWithoutFolder);
  266. }
  267. else // Path isn't prefixed, so try adding it
  268. {
  269. itr = m_virtualFiles.find(GetTestFolderPath() / path);
  270. }
  271. }
  272. if (itr != m_virtualFiles.end())
  273. {
  274. return &itr->second;
  275. }
  276. // Currently no test expects a file not to exist so we assert to make it easy to quickly find where something went wrong
  277. // If we ever need to test for a non-existent file this assert should just be conditionally disabled for that specific test
  278. AZ_Assert(false, "Failed to find virtual file %.*s", AZ_STRING_ARG(path))
  279. return nullptr;
  280. }
  281. void DisklessAssetManagerBase::SetUp()
  282. {
  283. using ::testing::_;
  284. using ::testing::NiceMock;
  285. using ::testing::Return;
  286. BaseAssetManagerTest::SetUp();
  287. ON_CALL(m_fileIO, Size(::testing::Matcher<const char*>(::testing::_), _))
  288. .WillByDefault(
  289. [this](const char* path, u64& size)
  290. {
  291. AZStd::scoped_lock lock(m_streamerWrapper->m_mutex);
  292. const auto* file = m_streamerWrapper->FindFile(path);
  293. if (file)
  294. {
  295. size = file->size();
  296. return ResultCode::Success;
  297. }
  298. AZ_Error("DisklessAssetManagerBase", false, "Failed to find virtual file %.*s", path);
  299. return ResultCode::Error;
  300. });
  301. m_prevFileIO = IO::FileIOBase::GetInstance();
  302. IO::FileIOBase::SetInstance(nullptr);
  303. IO::FileIOBase::SetInstance(&m_fileIO);
  304. }
  305. void DisklessAssetManagerBase::TearDown()
  306. {
  307. IO::FileIOBase::SetInstance(nullptr);
  308. IO::FileIOBase::SetInstance(m_prevFileIO);
  309. BaseAssetManagerTest::TearDown();
  310. }
  311. IO::IStreamer* DisklessAssetManagerBase::CreateStreamer()
  312. {
  313. m_streamerWrapper = AZStd::make_unique<MemoryStreamerWrapper>();
  314. return &(m_streamerWrapper->m_mockStreamer);
  315. }
  316. void DisklessAssetManagerBase::DestroyStreamer(IO::IStreamer*)
  317. {
  318. m_streamerWrapper = nullptr;
  319. }
  320. void DisklessAssetManagerBase::WriteAssetToDisk(const AZStd::string& assetName, const AZStd::string&)
  321. {
  322. AZ::IO::Path assetFileName = GetTestFolderPath() / assetName;
  323. AssetWithCustomData asset;
  324. EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile(assetFileName.Native(), &asset, m_serializeContext));
  325. }
  326. void DisklessAssetManagerBase::DeleteAssetFromDisk(const AZStd::string&)
  327. {
  328. }
  329. }