assetUtilsTest.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  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 <QHash>
  9. #include "native/tests/AssetProcessorTest.h"
  10. #include <native/utilities/PlatformConfiguration.h>
  11. #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
  12. #include <AzCore/std/parallel/thread.h>
  13. #include <AzTest/AzTest.h>
  14. #include <tests/UnitTestUtilities.h>
  15. using namespace AssetUtilities;
  16. class AssetUtilitiesTest
  17. : public AssetProcessor::AssetProcessorTest
  18. {
  19. void SetUp() override
  20. {
  21. AssetProcessorTest::SetUp();
  22. if (AZ::IO::FileIOBase::GetInstance() == nullptr)
  23. {
  24. m_localFileIo = aznew AZ::IO::LocalFileIO();
  25. AZ::IO::FileIOBase::SetInstance(m_localFileIo);
  26. }
  27. }
  28. void TearDown() override
  29. {
  30. if (m_localFileIo)
  31. {
  32. delete m_localFileIo;
  33. m_localFileIo = nullptr;
  34. AZ::IO::FileIOBase::SetInstance(nullptr);
  35. }
  36. AssetProcessorTest::TearDown();
  37. }
  38. AZ::IO::FileIOBase* m_localFileIo{};
  39. };
  40. TEST_F(AssetUtilitiesTest, NormlizeFilePath_NormalizedValidPathRelPath_Valid)
  41. {
  42. QString result = NormalizeFilePath("a/b\\c\\d/E.txt");
  43. EXPECT_STREQ(result.toUtf8().constData(), "a/b/c/d/E.txt");
  44. }
  45. TEST_F(AssetUtilitiesTest, NormlizeFilePath_NormalizedValidPathFullPath_Valid)
  46. {
  47. QString result = NormalizeFilePath("c:\\a/b\\c\\d/E.txt");
  48. // on windows, drive letters are normalized to full
  49. #if defined(AZ_PLATFORM_WINDOWS)
  50. ASSERT_TRUE(result.compare("C:/a/b/c/d/E.txt", Qt::CaseSensitive) == 0);
  51. #else
  52. // on other platforms, C: is a relative path to a file called 'c:')
  53. EXPECT_STREQ(result.toUtf8().constData(), "c:/a/b/c/d/E.txt");
  54. #endif
  55. }
  56. TEST_F(AssetUtilitiesTest, NormlizeFilePath_NormalizedValidDirRelPath_Valid)
  57. {
  58. QString result = NormalizeDirectoryPath("a/b\\c\\D");
  59. EXPECT_STREQ(result.toUtf8().constData(), "a/b/c/D");
  60. }
  61. TEST_F(AssetUtilitiesTest, NormlizeFilePath_NormalizedValidDirFullPath_Valid)
  62. {
  63. QString result = NormalizeDirectoryPath("c:\\a/b\\C\\d\\");
  64. // on windows, drive letters are normalized to full
  65. #if defined(AZ_PLATFORM_WINDOWS)
  66. EXPECT_STREQ(result.toUtf8().constData(), "C:/a/b/C/d");
  67. #else
  68. EXPECT_STREQ(result.toUtf8().constData(), "c:/a/b/C/d");
  69. #endif
  70. }
  71. TEST_F(AssetUtilitiesTest, ComputeCRC32Lowercase_IsCaseInsensitive)
  72. {
  73. const char* upperCaseString = "HELLOworld";
  74. const char* lowerCaseString = "helloworld";
  75. EXPECT_EQ(AssetUtilities::ComputeCRC32Lowercase(lowerCaseString), AssetUtilities::ComputeCRC32Lowercase(upperCaseString));
  76. // also try the length-based one.
  77. EXPECT_EQ(AssetUtilities::ComputeCRC32Lowercase(lowerCaseString, size_t(5)), AssetUtilities::ComputeCRC32Lowercase(upperCaseString, size_t(5)));
  78. }
  79. TEST_F(AssetUtilitiesTest, ComputeCRC32_IsCaseSensitive)
  80. {
  81. const char* upperCaseString = "HELLOworld";
  82. const char* lowerCaseString = "helloworld";
  83. EXPECT_NE(AssetUtilities::ComputeCRC32(lowerCaseString), AssetUtilities::ComputeCRC32(upperCaseString));
  84. // also try the length-based one.
  85. EXPECT_NE(AssetUtilities::ComputeCRC32(lowerCaseString, size_t(5)), AssetUtilities::ComputeCRC32(upperCaseString, size_t(5)));
  86. }
  87. TEST_F(AssetUtilitiesTest, UpdateToCorrectCase_MissingFile_ReturnsFalse)
  88. {
  89. QTemporaryDir dir;
  90. QDir tempPath(dir.path());
  91. QString canonicalTempDirPath = AssetUtilities::NormalizeDirectoryPath(tempPath.canonicalPath());
  92. UnitTestUtils::ScopedDir changeDir(canonicalTempDirPath);
  93. tempPath = QDir(canonicalTempDirPath);
  94. QString fileName = "someFile.txt";
  95. EXPECT_FALSE(AssetUtilities::UpdateToCorrectCase(canonicalTempDirPath, fileName));
  96. }
  97. TEST_F(AssetUtilitiesTest, UpdateToCorrectCase_ExistingFile_ReturnsTrue_CorrectsCase)
  98. {
  99. QTemporaryDir dir;
  100. QDir tempPath(dir.path());
  101. QString canonicalTempDirPath = AssetUtilities::NormalizeDirectoryPath(tempPath.canonicalPath());
  102. UnitTestUtils::ScopedDir changeDir(canonicalTempDirPath);
  103. tempPath = QDir(canonicalTempDirPath);
  104. QStringList thingsToTry;
  105. thingsToTry << "SomeFile.TxT";
  106. thingsToTry << "otherfile.txt";
  107. thingsToTry << "subfolder1/otherfile.txt";
  108. #if defined(AZ_PLATFORM_WINDOWS)
  109. thingsToTry << "subfolder2\\otherfile.txt";
  110. thingsToTry << "subFolder3\\somefile.txt";
  111. thingsToTry << "subFolder4\\subfolder6\\somefile.txt";
  112. thingsToTry << "subFolder5\\subfolder7/someFile.txt";
  113. #endif // AZ_PLATFORM_WINDOWS
  114. thingsToTry << "specialFileName[.txt";
  115. thingsToTry << "specialFileName].txt";
  116. thingsToTry << "specialFileName!.txt";
  117. thingsToTry << "specialFileName#.txt";
  118. thingsToTry << "specialFileName$.txt";
  119. thingsToTry << "specialFile%Name%.txt";
  120. thingsToTry << "specialFileName&.txt";
  121. thingsToTry << "specialFileName(.txt";
  122. thingsToTry << "specialFileName+.txt";
  123. thingsToTry << "specialFileName[9].txt";
  124. thingsToTry << "specialFileName[A-Za-z].txt"; // these should all be treated as literally the name of the file, not a regex!
  125. for (QString triedThing : thingsToTry)
  126. {
  127. triedThing = NormalizeFilePath(triedThing);
  128. EXPECT_TRUE(UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath(triedThing)));
  129. QString lowercaseVersion = triedThing.toLower();
  130. // each one should be found. If it fails, we'll pipe out the name of the file it fails on for extra context.
  131. EXPECT_TRUE(AssetUtilities::UpdateToCorrectCase(canonicalTempDirPath, lowercaseVersion)) << "File being Examined: " << lowercaseVersion.toUtf8().constData();
  132. // each one should correct, and return a normalized path.
  133. EXPECT_STREQ(AssetUtilities::NormalizeFilePath(lowercaseVersion).toUtf8().constData(), AssetUtilities::NormalizeFilePath(triedThing).toUtf8().constData());
  134. }
  135. }
  136. TEST_F(AssetUtilitiesTest, GenerateFingerprint_BasicTest)
  137. {
  138. QTemporaryDir dir;
  139. QDir tempPath(dir.path());
  140. QString canonicalTempDirPath = AssetUtilities::NormalizeDirectoryPath(tempPath.canonicalPath());
  141. UnitTestUtils::ScopedDir changeDir(canonicalTempDirPath);
  142. tempPath = QDir(canonicalTempDirPath);
  143. QString absoluteTestFilePath1 = tempPath.absoluteFilePath("basicfile.txt");
  144. QString absoluteTestFilePath2 = tempPath.absoluteFilePath("basicfile2.txt");
  145. EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFilePath1, "contents"));
  146. EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFilePath2, "contents"));
  147. AZStd::string fileEncoded1 = absoluteTestFilePath1.toUtf8().constData();
  148. AZStd::string fileEncoded2 = absoluteTestFilePath2.toUtf8().constData();
  149. AssetProcessor::JobDetails jobDetail;
  150. // it is expected that the only parts of jobDetails that matter are:
  151. // jobDetail.m_extraInformationForFingerprinting
  152. // jobDetail.m_fingerprintFiles
  153. // jobDetail.m_jobDependencyList
  154. jobDetail.m_extraInformationForFingerprinting = "extra info1";
  155. // the fingerprint should always be stable over repeated runs, even with minimal info:
  156. unsigned int result1 = AssetUtilities::GenerateFingerprint(jobDetail);
  157. unsigned int result2 = AssetUtilities::GenerateFingerprint(jobDetail);
  158. EXPECT_EQ(result1, result2);
  159. // the fingerprint should always be different when anything changes:
  160. jobDetail.m_extraInformationForFingerprinting = "extra info1";
  161. result1 = AssetUtilities::GenerateFingerprint(jobDetail);
  162. jobDetail.m_extraInformationForFingerprinting = "extra info2";
  163. result2 = AssetUtilities::GenerateFingerprint(jobDetail);
  164. EXPECT_NE(result1, result2);
  165. jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(absoluteTestFilePath1.toUtf8().constData(), "basicfile.txt"));
  166. result1 = AssetUtilities::GenerateFingerprint(jobDetail);
  167. result2 = AssetUtilities::GenerateFingerprint(jobDetail);
  168. EXPECT_EQ(result1, result2);
  169. // mutating the dependency list should mutate the fingerprint, even if the extra info doesn't change.
  170. jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(absoluteTestFilePath2.toUtf8().constData(), "basicfile2.txt"));
  171. result2 = AssetUtilities::GenerateFingerprint(jobDetail);
  172. EXPECT_NE(result1, result2);
  173. UnitTestUtils::SleepForMinimumFileSystemTime();
  174. // mutating the actual files should mutate the fingerprint, even if the file list doesn't change.
  175. // note that both files are in the file list, so changing just the one should result in a change in hash:
  176. EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFilePath1, "contents new"));
  177. result1 = AssetUtilities::GenerateFingerprint(jobDetail);
  178. EXPECT_NE(result1, result2);
  179. // changing the other should also change the hash:
  180. EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFilePath2, "contents new2"));
  181. result2 = AssetUtilities::GenerateFingerprint(jobDetail);
  182. EXPECT_NE(result1, result2);
  183. }
  184. TEST_F(AssetUtilitiesTest, GenerateFingerprint_Empty_Asserts)
  185. {
  186. AssetProcessor::JobDetails jobDetail;
  187. AssetUtilities::GenerateFingerprint(jobDetail);
  188. EXPECT_EQ(m_errorAbsorber->m_numAssertsAbsorbed, 1);
  189. m_errorAbsorber->Clear();
  190. }
  191. TEST_F(AssetUtilitiesTest, GenerateFingerprint_MissingFile_NotSameAsZeroByteFile)
  192. {
  193. QTemporaryDir dir;
  194. QDir tempPath(dir.path());
  195. QString canonicalTempDirPath = AssetUtilities::NormalizeDirectoryPath(tempPath.canonicalPath());
  196. UnitTestUtils::ScopedDir changeDir(canonicalTempDirPath);
  197. tempPath = QDir(canonicalTempDirPath);
  198. QString absoluteTestFilePath1 = tempPath.absoluteFilePath("basicfile.txt");
  199. QString absoluteTestFilePath2 = tempPath.absoluteFilePath("basicfile2.txt");
  200. EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFilePath1, "")); // empty file
  201. // note: basicfile1 exists but is empty, whereas basicfile2, 3 are missing entirely.
  202. AssetProcessor::JobDetails jobDetail;
  203. jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile.txt").toUtf8().constData(), "basicfile.txt"));
  204. AZ::u32 fingerprint1 = AssetUtilities::GenerateFingerprint(jobDetail);
  205. jobDetail.m_fingerprintFiles.clear();
  206. jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile2.txt").toUtf8().constData(), "basicfile2.txt"));
  207. AZ::u32 fingerprint2 = AssetUtilities::GenerateFingerprint(jobDetail);
  208. EXPECT_NE(fingerprint1, fingerprint2);
  209. }
  210. TEST_F(AssetUtilitiesTest, GenerateFingerprint_MissingFile_NotSameAsOtherMissingFile)
  211. {
  212. QTemporaryDir dir;
  213. QDir tempPath(dir.path());
  214. QString canonicalTempDirPath = AssetUtilities::NormalizeDirectoryPath(tempPath.canonicalPath());
  215. UnitTestUtils::ScopedDir changeDir(canonicalTempDirPath);
  216. tempPath = QDir(canonicalTempDirPath);
  217. QString absoluteTestFilePath1 = tempPath.absoluteFilePath("basicfile.txt");
  218. QString absoluteTestFilePath2 = tempPath.absoluteFilePath("basicfile2.txt");
  219. // we create no files on disk.
  220. AssetProcessor::JobDetails jobDetail;
  221. jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile.txt").toUtf8().constData(), "basicfile.txt"));
  222. AZ::u32 fingerprint1 = AssetUtilities::GenerateFingerprint(jobDetail);
  223. jobDetail.m_fingerprintFiles.clear();
  224. jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile2.txt").toUtf8().constData(), "basicfile2.txt"));
  225. AZ::u32 fingerprint2 = AssetUtilities::GenerateFingerprint(jobDetail);
  226. EXPECT_NE(fingerprint1, fingerprint2);
  227. }
  228. TEST_F(AssetUtilitiesTest, GenerateFingerprint_OneFile_Differs)
  229. {
  230. // this test makes sure that changing each part of jobDetail relevant to fingerprints causes the resulting fingerprint to change.
  231. QTemporaryDir dir;
  232. QDir tempPath(dir.path());
  233. QString canonicalTempDirPath = AssetUtilities::NormalizeDirectoryPath(tempPath.canonicalPath());
  234. UnitTestUtils::ScopedDir changeDir(canonicalTempDirPath);
  235. tempPath = QDir(canonicalTempDirPath);
  236. QString absoluteTestFilePath1 = tempPath.absoluteFilePath("basicfile.txt");
  237. QString absoluteTestFilePath2 = tempPath.absoluteFilePath("basicfile2.txt");
  238. QString absoluteTestFilePath3 = tempPath.absoluteFilePath("basicfile3.txt");
  239. EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFilePath1, "contents"));
  240. EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFilePath2, "contents")); // same contents
  241. EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFilePath3, "contents2")); // different contents
  242. AssetProcessor::JobDetails jobDetail;
  243. jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile.txt").toUtf8().constData(), "basicfile.txt"));
  244. AZ::u32 fingerprint1 = AssetUtilities::GenerateFingerprint(jobDetail);
  245. jobDetail.m_fingerprintFiles.clear();
  246. jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile2.txt").toUtf8().constData(), "basicfile2.txt"));
  247. AZ::u32 fingerprint2 = AssetUtilities::GenerateFingerprint(jobDetail);
  248. jobDetail.m_fingerprintFiles.clear();
  249. jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile3.txt").toUtf8().constData(), "basicfile3.txt"));
  250. AZ::u32 fingerprint3 = AssetUtilities::GenerateFingerprint(jobDetail);
  251. jobDetail.m_fingerprintFiles.clear();
  252. jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile.txt").toUtf8().constData(), "basicfile.txt"));
  253. EXPECT_EQ(AssetUtilities::GenerateFingerprint(jobDetail), fingerprint1);
  254. jobDetail.m_fingerprintFiles.clear();
  255. jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile2.txt").toUtf8().constData(), "basicfile2.txt"));
  256. EXPECT_EQ(AssetUtilities::GenerateFingerprint(jobDetail), fingerprint2);
  257. jobDetail.m_fingerprintFiles.clear();
  258. jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile3.txt").toUtf8().constData(), "basicfile3.txt"));
  259. EXPECT_EQ(AssetUtilities::GenerateFingerprint(jobDetail), fingerprint3);
  260. EXPECT_NE(fingerprint1, fingerprint2);
  261. EXPECT_NE(fingerprint2, fingerprint3);
  262. EXPECT_NE(fingerprint3, fingerprint1);
  263. }
  264. TEST_F(AssetUtilitiesTest, GenerateFingerprint_MultipleFile_Differs)
  265. {
  266. // given multiple files, make sure that the fingerprint for multiple files differs from the one file (that each file is taken into account)
  267. QTemporaryDir dir;
  268. QDir tempPath(dir.path());
  269. QString canonicalTempDirPath = AssetUtilities::NormalizeDirectoryPath(tempPath.canonicalPath());
  270. UnitTestUtils::ScopedDir changeDir(canonicalTempDirPath);
  271. tempPath = QDir(canonicalTempDirPath);
  272. QString absoluteTestFilePath1 = tempPath.absoluteFilePath("basicfile.txt");
  273. QString absoluteTestFilePath2 = tempPath.absoluteFilePath("basicfile2.txt");
  274. QString absoluteTestFilePath3 = tempPath.absoluteFilePath("basicfile3.txt");
  275. EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFilePath1, "contents"));
  276. EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFilePath2, "contents")); // same contents
  277. EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFilePath3, "contents2")); // different contents
  278. AssetProcessor::JobDetails jobDetail;
  279. jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile.txt").toUtf8().constData(), "basicfile.txt"));
  280. AZ::u32 fingerprint1 = AssetUtilities::GenerateFingerprint(jobDetail);
  281. jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile2.txt").toUtf8().constData(), "basicfile2.txt"));
  282. AZ::u32 fingerprint2 = AssetUtilities::GenerateFingerprint(jobDetail);
  283. jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile3.txt").toUtf8().constData(), "basicfile3.txt"));
  284. AZ::u32 fingerprint3 = AssetUtilities::GenerateFingerprint(jobDetail);
  285. EXPECT_NE(fingerprint1, fingerprint2);
  286. EXPECT_NE(fingerprint2, fingerprint3);
  287. EXPECT_NE(fingerprint3, fingerprint1);
  288. jobDetail.m_fingerprintFiles.clear();
  289. jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile.txt").toUtf8().constData(), "basicfile.txt"));
  290. jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile2.txt").toUtf8().constData(), "basicfile2.txt"));
  291. fingerprint1 = AssetUtilities::GenerateFingerprint(jobDetail);
  292. jobDetail.m_fingerprintFiles.clear();
  293. jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile2.txt").toUtf8().constData(), "basicfile2.txt"));
  294. jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile3.txt").toUtf8().constData(), "basicfile3.txt"));
  295. fingerprint2 = AssetUtilities::GenerateFingerprint(jobDetail);
  296. jobDetail.m_fingerprintFiles.clear();
  297. jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile1.txt").toUtf8().constData(), "basicfile1.txt"));
  298. jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile3.txt").toUtf8().constData(), "basicfile3.txt"));
  299. fingerprint3 = AssetUtilities::GenerateFingerprint(jobDetail);
  300. EXPECT_NE(fingerprint1, fingerprint2);
  301. EXPECT_NE(fingerprint2, fingerprint3);
  302. EXPECT_NE(fingerprint3, fingerprint1);
  303. }
  304. TEST_F(AssetUtilitiesTest, GenerateFingerprint_OrderOnceJobDependency_NoChange)
  305. {
  306. // OrderOnce Job dependency should not alter the fingerprint of the job
  307. QTemporaryDir dir;
  308. QDir tempPath(dir.path());
  309. UnitTests::MockPathConversion mockPathConversion(dir.path().toUtf8().constData());
  310. QString canonicalTempDirPath = AssetUtilities::NormalizeDirectoryPath(tempPath.canonicalPath());
  311. UnitTestUtils::ScopedDir changeDir(canonicalTempDirPath);
  312. tempPath = QDir(canonicalTempDirPath);
  313. const char relFile1Path[] = "file.txt";
  314. const char relFile2Path[] = "secondFile.txt";
  315. QString absoluteTestFile1Path = tempPath.absoluteFilePath(relFile1Path);
  316. QString absoluteTestFile2Path = tempPath.absoluteFilePath(relFile2Path);
  317. EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFile1Path, "contents"));
  318. EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFile2Path, "contents"));
  319. AssetProcessor::JobDetails jobDetail;
  320. jobDetail.m_jobEntry.m_sourceAssetReference = AssetProcessor::SourceAssetReference(tempPath.absolutePath(), relFile1Path);
  321. jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(absoluteTestFile1Path.toUtf8().constData(), relFile1Path));
  322. AZ::u32 fingerprintWithoutOrderOnceJobDependency = AssetUtilities::GenerateFingerprint(jobDetail);
  323. AssetBuilderSDK::SourceFileDependency dep = { relFile2Path, AZ::Uuid::CreateNull() };
  324. AssetBuilderSDK::JobDependency jobDep("key", "pc", AssetBuilderSDK::JobDependencyType::OrderOnce, dep);
  325. jobDetail.m_jobDependencyList.push_back(AssetProcessor::JobDependencyInternal(jobDep));
  326. AZ::u32 fingerprintWithOrderOnceJobDependency = AssetUtilities::GenerateFingerprint(jobDetail);
  327. EXPECT_EQ(fingerprintWithoutOrderOnceJobDependency, fingerprintWithOrderOnceJobDependency);
  328. }
  329. TEST_F(AssetUtilitiesTest, GenerateFingerprint_OrderOnlyJobDependency_NoChange)
  330. {
  331. // OrderOnly Job dependency should not alter the fingerprint of the job
  332. QTemporaryDir dir;
  333. QDir tempPath(dir.path());
  334. UnitTests::MockPathConversion mockPathConversion(dir.path().toUtf8().constData());
  335. QString canonicalTempDirPath = AssetUtilities::NormalizeDirectoryPath(tempPath.canonicalPath());
  336. UnitTestUtils::ScopedDir changeDir(canonicalTempDirPath);
  337. tempPath = QDir(canonicalTempDirPath);
  338. const char relFile1Path[] = "file.txt";
  339. const char relFile2Path[] = "secondFile.txt";
  340. QString absoluteTestFile1Path = tempPath.absoluteFilePath(relFile1Path);
  341. QString absoluteTestFile2Path = tempPath.absoluteFilePath(relFile2Path);
  342. EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFile1Path, "contents"));
  343. EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFile2Path, "contents"));
  344. AssetProcessor::JobDetails jobDetail;
  345. jobDetail.m_jobEntry.m_sourceAssetReference = AssetProcessor::SourceAssetReference(tempPath.absolutePath(), relFile1Path);
  346. jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(absoluteTestFile1Path.toUtf8().constData(), relFile1Path));
  347. AZ::u32 fingerprintWithoutOrderOnlyJobDependency = AssetUtilities::GenerateFingerprint(jobDetail);
  348. AssetBuilderSDK::SourceFileDependency dep = { relFile2Path, AZ::Uuid::CreateNull() };
  349. AssetBuilderSDK::JobDependency jobDep("key", "pc", AssetBuilderSDK::JobDependencyType::OrderOnly, dep);
  350. jobDetail.m_jobDependencyList.push_back(AssetProcessor::JobDependencyInternal(jobDep));
  351. AZ::u32 fingerprintWithOrderOnlyJobDependency = AssetUtilities::GenerateFingerprint(jobDetail);
  352. EXPECT_EQ(fingerprintWithoutOrderOnlyJobDependency, fingerprintWithOrderOnlyJobDependency);
  353. }
  354. namespace AssetUtilsTest
  355. {
  356. class MockJobDependencyResponder : public AssetProcessor::ProcessingJobInfoBus::Handler
  357. {
  358. public:
  359. MOCK_METHOD1(GetJobFingerprint, AZ::u32(const AssetProcessor::JobIndentifier&));
  360. ~MockJobDependencyResponder()
  361. {
  362. BusDisconnect();
  363. }
  364. };
  365. }
  366. TEST_F(AssetUtilitiesTest, GenerateFingerprint_GivenJobDependencies_AffectsOutcome)
  367. {
  368. using namespace testing;
  369. using ::testing::NiceMock;
  370. NiceMock<AssetUtilsTest::MockJobDependencyResponder> responder;
  371. responder.BusConnect();
  372. QTemporaryDir dir;
  373. QDir tempPath(dir.path());
  374. UnitTests::MockPathConversion mockPathConversion(dir.path().toUtf8().constData());
  375. QString canonicalTempDirPath = AssetUtilities::NormalizeDirectoryPath(tempPath.canonicalPath());
  376. UnitTestUtils::ScopedDir changeDir(canonicalTempDirPath);
  377. tempPath = QDir(canonicalTempDirPath);
  378. QString absoluteTestFilePath1 = tempPath.absoluteFilePath("basicfile.txt");
  379. AssetProcessor::JobDetails jobDetail;
  380. jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile.txt").toUtf8().constData(), "basicfile.txt"));
  381. AZ::u32 fingerprint1 = AssetUtilities::GenerateFingerprint(jobDetail);
  382. // add a job dependency - it should alter the fingerprint, even if the file does not exist.
  383. AssetBuilderSDK::JobDependency jobDep("thing", "pc", AssetBuilderSDK::JobDependencyType::Order, AssetBuilderSDK::SourceFileDependency(tempPath.absoluteFilePath("basicfile.txt").toUtf8().constData(), AZ::Uuid::CreateNull()));
  384. AssetProcessor::JobDependencyInternal internalJobDep(jobDep);
  385. internalJobDep.m_builderUuidList.insert(AZ::Uuid::CreateRandom());
  386. jobDetail.m_jobDependencyList.push_back(internalJobDep);
  387. EXPECT_CALL(responder, GetJobFingerprint(_))
  388. .WillOnce(
  389. Return(0x12341234));
  390. AZ::u32 fingerprint2 = AssetUtilities::GenerateFingerprint(jobDetail);
  391. // different job fingerprint -> different result
  392. EXPECT_CALL(responder, GetJobFingerprint(_))
  393. .WillOnce(
  394. Return(0x11111111));
  395. AZ::u32 fingerprint3 = AssetUtilities::GenerateFingerprint(jobDetail);
  396. EXPECT_NE(fingerprint1, fingerprint2);
  397. EXPECT_NE(fingerprint2, fingerprint3);
  398. EXPECT_NE(fingerprint3, fingerprint1);
  399. }
  400. TEST_F(AssetUtilitiesTest, GetFileFingerprint_BasicTest)
  401. {
  402. QTemporaryDir dir;
  403. QDir tempPath(dir.path());
  404. UnitTests::MockPathConversion mockPathConversion(dir.path().toUtf8().constData());
  405. QString canonicalTempDirPath = AssetUtilities::NormalizeDirectoryPath(tempPath.canonicalPath());
  406. UnitTestUtils::ScopedDir changeDir(canonicalTempDirPath);
  407. tempPath = QDir(canonicalTempDirPath);
  408. QString absoluteTestFilePath1 = tempPath.absoluteFilePath("basicfile.txt");
  409. QString absoluteTestFilePath2 = tempPath.absoluteFilePath("basicfile2.txt");
  410. EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFilePath1, "contents"));
  411. UnitTestUtils::SleepForMinimumFileSystemTime();
  412. EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFilePath2, "contents2"));
  413. AZStd::string fileEncoded1 = absoluteTestFilePath1.toUtf8().constData();
  414. AZStd::string fileEncoded2 = absoluteTestFilePath2.toUtf8().constData();
  415. // repeatedly hashing the same file should result in the same hash:
  416. EXPECT_STREQ(AssetUtilities::GetFileFingerprint(fileEncoded1, "").c_str(), AssetUtilities::GetFileFingerprint(fileEncoded1, "").c_str());
  417. EXPECT_STREQ(AssetUtilities::GetFileFingerprint(fileEncoded1, "Name").c_str(), AssetUtilities::GetFileFingerprint(fileEncoded1, "Name").c_str());
  418. EXPECT_STREQ(AssetUtilities::GetFileFingerprint(fileEncoded2, "").c_str(), AssetUtilities::GetFileFingerprint(fileEncoded2, "").c_str());
  419. EXPECT_STREQ(AssetUtilities::GetFileFingerprint(fileEncoded2, "Name").c_str(), AssetUtilities::GetFileFingerprint(fileEncoded2, "Name").c_str());
  420. // mutating the 'name' should mutate the fingerprint:
  421. EXPECT_STRNE(AssetUtilities::GetFileFingerprint(fileEncoded1, "").c_str(), AssetUtilities::GetFileFingerprint(fileEncoded1, "Name").c_str());
  422. // two different files should not hash to the same fingerprint:
  423. EXPECT_STRNE(AssetUtilities::GetFileFingerprint(fileEncoded1, "").c_str(), AssetUtilities::GetFileFingerprint(fileEncoded2, "").c_str());
  424. UnitTestUtils::SleepForMinimumFileSystemTime();
  425. AZStd::string oldFingerprint1 = AssetUtilities::GetFileFingerprint(fileEncoded1, "");
  426. AZStd::string oldFingerprint2 = AssetUtilities::GetFileFingerprint(fileEncoded2, "");
  427. AZStd::string oldFingerprint1a = AssetUtilities::GetFileFingerprint(fileEncoded1, "Name1");
  428. AZStd::string oldFingerprint2a = AssetUtilities::GetFileFingerprint(fileEncoded2, "Name2");
  429. EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFilePath1, "contents1a"));
  430. EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFilePath2, "contents2a"));
  431. EXPECT_STRNE(oldFingerprint1.c_str(), AssetUtilities::GetFileFingerprint(fileEncoded1, "").c_str());
  432. EXPECT_STRNE(oldFingerprint2.c_str(), AssetUtilities::GetFileFingerprint(fileEncoded2, "").c_str());
  433. EXPECT_STRNE(oldFingerprint1a.c_str(), AssetUtilities::GetFileFingerprint(fileEncoded1, "Name1").c_str());
  434. EXPECT_STRNE(oldFingerprint2a.c_str(), AssetUtilities::GetFileFingerprint(fileEncoded2, "Name2").c_str());
  435. }
  436. TEST_F(AssetUtilitiesTest, GetFileFingerprint_NonExistentFiles)
  437. {
  438. AZStd::string nonExistentFile1 = AZ::Uuid::CreateRandom().ToString<AZStd::string>() + ".txt";
  439. ASSERT_FALSE(QFileInfo::exists(nonExistentFile1.c_str()));
  440. EXPECT_STRNE(AssetUtilities::GetFileFingerprint(nonExistentFile1, "").c_str(), AssetUtilities::GetFileFingerprint(nonExistentFile1, "Name").c_str());
  441. EXPECT_STREQ(AssetUtilities::GetFileFingerprint(nonExistentFile1, "Name").c_str(), AssetUtilities::GetFileFingerprint(nonExistentFile1, "Name").c_str());
  442. }
  443. TEST_F(AssetUtilitiesTest, CreateDirWithTimeout_Valid)
  444. {
  445. QTemporaryDir tempDir;
  446. QDir tempPath(tempDir.path());
  447. QDir dir(tempPath.filePath("folder"));
  448. unsigned int timeToWaitInSecs = 3;
  449. AZStd::vector<AZStd::thread*> threadList;
  450. AZStd::vector<bool> resultList;
  451. AZStd::mutex resultMutex;
  452. auto runFunc = [&]()
  453. {
  454. AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(100));// sleeping to sync all the threads
  455. bool result = AssetUtilities::CreateDirectoryWithTimeout(dir, timeToWaitInSecs);
  456. AZStd::lock_guard<AZStd::mutex> locker(resultMutex);
  457. resultList.push_back(result);
  458. };
  459. int numberOfThreads = 5;
  460. ASSERT_FALSE(dir.exists());
  461. for (int idx = 0; idx < numberOfThreads; idx++)
  462. {
  463. AZStd::thread* workerThread = new AZStd::thread(runFunc);
  464. threadList.emplace_back(workerThread);
  465. }
  466. for (auto thread : threadList)
  467. {
  468. if (thread->joinable())
  469. {
  470. thread->join();
  471. }
  472. delete thread;
  473. }
  474. for (int idx = 0; idx < numberOfThreads; idx++)
  475. {
  476. ASSERT_TRUE(resultList[idx]);
  477. }
  478. ASSERT_TRUE(dir.exists());
  479. }
  480. TEST_F(AssetUtilitiesTest, CreateDir_InvalidDir_Timeout_Valid)
  481. {
  482. QDir dir(":\folder");
  483. unsigned int timeToWaitInSecs = 1;
  484. AZStd::vector<AZStd::thread*> threadList;
  485. AZStd::vector<bool> resultList;
  486. AZStd::mutex resultMutex;
  487. auto runFunc = [&]()
  488. {
  489. AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(100));// sleeping to sync all the threads
  490. bool result = AssetUtilities::CreateDirectoryWithTimeout(dir, timeToWaitInSecs);
  491. AZStd::lock_guard<AZStd::mutex> locker(resultMutex);
  492. resultList.push_back(result);
  493. };
  494. int numberOfThreads = 5;
  495. ASSERT_FALSE(dir.exists());
  496. for (int idx = 0; idx < numberOfThreads; idx++)
  497. {
  498. AZStd::thread* workerThread = new AZStd::thread(runFunc);
  499. threadList.emplace_back(workerThread);
  500. }
  501. for (auto thread : threadList)
  502. {
  503. if (thread->joinable())
  504. {
  505. thread->join();
  506. }
  507. delete thread;
  508. }
  509. for (int idx = 0; idx < numberOfThreads; idx++)
  510. {
  511. ASSERT_FALSE(resultList[idx]);
  512. }
  513. ASSERT_FALSE(dir.exists());
  514. }