MissingDependencyScannerTests.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  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 <native/tests/AssetProcessorTest.h>
  9. #include <AzCore/Outcome/Outcome.h>
  10. #include <AzToolsFramework/API/AssetDatabaseBus.h>
  11. #include <native/utilities/MissingDependencyScanner.h>
  12. #include <native/tests/MockAssetDatabaseRequestsHandler.h>
  13. #include <AssetDatabase/AssetDatabase.h>
  14. #include <native/utilities/assetUtils.h>
  15. namespace AssetProcessor
  16. {
  17. class MissingDependencyScanner_Test
  18. : public MissingDependencyScanner
  19. {
  20. public:
  21. AZStd::unordered_map<AZStd::string, AZStd::vector<AZStd::string>>& GetDependenciesRulesMap()
  22. {
  23. return m_dependenciesRulesMap;
  24. }
  25. };
  26. class MissingDependencyScannerTest
  27. : public AssetProcessorTest
  28. {
  29. public:
  30. MissingDependencyScannerTest()
  31. {
  32. }
  33. protected:
  34. void SetUp() override
  35. {
  36. using namespace testing;
  37. using ::testing::NiceMock;
  38. AssetProcessorTest::SetUp();
  39. m_errorAbsorber = nullptr;
  40. m_data = AZStd::make_unique<StaticData>();
  41. QDir assetRootPath(m_data->m_databaseLocationListener.GetAssetRootDir().c_str());
  42. m_data->m_dbConn = AZStd::shared_ptr<AssetDatabaseConnection>(aznew AssetDatabaseConnection());
  43. m_data->m_dbConn->OpenDatabase();
  44. m_data->m_scopedDir.Setup(assetRootPath.absolutePath());
  45. }
  46. void TearDown() override
  47. {
  48. m_data = nullptr;
  49. AssetProcessorTest::TearDown();
  50. }
  51. AZ::Outcome<AZ::s64, AZStd::string> CreateScanFolder(const AZStd::string& scanFolderName, const AZStd::string& scanFolderPath)
  52. {
  53. AzToolsFramework::AssetDatabase::ScanFolderDatabaseEntry scanFolder;
  54. scanFolder.m_displayName = scanFolderName;
  55. scanFolder.m_portableKey = scanFolderName;
  56. scanFolder.m_scanFolder = scanFolderPath;
  57. if (!m_data->m_dbConn->SetScanFolder(scanFolder))
  58. {
  59. return AZ::Failure(AZStd::string::format("Could not set create scan folder %s", scanFolderName.c_str()));
  60. }
  61. // update the mock scan folder info as well, or else it will be using the default "c:/somepath" as the scan folder
  62. // which only works if we are using a mock file IO, which this test is not using. It would fail on posix systems otherwise.
  63. ScanFolderInfo info{QString::fromUtf8(scanFolderPath.c_str()),
  64. QString::fromUtf8(scanFolderName.c_str()),
  65. QString::fromUtf8(scanFolderName.c_str()),
  66. true, true, { AssetBuilderSDK::PlatformInfo{ "pc", {} } }, 0, 1 };
  67. m_data->m_pathConversion.SetScanFolder(info);
  68. return AZ::Success(scanFolder.m_scanFolderID);
  69. }
  70. struct SourceAndProductInfo
  71. {
  72. AZ::Uuid m_uuid;
  73. AZ::s64 m_productId;
  74. };
  75. AZ::Outcome<SourceAndProductInfo, AZStd::string> CreateSourceAndProductAsset(AZ::s64 scanFolderPK, const AZStd::string& sourceName, const AZStd::string& platform, const AZStd::string& productName)
  76. {
  77. SourceAssetReference sourceAsset(scanFolderPK, sourceName.c_str());
  78. UnitTestUtils::CreateDummyFile(sourceAsset.AbsolutePath().c_str());
  79. using namespace AzToolsFramework::AssetDatabase;
  80. SourceDatabaseEntry sourceEntry;
  81. sourceEntry.m_sourceName = sourceName;
  82. sourceEntry.m_scanFolderPK = scanFolderPK;
  83. sourceEntry.m_sourceGuid = AssetUtilities::GetSourceUuid(sourceAsset).GetValueOr(AZ::Uuid());
  84. EXPECT_FALSE(sourceEntry.m_sourceGuid.IsNull());
  85. if (!m_data->m_dbConn->SetSource(sourceEntry))
  86. {
  87. return AZ::Failure(AZStd::string::format("Could not set source in the asset database for %s", sourceName.c_str()));
  88. }
  89. SourceAndProductInfo result;
  90. result.m_uuid = sourceEntry.m_sourceGuid;
  91. JobDatabaseEntry jobEntry;
  92. jobEntry.m_sourcePK = sourceEntry.m_sourceID;
  93. jobEntry.m_platform = platform;
  94. jobEntry.m_jobRunKey = 1;
  95. if(!m_data->m_dbConn->SetJob(jobEntry))
  96. {
  97. return AZ::Failure(AZStd::string::format("Could not set job in the asset database for %s", sourceName.c_str()));
  98. }
  99. ProductDatabaseEntry productEntry;
  100. productEntry.m_jobPK = jobEntry.m_jobID;
  101. productEntry.m_productName = AZStd::string::format("%s/%s", platform.c_str(), productName.c_str());
  102. if(!m_data->m_dbConn->SetProduct(productEntry))
  103. {
  104. return AZ::Failure(AZStd::string::format("Could not set product in the asset database for %s", sourceName.c_str()));
  105. }
  106. result.m_productId = productEntry.m_productID;
  107. return AZ::Success(result);
  108. }
  109. void CreateAndValidateMissingProductDependency(const AZStd::string& missingProductName)
  110. {
  111. using namespace AzToolsFramework::AssetDatabase;
  112. QDir assetRootPath(m_data->m_databaseLocationListener.GetAssetRootDir().c_str());
  113. QString testFilePath = assetRootPath.absoluteFilePath("subfolder1/assetProcessorManagerTest.txt");
  114. AZStd::string testPlatform("pc");
  115. AZStd::string missingProductPath(AZStd::string::format("test/%s", missingProductName.c_str()));
  116. ASSERT_TRUE(UnitTestUtils::CreateDummyFile(testFilePath, missingProductName.c_str()));
  117. // Create the referenced product
  118. AZ::Outcome<AZ::s64, AZStd::string> scanResult = CreateScanFolder("Test", assetRootPath.absoluteFilePath("subfolder1").toUtf8().constData());
  119. ASSERT_TRUE(scanResult.IsSuccess());
  120. AZ::s64 scanFolderIndex(scanResult.GetValue());
  121. AZ::Outcome<SourceAndProductInfo, AZStd::string> firstAsset = CreateSourceAndProductAsset(scanFolderIndex, "tests/1", testPlatform, missingProductPath);
  122. ASSERT_TRUE(firstAsset.IsSuccess());
  123. AZ::Uuid actualTestGuid(firstAsset.GetValue().m_uuid);
  124. // Create the product that references the product above. This represents the dummy file we created up above
  125. AZ::Outcome<SourceAndProductInfo, AZStd::string> secondAsset = CreateSourceAndProductAsset(scanFolderIndex, "tests/2", testPlatform, "test/tests/2.product");
  126. ASSERT_TRUE(secondAsset.IsSuccess());
  127. AZ::s64 productId = secondAsset.GetValue().m_productId;
  128. AzToolsFramework::AssetDatabase::ProductDependencyDatabaseEntryContainer container;
  129. m_data->m_scanner.ScanFile(testFilePath.toUtf8().constData(), AssetProcessor::MissingDependencyScanner::DefaultMaxScanIteration, productId, container, m_data->m_dbConn, false, [](AZStd::string /*dependencyFile*/) {});
  130. MissingProductDependencyDatabaseEntryContainer missingDeps;
  131. ASSERT_TRUE(m_data->m_dbConn->GetMissingProductDependenciesByProductId(productId, missingDeps));
  132. ASSERT_EQ(missingDeps.size(), 1);
  133. ASSERT_EQ(missingDeps[0].m_productPK, productId);
  134. ASSERT_EQ(missingDeps[0].m_dependencySourceGuid, actualTestGuid);
  135. }
  136. struct StaticData
  137. {
  138. AZStd::string m_databaseLocation;
  139. MockAssetDatabaseRequestsHandler m_databaseLocationListener;
  140. AZStd::shared_ptr<AssetDatabaseConnection> m_dbConn;
  141. MissingDependencyScanner_Test m_scanner;
  142. UnitTests::MockPathConversion m_pathConversion;
  143. AzToolsFramework::UuidUtilComponent m_uuidUtil;
  144. AzToolsFramework::MetadataManager m_metadataManager;
  145. AssetProcessor::UuidManager m_uuidManager;
  146. UnitTestUtils::ScopedDir m_scopedDir; // Sets up FileIO instance
  147. };
  148. AZStd::unique_ptr<StaticData> m_data;
  149. };
  150. TEST_F(MissingDependencyScannerTest, ScanFile_FindsValidReferenceToProduct)
  151. {
  152. CreateAndValidateMissingProductDependency("tests/1.product");
  153. }
  154. TEST_F(MissingDependencyScannerTest, ScanFile_ValidReferenceToFileWithDash_FindsMissingReference)
  155. {
  156. CreateAndValidateMissingProductDependency("tests/1-withdash.product");
  157. }
  158. TEST_F(MissingDependencyScannerTest, ScanFile_CPP_File_FindsValidReferenceToProduct)
  159. {
  160. using namespace AzToolsFramework::AssetDatabase;
  161. QDir assetRootPath(m_data->m_databaseLocationListener.GetAssetRootDir().c_str());
  162. // Create the referenced product
  163. ScanFolderDatabaseEntry scanFolder;
  164. scanFolder.m_displayName = "Test";
  165. scanFolder.m_portableKey = "Test";
  166. scanFolder.m_scanFolder = assetRootPath.absoluteFilePath("subfolder1").toUtf8().constData();
  167. ASSERT_TRUE(m_data->m_dbConn->SetScanFolder(scanFolder));
  168. SourceAssetReference sourceAsset(1, "tests/1.source");
  169. EXPECT_TRUE(UnitTestUtils::CreateDummyFile(sourceAsset.AbsolutePath().c_str()));
  170. SourceDatabaseEntry sourceEntry;
  171. sourceEntry.m_sourceName = sourceAsset.RelativePath().c_str();
  172. sourceEntry.m_scanFolderPK = sourceAsset.ScanFolderId();
  173. sourceEntry.m_sourceGuid = AssetUtilities::GetSourceUuid(sourceAsset).GetValueOr(AZ::Uuid());
  174. ASSERT_TRUE(m_data->m_dbConn->SetSource(sourceEntry));
  175. JobDatabaseEntry jobEntry;
  176. jobEntry.m_sourcePK = sourceEntry.m_sourceID;
  177. jobEntry.m_platform = "pc";
  178. jobEntry.m_jobRunKey = 1;
  179. ASSERT_TRUE(m_data->m_dbConn->SetJob(jobEntry));
  180. ProductDatabaseEntry productEntry;
  181. productEntry.m_jobPK = jobEntry.m_jobID;
  182. productEntry.m_productName = "pc/test/tests/1.product";
  183. ASSERT_TRUE(m_data->m_dbConn->SetProduct(productEntry));
  184. AZStd::string productReference("tests/1.product");
  185. // Create a cpp file that references the product above.
  186. QString sourceFilePath = assetRootPath.absoluteFilePath("subfolder1/TestFile.cpp");
  187. AZStd::string codeSourceCode = AZStd::string::format(R"(#include <Dummy/Dummy.h>;
  188. #define PRODUCT_REFERENCE "%s")", productReference.c_str());
  189. ASSERT_TRUE(UnitTestUtils::CreateDummyFile(sourceFilePath, codeSourceCode.c_str()));
  190. AZStd::string productDependency;
  191. auto missingDependencyCallback = [&](AZStd::string relativeDependencyFilePath)
  192. {
  193. productDependency = relativeDependencyFilePath;
  194. };
  195. AzToolsFramework::AssetDatabase::ProductDependencyDatabaseEntryContainer container;
  196. AZStd::string dependencyToken = "dummy";
  197. // Since dependency rule map is empty this should show a missing dependency
  198. m_data->m_scanner.ScanFile(sourceFilePath.toUtf8().constData(), AssetProcessor::MissingDependencyScanner::DefaultMaxScanIteration, m_data->m_dbConn, dependencyToken, false, missingDependencyCallback);
  199. ASSERT_EQ(productDependency, productReference);
  200. productDependency.clear();
  201. QString anotherSourceFilePath = assetRootPath.absoluteFilePath("subfolder1/TestFile.cpp");
  202. codeSourceCode = AZStd::string::format(R"(#include <Dummy/Dummy.h>;
  203. AZStd::string filePath("%s")", productReference.c_str());
  204. ASSERT_TRUE(UnitTestUtils::CreateDummyFile(anotherSourceFilePath, codeSourceCode.c_str()));
  205. m_data->m_scanner.ScanFile(anotherSourceFilePath.toUtf8().constData(), AssetProcessor::MissingDependencyScanner::DefaultMaxScanIteration, m_data->m_dbConn, dependencyToken, false, missingDependencyCallback);
  206. ASSERT_EQ(productDependency, productReference);
  207. AZStd::vector<AZStd::string> rulesMap;
  208. rulesMap.emplace_back("*.product");
  209. m_data->m_scanner.GetDependenciesRulesMap()[dependencyToken] = rulesMap;
  210. productDependency.clear();
  211. m_data->m_scanner.ScanFile(sourceFilePath.toUtf8().constData(), AssetProcessor::MissingDependencyScanner::DefaultMaxScanIteration, m_data->m_dbConn, dependencyToken, false, missingDependencyCallback);
  212. ASSERT_TRUE(productDependency.empty());
  213. }
  214. }