/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern char g_cachedEngineRoot[AZ_MAX_PATH_LEN]; namespace AssetBundler { class AssetBundlerBatchUtilsTest : public UnitTest::ScopedAllocatorSetupFixture { }; TEST_F(AssetBundlerBatchUtilsTest, SplitFilename_MacFile_OutputBaseNameAndPlatform) { AZStd::string filePath = "assetInfoFile_osx_gl.xml"; AZStd::string baseFilename; AZStd::string platformIdentifier; AzToolsFramework::SplitFilename(filePath, baseFilename, platformIdentifier); ASSERT_EQ(baseFilename, "assetInfoFile"); ASSERT_EQ(platformIdentifier, "osx_gl"); } TEST_F(AssetBundlerBatchUtilsTest, SplitFilename_PcFile_OutputBaseNameAndPlatform) { AZStd::string filePath = "assetInfoFile_pc.xml"; AZStd::string baseFilename; AZStd::string platformIdentifier; AzToolsFramework::SplitFilename(filePath, baseFilename, platformIdentifier); ASSERT_EQ(baseFilename, "assetInfoFile"); ASSERT_EQ(platformIdentifier, "pc"); } TEST_F(AssetBundlerBatchUtilsTest, SplitFilename_MacFileWithUnderScoreInFileName_OutputBaseNameAndPlatform) { AZStd::string filePath = "assetInfoFile_test_osx_gl.xml"; AZStd::string baseFilename; AZStd::string platformIdentifier; AzToolsFramework::SplitFilename(filePath, baseFilename, platformIdentifier); ASSERT_EQ(baseFilename, "assetInfoFile_test"); ASSERT_EQ(platformIdentifier, "osx_gl"); } TEST_F(AssetBundlerBatchUtilsTest, SplitFilename_PcFileWithUnderScoreInFileName_OutputBaseNameAndPlatform) { AZStd::string filePath = "assetInfoFile_test_pc.xml"; AZStd::string baseFilename; AZStd::string platformIdentifier; AzToolsFramework::SplitFilename(filePath, baseFilename, platformIdentifier); ASSERT_EQ(baseFilename, "assetInfoFile_test"); ASSERT_EQ(platformIdentifier, "pc"); } const char RelativeTestFolder[] = "Code/Tools/AssetBundler/tests"; const char GemsFolder[] = "Gems"; const char EngineFolder[] = "Engine"; const char PlatformsFolder[] = "Platforms"; const char DummyProjectFolder[] = "DummyProject"; class AssetBundlerGemsUtilTest : public UnitTest::ScopedAllocatorSetupFixture { public: void SetUp() override { m_data = AZStd::make_unique(); m_data->m_application.reset(aznew AzToolsFramework::ToolsApplication()); m_data->m_application.get()->Start(AzFramework::Application::Descriptor()); // 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); if (!AZ::SettingsRegistry::Get()) { AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_Bootstrap(m_registry); AZ::SettingsRegistry::Register(&m_registry); } const char* engineRoot = nullptr; AzFramework::ApplicationRequests::Bus::BroadcastResult(engineRoot, &AzFramework::ApplicationRequests::GetEngineRoot); if (!engineRoot) { GTEST_FATAL_FAILURE_(AZStd::string::format("Unable to locate engine root.\n").c_str()); } AzFramework::StringFunc::Path::Join(engineRoot, RelativeTestFolder, m_data->m_testEngineRoot); m_data->m_localFileIO = aznew AZ::IO::LocalFileIO(); m_data->m_priorFileIO = AZ::IO::FileIOBase::GetInstance(); // we need to set it to nullptr first because otherwise the // underneath code assumes that we might be leaking the previous instance AZ::IO::FileIOBase::SetInstance(nullptr); AZ::IO::FileIOBase::SetInstance(m_data->m_localFileIO); AddGemData(m_data->m_testEngineRoot.c_str(), "GemA"); AddGemData(m_data->m_testEngineRoot.c_str(), "GemB"); AZStd::string absoluteEngineSeedFilePath; AzFramework::StringFunc::Path::ConstructFull(m_data->m_testEngineRoot.c_str(), EngineFolder, "SeedAssetList", AzToolsFramework::AssetSeedManager::GetSeedFileExtension(), absoluteEngineSeedFilePath, true); m_data->m_gemSeedFilePairList.emplace_back(AZStd::make_pair(absoluteEngineSeedFilePath, true)); AddGemData(m_data->m_testEngineRoot.c_str(), "GemC", false); AZStd::string absoluteProjectSeedFilePath; AzFramework::StringFunc::Path::ConstructFull(m_data->m_testEngineRoot.c_str(), DummyProjectFolder, "SeedAssetList", AzToolsFramework::AssetSeedManager::GetSeedFileExtension(), absoluteProjectSeedFilePath, true); m_data->m_gemSeedFilePairList.emplace_back(AZStd::make_pair(absoluteProjectSeedFilePath, true)); } void TearDown() override { AZ::IO::FileIOBase::SetInstance(nullptr); delete m_data->m_localFileIO; AZ::IO::FileIOBase::SetInstance(m_data->m_priorFileIO); m_data->m_gemInfoList.set_capacity(0); m_data->m_gemSeedFilePairList.set_capacity(0); m_data->m_application.get()->Stop(); m_data->m_application.reset(); } void AddGemData(const char* engineRoot, const char* gemName, bool seedFileExists = true) { AZ::IO::Path relativeGemPath{ GemsFolder }; relativeGemPath /= gemName; AZ::IO::Path absoluteGemPath{ engineRoot }; absoluteGemPath /= relativeGemPath; AZ::IO::Path absoluteGemSeedFilePath = absoluteGemPath; absoluteGemSeedFilePath /= "Assets/seedList"; absoluteGemSeedFilePath.ReplaceExtension(AZ::IO::PathView{ AzToolsFramework::AssetSeedManager::GetSeedFileExtension() }); absoluteGemSeedFilePath = absoluteGemSeedFilePath.LexicallyNormal(); m_data->m_gemSeedFilePairList.emplace_back(absoluteGemSeedFilePath, seedFileExists); m_data->m_gemInfoList.emplace_back(AzToolsFramework::AssetUtils::GemInfo(gemName, relativeGemPath.Native(), absoluteGemPath.Native(), AZ::Uuid::CreateRandom().ToString().c_str(), false, false)); AZ::IO::Path platformsDirectory = absoluteGemPath / "Assets" / PlatformsFolder; if (m_data->m_localFileIO->Exists(platformsDirectory.c_str())) { m_data->m_localFileIO->FindFiles(platformsDirectory.c_str(), AZStd::string::format("*.%s", AzToolsFramework::AssetSeedManager::GetSeedFileExtension()).c_str(), [&](const char* fileName) { AZStd::string normalizedFilePath = fileName; AzFramework::StringFunc::Path::Normalize(normalizedFilePath); m_data->m_gemSeedFilePairList.emplace_back(AZStd::make_pair(normalizedFilePath, seedFileExists)); return true; }); } AZ::IO::Path iosDirectory = platformsDirectory / AzFramework::PlatformIOS; if (m_data->m_localFileIO->Exists(iosDirectory.c_str())) { bool recurse = true; AZ::Outcome, AZStd::string> result = AzFramework::FileFunc::FindFileList(iosDirectory.Native(), AZStd::string::format("*.%s", AzToolsFramework::AssetSeedManager::GetSeedFileExtension()).c_str(), recurse); if (result.IsSuccess()) { AZStd::list seedFiles = result.TakeValue(); for(AZStd::string& seedFile : seedFiles) { AZStd::string normalizedFilePath = seedFile; AzFramework::StringFunc::Path::Normalize(normalizedFilePath); m_data->m_gemSeedFilePairList.emplace_back(AZStd::make_pair(normalizedFilePath, seedFileExists)); } } } } struct StaticData { AZStd::vector m_gemInfoList; AZStd::vector> m_gemSeedFilePairList; AZStd::unique_ptr m_application = {}; AZ::IO::FileIOBase* m_priorFileIO = nullptr; AZ::IO::FileIOBase* m_localFileIO = nullptr; AZStd::string m_testEngineRoot; }; const int GemAIndex = 0; const int GemBIndex = 1; const int GemBSharedFileIndex = 2; const int GemBIosFileIndex = 3; const int EngineIndex = 4; const int GemCIndex = 5; const int ProjectIndex = 6; AZStd::unique_ptr m_data; AZ::SettingsRegistryImpl m_registry; }; TEST_F(AssetBundlerGemsUtilTest, GetDefaultSeedFiles_AllSeedFiles_Found) { // DummyProject and fake Engine/Gem structure lives at dev/Code/Tools/AssetBundler/tests/ auto defaultSeedList = AssetBundler::GetDefaultSeedListFiles(m_data->m_testEngineRoot.c_str(), DummyProjectFolder, m_data->m_gemInfoList, AzFramework::PlatformFlags::Platform_PC); ASSERT_EQ(defaultSeedList.size(), 5); //adding one for the engine seed file and one for the project file // Validate whether both GemA and GemB seed file are present EXPECT_NE(defaultSeedList.find(m_data->m_gemSeedFilePairList[GemAIndex].first), defaultSeedList.end()); EXPECT_NE(defaultSeedList.find(m_data->m_gemSeedFilePairList[GemBIndex].first), defaultSeedList.end()); EXPECT_NE(defaultSeedList.find(m_data->m_gemSeedFilePairList[GemBSharedFileIndex].first), defaultSeedList.end()); // Validate that the engine seed file is present EXPECT_NE(defaultSeedList.find(m_data->m_gemSeedFilePairList[EngineIndex].first), defaultSeedList.end()); EXPECT_NE(defaultSeedList.find(m_data->m_gemSeedFilePairList[ProjectIndex].first), defaultSeedList.end()); } TEST_F(AssetBundlerGemsUtilTest, GetDefaultSeedFilesForMultiplePlatforms_AllSeedFiles_Found) { // DummyProject and fake Engine/Gem structure lives at dev/Code/Tools/AssetBundler/tests/ auto defaultSeedList = AssetBundler::GetDefaultSeedListFiles(m_data->m_testEngineRoot.c_str(), DummyProjectFolder, m_data->m_gemInfoList, AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_IOS); ASSERT_EQ(defaultSeedList.size(), 6); //adding one for the engine seed file and one for the project file // Validate whether both GemA and GemB seed file are present EXPECT_NE(defaultSeedList.find(m_data->m_gemSeedFilePairList[GemAIndex].first), defaultSeedList.end()); EXPECT_NE(defaultSeedList.find(m_data->m_gemSeedFilePairList[GemBIndex].first), defaultSeedList.end()); EXPECT_NE(defaultSeedList.find(m_data->m_gemSeedFilePairList[GemBSharedFileIndex].first), defaultSeedList.end()); EXPECT_NE(defaultSeedList.find(m_data->m_gemSeedFilePairList[GemBIosFileIndex].first), defaultSeedList.end()); // Validate that the engine seed file is present EXPECT_NE(defaultSeedList.find(m_data->m_gemSeedFilePairList[EngineIndex].first), defaultSeedList.end()); EXPECT_NE(defaultSeedList.find(m_data->m_gemSeedFilePairList[ProjectIndex].first), defaultSeedList.end()); } TEST_F(AssetBundlerGemsUtilTest, IsSeedFileValid_Ok) { for (const auto& pair : m_data->m_gemSeedFilePairList) { bool result = IsGemSeedFilePathValid(m_data->m_testEngineRoot.c_str(), pair.first, m_data->m_gemInfoList, AzFramework::PlatformFlags::Platform_PC | AzFramework::PlatformFlags::Platform_IOS); EXPECT_EQ(result,pair.second); } } const char TestProject[] = "TestProject"; const char TestProjectLowerCase[] = "testproject"; #if AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS const char TestRoot[] = "D:\\Dummy\\Test\\dev\\"; #else const char TestRoot[] = "/Dummy/Test/dev/"; #endif class MockApplication : public AzFramework::ApplicationRequests::Bus::Handler { public: MockApplication() { // ensure the cached engine root from previous tests is cleared // so the mock application behaves properly g_cachedEngineRoot[0] = 0; if (AZ::SettingsRegistry::Get() == nullptr) { m_settingsRegistry = AZStd::make_unique(); AZ::SettingsRegistry::Register(m_settingsRegistry.get()); auto gameProjectKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/%s", AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey, "sys_game_folder"); m_settingsRegistry->Set(gameProjectKey, TestProject); } AzFramework::ApplicationRequests::Bus::Handler::BusConnect(); } ~MockApplication() { AzFramework::ApplicationRequests::Bus::Handler::BusDisconnect(); if (m_settingsRegistry.get() == AZ::SettingsRegistry::Get()) { AZ::SettingsRegistry::Unregister(m_settingsRegistry.get()); } } // AzFramework::ApplicationRequests::Bus::Handler interface void NormalizePath(AZStd::string& /*path*/) override {}; void NormalizePathKeepCase(AZStd::string& /*path*/) override {}; void CalculateBranchTokenForAppRoot(AZStd::string& /*token*/) const override {}; const char* GetEngineRoot() const { return TestRoot; } private: AZStd::unique_ptr m_settingsRegistry; }; class AssetBundlerPathUtilTest : public UnitTest::ScopedAllocatorSetupFixture { public: void SetUp() override { UnitTest::ScopedAllocatorSetupFixture::SetUp(); m_data = AZStd::make_unique(); } void TearDown() override { m_data.reset(); UnitTest::ScopedAllocatorSetupFixture::TearDown(); } struct StaticData { MockApplication m_mockApplication; }; AZStd::unique_ptr m_data; }; TEST_F(AssetBundlerPathUtilTest, ComputeAssetAliasAndGameName_AssetCatalogPathNotProvided_Valid) { AZStd::string platformIdentifier = "pc"; AZStd::string assetCatalogFile; AZStd::string assetAlias; AZStd::string gameName; EXPECT_TRUE(ComputeAssetAliasAndGameName(platformIdentifier, assetCatalogFile, assetAlias, gameName).IsSuccess()); EXPECT_TRUE(gameName == TestProject); AZStd::string assetPath; bool success = AzFramework::StringFunc::Path::ConstructFull(TestRoot, "Cache", assetPath) && AzFramework::StringFunc::Path::ConstructFull(assetPath.c_str(), TestProject, assetPath) && AzFramework::StringFunc::Path::ConstructFull(assetPath.c_str(), platformIdentifier.c_str(), assetPath) && AzFramework::StringFunc::Path::ConstructFull(assetPath.c_str(), TestProjectLowerCase, assetPath); EXPECT_EQ(assetAlias, assetPath); } TEST_F(AssetBundlerPathUtilTest, ComputeAssetAliasAndGameName_AssetCatalogPathProvided_Valid) { AZStd::string platformIdentifier = "pc"; #if AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS AZStd::string assetCatalogFile = "D:\\Dummy\\Test\\dev\\Cache\\TestProject1\\pc\\testproject1\\assetcatalog.xml"; #else AZStd::string assetCatalogFile = "/Dummy/Test/dev/Cache/TestProject1/pc/testproject1/assetcatalog.xml"; #endif AZStd::string assetAlias; AZStd::string gameName; EXPECT_TRUE(ComputeAssetAliasAndGameName(platformIdentifier, assetCatalogFile, assetAlias, gameName).IsSuccess()); EXPECT_EQ(gameName, "TestProject1"); AZStd::string assetPath; bool success = AzFramework::StringFunc::Path::ConstructFull(TestRoot, "Cache", assetPath) && AzFramework::StringFunc::Path::ConstructFull(assetPath.c_str(), "TestProject1", assetPath) && AzFramework::StringFunc::Path::ConstructFull(assetPath.c_str(), platformIdentifier.c_str(), assetPath) && AzFramework::StringFunc::Path::ConstructFull(assetPath.c_str(), "testproject1", assetPath); EXPECT_EQ(assetAlias, assetPath); } TEST_F(AssetBundlerPathUtilTest, ComputeAssetAliasAndGameName_GameNameMismatch_Failure) { AZStd::string platformIdentifier = "pc"; #if AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS AZStd::string assetCatalogFile = "D:\\Dummy\\Cache\\TestProject1\\pc\\testproject1\\assetcatalog.xml"; #else AZStd::string assetCatalogFile = "/Dummy/Cache/TestProject1/pc/testproject1/assetcatalog.xml"; #endif AZStd::string assetAlias; AZStd::string gameName="SomeOtherGamename"; EXPECT_FALSE(ComputeAssetAliasAndGameName(platformIdentifier, assetCatalogFile, assetAlias, gameName).IsSuccess()); } } int main(int argc, char* argv[]) { INVOKE_AZ_UNIT_TEST_MAIN(); AZ::AllocatorInstance::Create(); int runSuccess = 0; { AssetBundler::ApplicationManager applicationManger(&argc, &argv); applicationManger.Init(); runSuccess = applicationManger.Run() ? 0 : 1; } AZ::AllocatorInstance::Destroy(); return runSuccess; }