FileIO.cpp 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937
  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/FileIO.h>
  9. #include <AzCore/IO/Path/Path.h>
  10. #include <AzCore/IO/SystemFile.h>
  11. #include <AzCore/std/string/string.h>
  12. #include <AzCore/PlatformIncl.h>
  13. #include <AzCore/UnitTest/TestTypes.h>
  14. #include <AzCore/Utils/Utils.h>
  15. #include <AzFramework/IO/LocalFileIO.h>
  16. #include <AzFramework/IO/FileOperations.h>
  17. #include <time.h>
  18. #include <AzTest/Utils.h>
  19. #include <AzFrameworkTests_Traits_Platform.h>
  20. #if AZ_TRAIT_USE_WINDOWS_FILE_API
  21. #include <sys/stat.h>
  22. #include <io.h>
  23. #endif
  24. using namespace AZ;
  25. using namespace AZ::IO;
  26. using namespace AZ::Debug;
  27. namespace UnitTest
  28. {
  29. class NameMatchesFilterTest
  30. : public AllocatorsFixture
  31. {
  32. public:
  33. void run()
  34. {
  35. AZ_TEST_ASSERT(NameMatchesFilter("hello", "hello") == true);
  36. AZ_TEST_ASSERT(NameMatchesFilter("hello", "he?l?") == true);
  37. AZ_TEST_ASSERT(NameMatchesFilter("hello", "he???") == true);
  38. AZ_TEST_ASSERT(NameMatchesFilter("hello", "he*") == true);
  39. AZ_TEST_ASSERT(NameMatchesFilter("hello", "he*o") == true);
  40. AZ_TEST_ASSERT(NameMatchesFilter("hello", "?*?o") == true);
  41. AZ_TEST_ASSERT(NameMatchesFilter("hello", "h?*?") == true);
  42. AZ_TEST_ASSERT(NameMatchesFilter("hello", "h?*?o") == true);
  43. AZ_TEST_ASSERT(NameMatchesFilter("hello", "h?*?o?") == false);
  44. AZ_TEST_ASSERT(NameMatchesFilter("hello", "h***o*") == true);
  45. AZ_TEST_ASSERT(NameMatchesFilter("something", "some??") == false);
  46. AZ_TEST_ASSERT(NameMatchesFilter("hello", "?????*") == true);
  47. AZ_TEST_ASSERT(NameMatchesFilter("hello", "????*") == true);
  48. AZ_TEST_ASSERT(NameMatchesFilter("hello", "h??*") == true);
  49. AZ_TEST_ASSERT(NameMatchesFilter("hello", "??L*") == true);
  50. AZ_TEST_ASSERT(NameMatchesFilter("anything", "**") == true);
  51. AZ_TEST_ASSERT(NameMatchesFilter("any.thing", "*") == true);
  52. AZ_TEST_ASSERT(NameMatchesFilter("anything", "") == false);
  53. AZ_TEST_ASSERT(NameMatchesFilter("system.pak", "*.pak") == true);
  54. AZ_TEST_ASSERT(NameMatchesFilter("system.pakx", "*.pak") == false);
  55. AZ_TEST_ASSERT(NameMatchesFilter("system.pa", "*.pak") == false);
  56. AZ_TEST_ASSERT(NameMatchesFilter("system.pak.3", "*.pak.*") == true);
  57. AZ_TEST_ASSERT(NameMatchesFilter("system.pa.pak", "*.pak") == true);
  58. AZ_TEST_ASSERT(NameMatchesFilter("log1234.log", "log????.log") == true);
  59. AZ_TEST_ASSERT(NameMatchesFilter("log1234.log", "log?????.log") == false);
  60. AZ_TEST_ASSERT(NameMatchesFilter("log151234.log", "log*.log") == true);
  61. AZ_TEST_ASSERT(NameMatchesFilter(".pak", "*.pak") == true);
  62. AZ_TEST_ASSERT(NameMatchesFilter("", "*.pak") == false);
  63. AZ_TEST_ASSERT(NameMatchesFilter("", "") == true);
  64. AZ_TEST_ASSERT(NameMatchesFilter("test.test", "????.????") == true);
  65. AZ_TEST_ASSERT(NameMatchesFilter("testatest", "????.????") == false);
  66. }
  67. };
  68. TEST_F(NameMatchesFilterTest, Test)
  69. {
  70. run();
  71. }
  72. /**
  73. * FileIOStream test
  74. */
  75. class FileIOStreamTest
  76. : public AllocatorsFixture
  77. {
  78. public:
  79. AZ::IO::LocalFileIO m_fileIO;
  80. AZ::IO::FileIOBase* m_prevFileIO;
  81. FileIOStreamTest()
  82. {
  83. }
  84. void SetUp() override
  85. {
  86. AllocatorsFixture::SetUp();
  87. m_prevFileIO = AZ::IO::FileIOBase::GetInstance();
  88. AZ::IO::FileIOBase::SetInstance(&m_fileIO);
  89. }
  90. ~FileIOStreamTest() override
  91. {
  92. }
  93. void TearDown() override
  94. {
  95. AZ::IO::FileIOBase::SetInstance(m_prevFileIO);
  96. AllocatorsFixture::TearDown();
  97. }
  98. void run()
  99. {
  100. AZ::Test::ScopedAutoTempDirectory tempDir;
  101. char fileIOTestPath[AZ::IO::MaxPathLength];
  102. azsnprintf(fileIOTestPath, AZ::IO::MaxPathLength, "%s/fileiotest.txt", tempDir.GetDirectory());
  103. FileIOStream stream(fileIOTestPath, AZ::IO::OpenMode::ModeWrite);
  104. AZ_TEST_ASSERT(stream.IsOpen());
  105. char output[256];
  106. azsnprintf(output, sizeof(output), "magic string");
  107. AZ_TEST_ASSERT(strlen(output) + 1 == stream.Write(strlen(output) + 1, output));
  108. stream.Close();
  109. stream.Open(fileIOTestPath, AZ::IO::OpenMode::ModeRead);
  110. AZ_TEST_ASSERT(stream.IsOpen());
  111. AZ_TEST_ASSERT(strlen(output) + 1 == stream.Read(strlen(output) + 1, output));
  112. AZ_TEST_ASSERT(strcmp(output, "magic string") == 0);
  113. stream.Close();
  114. }
  115. };
  116. TEST_F(FileIOStreamTest, Test)
  117. {
  118. run();
  119. }
  120. namespace LocalFileIOTest
  121. {
  122. class FolderFixture
  123. : public ScopedAllocatorSetupFixture
  124. {
  125. public:
  126. AZ::Test::ScopedAutoTempDirectory m_tempDir;
  127. AZ::IO::Path m_root;
  128. AZ::IO::Path m_folderName;
  129. AZ::IO::Path m_deepFolder;
  130. AZ::IO::Path m_extraFolder;
  131. AZ::IO::Path m_fileRoot;
  132. AZ::IO::Path m_file01Name;
  133. AZ::IO::Path m_file02Name;
  134. AZ::IO::Path m_file03Name;
  135. int m_randomFolderKey = 0;
  136. FolderFixture()
  137. {
  138. }
  139. void ChooseRandomFolder()
  140. {
  141. m_root = m_tempDir.GetDirectory();
  142. m_folderName = m_root / AZStd::string::format("tmp%08x", m_randomFolderKey);
  143. m_deepFolder = m_folderName / "test" / "subdir";
  144. m_extraFolder = m_deepFolder / "subdir2";
  145. // make a couple files there, and in the root:
  146. m_fileRoot = m_extraFolder;
  147. }
  148. void SetUp() override
  149. {
  150. // lets use a random temp folder name
  151. srand(clock());
  152. m_randomFolderKey = rand();
  153. LocalFileIO local;
  154. do
  155. {
  156. ChooseRandomFolder();
  157. ++m_randomFolderKey;
  158. } while (local.IsDirectory(m_fileRoot.c_str()));
  159. m_file01Name = m_fileRoot / "file01.txt";
  160. m_file02Name = m_fileRoot / "file02.asdf";
  161. m_file03Name = m_fileRoot / "test123.wha";
  162. }
  163. void TearDown() override
  164. {
  165. }
  166. void CreateTestFiles()
  167. {
  168. constexpr auto openMode = SystemFile::OpenMode::SF_OPEN_WRITE_ONLY
  169. | SystemFile::OpenMode::SF_OPEN_CREATE
  170. | SystemFile::OpenMode::SF_OPEN_CREATE_NEW;
  171. constexpr AZStd::string_view testContent("this is just a test");
  172. LocalFileIO local;
  173. AZ_TEST_ASSERT(local.CreatePath(m_fileRoot.c_str()));
  174. AZ_TEST_ASSERT(local.IsDirectory(m_fileRoot.c_str()));
  175. for (const AZ::IO::Path& filename : { m_file01Name, m_file02Name, m_file03Name })
  176. {
  177. SystemFile tempFile;
  178. tempFile.Open(filename.c_str(), openMode);
  179. tempFile.Write(testContent.data(), testContent.size());
  180. tempFile.Close();
  181. }
  182. }
  183. };
  184. class DirectoryTest
  185. : public FolderFixture
  186. {
  187. public:
  188. void run()
  189. {
  190. LocalFileIO local;
  191. AZ_TEST_ASSERT(!local.Exists(m_folderName.c_str()));
  192. AZ::IO::Path longPathCreateTest = m_folderName / "one" / "two" / "three";
  193. AZ_TEST_ASSERT(!local.Exists(longPathCreateTest.c_str()));
  194. AZ_TEST_ASSERT(!local.IsDirectory(longPathCreateTest.c_str()));
  195. AZ_TEST_ASSERT(local.CreatePath(longPathCreateTest.c_str()));
  196. AZ_TEST_ASSERT(local.IsDirectory(longPathCreateTest.c_str()));
  197. AZ_TEST_ASSERT(!local.Exists(m_deepFolder.c_str()));
  198. AZ_TEST_ASSERT(!local.IsDirectory(m_deepFolder.c_str()));
  199. AZ_TEST_ASSERT(local.CreatePath(m_deepFolder.c_str()));
  200. AZ_TEST_ASSERT(local.IsDirectory(m_deepFolder.c_str()));
  201. AZ_TEST_ASSERT(local.Exists(m_deepFolder.c_str()));
  202. AZ_TEST_ASSERT(local.CreatePath(m_deepFolder.c_str()));
  203. AZ_TEST_ASSERT(local.Exists(m_deepFolder.c_str()));
  204. }
  205. };
  206. TEST_F(DirectoryTest, Test)
  207. {
  208. run();
  209. }
  210. class ReadWriteTest
  211. : public FolderFixture
  212. {
  213. public:
  214. void run()
  215. {
  216. LocalFileIO local;
  217. AZ_TEST_ASSERT(!local.Exists(m_fileRoot.c_str()));
  218. AZ_TEST_ASSERT(!local.IsDirectory(m_fileRoot.c_str()));
  219. AZ_TEST_ASSERT(local.CreatePath(m_fileRoot.c_str()));
  220. AZ_TEST_ASSERT(local.IsDirectory(m_fileRoot.c_str()));
  221. constexpr auto openMode = SystemFile::OpenMode::SF_OPEN_WRITE_ONLY
  222. | SystemFile::OpenMode::SF_OPEN_CREATE
  223. | SystemFile::OpenMode::SF_OPEN_CREATE_NEW;
  224. SystemFile tempFile;
  225. tempFile.Open(m_file01Name.c_str(), openMode);
  226. constexpr AZStd::string_view testContent("this is just a test");
  227. tempFile.Write(testContent.data(), testContent.size());
  228. tempFile.Close();
  229. AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
  230. AZ_TEST_ASSERT(!local.Open("", AZ::IO::OpenMode::ModeWrite, fileHandle));
  231. AZ_TEST_ASSERT(fileHandle == AZ::IO::InvalidHandle);
  232. // test size without opening:
  233. AZ::u64 fs = 0;
  234. AZ_TEST_ASSERT(local.Size(m_file01Name.c_str(), fs));
  235. AZ_TEST_ASSERT(fs == 19);
  236. fileHandle = AZ::IO::InvalidHandle;
  237. AZ::u64 modTimeA = local.ModificationTime(m_file01Name.c_str());
  238. AZ_TEST_ASSERT(modTimeA != 0);
  239. // test invalid handle ops:
  240. AZ_TEST_ASSERT(!local.Seek(fileHandle, 0, AZ::IO::SeekType::SeekFromStart));
  241. AZ_TEST_ASSERT(!local.Close(fileHandle));
  242. AZ_TEST_ASSERT(!local.Eof(fileHandle));
  243. AZ_TEST_ASSERT(!local.Flush(fileHandle));
  244. AZ_TEST_ASSERT(!local.ModificationTime(fileHandle));
  245. AZ_TEST_ASSERT(!local.Read(fileHandle, nullptr, 0, false));
  246. AZ_TEST_ASSERT(!local.Tell(fileHandle, fs));
  247. AZ_TEST_ASSERT(!local.Exists((m_file01Name.Native() + "notexist").c_str()));
  248. AZ_TEST_ASSERT(local.Exists(m_file01Name.c_str()));
  249. AZ_TEST_ASSERT(!local.IsReadOnly(m_file01Name.c_str()));
  250. AZ_TEST_ASSERT(!local.IsDirectory(m_file01Name.c_str()));
  251. // test reads and seeks.
  252. AZ_TEST_ASSERT(local.Open(m_file01Name.c_str(), AZ::IO::OpenMode::ModeRead, fileHandle));
  253. AZ_TEST_ASSERT(fileHandle != AZ::IO::InvalidHandle);
  254. // use this again later...
  255. AZ::u64 modTimeB = local.ModificationTime(fileHandle);
  256. AZ_TEST_ASSERT(modTimeB != 0);
  257. static const size_t testStringLen = 256;
  258. char testString[testStringLen] = { 0 };
  259. // test size on open handle:
  260. fs = 0;
  261. AZ_TEST_ASSERT(local.Size(fileHandle, fs));
  262. AZ_TEST_ASSERT(fs == 19);
  263. // test size without opening, after its already open:
  264. fs = 0;
  265. AZ_TEST_ASSERT(local.Size(m_file01Name.c_str(), fs));
  266. AZ_TEST_ASSERT(fs == 19);
  267. AZ::u64 offs = 0;
  268. AZ_TEST_ASSERT(local.Tell(fileHandle, offs));
  269. AZ_TEST_ASSERT(offs == 0);
  270. AZ_TEST_ASSERT(local.Seek(fileHandle, 5, AZ::IO::SeekType::SeekFromStart));
  271. AZ_TEST_ASSERT(!local.Eof(fileHandle));
  272. AZ::u64 actualBytesRead = 0;
  273. // situation
  274. // this is just a test
  275. // ^-------------
  276. // 15 chars
  277. AZ_TEST_ASSERT(local.Tell(fileHandle, offs));
  278. AZ_TEST_ASSERT(offs == 5);
  279. AZ_TEST_ASSERT(!local.Eof(fileHandle));
  280. AZ_TEST_ASSERT(local.Read(fileHandle, testString, testStringLen, false, &actualBytesRead));
  281. AZ_TEST_ASSERT(actualBytesRead == 14);
  282. AZ_TEST_ASSERT(strncmp(testString, "is just a test", 14) == 0);
  283. AZ_TEST_ASSERT(local.Eof(fileHandle));
  284. // this is just a test
  285. // ^
  286. AZ_TEST_ASSERT(local.Seek(fileHandle, -5, AZ::IO::SeekType::SeekFromCurrent));
  287. // this is just a test
  288. // ^----
  289. AZ_TEST_ASSERT(local.Tell(fileHandle, offs));
  290. AZ_TEST_ASSERT(offs == 14);
  291. AZ_TEST_ASSERT(!local.Eof(fileHandle));
  292. AZ_TEST_ASSERT(local.Read(fileHandle, testString, testStringLen, false, &actualBytesRead));
  293. AZ_TEST_ASSERT(actualBytesRead == 5);
  294. AZ_TEST_ASSERT(strncmp(testString, " test", 5) == 0);
  295. AZ_TEST_ASSERT(local.Eof(fileHandle));
  296. // this is just a test
  297. // ^
  298. AZ_TEST_ASSERT(local.Seek(fileHandle, -6, AZ::IO::SeekType::SeekFromEnd));
  299. // this is just a test
  300. // ^---
  301. AZ_TEST_ASSERT(local.Tell(fileHandle, offs));
  302. AZ_TEST_ASSERT(offs == 13);
  303. AZ_TEST_ASSERT(!local.Eof(fileHandle));
  304. AZ_TEST_ASSERT(local.Read(fileHandle, testString, 4, true, &actualBytesRead));
  305. AZ_TEST_ASSERT(actualBytesRead == 4);
  306. AZ_TEST_ASSERT(strncmp(testString, "a te", 4) == 0);
  307. AZ_TEST_ASSERT(local.Tell(fileHandle, offs));
  308. AZ_TEST_ASSERT(offs == 17);
  309. AZ_TEST_ASSERT(!local.Eof(fileHandle));
  310. // fail when not enough bytes:
  311. AZ_TEST_ASSERT(!local.Read(fileHandle, testString, testStringLen, true, &actualBytesRead));
  312. AZ_TEST_ASSERT(local.Eof(fileHandle));
  313. AZ_TEST_ASSERT(local.Close(fileHandle));
  314. fileHandle = AZ::IO::InvalidHandle;
  315. }
  316. };
  317. TEST_F(ReadWriteTest, Test)
  318. {
  319. run();
  320. }
  321. class PermissionsTest
  322. : public FolderFixture
  323. {
  324. public:
  325. void run()
  326. {
  327. LocalFileIO local;
  328. CreateTestFiles();
  329. #if AZ_TRAIT_AZFRAMEWORKTEST_PERFORM_CHMOD_TEST
  330. #if AZ_TRAIT_USE_WINDOWS_FILE_API
  331. _chmod(m_file01Name.c_str(), _S_IREAD);
  332. #else
  333. chmod(m_file01Name.c_str(), S_IRUSR | S_IRGRP | S_IROTH);
  334. #endif
  335. AZ_TEST_ASSERT(local.IsReadOnly(m_file01Name.c_str()));
  336. #if AZ_TRAIT_USE_WINDOWS_FILE_API
  337. _chmod(m_file01Name.c_str(), _S_IREAD | _S_IWRITE);
  338. #else
  339. chmod(m_file01Name.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
  340. #endif
  341. #endif
  342. AZ_TEST_ASSERT(!local.IsReadOnly(m_file01Name.c_str()));
  343. }
  344. };
  345. TEST_F(PermissionsTest, Test)
  346. {
  347. run();
  348. }
  349. class CopyMoveTests
  350. : public FolderFixture
  351. {
  352. public:
  353. void run()
  354. {
  355. LocalFileIO local;
  356. AZ_TEST_ASSERT(local.CreatePath(m_fileRoot.c_str()));
  357. AZ_TEST_ASSERT(local.IsDirectory(m_fileRoot.c_str()));
  358. {
  359. #ifdef AZ_COMPILER_MSVC
  360. FILE* tempFile;
  361. fopen_s(&tempFile, m_file01Name.c_str(), "wb");
  362. #else
  363. FILE* tempFile = fopen(m_file01Name.c_str(), "wb");
  364. #endif
  365. fwrite("this is just a test", 1, 19, tempFile);
  366. fclose(tempFile);
  367. }
  368. // make sure attributes are copied (such as modtime) even if they're copied:
  369. AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(1500));
  370. AZ_TEST_ASSERT(local.Copy(m_file01Name.c_str(), m_file02Name.c_str()));
  371. AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(1500));
  372. AZ_TEST_ASSERT(local.Copy(m_file01Name.c_str(), m_file03Name.c_str()));
  373. AZ_TEST_ASSERT(local.Exists(m_file01Name.c_str()));
  374. AZ_TEST_ASSERT(local.Exists(m_file02Name.c_str()));
  375. AZ_TEST_ASSERT(local.Exists(m_file03Name.c_str()));
  376. AZ_TEST_ASSERT(!local.DestroyPath(m_file01Name.c_str())); // you may not destroy files.
  377. AZ_TEST_ASSERT(!local.DestroyPath(m_file02Name.c_str()));
  378. AZ_TEST_ASSERT(!local.DestroyPath(m_file03Name.c_str()));
  379. AZ_TEST_ASSERT(local.Exists(m_file01Name.c_str()));
  380. AZ_TEST_ASSERT(local.Exists(m_file02Name.c_str()));
  381. AZ_TEST_ASSERT(local.Exists(m_file03Name.c_str()));
  382. AZ::u64 f1s = 0;
  383. AZ::u64 f2s = 0;
  384. AZ::u64 f3s = 0;
  385. AZ_TEST_ASSERT(local.Size(m_file01Name.c_str(), f1s));
  386. AZ_TEST_ASSERT(local.Size(m_file02Name.c_str(), f2s));
  387. AZ_TEST_ASSERT(local.Size(m_file03Name.c_str(), f3s));
  388. AZ_TEST_ASSERT(f1s == f2s);
  389. AZ_TEST_ASSERT(f1s == f3s);
  390. // Copying over top other files is allowed
  391. SystemFile file;
  392. EXPECT_TRUE(file.Open(m_file01Name.c_str(), SystemFile::SF_OPEN_WRITE_ONLY));
  393. file.Write("this is just a test that is longer", 34);
  394. file.Close();
  395. // make sure attributes are copied (such as modtime) even if they're copied:
  396. AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(1500));
  397. EXPECT_TRUE(local.Copy(m_file01Name.c_str(), m_file02Name.c_str()));
  398. f1s = 0;
  399. f2s = 0;
  400. f3s = 0;
  401. EXPECT_TRUE(local.Size(m_file01Name.c_str(), f1s));
  402. EXPECT_TRUE(local.Size(m_file02Name.c_str(), f2s));
  403. EXPECT_TRUE(local.Size(m_file03Name.c_str(), f3s));
  404. EXPECT_EQ(f1s, f2s);
  405. EXPECT_NE(f1s, f3s);
  406. }
  407. };
  408. TEST_F(CopyMoveTests, Test)
  409. {
  410. run();
  411. }
  412. class ModTimeTest
  413. : public FolderFixture
  414. {
  415. public:
  416. void run()
  417. {
  418. AZ::IO::LocalFileIO local;
  419. CreateTestFiles();
  420. AZ::u64 modTimeC = 0;
  421. AZ::u64 modTimeD = 0;
  422. modTimeC = local.ModificationTime(m_file02Name.c_str());
  423. modTimeD = local.ModificationTime(m_file03Name.c_str());
  424. // make sure modtimes are in ascending order (at least)
  425. AZ_TEST_ASSERT(modTimeD >= modTimeC);
  426. // now touch some of the files. This is also how we test append mode, and write mode.
  427. AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
  428. AZ_TEST_ASSERT(local.Open(m_file02Name.c_str(), AZ::IO::OpenMode::ModeAppend | AZ::IO::OpenMode::ModeBinary, fileHandle));
  429. AZ_TEST_ASSERT(fileHandle != AZ::IO::InvalidHandle);
  430. AZ_TEST_ASSERT(local.Write(fileHandle, "more", 4));
  431. AZ_TEST_ASSERT(local.Close(fileHandle));
  432. AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(1500));
  433. // No-append-mode
  434. AZ_TEST_ASSERT(local.Open(m_file03Name.c_str(), AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeBinary, fileHandle));
  435. AZ_TEST_ASSERT(fileHandle != AZ::IO::InvalidHandle);
  436. AZ_TEST_ASSERT(local.Write(fileHandle, "more", 4));
  437. AZ_TEST_ASSERT(local.Close(fileHandle));
  438. modTimeC = local.ModificationTime(m_file02Name.c_str());
  439. modTimeD = local.ModificationTime(m_file03Name.c_str());
  440. AZ_TEST_ASSERT(modTimeD > modTimeC);
  441. AZ::u64 f1s = 0;
  442. AZ::u64 f2s = 0;
  443. AZ::u64 f3s = 0;
  444. AZ_TEST_ASSERT(local.Size(m_file01Name.c_str(), f1s));
  445. AZ_TEST_ASSERT(local.Size(m_file02Name.c_str(), f2s));
  446. AZ_TEST_ASSERT(local.Size(m_file03Name.c_str(), f3s));
  447. AZ_TEST_ASSERT(f2s == f1s + 4);
  448. AZ_TEST_ASSERT(f3s == 4);
  449. }
  450. };
  451. TEST_F(ModTimeTest, Test)
  452. {
  453. run();
  454. }
  455. class FindFilesTest
  456. : public FolderFixture
  457. {
  458. public:
  459. void run()
  460. {
  461. AZ::IO::LocalFileIO local;
  462. CreateTestFiles();
  463. AZStd::vector<AZ::IO::Path> resultFiles;
  464. bool foundOK = local.FindFiles(m_fileRoot.c_str(), "*",
  465. [&](const char* filePath) -> bool
  466. {
  467. resultFiles.push_back(filePath);
  468. return false; // early out!
  469. });
  470. AZ_TEST_ASSERT(foundOK);
  471. AZ_TEST_ASSERT(resultFiles.size() == 1);
  472. resultFiles.clear();
  473. foundOK = local.FindFiles(m_fileRoot.c_str(), "*",
  474. [&](const char* filePath) -> bool
  475. {
  476. resultFiles.push_back(filePath);
  477. return true; // continue iterating
  478. });
  479. AZ_TEST_ASSERT(foundOK);
  480. AZ_TEST_ASSERT(resultFiles.size() == 3);
  481. // note: following tests accumulate more files without clearing resultfiles.
  482. foundOK = local.FindFiles(m_fileRoot.c_str(), "*.txt",
  483. [&](const char* filePath) -> bool
  484. {
  485. resultFiles.push_back(filePath);
  486. return true; // continue iterating
  487. });
  488. AZ_TEST_ASSERT(foundOK);
  489. AZ_TEST_ASSERT(resultFiles.size() == 4);
  490. foundOK = local.FindFiles(m_fileRoot.c_str(), "file*.asdf",
  491. [&](const char* filePath) -> bool
  492. {
  493. resultFiles.push_back(filePath);
  494. return true; // continue iterating
  495. });
  496. AZ_TEST_ASSERT(foundOK);
  497. AZ_TEST_ASSERT(resultFiles.size() == 5);
  498. foundOK = local.FindFiles(m_fileRoot.c_str(), "asaf.asdf",
  499. [&](const char* filePath) -> bool
  500. {
  501. resultFiles.push_back(filePath);
  502. return true; // continue iterating
  503. });
  504. AZ_TEST_ASSERT(foundOK);
  505. AZ_TEST_ASSERT(resultFiles.size() == 5);
  506. resultFiles.clear();
  507. // test to make sure directories show up:
  508. foundOK = local.FindFiles(m_deepFolder.c_str(), "*",
  509. [&](const char* filePath) -> bool
  510. {
  511. resultFiles.push_back(filePath);
  512. return true; // continue iterating
  513. });
  514. // canonicalize the name in the same way that find does.
  515. //AZStd::replace() m_extraFolder.replace('\\', '/'); FIXME PPATEL
  516. AZ_TEST_ASSERT(foundOK);
  517. AZ_TEST_ASSERT(resultFiles.size() == 1);
  518. AZ_TEST_ASSERT(resultFiles[0] == m_extraFolder);
  519. resultFiles.clear();
  520. foundOK = local.FindFiles("o:137787621!@#$%^&&**())_+[])_", "asaf.asdf",
  521. [&](const char* filePath) -> bool
  522. {
  523. resultFiles.push_back(filePath);
  524. return true; // continue iterating
  525. });
  526. AZ_TEST_ASSERT(!foundOK);
  527. AZ_TEST_ASSERT(resultFiles.size() == 0);
  528. AZ::IO::Path file04Name = m_fileRoot / "test.wha";
  529. // test rename
  530. AZ_TEST_ASSERT(local.Rename(m_file03Name.c_str(), file04Name.c_str()));
  531. AZ_TEST_ASSERT(!local.Rename(m_file03Name.c_str(), file04Name.c_str()));
  532. AZ_TEST_ASSERT(local.Rename(file04Name.c_str(), file04Name.c_str())); // this is valid and ok
  533. AZ_TEST_ASSERT(local.Exists(file04Name.c_str()));
  534. AZ_TEST_ASSERT(!local.Exists(m_file03Name.c_str()));
  535. AZ_TEST_ASSERT(!local.IsDirectory(file04Name.c_str()));
  536. AZ::u64 f3s = 0;
  537. AZ_TEST_ASSERT(local.Size(file04Name.c_str(), f3s));
  538. AZ_TEST_ASSERT(f3s == 19);
  539. // deep destroy directory:
  540. AZ_TEST_ASSERT(local.DestroyPath(m_folderName.c_str()));
  541. AZ_TEST_ASSERT(!local.Exists(m_folderName.c_str()));
  542. }
  543. };
  544. TEST_F(FindFilesTest, Test)
  545. {
  546. run();
  547. }
  548. using AliasTest = FolderFixture;
  549. TEST_F(AliasTest, Test)
  550. {
  551. AZ::IO::LocalFileIO local;
  552. // test aliases
  553. local.SetAlias("@test@", m_folderName.c_str());
  554. const char* testDest1 = local.GetAlias("@test@");
  555. AZ_TEST_ASSERT(testDest1 != nullptr);
  556. const char* testDest2 = local.GetAlias("@NOPE@");
  557. AZ_TEST_ASSERT(testDest2 == nullptr);
  558. testDest1 = local.GetAlias("@test@"); // try with different case
  559. AZ_TEST_ASSERT(testDest1 != nullptr);
  560. // test resolving
  561. const char* aliasTestPath = "@test@\\some\\path\\somefile.txt";
  562. char aliasResolvedPath[AZ::IO::MaxPathLength];
  563. bool resolveDidWork = local.ResolvePath(aliasTestPath, aliasResolvedPath, AZ::IO::MaxPathLength);
  564. AZ_TEST_ASSERT(resolveDidWork);
  565. AZ::IO::Path expectedResolvedPath = m_folderName / "some/path/somefile.txt";
  566. AZ_TEST_ASSERT(aliasResolvedPath == expectedResolvedPath);
  567. // more resolve path tests with invalid inputs
  568. const char* testPath = nullptr;
  569. char* testResolvedPath = nullptr;
  570. resolveDidWork = local.ResolvePath(testPath, aliasResolvedPath, AZ::IO::MaxPathLength);
  571. AZ_TEST_ASSERT(!resolveDidWork);
  572. resolveDidWork = local.ResolvePath(aliasTestPath, testResolvedPath, AZ::IO::MaxPathLength);
  573. AZ_TEST_ASSERT(!resolveDidWork);
  574. resolveDidWork = local.ResolvePath(aliasTestPath, aliasResolvedPath, 0);
  575. AZ_TEST_ASSERT(!resolveDidWork);
  576. // Test that sending in a too small output path fails,
  577. // if the output buffer is smaller than the string being resolved
  578. size_t SMALLER_THAN_PATH_BEING_RESOLVED = strlen(aliasTestPath) - 1;
  579. AZ_TEST_START_TRACE_SUPPRESSION;
  580. resolveDidWork = local.ResolvePath(aliasTestPath, aliasResolvedPath, SMALLER_THAN_PATH_BEING_RESOLVED);
  581. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  582. AZ_TEST_ASSERT(!resolveDidWork);
  583. // Test that sending in a too small output path fails,
  584. // if the output buffer is too small to hold the resolved path
  585. size_t SMALLER_THAN_FINAL_RESOLVED_PATH = expectedResolvedPath.Native().length() - 1;
  586. AZ_TEST_START_TRACE_SUPPRESSION;
  587. resolveDidWork = local.ResolvePath(aliasTestPath, aliasResolvedPath, SMALLER_THAN_FINAL_RESOLVED_PATH);
  588. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  589. AZ_TEST_ASSERT(!resolveDidWork);
  590. // test clearing an alias
  591. local.ClearAlias("@test@");
  592. testDest1 = local.GetAlias("@test@");
  593. AZ_TEST_ASSERT(testDest1 == nullptr);;
  594. }
  595. TEST_F(AliasTest, ResolvePath_PathViewOverload_Succeeds)
  596. {
  597. AZ::IO::LocalFileIO local;
  598. local.SetAlias("@test@", m_folderName.c_str());
  599. AZ::IO::PathView aliasTestPath = "@test@\\some\\path\\somefile.txt";
  600. AZ::IO::FixedMaxPath aliasResolvedPath;
  601. ASSERT_TRUE(local.ResolvePath(aliasResolvedPath, aliasTestPath));
  602. AZ::IO::Path expectedResolvedPath = m_folderName / "some" / "path" / "somefile.txt";
  603. EXPECT_EQ(expectedResolvedPath, aliasResolvedPath);
  604. AZStd::optional<AZ::IO::FixedMaxPath> optionalResolvedPath = local.ResolvePath(aliasTestPath);
  605. ASSERT_TRUE(optionalResolvedPath);
  606. EXPECT_EQ(expectedResolvedPath, optionalResolvedPath.value());
  607. }
  608. TEST_F(AliasTest, ResolvePath_PathViewOverloadWithEmptyPath_Fails)
  609. {
  610. AZ::IO::LocalFileIO local;
  611. local.SetAlias("@test@", m_folderName.c_str());
  612. AZ::IO::FixedMaxPath aliasResolvedPath;
  613. EXPECT_FALSE(local.ResolvePath(aliasResolvedPath, {}));
  614. }
  615. TEST_F(AliasTest, ConvertToAlias_PathViewOverloadContainingExactAliasPath_Succeeds)
  616. {
  617. AZ::IO::LocalFileIO local;
  618. AZ::IO::FixedMaxPathString aliasFolder;
  619. EXPECT_TRUE(local.ConvertToAbsolutePath("/temp", aliasFolder.data(), aliasFolder.capacity()));
  620. aliasFolder.resize_no_construct(AZStd::char_traits<char>::length(aliasFolder.data()));
  621. local.SetAlias("@test@", aliasFolder.c_str());
  622. AZ::IO::FixedMaxPath aliasPath;
  623. ASSERT_TRUE(local.ConvertToAlias(aliasPath, AZ::IO::PathView(aliasFolder)));
  624. EXPECT_STREQ("@test@", aliasPath.c_str());
  625. AZStd::optional<AZ::IO::FixedMaxPath> optionalAliasPath = local.ConvertToAlias(AZ::IO::PathView(aliasFolder));
  626. ASSERT_TRUE(optionalAliasPath);
  627. EXPECT_STREQ("@test@", optionalAliasPath->c_str());
  628. }
  629. TEST_F(AliasTest, ConvertToAlias_PathViewOverloadStartingWithAliasPath_Succeeds)
  630. {
  631. AZ::IO::LocalFileIO local;
  632. AZ::IO::FixedMaxPathString aliasFolder;
  633. EXPECT_TRUE(local.ConvertToAbsolutePath("/temp", aliasFolder.data(), aliasFolder.capacity()));
  634. aliasFolder.resize_no_construct(AZStd::char_traits<char>::length(aliasFolder.data()));
  635. local.SetAlias("@test@", aliasFolder.c_str());
  636. const auto testPath = AZ::IO::FixedMaxPathString::format("%s/Dir", aliasFolder.c_str());
  637. AZ::IO::FixedMaxPath aliasPath;
  638. ASSERT_TRUE(local.ConvertToAlias(aliasPath, AZ::IO::PathView(testPath)));
  639. EXPECT_STREQ("@test@/Dir", aliasPath.c_str());
  640. }
  641. TEST_F(AliasTest, ConvertToAlias_PathViewOverloadInputPathWithoutPathSeparatorAndStartWithAliasPath_DoesNotSubstituteAlias)
  642. {
  643. AZ::IO::LocalFileIO local;
  644. AZ::IO::FixedMaxPathString aliasFolder;
  645. EXPECT_TRUE(local.ConvertToAbsolutePath("/temp", aliasFolder.data(), aliasFolder.capacity()));
  646. aliasFolder.resize_no_construct(AZStd::char_traits<char>::length(aliasFolder.data()));
  647. local.SetAlias("@test@", aliasFolder.c_str());
  648. // Because there is no trailing path separator, the input path is really "/tempDir"
  649. // Therefore the "/temp" alias shouldn't match as an alias should match a full directory
  650. const auto testPath = AZ::IO::FixedMaxPathString::format("%sDir", aliasFolder.c_str());
  651. AZ::IO::FixedMaxPath aliasPath{ testPath };
  652. EXPECT_TRUE(local.ConvertToAlias(aliasPath, AZ::IO::PathView(testPath)));
  653. EXPECT_STREQ(testPath.c_str(), aliasPath.c_str());
  654. }
  655. TEST_F(AliasTest, ConvertToAlias_PathViewOverloadWithTooLongPath_ReturnsFalse)
  656. {
  657. AZ::IO::LocalFileIO local;
  658. AZ::IO::FixedMaxPathString aliasFolder;
  659. EXPECT_TRUE(local.ConvertToAbsolutePath("/temp", aliasFolder.data(), aliasFolder.capacity()));
  660. aliasFolder.resize_no_construct(AZStd::char_traits<char>::length(aliasFolder.data()));
  661. local.SetAlias("@LongAliasThatIsLong@", aliasFolder.c_str());
  662. AZStd::string path = static_cast<AZStd::string_view>(aliasFolder);
  663. path.push_back(AZ_CORRECT_FILESYSTEM_SEPARATOR);
  664. // The length of "@alias@" is longer than the aliased path
  665. // Therefore ConvertToAlias should fail due to not being able to fit the alias in the buffer
  666. path.append(AZ::IO::MaxPathLength, 'a');
  667. AZ::IO::FixedMaxPath aliasPath;
  668. AZ_TEST_START_TRACE_SUPPRESSION;
  669. EXPECT_FALSE(local.ConvertToAlias(aliasPath, AZ::IO::PathView(path)));
  670. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  671. }
  672. TEST_F(AliasTest, GetAlias_LogsError_WhenAccessingDeprecatedAlias_Succeeds)
  673. {
  674. AZ::IO::LocalFileIO local;
  675. AZ::IO::FixedMaxPathString aliasFolder;
  676. EXPECT_TRUE(local.ConvertToAbsolutePath("/temp", aliasFolder.data(), aliasFolder.capacity()));
  677. aliasFolder.resize_no_construct(AZStd::char_traits<char>::length(aliasFolder.data()));
  678. local.SetAlias("@test@", aliasFolder.c_str());
  679. local.SetDeprecatedAlias("@deprecated@", "@test@");
  680. local.SetDeprecatedAlias("@deprecatednonexistent@", "@nonexistent@");
  681. local.SetDeprecatedAlias("@deprecatedsecond@", "@deprecated@");
  682. local.SetDeprecatedAlias("@deprecatednonaliaspath@", aliasFolder);
  683. AZ_TEST_START_TRACE_SUPPRESSION;
  684. const char* testAlias = local.GetAlias("@test@");
  685. ASSERT_NE(nullptr, testAlias);
  686. EXPECT_EQ(AZ::IO::PathView(aliasFolder), AZ::IO::PathView(testAlias));
  687. AZ_TEST_STOP_TRACE_SUPPRESSION(0);
  688. // Validate that accessing Deprecated Alias results in AZ_Error
  689. AZ_TEST_START_TRACE_SUPPRESSION;
  690. testAlias = local.GetAlias("@deprecated@");
  691. ASSERT_NE(nullptr, testAlias);
  692. EXPECT_EQ(AZ::IO::PathView(aliasFolder), AZ::IO::PathView(testAlias));
  693. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  694. AZ_TEST_START_TRACE_SUPPRESSION;
  695. testAlias = local.GetAlias("@deprecatednonexistent@");
  696. EXPECT_EQ(nullptr, testAlias);
  697. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  698. AZ_TEST_START_TRACE_SUPPRESSION;
  699. testAlias = local.GetAlias("@deprecatedsecond@");
  700. ASSERT_NE(nullptr, testAlias);
  701. EXPECT_EQ(AZ::IO::PathView(aliasFolder), AZ::IO::PathView(testAlias));
  702. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  703. AZ_TEST_START_TRACE_SUPPRESSION;
  704. testAlias = local.GetAlias("@deprecatednonaliaspath@");
  705. ASSERT_NE(nullptr, testAlias);
  706. EXPECT_EQ(AZ::IO::PathView(aliasFolder), AZ::IO::PathView(testAlias));
  707. AZ_TEST_STOP_TRACE_SUPPRESSION(1);
  708. }
  709. class SmartMoveTests
  710. : public FolderFixture
  711. {
  712. public:
  713. void run()
  714. {
  715. LocalFileIO localFileIO;
  716. AZ::IO::FileIOBase::SetInstance(&localFileIO);
  717. AZ::IO::Path path = m_file01Name.ParentPath();
  718. AZ_TEST_ASSERT(localFileIO.CreatePath(path.c_str()));
  719. path = m_file01Name.ParentPath();
  720. AZ_TEST_ASSERT(localFileIO.CreatePath(path.c_str()));
  721. AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
  722. localFileIO.Open(m_file01Name.c_str(), OpenMode::ModeWrite | OpenMode::ModeText, fileHandle);
  723. localFileIO.Write(fileHandle, "DummyFile", 9);
  724. localFileIO.Close(fileHandle);
  725. AZ::IO::HandleType fileHandle1 = AZ::IO::InvalidHandle;
  726. localFileIO.Open(m_file02Name.c_str(), OpenMode::ModeWrite | OpenMode::ModeText, fileHandle1);
  727. localFileIO.Write(fileHandle1, "TestFile", 8);
  728. localFileIO.Close(fileHandle1);
  729. fileHandle1 = AZ::IO::InvalidHandle;
  730. localFileIO.Open(m_file02Name.c_str(), OpenMode::ModeRead | OpenMode::ModeText, fileHandle1);
  731. static const size_t testStringLen = 256;
  732. char testString[testStringLen] = { 0 };
  733. localFileIO.Read(fileHandle1, testString, testStringLen);
  734. localFileIO.Close(fileHandle1);
  735. AZ_TEST_ASSERT(strncmp(testString, "TestFile", 8) == 0);
  736. // try swapping files when none of the files are in use
  737. AZ_TEST_ASSERT(AZ::IO::SmartMove(m_file01Name.c_str(), m_file02Name.c_str()));
  738. fileHandle1 = AZ::IO::InvalidHandle;
  739. localFileIO.Open(m_file02Name.c_str(), OpenMode::ModeRead | OpenMode::ModeText, fileHandle1);
  740. testString[0] = '\0';
  741. localFileIO.Read(fileHandle1, testString, testStringLen);
  742. localFileIO.Close(fileHandle1);
  743. AZ_TEST_ASSERT(strncmp(testString, "DummyFile", 9) == 0);
  744. //try swapping files when source file is not present, this should fail
  745. AZ_TEST_ASSERT(!AZ::IO::SmartMove(m_file01Name.c_str(), m_file02Name.c_str()));
  746. fileHandle = AZ::IO::InvalidHandle;
  747. localFileIO.Open(m_file01Name.c_str(), OpenMode::ModeWrite | OpenMode::ModeText, fileHandle);
  748. localFileIO.Write(fileHandle, "TestFile", 8);
  749. localFileIO.Close(fileHandle);
  750. #if AZ_TRAIT_AZFRAMEWORKTEST_MOVE_WHILE_OPEN
  751. fileHandle1 = AZ::IO::InvalidHandle;
  752. localFileIO.Open(m_file02Name.c_str(), OpenMode::ModeRead | OpenMode::ModeText, fileHandle1);
  753. testString[0] = '\0';
  754. localFileIO.Read(fileHandle1, testString, testStringLen);
  755. // try swapping files when the destination file is open for read only,
  756. // since window is unable to move files that are open for read, this will fail.
  757. AZ_TEST_ASSERT(!AZ::IO::SmartMove(m_file01Name.c_str(), m_file02Name.c_str()));
  758. localFileIO.Close(fileHandle1);
  759. #endif
  760. fileHandle = AZ::IO::InvalidHandle;
  761. localFileIO.Open(m_file01Name.c_str(), OpenMode::ModeRead | OpenMode::ModeText, fileHandle);
  762. // try swapping files when the source file is open for read only
  763. AZ_TEST_ASSERT(AZ::IO::SmartMove(m_file01Name.c_str(), m_file02Name.c_str()));
  764. localFileIO.Close(fileHandle);
  765. fileHandle1 = AZ::IO::InvalidHandle;
  766. localFileIO.Open(m_file02Name.c_str(), OpenMode::ModeRead | OpenMode::ModeText, fileHandle1);
  767. testString[0] = '\0';
  768. localFileIO.Read(fileHandle1, testString, testStringLen);
  769. AZ_TEST_ASSERT(strncmp(testString, "TestFile", 8) == 0);
  770. localFileIO.Close(fileHandle1);
  771. localFileIO.Remove(m_file01Name.c_str());
  772. localFileIO.Remove(m_file02Name.c_str());
  773. localFileIO.DestroyPath(m_root.c_str());
  774. AZ::IO::FileIOBase::SetInstance(nullptr);
  775. }
  776. };
  777. TEST_F(SmartMoveTests, Test)
  778. {
  779. run();
  780. }
  781. }
  782. } // namespace UnitTest