You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
881 lines
40 KiB
C++
881 lines
40 KiB
C++
/*
|
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
*
|
|
*/
|
|
|
|
#include <AzTest/AzTest.h>
|
|
#include <AzCore/Console/IConsole.h>
|
|
#include <AzCore/UnitTest/TestTypes.h>
|
|
#include <AzCore/UnitTest/UnitTest.h>
|
|
|
|
#include <AzCore/IO/SystemFile.h> // for max path decl
|
|
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
|
#include <AzCore/std/parallel/thread.h>
|
|
#include <AzCore/std/parallel/semaphore.h>
|
|
#include <AzCore/std/functional.h> // for function<> in the find files callback.
|
|
#include <AzCore/UserSettings/UserSettingsComponent.h>
|
|
#include <AzFramework/Application/Application.h>
|
|
#include <AzFramework/IO/LocalFileIO.h>
|
|
#include <AzFramework/Archive/ArchiveFileIO.h>
|
|
#include <AzFramework/Archive/Archive.h>
|
|
#include <AzFramework/Archive/ArchiveVars.h>
|
|
#include <AzFramework/Archive/INestedArchive.h>
|
|
|
|
namespace UnitTest
|
|
{
|
|
class ArchiveTestFixture
|
|
: public ScopedAllocatorSetupFixture
|
|
{
|
|
public:
|
|
// Use an Immediately invoked function to initlaize the m_stackRecordLevels value of the AZ::SystemAllocator::Descriptor class
|
|
ArchiveTestFixture()
|
|
: ScopedAllocatorSetupFixture(
|
|
[]() { AZ::SystemAllocator::Descriptor desc; desc.m_stackRecordLevels = 30; return desc; }()
|
|
)
|
|
, m_application{ AZStd::make_unique<AzFramework::Application>() }
|
|
{
|
|
}
|
|
|
|
void SetUp() override
|
|
{
|
|
AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get();
|
|
|
|
auto projectPathKey =
|
|
AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path";
|
|
registry->Set(projectPathKey, "AutomatedTesting");
|
|
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry);
|
|
|
|
m_application->Start({});
|
|
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
|
|
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
|
|
// in the unit tests.
|
|
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
|
|
}
|
|
|
|
void TearDown() override
|
|
{
|
|
m_application->Stop();
|
|
}
|
|
|
|
protected:
|
|
bool IsPackValid(const char* path)
|
|
{
|
|
AZ::IO::IArchive* archive = AZ::Interface<AZ::IO::IArchive>::Get();
|
|
if (!archive)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return archive->OpenPack(path) && archive->ClosePack(path);
|
|
}
|
|
|
|
template <class Function>
|
|
void RunConcurrentUnitTest(AZ::u32 numIterations, AZ::u32 numThreads, Function testFunction)
|
|
{
|
|
AZStd::atomic_int successCount{};
|
|
constexpr size_t maxTestThreads = 16;
|
|
|
|
for (AZ::u32 testIteration = 0; testIteration < numIterations; ++testIteration)
|
|
{
|
|
AZStd::fixed_vector<AZStd::thread, maxTestThreads> testThreads;
|
|
successCount = 0;
|
|
|
|
for (AZ::u32 threadIdx = 0; threadIdx < numThreads; ++threadIdx)
|
|
{
|
|
auto threadFunctor = [&testFunction, &successCount]()
|
|
{
|
|
// Add some variability to thread timing by yielding each thread
|
|
AZStd::this_thread::yield();
|
|
|
|
if (testFunction())
|
|
{
|
|
++successCount;
|
|
}
|
|
};
|
|
testThreads.emplace_back(threadFunctor);
|
|
}
|
|
|
|
for (AZStd::thread& testThread : testThreads)
|
|
{
|
|
testThread.join();
|
|
}
|
|
|
|
EXPECT_EQ(numThreads, successCount);
|
|
}
|
|
}
|
|
|
|
void TestFGetCachedFileData(const char* testFilePath, size_t dataLen, const char* testData)
|
|
{
|
|
AZ::IO::IArchive* archive = AZ::Interface<AZ::IO::IArchive>::Get();
|
|
ASSERT_NE(nullptr, archive);
|
|
|
|
constexpr uint32_t numThreadedIterations = 1;
|
|
constexpr uint32_t numTestThreads = 5;
|
|
|
|
{
|
|
// Canary tests first
|
|
AZ::IO::HandleType fileHandle = archive->FOpen(testFilePath, "rb");
|
|
|
|
ASSERT_NE(AZ::IO::InvalidHandle, fileHandle);
|
|
|
|
size_t fileSize = 0;
|
|
char* pFileBuffer = (char*)archive->FGetCachedFileData(fileHandle, fileSize);
|
|
ASSERT_NE(nullptr, pFileBuffer);
|
|
EXPECT_EQ(dataLen, fileSize);
|
|
EXPECT_EQ(0, memcmp(pFileBuffer, testData, dataLen));
|
|
|
|
// 2nd call to FGetCachedFileData, same file handle
|
|
fileSize = 0;
|
|
char* pFileBuffer2 = (char*)archive->FGetCachedFileData(fileHandle, fileSize);
|
|
EXPECT_NE(nullptr, pFileBuffer2);
|
|
EXPECT_EQ(pFileBuffer, pFileBuffer2);
|
|
EXPECT_EQ(dataLen, fileSize);
|
|
|
|
// open already open file and call FGetCachedFileData
|
|
fileSize = 0;
|
|
{
|
|
AZ::IO::HandleType fileHandle2 = archive->FOpen(testFilePath, "rb");
|
|
char* pFileBuffer3 = (char*)archive->FGetCachedFileData(fileHandle2, fileSize);
|
|
ASSERT_NE(nullptr, pFileBuffer3);
|
|
EXPECT_EQ(dataLen, fileSize);
|
|
EXPECT_EQ(0, memcmp(pFileBuffer3, testData, dataLen));
|
|
archive->FClose(fileHandle2);
|
|
}
|
|
|
|
// Multithreaded test #1 reading from the same file handle in parallel
|
|
auto parallelArchiveFileReadFunc = [archive, fileHandle, pFileBuffer, dataLen, testFilePath]()
|
|
{
|
|
size_t fileSize = 0;
|
|
auto pFileBufferThread = reinterpret_cast<const char*>(archive->FGetCachedFileData(fileHandle, fileSize));
|
|
if (pFileBufferThread == nullptr)
|
|
{
|
|
EXPECT_NE(nullptr, pFileBufferThread) << "FGetCachedFileData returned nullptr for file " << testFilePath;
|
|
return false;
|
|
}
|
|
if (pFileBuffer != pFileBufferThread)
|
|
{
|
|
EXPECT_EQ(pFileBufferThread, pFileBuffer) << "Read file data for file " << testFilePath << "Does not match expected file data";
|
|
return false;
|
|
}
|
|
if (fileSize != dataLen)
|
|
{
|
|
EXPECT_EQ(dataLen, fileSize) << "Read filesize does not match expected filesize for file " << testFilePath;
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
RunConcurrentUnitTest(numThreadedIterations, numTestThreads, parallelArchiveFileReadFunc);
|
|
|
|
archive->FClose(fileHandle);
|
|
}
|
|
|
|
// Multithreaded Test #2 reading from the same file concurrently
|
|
auto concurrentArchiveFileReadFunc = [archive, testFilePath, dataLen, testData]()
|
|
{
|
|
AZ::IO::HandleType threadFileHandle = archive->FOpen(testFilePath, "rb");
|
|
|
|
if (threadFileHandle == AZ::IO::InvalidHandle)
|
|
{
|
|
EXPECT_NE(AZ::IO::InvalidHandle, threadFileHandle) << "Failed to open file handle " << testFilePath;
|
|
return false;
|
|
}
|
|
size_t fileSize = 0;
|
|
auto pFileBufferThread = reinterpret_cast<const char*>(archive->FGetCachedFileData(threadFileHandle, fileSize));
|
|
if (pFileBufferThread == nullptr)
|
|
{
|
|
EXPECT_NE(nullptr, pFileBufferThread) << "FGetCachedFileData returned nullptr for file " << testFilePath;
|
|
return false;
|
|
}
|
|
if (fileSize != dataLen)
|
|
{
|
|
EXPECT_EQ(dataLen, fileSize) << "Read filesize does not match expected filesize for file " << testFilePath;
|
|
return false;
|
|
}
|
|
if (memcmp(pFileBufferThread, testData, dataLen) != 0)
|
|
{
|
|
ADD_FAILURE() << "Read file data for file " << testFilePath << "Does not match expected file data";
|
|
}
|
|
archive->FClose(threadFileHandle);
|
|
return true;
|
|
};
|
|
RunConcurrentUnitTest(numThreadedIterations, numTestThreads, concurrentArchiveFileReadFunc);
|
|
}
|
|
|
|
AZStd::unique_ptr<AzFramework::Application> m_application;
|
|
};
|
|
|
|
struct CVarIntValueScope
|
|
{
|
|
CVarIntValueScope(AZ::IConsole& console, const char* cvarName)
|
|
: m_console{ console }
|
|
, m_cvarName{ cvarName }
|
|
{
|
|
// Store current CVar value
|
|
m_valueStored = m_cvarName != nullptr && m_console.GetCvarValue(cvarName, m_oldValue) == AZ::GetValueResult::Success;
|
|
}
|
|
~CVarIntValueScope()
|
|
{
|
|
// Restore the old value if it was successfully stored
|
|
if (m_valueStored)
|
|
{
|
|
m_console.PerformCommand(m_cvarName, { AZ::CVarFixedString::format("%d", m_oldValue) });
|
|
}
|
|
}
|
|
AZ::IConsole& m_console;
|
|
const char* m_cvarName{};
|
|
int32_t m_oldValue{};
|
|
bool m_valueStored{};
|
|
};
|
|
|
|
TEST_F(ArchiveTestFixture, TestArchiveFGetCachedFileData_PakFile)
|
|
{
|
|
// Test setup - from Archive
|
|
constexpr const char* fileInArchiveFile = "levels\\mylevel\\levelinfo.xml";
|
|
constexpr AZStd::string_view dataString = "HELLO WORLD"; // other unit tests make sure writing and reading is working, so don't test that here
|
|
|
|
AZ::IO::IArchive* archive = AZ::Interface<AZ::IO::IArchive>::Get();
|
|
ASSERT_NE(nullptr, archive);
|
|
|
|
AZ::IO::FileIOBase* fileIo = AZ::IO::FileIOBase::GetInstance();
|
|
ASSERT_NE(nullptr, fileIo);
|
|
|
|
auto console = AZ::Interface<AZ::IConsole>::Get();
|
|
ASSERT_NE(nullptr, console);
|
|
|
|
{
|
|
AZStd::string testArchivePath_withSubfolders = "@usercache@/immediate.pak";
|
|
AZStd::string testArchivePath_withMountPoint = "@usercache@/levels/test/flatarchive.pak";
|
|
|
|
// delete test files in case they already exist
|
|
archive->ClosePack(testArchivePath_withSubfolders.c_str());
|
|
fileIo->Remove(testArchivePath_withSubfolders.c_str());
|
|
fileIo->Remove(testArchivePath_withMountPoint.c_str());
|
|
fileIo->CreatePath("@usercache@/levels/test");
|
|
|
|
// setup test archive and file
|
|
AZStd::intrusive_ptr<AZ::IO::INestedArchive> pArchive = archive->OpenArchive(testArchivePath_withSubfolders.c_str(), nullptr, AZ::IO::INestedArchive::FLAGS_CREATE_NEW);
|
|
EXPECT_NE(nullptr, pArchive);
|
|
EXPECT_EQ(0, pArchive->UpdateFile(fileInArchiveFile, dataString.data(), dataString.size(), AZ::IO::INestedArchive::METHOD_COMPRESS, AZ::IO::INestedArchive::LEVEL_FASTEST));
|
|
pArchive.reset();
|
|
EXPECT_TRUE(IsPackValid(testArchivePath_withSubfolders.c_str()));
|
|
|
|
EXPECT_TRUE(archive->OpenPack("@assets@", testArchivePath_withSubfolders.c_str()));
|
|
|
|
EXPECT_TRUE(archive->IsFileExist(fileInArchiveFile));
|
|
}
|
|
|
|
// Prevent Archive file searches from using the OS filesystem
|
|
// Also enable extra verbosity in the AZ::IO::Archive code
|
|
CVarIntValueScope previousLocationPriority{ *console, "sys_pakPriority" };
|
|
CVarIntValueScope oldArchiveVerbosity{ *console, "az_archive_verbosity" };
|
|
console->PerformCommand("sys_PakPriority", { AZ::CVarFixedString::format("%d", aznumeric_cast<int>(AZ::IO::ArchiveLocationPriority::ePakPriorityPakOnly)) });
|
|
console->PerformCommand("az_archive_verbosity", { "1" });
|
|
|
|
// ---- Archive FGetCachedFileDataTests (these leverage Archive CachedFile mechanism for caching data ---
|
|
TestFGetCachedFileData(fileInArchiveFile, dataString.size(), dataString.data());
|
|
}
|
|
|
|
TEST_F(ArchiveTestFixture, TestArchiveOpenPacks_FindsMultiplePaks_Works)
|
|
{
|
|
AZ::IO::IArchive* archive = AZ::Interface<AZ::IO::IArchive>::Get();
|
|
ASSERT_NE(nullptr, archive);
|
|
|
|
AZ::IO::FileIOBase* fileIo = AZ::IO::FileIOBase::GetInstance();
|
|
ASSERT_NE(nullptr, fileIo);
|
|
|
|
auto resetArchiveFile = [archive, fileIo](const AZStd::string& filePath)
|
|
{
|
|
archive->ClosePack(filePath.c_str());
|
|
fileIo->Remove(filePath.c_str());
|
|
|
|
auto pArchive = archive->OpenArchive(filePath.c_str(), nullptr, AZ::IO::INestedArchive::FLAGS_CREATE_NEW);
|
|
EXPECT_NE(nullptr, pArchive);
|
|
pArchive.reset();
|
|
archive->ClosePack(filePath.c_str());
|
|
};
|
|
|
|
AZStd::string testArchivePath_pakOne = "@usercache@/one.pak";
|
|
AZStd::string testArchivePath_pakTwo = "@usercache@/two.pak";
|
|
|
|
// reset test files in case they already exist
|
|
resetArchiveFile(testArchivePath_pakOne);
|
|
resetArchiveFile(testArchivePath_pakTwo);
|
|
|
|
// open and fetch the opened pak file using a *.pak
|
|
AZStd::vector<AZ::IO::FixedMaxPathString> fullPaths;
|
|
archive->OpenPacks("@usercache@/*.pak", &fullPaths);
|
|
EXPECT_TRUE(AZStd::any_of(fullPaths.cbegin(), fullPaths.cend(), [](auto& path) { return path.ends_with("one.pak"); }));
|
|
EXPECT_TRUE(AZStd::any_of(fullPaths.cbegin(), fullPaths.cend(), [](auto& path) { return path.ends_with("two.pak"); }));
|
|
}
|
|
|
|
TEST_F(ArchiveTestFixture, TestArchiveFGetCachedFileData_LooseFile)
|
|
{
|
|
// ------setup loose file FGetCachedFileData tests -------------------------
|
|
|
|
constexpr AZStd::string_view dataString = "HELLO WORLD";
|
|
|
|
AZ::IO::IArchive* archive = AZ::Interface<AZ::IO::IArchive>::Get();
|
|
ASSERT_NE(nullptr, archive);
|
|
const char* testRootPath = "@log@/unittesttemp";
|
|
const char* looseTestFilePath = "@log@/unittesttemp/realfileforunittest.txt";
|
|
AZ::IO::ArchiveFileIO cpfio(archive);
|
|
|
|
// remove existing
|
|
EXPECT_EQ(AZ::IO::ResultCode::Success, cpfio.DestroyPath(testRootPath));
|
|
|
|
// create test file
|
|
EXPECT_EQ(AZ::IO::ResultCode::Success, cpfio.CreatePath(testRootPath));
|
|
|
|
AZ::IO::HandleType normalFileHandle;
|
|
EXPECT_EQ(AZ::IO::ResultCode::Success, cpfio.Open(looseTestFilePath, AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeBinary, normalFileHandle));
|
|
|
|
AZ::u64 bytesWritten = 0;
|
|
EXPECT_EQ(AZ::IO::ResultCode::Success, cpfio.Write(normalFileHandle, dataString.data(), dataString.size(), &bytesWritten));
|
|
EXPECT_EQ(dataString.size(), bytesWritten);
|
|
|
|
EXPECT_EQ(AZ::IO::ResultCode::Success, cpfio.Close(normalFileHandle));
|
|
EXPECT_TRUE(cpfio.Exists(looseTestFilePath));
|
|
|
|
TestFGetCachedFileData(looseTestFilePath, dataString.size(), dataString.data());
|
|
}
|
|
|
|
// a bug was found that causes problems reading data from packs if they are immediately mounted after writing.
|
|
// this unit test adjusts for that.
|
|
TEST_F(ArchiveTestFixture, TestArchivePackImmediateReading)
|
|
{
|
|
// the strategy is to create a archive file similar to how the level system does
|
|
// one which contains subfolders
|
|
// and a file inside that subfolder
|
|
// to be successful, it must be possible to write that pack, close it, then open it via Archive
|
|
// and be able to IMMEDIATELY
|
|
// * read the file in the subfolder
|
|
// * enumerate the folders (including that subfolder) even though they are 'virtual', not real folders on physical media
|
|
// * all of the above even though the mount point for the archive is @assets@ wheras the physical pack lives in @usercache@
|
|
// finally, we're going to repeat the above test but with files mounted with subfolders
|
|
// so for example, the pack will contain levelinfo.xml at the root of it
|
|
// but it will be mounted at a subfolder (levels/mylevel).
|
|
// this must cause FindNext and Open to work for levels/mylevel/levelinfo.xml.
|
|
|
|
constexpr const char* testArchivePath_withSubfolders = "@usercache@/immediate.pak";
|
|
constexpr const char* testArchivePath_withMountPoint = "@usercache@/levels/test/flatarchive.pak";
|
|
|
|
AZ::IO::FileIOBase* fileIo = AZ::IO::FileIOBase::GetInstance();
|
|
ASSERT_NE(nullptr, fileIo);
|
|
|
|
AZ::IO::IArchive* archive = AZ::Interface<AZ::IO::IArchive>::Get();
|
|
ASSERT_NE(nullptr, archive);
|
|
|
|
auto console = AZ::Interface<AZ::IConsole>::Get();
|
|
ASSERT_NE(nullptr, console);
|
|
|
|
constexpr AZStd::string_view dataString = "HELLO WORLD"; // other unit tests make sure writing and reading is working, so don't test that here
|
|
|
|
|
|
// delete test files in case they already exist
|
|
archive->ClosePack(testArchivePath_withSubfolders);
|
|
archive->ClosePack(testArchivePath_withMountPoint);
|
|
fileIo->Remove(testArchivePath_withSubfolders);
|
|
fileIo->Remove(testArchivePath_withMountPoint);
|
|
fileIo->CreatePath("@usercache@/levels/test");
|
|
|
|
|
|
AZStd::intrusive_ptr<AZ::IO::INestedArchive> pArchive = archive->OpenArchive(testArchivePath_withSubfolders, {}, AZ::IO::INestedArchive::FLAGS_CREATE_NEW);
|
|
EXPECT_NE(nullptr, pArchive);
|
|
EXPECT_EQ(0, pArchive->UpdateFile("levels\\mylevel\\levelinfo.xml", dataString.data(), dataString.size(), AZ::IO::INestedArchive::METHOD_COMPRESS, AZ::IO::INestedArchive::LEVEL_FASTEST));
|
|
pArchive.reset();
|
|
EXPECT_TRUE(IsPackValid(testArchivePath_withSubfolders));
|
|
|
|
EXPECT_TRUE(archive->OpenPack("@assets@", testArchivePath_withSubfolders));
|
|
// ---- BARRAGE OF TESTS
|
|
EXPECT_TRUE(archive->IsFileExist("levels\\mylevel\\levelinfo.xml"));
|
|
EXPECT_TRUE(archive->IsFileExist("levels//mylevel//levelinfo.xml"));
|
|
|
|
bool found_mylevel_folder = false;
|
|
AZ::IO::ArchiveFileIterator handle = archive->FindFirst("levels\\*");
|
|
|
|
EXPECT_TRUE(static_cast<bool>(handle));
|
|
if (handle)
|
|
{
|
|
do
|
|
{
|
|
if ((handle.m_fileDesc.nAttrib & AZ::IO::FileDesc::Attribute::Subdirectory) == AZ::IO::FileDesc::Attribute::Subdirectory)
|
|
{
|
|
if (azstricmp(handle.m_filename.data(), "mylevel") == 0)
|
|
{
|
|
found_mylevel_folder = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
EXPECT_STRCASENE("levelinfo.xml", handle.m_filename.data()); // you may not find files inside the archive in this folder.
|
|
}
|
|
} while (handle = archive->FindNext(handle));
|
|
|
|
archive->FindClose(handle);
|
|
}
|
|
|
|
EXPECT_TRUE(found_mylevel_folder);
|
|
|
|
bool found_mylevel_file = false;
|
|
|
|
handle = archive->FindFirst("levels\\mylevel\\*");
|
|
|
|
EXPECT_TRUE(static_cast<bool>(handle));
|
|
if (handle)
|
|
{
|
|
do
|
|
{
|
|
if ((handle.m_fileDesc.nAttrib & AZ::IO::FileDesc::Attribute::Subdirectory) != AZ::IO::FileDesc::Attribute::Subdirectory)
|
|
{
|
|
if (azstricmp(handle.m_filename.data(), "levelinfo.xml") == 0)
|
|
{
|
|
found_mylevel_file = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
EXPECT_STRCASENE("mylevel", handle.m_filename.data()); // you may not find the level subfolder here since we're in the subfolder already
|
|
EXPECT_STRCASENE("levels\\mylevel", handle.m_filename.data());
|
|
EXPECT_STRCASENE("levels//mylevel", handle.m_filename.data());
|
|
}
|
|
} while (handle = archive->FindNext(handle));
|
|
|
|
archive->FindClose(handle);
|
|
}
|
|
|
|
EXPECT_TRUE(found_mylevel_file);
|
|
|
|
// now test clean-up
|
|
archive->ClosePack(testArchivePath_withSubfolders);
|
|
fileIo->Remove(testArchivePath_withSubfolders);
|
|
|
|
EXPECT_FALSE(archive->IsFileExist("levels\\mylevel\\levelinfo.xml"));
|
|
EXPECT_FALSE(archive->IsFileExist("levels//mylevel//levelinfo.xml"));
|
|
|
|
// Once the archive has been deleted it should no longer be searched
|
|
CVarIntValueScope previousLocationPriority{ *console, "sys_pakPriority" };
|
|
console->PerformCommand("sys_PakPriority", { AZ::CVarFixedString::format("%d", aznumeric_cast<int>(AZ::IO::ArchiveLocationPriority::ePakPriorityPakOnly)) });
|
|
|
|
handle = archive->FindFirst("levels\\*");
|
|
EXPECT_FALSE(static_cast<bool>(handle));
|
|
}
|
|
TEST_F(ArchiveTestFixture, FilesInArchive_AreSearchable)
|
|
{
|
|
// ----------- SECOND TEST. File in levels/mylevel/ showing up as searchable.
|
|
// note that the actual file's folder has nothing to do with the mount point.
|
|
|
|
AZ::IO::FileIOBase* fileIo = AZ::IO::FileIOBase::GetInstance();
|
|
ASSERT_NE(nullptr, fileIo);
|
|
|
|
AZ::IO::IArchive* archive = AZ::Interface<AZ::IO::IArchive>::Get();
|
|
ASSERT_NE(nullptr, archive);
|
|
|
|
constexpr AZStd::string_view dataString = "HELLO WORLD";
|
|
constexpr const char* testArchivePath_withMountPoint = "@usercache@/levels/test/flatarchive.pak";
|
|
bool found_mylevel_file{};
|
|
bool found_mylevel_folder{};
|
|
|
|
AZStd::intrusive_ptr<AZ::IO::INestedArchive> pArchive = archive->OpenArchive(testArchivePath_withMountPoint, nullptr, AZ::IO::INestedArchive::FLAGS_CREATE_NEW);
|
|
EXPECT_NE(nullptr, pArchive);
|
|
EXPECT_EQ(0, pArchive->UpdateFile("levelinfo.xml", dataString.data(), dataString.size(), AZ::IO::INestedArchive::METHOD_COMPRESS, AZ::IO::INestedArchive::LEVEL_FASTEST));
|
|
pArchive.reset();
|
|
EXPECT_TRUE(IsPackValid(testArchivePath_withMountPoint));
|
|
|
|
EXPECT_TRUE(archive->OpenPack("@assets@\\uniquename\\mylevel2", testArchivePath_withMountPoint));
|
|
|
|
// ---- BARRAGE OF TESTS
|
|
EXPECT_TRUE(archive->IsFileExist("uniquename\\mylevel2\\levelinfo.xml"));
|
|
EXPECT_TRUE(archive->IsFileExist("uniquename//mylevel2//levelinfo.xml"));
|
|
|
|
EXPECT_TRUE(!archive->IsFileExist("uniquename\\mylevel\\levelinfo.xml"));
|
|
EXPECT_TRUE(!archive->IsFileExist("uniquename//mylevel//levelinfo.xml"));
|
|
EXPECT_TRUE(!archive->IsFileExist("uniquename\\test\\levelinfo.xml"));
|
|
EXPECT_TRUE(!archive->IsFileExist("uniquename//test//levelinfo.xml"));
|
|
|
|
found_mylevel_folder = false;
|
|
AZ::IO::ArchiveFileIterator handle = archive->FindFirst("uniquename\\*");
|
|
|
|
EXPECT_TRUE(static_cast<bool>(handle));
|
|
if (handle)
|
|
{
|
|
do
|
|
{
|
|
if ((handle.m_fileDesc.nAttrib & AZ::IO::FileDesc::Attribute::Subdirectory) == AZ::IO::FileDesc::Attribute::Subdirectory)
|
|
{
|
|
if (azstricmp(handle.m_filename.data(), "mylevel2") == 0)
|
|
{
|
|
found_mylevel_folder = true;
|
|
}
|
|
}
|
|
} while (handle = archive->FindNext(handle));
|
|
|
|
archive->FindClose(handle);
|
|
}
|
|
|
|
EXPECT_TRUE(found_mylevel_folder);
|
|
|
|
found_mylevel_file = false;
|
|
|
|
handle = archive->FindFirst("uniquename\\mylevel2\\*");
|
|
|
|
EXPECT_TRUE(static_cast<bool>(handle));
|
|
if (handle)
|
|
{
|
|
do
|
|
{
|
|
if ((handle.m_fileDesc.nAttrib & AZ::IO::FileDesc::Attribute::Subdirectory) != AZ::IO::FileDesc::Attribute::Subdirectory)
|
|
{
|
|
if (azstricmp(handle.m_filename.data(), "levelinfo.xml") == 0)
|
|
{
|
|
found_mylevel_file = true;
|
|
}
|
|
}
|
|
} while (handle = archive->FindNext(handle));
|
|
|
|
archive->FindClose(handle);
|
|
}
|
|
|
|
EXPECT_TRUE(found_mylevel_file);
|
|
|
|
archive->ClosePack(testArchivePath_withMountPoint);
|
|
|
|
// --- test to make sure that when you iterate only the first component is found, so bury it deep and ask for the root
|
|
EXPECT_TRUE(archive->OpenPack("@assets@\\uniquename\\mylevel2\\mylevel3\\mylevel4", testArchivePath_withMountPoint));
|
|
|
|
found_mylevel_folder = false;
|
|
handle = archive->FindFirst("uniquename\\*");
|
|
|
|
int numFound = 0;
|
|
EXPECT_TRUE(static_cast<bool>(handle));
|
|
if (handle)
|
|
{
|
|
do
|
|
{
|
|
if ((handle.m_fileDesc.nAttrib & AZ::IO::FileDesc::Attribute::Subdirectory) == AZ::IO::FileDesc::Attribute::Subdirectory)
|
|
{
|
|
++numFound;
|
|
if (azstricmp(handle.m_filename.data(), "mylevel2") == 0)
|
|
{
|
|
found_mylevel_folder = true;
|
|
}
|
|
}
|
|
} while (handle = archive->FindNext(handle));
|
|
|
|
archive->FindClose(handle);
|
|
}
|
|
|
|
EXPECT_EQ(1, numFound);
|
|
EXPECT_TRUE(found_mylevel_folder);
|
|
|
|
numFound = 0;
|
|
found_mylevel_folder = false;
|
|
|
|
// now make sure no red herrings appear
|
|
// for example, if a file is mounted at "@assets@\\uniquename\\mylevel2\\mylevel3\\mylevel4"
|
|
// and the file "@assets@\\somethingelse" is requested it should not be found
|
|
// in addition if the file "@assets@\\uniquename\\mylevel3" is requested it should not be found
|
|
handle = archive->FindFirst("somethingelse\\*");
|
|
EXPECT_FALSE(static_cast<bool>(handle));
|
|
|
|
handle = archive->FindFirst("uniquename\\mylevel3*");
|
|
EXPECT_FALSE(static_cast<bool>(handle));
|
|
|
|
archive->ClosePack(testArchivePath_withMountPoint);
|
|
fileIo->Remove(testArchivePath_withMountPoint);
|
|
}
|
|
|
|
// test that ArchiveFileIO class works as expected
|
|
TEST_F(ArchiveTestFixture, TestArchiveViaFileIO)
|
|
{
|
|
// strategy:
|
|
// create a loose file and a packed file
|
|
// mount the pack
|
|
// make sure that the pack and loose file both appear when the PaKFileIO interface is used.
|
|
using namespace AZ::IO;
|
|
AZ::IO::IArchive* archive = AZ::Interface<AZ::IO::IArchive>::Get();
|
|
AZ::IO::ArchiveFileIO cpfio(archive);
|
|
|
|
constexpr const char* genericArchiveFileName = "@usercache@/testarchiveio.pak";
|
|
ASSERT_NE(nullptr, archive);
|
|
const char* dataString = "HELLO WORLD"; // other unit tests make sure writing and reading is working, so don't test that here
|
|
size_t dataLen = strlen(dataString);
|
|
AZStd::vector<char> dataBuffer;
|
|
dataBuffer.resize(dataLen);
|
|
|
|
// delete test files in case they already exist
|
|
archive->ClosePack(genericArchiveFileName);
|
|
cpfio.Remove(genericArchiveFileName);
|
|
|
|
// create the asset alias directory
|
|
cpfio.CreatePath("@assets@");
|
|
|
|
// create generic file
|
|
|
|
HandleType normalFileHandle;
|
|
AZ::u64 bytesWritten = 0;
|
|
EXPECT_EQ(ResultCode::Success, cpfio.DestroyPath("@log@/unittesttemp"));
|
|
EXPECT_TRUE(!cpfio.Exists("@log@/unittesttemp"));
|
|
EXPECT_TRUE(!cpfio.IsDirectory("@log@/unittesttemp"));
|
|
EXPECT_EQ(ResultCode::Success, cpfio.CreatePath("@log@/unittesttemp"));
|
|
EXPECT_TRUE(cpfio.Exists("@log@/unittesttemp"));
|
|
EXPECT_TRUE(cpfio.IsDirectory("@log@/unittesttemp"));
|
|
EXPECT_EQ(ResultCode::Success, cpfio.Open("@log@/unittesttemp/realfileforunittest.xml", OpenMode::ModeWrite | OpenMode::ModeBinary, normalFileHandle));
|
|
EXPECT_EQ(ResultCode::Success, cpfio.Write(normalFileHandle, dataString, dataLen, &bytesWritten));
|
|
EXPECT_EQ(dataLen, bytesWritten);
|
|
EXPECT_EQ(ResultCode::Success, cpfio.Close(normalFileHandle));
|
|
normalFileHandle = InvalidHandle;
|
|
EXPECT_TRUE(cpfio.Exists("@log@/unittesttemp/realfileforunittest.xml"));
|
|
|
|
AZStd::intrusive_ptr<AZ::IO::INestedArchive> pArchive = archive->OpenArchive(genericArchiveFileName, nullptr, AZ::IO::INestedArchive::FLAGS_CREATE_NEW);
|
|
EXPECT_NE(nullptr, pArchive);
|
|
EXPECT_EQ(0, pArchive->UpdateFile("testfile.xml", dataString, aznumeric_cast<uint32_t>(dataLen), AZ::IO::INestedArchive::METHOD_COMPRESS, AZ::IO::INestedArchive::LEVEL_FASTEST));
|
|
pArchive.reset();
|
|
EXPECT_TRUE(IsPackValid(genericArchiveFileName));
|
|
|
|
EXPECT_TRUE(archive->OpenPack("@assets@", genericArchiveFileName));
|
|
|
|
// ---- BARRAGE OF TESTS
|
|
EXPECT_TRUE(cpfio.Exists("testfile.xml"));
|
|
EXPECT_TRUE(cpfio.Exists("@assets@/testfile.xml")); // this should be hte same file
|
|
EXPECT_TRUE(!cpfio.Exists("@log@/testfile.xml"));
|
|
EXPECT_TRUE(!cpfio.Exists("@usercache@/testfile.xml"));
|
|
EXPECT_TRUE(cpfio.Exists("@log@/unittesttemp/realfileforunittest.xml"));
|
|
|
|
// --- Coverage test ----
|
|
AZ::u64 fileSize = 0;
|
|
AZ::u64 bytesRead = 0;
|
|
AZ::u64 currentOffset = 0;
|
|
char fileNameBuffer[AZ_MAX_PATH_LEN] = { 0 };
|
|
EXPECT_EQ(ResultCode::Success, cpfio.Size("testfile.xml", fileSize));
|
|
EXPECT_EQ(dataLen, fileSize);
|
|
EXPECT_EQ(ResultCode::Success, cpfio.Open("testfile.xml", OpenMode::ModeRead | OpenMode::ModeBinary, normalFileHandle));
|
|
EXPECT_NE(InvalidHandle, normalFileHandle);
|
|
EXPECT_EQ(ResultCode::Success, cpfio.Size(normalFileHandle, fileSize));
|
|
EXPECT_EQ(dataLen, fileSize);
|
|
EXPECT_EQ(ResultCode::Success, cpfio.Read(normalFileHandle, dataBuffer.data(), 2, true, &bytesRead));
|
|
EXPECT_EQ(2, bytesRead);
|
|
EXPECT_EQ('H', dataBuffer[0]);
|
|
EXPECT_EQ('E', dataBuffer[1]);
|
|
EXPECT_EQ(ResultCode::Success, cpfio.Tell(normalFileHandle, currentOffset));
|
|
EXPECT_EQ(2, currentOffset);
|
|
EXPECT_EQ(ResultCode::Success, cpfio.Seek(normalFileHandle, 2, SeekType::SeekFromCurrent));
|
|
EXPECT_EQ(ResultCode::Success, cpfio.Tell(normalFileHandle, currentOffset));
|
|
EXPECT_EQ(4, currentOffset);
|
|
EXPECT_TRUE(!cpfio.Eof(normalFileHandle));
|
|
EXPECT_EQ(ResultCode::Success, cpfio.Seek(normalFileHandle, 2, SeekType::SeekFromStart));
|
|
EXPECT_EQ(ResultCode::Success, cpfio.Tell(normalFileHandle, currentOffset));
|
|
EXPECT_EQ(2, currentOffset);
|
|
EXPECT_EQ(ResultCode::Success, cpfio.Seek(normalFileHandle, -2, SeekType::SeekFromEnd));
|
|
EXPECT_EQ(ResultCode::Success, cpfio.Tell(normalFileHandle, currentOffset));
|
|
EXPECT_EQ(dataLen - 2, currentOffset);
|
|
EXPECT_NE(ResultCode::Success, cpfio.Read(normalFileHandle, dataBuffer.data(), 4, true, &bytesRead));
|
|
EXPECT_EQ(2, bytesRead);
|
|
EXPECT_TRUE(cpfio.Eof(normalFileHandle));
|
|
EXPECT_TRUE(cpfio.GetFilename(normalFileHandle, fileNameBuffer, AZ_MAX_PATH_LEN));
|
|
EXPECT_NE(AZStd::string_view::npos, AZStd::string_view(fileNameBuffer).find("testfile.xml"));
|
|
// just coverage-call this function:
|
|
EXPECT_EQ(archive->GetModificationTime(normalFileHandle), cpfio.ModificationTime(normalFileHandle));
|
|
EXPECT_EQ(archive->GetModificationTime(normalFileHandle), cpfio.ModificationTime("testfile.xml"));
|
|
EXPECT_NE(0, cpfio.ModificationTime("testfile.xml"));
|
|
EXPECT_NE(0, cpfio.ModificationTime("@log@/unittesttemp/realfileforunittest.xml"));
|
|
|
|
EXPECT_EQ(ResultCode::Success, cpfio.Close(normalFileHandle));
|
|
|
|
EXPECT_TRUE(!cpfio.IsDirectory("testfile.xml"));
|
|
EXPECT_TRUE(cpfio.IsDirectory("@assets@"));
|
|
EXPECT_TRUE(cpfio.IsReadOnly("testfile.xml"));
|
|
EXPECT_TRUE(cpfio.IsReadOnly("@assets@/testfile.xml"));
|
|
EXPECT_TRUE(!cpfio.IsReadOnly("@log@/unittesttemp/realfileforunittest.xml"));
|
|
|
|
|
|
// copy file from inside archive:
|
|
EXPECT_EQ(ResultCode::Success, cpfio.Copy("testfile.xml", "@log@/unittesttemp/copiedfile.xml"));
|
|
EXPECT_TRUE(cpfio.Exists("@log@/unittesttemp/copiedfile.xml"));
|
|
|
|
// make sure copy is ok
|
|
|
|
EXPECT_EQ(ResultCode::Success, cpfio.Open("@log@/unittesttemp/copiedfile.xml", OpenMode::ModeRead | OpenMode::ModeBinary, normalFileHandle));
|
|
EXPECT_EQ(ResultCode::Success, cpfio.Size(normalFileHandle, fileSize));
|
|
EXPECT_EQ(dataLen, fileSize);
|
|
EXPECT_EQ(ResultCode::Success, cpfio.Read(normalFileHandle, dataBuffer.data(), dataLen + 10, false, &bytesRead)); // allowed to read less
|
|
EXPECT_EQ(dataLen, bytesRead);
|
|
EXPECT_EQ(0, memcmp(dataBuffer.data(), dataString, dataLen));
|
|
EXPECT_EQ(ResultCode::Success, cpfio.Close(normalFileHandle));
|
|
|
|
// make sure file does not exist, since copy will NOT overwrite:
|
|
cpfio.Remove("@log@/unittesttemp/copiedfile2.xml");
|
|
|
|
EXPECT_EQ(ResultCode::Success, cpfio.Rename("@log@/unittesttemp/copiedfile.xml", "@log@/unittesttemp/copiedfile2.xml"));
|
|
EXPECT_TRUE(!cpfio.Exists("@log@/unittesttemp/copiedfile.xml"));
|
|
EXPECT_TRUE(cpfio.Exists("@log@/unittesttemp/copiedfile2.xml"));
|
|
|
|
// find files test.
|
|
AZ::IO::FixedMaxPath resolvedTestFilePath;
|
|
EXPECT_TRUE(cpfio.ResolvePath(resolvedTestFilePath, AZ::IO::PathView("@assets@/testfile.xml")));
|
|
bool foundIt = false;
|
|
// note that this file exists only in the archive.
|
|
cpfio.FindFiles("@assets@", "*.xml", [&foundIt, &cpfio, &resolvedTestFilePath](const char* foundName)
|
|
{
|
|
AZ::IO::FixedMaxPath resolvedFoundPath;
|
|
EXPECT_TRUE(cpfio.ResolvePath(resolvedFoundPath, AZ::IO::PathView(foundName)));
|
|
// according to the contract stated in the FileIO.h file, we expect full paths. (Aliases are full paths)
|
|
if (resolvedTestFilePath == resolvedFoundPath)
|
|
{
|
|
foundIt = true;
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
|
|
EXPECT_TRUE(foundIt);
|
|
|
|
|
|
// The following test is disabled because it will trigger an AZ_ERROR which will affect the outcome of this entire test
|
|
// EXPECT_NE(ResultCode::Success, cpfio.Remove("@assets@/testfile.xml")); // may not delete archive files
|
|
|
|
// make sure it works with and without alias:
|
|
EXPECT_TRUE(cpfio.Exists("@assets@/testfile.xml"));
|
|
EXPECT_TRUE(cpfio.Exists("testfile.xml"));
|
|
|
|
EXPECT_TRUE(cpfio.Exists("@log@/unittesttemp/realfileforunittest.xml"));
|
|
EXPECT_EQ(ResultCode::Success, cpfio.Remove("@log@/unittesttemp/realfileforunittest.xml"));
|
|
EXPECT_TRUE(!cpfio.Exists("@log@/unittesttemp/realfileforunittest.xml"));
|
|
|
|
// now test clean-up
|
|
archive->ClosePack(genericArchiveFileName);
|
|
cpfio.Remove(genericArchiveFileName);
|
|
|
|
EXPECT_EQ(ResultCode::Success, cpfio.DestroyPath("@log@/unittesttemp"));
|
|
EXPECT_TRUE(!cpfio.Exists("@log@/unittesttemp/realfileforunittest.xml"));
|
|
EXPECT_TRUE(!cpfio.Exists("@log@/unittesttemp"));
|
|
EXPECT_TRUE(!archive->IsFileExist("testfile.xml"));
|
|
}
|
|
|
|
TEST_F(ArchiveTestFixture, TestArchiveFolderAliases)
|
|
{
|
|
AZ::IO::FileIOBase* fileIo = AZ::IO::FileIOBase::GetInstance();
|
|
ASSERT_NE(nullptr, fileIo);
|
|
|
|
// test whether aliasing works as expected. We'll create a archive in the cache, but we'll map it to a bunch of folders
|
|
constexpr const char* testArchivePath = "@usercache@/archivetest.pak";
|
|
|
|
char realNameBuf[AZ_MAX_PATH_LEN] = { 0 };
|
|
EXPECT_TRUE(fileIo->ResolvePath(testArchivePath, realNameBuf, AZ_MAX_PATH_LEN));
|
|
|
|
AZ::IO::IArchive* archive = AZ::Interface<AZ::IO::IArchive>::Get();
|
|
ASSERT_NE(nullptr, archive);
|
|
|
|
// delete test files in case they already exist
|
|
archive->ClosePack(testArchivePath);
|
|
fileIo->Remove(testArchivePath);
|
|
|
|
// ------------ BASIC TEST: Create and read Empty Archive ------------
|
|
AZStd::intrusive_ptr<AZ::IO::INestedArchive> pArchive = archive->OpenArchive(testArchivePath, nullptr, AZ::IO::INestedArchive::FLAGS_CREATE_NEW);
|
|
EXPECT_NE(nullptr, pArchive);
|
|
|
|
EXPECT_EQ(0, pArchive->UpdateFile("foundit.dat", const_cast<char*>("test"), 4, AZ::IO::INestedArchive::METHOD_COMPRESS, AZ::IO::INestedArchive::LEVEL_BEST));
|
|
|
|
pArchive.reset();
|
|
EXPECT_TRUE(IsPackValid(testArchivePath));
|
|
|
|
EXPECT_TRUE(archive->OpenPack("@usercache@", realNameBuf));
|
|
EXPECT_TRUE(archive->IsFileExist("@usercache@/foundit.dat"));
|
|
EXPECT_FALSE(archive->IsFileExist("@usercache@/foundit.dat", AZ::IO::IArchive::eFileLocation_OnDisk));
|
|
EXPECT_FALSE(archive->IsFileExist("@usercache@/notfoundit.dat"));
|
|
EXPECT_TRUE(archive->ClosePack(realNameBuf));
|
|
|
|
// change its actual location:
|
|
EXPECT_TRUE(archive->OpenPack("@assets@", realNameBuf));
|
|
EXPECT_TRUE(archive->IsFileExist("@assets@/foundit.dat"));
|
|
EXPECT_FALSE(archive->IsFileExist("@usercache@/foundit.dat")); // do not find it in the previous location!
|
|
EXPECT_FALSE(archive->IsFileExist("@assets@/foundit.dat", AZ::IO::IArchive::eFileLocation_OnDisk));
|
|
EXPECT_FALSE(archive->IsFileExist("@assets@/notfoundit.dat"));
|
|
EXPECT_TRUE(archive->ClosePack(realNameBuf));
|
|
|
|
// try sub-folders
|
|
EXPECT_TRUE(archive->OpenPack("@assets@/mystuff", realNameBuf));
|
|
EXPECT_TRUE(archive->IsFileExist("@assets@/mystuff/foundit.dat"));
|
|
EXPECT_FALSE(archive->IsFileExist("@assets@/foundit.dat")); // do not find it in the previous locations!
|
|
EXPECT_FALSE(archive->IsFileExist("@usercache@/foundit.dat")); // do not find it in the previous locations!
|
|
EXPECT_FALSE(archive->IsFileExist("@assets@/foundit.dat", AZ::IO::IArchive::eFileLocation_OnDisk));
|
|
EXPECT_FALSE(archive->IsFileExist("@assets@/mystuff/foundit.dat", AZ::IO::IArchive::eFileLocation_OnDisk));
|
|
EXPECT_FALSE(archive->IsFileExist("@assets@/notfoundit.dat")); // non-existent file
|
|
EXPECT_FALSE(archive->IsFileExist("@assets@/mystuff/notfoundit.dat")); // non-existent file
|
|
EXPECT_TRUE(archive->ClosePack(realNameBuf));
|
|
}
|
|
|
|
TEST_F(ArchiveTestFixture, IResourceList_Add_EmptyFileName_DoesNotInsert)
|
|
{
|
|
AZ::IO::IResourceList* reslist = AZ::Interface<AZ::IO::IArchive>::Get()->GetResourceList(AZ::IO::IArchive::RFOM_EngineStartup);
|
|
ASSERT_NE(nullptr, reslist);
|
|
|
|
reslist->Clear();
|
|
reslist->Add("");
|
|
EXPECT_EQ(nullptr, reslist->GetFirst());
|
|
}
|
|
|
|
TEST_F(ArchiveTestFixture, IResourceList_Add_RegularFileName_ResolvesAppropriately)
|
|
{
|
|
AZ::IO::IResourceList* reslist = AZ::Interface<AZ::IO::IArchive>::Get()->GetResourceList(AZ::IO::IArchive::RFOM_EngineStartup);
|
|
ASSERT_NE(nullptr, reslist);
|
|
|
|
AZ::IO::FileIOBase* ioBase = AZ::IO::FileIOBase::GetInstance();
|
|
ASSERT_NE(nullptr, ioBase);
|
|
AZ::IO::FixedMaxPath resolvedTestPath;
|
|
EXPECT_TRUE(ioBase->ResolvePath(resolvedTestPath, "blah/blah/abcde"));
|
|
|
|
reslist->Clear();
|
|
reslist->Add("blah\\blah/AbCDE");
|
|
AZ::IO::FixedMaxPath resolvedAddedPath;
|
|
EXPECT_TRUE(ioBase->ResolvePath(resolvedAddedPath, reslist->GetFirst()));
|
|
EXPECT_EQ(resolvedTestPath, resolvedAddedPath);
|
|
reslist->Clear();
|
|
}
|
|
|
|
TEST_F(ArchiveTestFixture, IResourceList_Add_ReallyShortFileName_ResolvesAppropriately)
|
|
{
|
|
AZ::IO::IResourceList* reslist = AZ::Interface<AZ::IO::IArchive>::Get()->GetResourceList(AZ::IO::IArchive::RFOM_EngineStartup);
|
|
ASSERT_NE(nullptr, reslist);
|
|
|
|
AZ::IO::FileIOBase* ioBase = AZ::IO::FileIOBase::GetInstance();
|
|
ASSERT_NE(nullptr, ioBase);
|
|
AZ::IO::FixedMaxPath resolvedTestPath;
|
|
EXPECT_TRUE(ioBase->ResolvePath(resolvedTestPath, "a"));
|
|
|
|
reslist->Clear();
|
|
reslist->Add("A");
|
|
AZ::IO::FixedMaxPath resolvedAddedPath;
|
|
EXPECT_TRUE(ioBase->ResolvePath(resolvedAddedPath, reslist->GetFirst()));
|
|
EXPECT_EQ(resolvedTestPath, resolvedAddedPath);
|
|
reslist->Clear();
|
|
}
|
|
|
|
TEST_F(ArchiveTestFixture, IResourceList_Add_AbsolutePath_RemovesAndReplacesWithAlias)
|
|
{
|
|
AZ::IO::IResourceList* reslist = AZ::Interface<AZ::IO::IArchive>::Get()->GetResourceList(AZ::IO::IArchive::RFOM_EngineStartup);
|
|
ASSERT_NE(nullptr, reslist);
|
|
|
|
AZ::IO::FileIOBase* ioBase = AZ::IO::FileIOBase::GetInstance();
|
|
ASSERT_NE(nullptr, ioBase);
|
|
|
|
const char* assetsPath = ioBase->GetAlias("@assets@");
|
|
ASSERT_NE(nullptr, assetsPath);
|
|
|
|
auto stringToAdd = AZ::IO::Path(assetsPath) / "textures" / "test.dds";
|
|
|
|
reslist->Clear();
|
|
reslist->Add(stringToAdd.Native());
|
|
|
|
// it normalizes the string, so the slashes flip and everything is lowercased.
|
|
AZ::IO::FixedMaxPath resolvedAddedPath;
|
|
AZ::IO::FixedMaxPath resolvedResourcePath;
|
|
EXPECT_TRUE(ioBase->ReplaceAlias(resolvedAddedPath, "@assets@/textures/test.dds"));
|
|
EXPECT_TRUE(ioBase->ReplaceAlias(resolvedResourcePath, reslist->GetFirst()));
|
|
EXPECT_EQ(resolvedAddedPath, resolvedResourcePath);
|
|
reslist->Clear();
|
|
}
|
|
}
|