CryPakUnitTests.cpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. /*
  2. * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
  3. * its licensors.
  4. *
  5. * For complete copyright and license terms please see the LICENSE at the root of this
  6. * distribution (the "License"). All use of this software is governed by the License,
  7. * or, if provided, by the license below or the license accompanying this file. Do not
  8. * remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
  9. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. *
  11. */
  12. #include "CrySystem_precompiled.h"
  13. #include <AzTest/AzTest.h>
  14. #include <AzCore/UnitTest/UnitTest.h>
  15. #include <AzCore/Component/ComponentApplication.h>
  16. #include <AzCore/IO/SystemFile.h> // for max path decl
  17. #include <AzCore/std/parallel/thread.h>
  18. #include <AzCore/std/parallel/semaphore.h>
  19. #include <AzCore/std/functional.h> // for function<> in the find files callback.
  20. #include <AzFramework/IO/LocalFileIO.h>
  21. #include <AzFramework/Archive/ArchiveFileIO.h>
  22. #include <AzFramework/Archive/Archive.h>
  23. #include <AzFramework/Archive/INestedArchive.h>
  24. #include <ILevelSystem.h>
  25. namespace CryPakUnitTests
  26. {
  27. #if defined(AZ_PLATFORM_WINDOWS)
  28. // Note: none of the below is really a unit test, its all basic feature tests
  29. // for critical functionality
  30. class Integ_CryPakUnitTests
  31. : public ::testing::Test
  32. {
  33. protected:
  34. bool IsPackValid(const char* path)
  35. {
  36. AZ::IO::IArchive* pak = gEnv->pCryPak;
  37. if (!pak)
  38. {
  39. return false;
  40. }
  41. if (!pak->OpenPack(path, AZ::IO::IArchive::FLAGS_PATH_REAL))
  42. {
  43. return false;
  44. }
  45. pak->ClosePack(path);
  46. return true;
  47. }
  48. };
  49. TEST_F(Integ_CryPakUnitTests, TestCryPakArchiveContainingLevels)
  50. {
  51. AZ::IO::FileIOBase* fileIo = AZ::IO::FileIOBase::GetInstance();
  52. ASSERT_NE(nullptr, fileIo);
  53. constexpr const char* testPakPath = "@usercache@/archivecontainerlevel.pak";
  54. char resolvedArchivePath[AZ_MAX_PATH_LEN] = { 0 };
  55. EXPECT_TRUE(fileIo->ResolvePath(testPakPath, resolvedArchivePath, AZ_MAX_PATH_LEN));
  56. AZ::IO::IArchive* pak = gEnv->pCryPak;
  57. ASSERT_NE(nullptr, pak);
  58. // delete test files in case they already exist
  59. pak->ClosePack(testPakPath);
  60. fileIo->Remove(testPakPath);
  61. ILevelSystem* levelSystem = gEnv->pSystem->GetILevelSystem();
  62. EXPECT_NE(nullptr, levelSystem);
  63. // ------------ Create an archive with a dummy level in it ------------
  64. AZStd::intrusive_ptr<AZ::IO::INestedArchive> pArchive = pak->OpenArchive(testPakPath, nullptr, AZ::IO::INestedArchive::FLAGS_CREATE_NEW);
  65. EXPECT_NE(nullptr, pArchive);
  66. const char levelInfoFile[] = "levelInfo.xml";
  67. AZStd::string relativeLevelPakPath = AZStd::string::format("levels/dummy/%s", ILevelSystem::LevelPakName);
  68. AZStd::string relativeLevelInfoPath = AZStd::string::format("levels/dummy/%s", levelInfoFile);
  69. EXPECT_EQ(0, pArchive->UpdateFile(relativeLevelPakPath.c_str(), const_cast<char*>("test"), 4, AZ::IO::INestedArchive::METHOD_COMPRESS, AZ::IO::INestedArchive::LEVEL_BEST));
  70. EXPECT_EQ(0, pArchive->UpdateFile(relativeLevelInfoPath.c_str(), const_cast<char*>("test"), 4, AZ::IO::INestedArchive::METHOD_COMPRESS, AZ::IO::INestedArchive::LEVEL_BEST));
  71. pArchive.reset();
  72. EXPECT_TRUE(IsPackValid(testPakPath));
  73. AZStd::fixed_string<AZ::IO::IArchive::MaxPath> fullLevelPakPath;
  74. bool addLevel = true;
  75. EXPECT_TRUE(pak->OpenPack("@assets@", resolvedArchivePath, AZ::IO::IArchive::FLAGS_LEVEL_PAK_INSIDE_PAK, nullptr, &fullLevelPakPath, addLevel));
  76. ILevelInfo* levelInfo = nullptr;
  77. // Since the archive was open, we should be able to find the level "dummy"
  78. levelInfo = levelSystem->GetLevelInfo("dummy");
  79. EXPECT_NE(nullptr, levelInfo);
  80. EXPECT_TRUE(pak->ClosePack(resolvedArchivePath));
  81. // After closing the archive we should not be able to find the level "dummy"
  82. levelInfo = levelSystem->GetLevelInfo("dummy");
  83. EXPECT_EQ(nullptr, levelInfo);
  84. }
  85. TEST_F(Integ_CryPakUnitTests, TestCryPakModTime)
  86. {
  87. AZ::IO::FileIOBase* fileIo = AZ::IO::FileIOBase::GetInstance();
  88. ASSERT_NE(nullptr, fileIo);
  89. AZ::IO::IArchive* pak = gEnv->pCryPak;
  90. // repeat the following test multiple times, since timing (seconds) can affect it and it involves time!
  91. for (int iteration = 0; iteration < 10; ++iteration)
  92. {
  93. AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds{ 100 });
  94. // helper paths and strings
  95. AZStd::string gameFolder = fileIo->GetAlias("@usercache@");
  96. AZStd::string testFile = "unittest.bin";
  97. AZStd::string testFilePath = gameFolder + "\\" + testFile;
  98. AZStd::string testPak = "unittest.pak";
  99. AZStd::string testPakPath = gameFolder + "\\" + testPak;
  100. AZStd::string zipCmd = "-zip=" + testPakPath;
  101. // delete test files in case they already exist
  102. fileIo->Remove(testFilePath.c_str());
  103. pak->ClosePack(testPakPath);
  104. fileIo->Remove(testPakPath.c_str());
  105. // create a test file
  106. char data[] = "unittest";
  107. FILE* f = nullptr;
  108. azfopen(&f, testFilePath.c_str(), "wb");
  109. EXPECT_TRUE(f != nullptr); // file successfully opened for writing
  110. EXPECT_TRUE(fwrite(data, sizeof(char), sizeof(data), f) == sizeof(data)); // file written to successfully
  111. EXPECT_TRUE(fclose(f) == 0); // file closed successfully
  112. AZ::IO::HandleType fDisk = pak->FOpen(testFilePath.c_str(), "rb");
  113. EXPECT_TRUE(fDisk > 0); // opened file on disk successfully
  114. uint64_t modTimeDisk = pak->GetModificationTime(fDisk); // high res mod time extracted from file on disk
  115. EXPECT_TRUE(pak->FClose(fDisk) == 0); // file closed successfully
  116. // create a low res copy of disk file's mod time
  117. uint64_t absDiff, maxDiff = 20000000ul;
  118. uint16_t dosDate, dosTime;
  119. FILETIME ft;
  120. LARGE_INTEGER lt;
  121. ft.dwHighDateTime = modTimeDisk >> 32;
  122. ft.dwLowDateTime = modTimeDisk & 0xFFFFFFFF;
  123. EXPECT_TRUE(FileTimeToDosDateTime(&ft, &dosDate, &dosTime) != FALSE); // converted to DOSTIME successfully
  124. ft.dwHighDateTime = 0;
  125. ft.dwLowDateTime = 0;
  126. EXPECT_TRUE(DosDateTimeToFileTime(dosDate, dosTime, &ft) != FALSE); // converted to FILETIME successfully
  127. lt.HighPart = ft.dwHighDateTime;
  128. lt.LowPart = ft.dwLowDateTime;
  129. uint64_t modTimeDiskLowRes = lt.QuadPart;
  130. absDiff = modTimeDiskLowRes >= modTimeDisk ? modTimeDiskLowRes - modTimeDisk : modTimeDisk - modTimeDiskLowRes;
  131. EXPECT_LE(absDiff, maxDiff); // FILETIME (high res) and DOSTIME (low res) should be at most 2 seconds apart
  132. gEnv->pResourceCompilerHelper->CallResourceCompiler(testFilePath.c_str(), zipCmd.c_str());
  133. EXPECT_EQ(AZ::IO::ResultCode::Success, fileIo->Remove(testFilePath.c_str())); // test file on disk deleted successfully
  134. EXPECT_TRUE(pak->OpenPack(testPakPath)); // opened pak successfully
  135. AZ::IO::HandleType fPak = pak->FOpen(testFilePath.c_str(), "rb");
  136. EXPECT_GT(fPak, 0); // file (in pak) opened correctly
  137. uint64_t modTimePak = pak->GetModificationTime(fPak); // low res mod time extracted from file in pak
  138. EXPECT_EQ(0, pak->FClose(fPak)); // file closed successfully
  139. EXPECT_TRUE(pak->ClosePack(testPakPath)); // closed pak successfully
  140. EXPECT_EQ(AZ::IO::ResultCode::Success, fileIo->Remove(testPakPath.c_str())); // test pak file deleted successfully
  141. absDiff = modTimePak >= modTimeDisk ? modTimePak - modTimeDisk : modTimeDisk - modTimePak;
  142. // compare mod times. They are allowed to be up to 2 seconds apart but no more
  143. EXPECT_LE(absDiff, maxDiff); // FILETIME (disk) and DOSTIME (pak) should be at most 2 seconds apart
  144. // note: Do not directly compare the disk time and pack time, the resolution drops the last digit off in some cases in pak
  145. // it only has a 2 second resolution. you may compare to make sure that the pak time is WITHIN 2 seconds (as above) but not equal.
  146. // we depend on the fact that crypak is rounding up, instead of down
  147. EXPECT_GE(modTimePak, modTimeDisk);
  148. }
  149. }
  150. #endif
  151. }