/* * 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 * */ #pragma once #include #include #include #include "native/tests/AssetProcessorTest.h" #include #include "native/assetprocessor.h" #include "native/unittests/UnitTestRunner.h" #include "native/AssetManager/assetProcessorManager.h" #include "native/utilities/PlatformConfiguration.h" #include "native/unittests/MockApplicationManager.h" #include #include #include #include #include #include #include #include #include "resourcecompiler/rccontroller.h" class AssetProcessorManager_Test; class MockDatabaseLocationListener : public AzToolsFramework::AssetDatabase::AssetDatabaseRequests::Bus::Handler { public: MOCK_METHOD1(GetAssetDatabaseLocation, bool(AZStd::string&)); }; class AssetProcessorManager_Test : public AssetProcessor::AssetProcessorManager { public: friend class GTEST_TEST_CLASS_NAME_( AssetProcessorManagerTest, AssetProcessedImpl_DifferentProductDependenciesPerProduct_SavesCorrectlyToDatabase); friend class GTEST_TEST_CLASS_NAME_(MultiplatformPathDependencyTest, AssetProcessed_Impl_MultiplatformDependencies); friend class GTEST_TEST_CLASS_NAME_(MultiplatformPathDependencyTest, AssetProcessed_Impl_MultiplatformDependencies_DeferredResolution); friend class GTEST_TEST_CLASS_NAME_(MultiplatformPathDependencyTest, SameFilenameForAllPlatforms); friend class GTEST_TEST_CLASS_NAME_(MultiplatformPathDependencyTest, AssetProcessed_Impl_MultiplatformDependencies_SourcePath); friend class GTEST_TEST_CLASS_NAME_(AssetProcessorManagerTest, DeleteFolder_SignalsDeleteOfContainedFiles); friend class GTEST_TEST_CLASS_NAME_(AssetProcessorManagerTest, QueryAbsolutePathDependenciesRecursive_BasicTest); friend class GTEST_TEST_CLASS_NAME_(AssetProcessorManagerTest, QueryAbsolutePathDependenciesRecursive_WithDifferentTypes_BasicTest); friend class GTEST_TEST_CLASS_NAME_(AssetProcessorManagerTest, QueryAbsolutePathDependenciesRecursive_Reverse_BasicTest); friend class GTEST_TEST_CLASS_NAME_( AssetProcessorManagerTest, QueryAbsolutePathDependenciesRecursive_MissingFiles_ReturnsNoPathWithPlaceholders); friend class GTEST_TEST_CLASS_NAME_(AssetProcessorManagerTest, BuilderDirtiness_BeforeComputingDirtiness_AllDirty); friend class GTEST_TEST_CLASS_NAME_(AssetProcessorManagerTest, BuilderDirtiness_EmptyDatabase_AllDirty); friend class GTEST_TEST_CLASS_NAME_(AssetProcessorManagerTest, BuilderDirtiness_SameAsLastTime_NoneDirty); friend class GTEST_TEST_CLASS_NAME_(AssetProcessorManagerTest, BuilderDirtiness_MoreThanLastTime_NewOneIsDirty); friend class GTEST_TEST_CLASS_NAME_(AssetProcessorManagerTest, BuilderDirtiness_FewerThanLastTime_Dirty); friend class GTEST_TEST_CLASS_NAME_(AssetProcessorManagerTest, BuilderDirtiness_ChangedPattern_CountsAsNew); friend class GTEST_TEST_CLASS_NAME_(AssetProcessorManagerTest, BuilderDirtiness_ChangedPatternType_CountsAsNew); friend class GTEST_TEST_CLASS_NAME_(AssetProcessorManagerTest, BuilderDirtiness_NewPattern_CountsAsNewBuilder); friend class GTEST_TEST_CLASS_NAME_(AssetProcessorManagerTest, BuilderDirtiness_NewVersionNumber_IsNotANewBuilder); friend class GTEST_TEST_CLASS_NAME_(AssetProcessorManagerTest, BuilderDirtiness_NewAnalysisFingerprint_IsNotANewBuilder); friend class GTEST_TEST_CLASS_NAME_(AssetProcessorManagerTest, UpdateSourceFileDependenciesDatabase_BasicTest); friend class GTEST_TEST_CLASS_NAME_(AssetProcessorManagerTest, UpdateSourceFileDependenciesDatabase_UpdateTest); friend class GTEST_TEST_CLASS_NAME_(AssetProcessorManagerTest, UpdateSourceFileDependenciesDatabase_MissingFiles_ByUuid); friend class GTEST_TEST_CLASS_NAME_(AssetProcessorManagerTest, UpdateSourceFileDependenciesDatabase_MissingFiles_ByName); friend class GTEST_TEST_CLASS_NAME_( AssetProcessorManagerTest, UpdateSourceFileDependenciesDatabase_MissingFiles_ByUuid_UpdatesWhenTheyAppear); friend class GTEST_TEST_CLASS_NAME_( AssetProcessorManagerTest, UpdateSourceFileDependenciesDatabase_MissingFiles_ByName_UpdatesWhenTheyAppear); friend class GTEST_TEST_CLASS_NAME_( AssetProcessorManagerTest, UpdateSourceFileDependenciesDatabase_WildcardMissingFiles_ByName_UpdatesWhenTheyAppear); friend class GTEST_TEST_CLASS_NAME_(AssetProcessorManagerTest, JobDependencyOrderOnce_MultipleJobs_EmitOK); friend class GTEST_TEST_CLASS_NAME_(AssetProcessorManagerTest, SourceFileProcessFailure_ClearsFingerprint); friend class GTEST_TEST_CLASS_NAME_( AbsolutePathProductDependencyTest, UnresolvedProductPathDependency_AssetProcessedTwice_DoesNotDuplicateDependency); friend class GTEST_TEST_CLASS_NAME_( AbsolutePathProductDependencyTest, AbsolutePathProductDependency_RetryDeferredDependenciesWithMatchingSource_DependencyResolves); friend class GTEST_TEST_CLASS_NAME_( AbsolutePathProductDependencyTest, UnresolvedProductPathDependency_AssetProcessedTwice_ValidatePathDependenciesMap); friend class GTEST_TEST_CLASS_NAME_( AbsolutePathProductDependencyTest, UnresolvedSourceFileTypeProductPathDependency_DependencyHasNoProductOutput_ValidatePathDependenciesMap); friend class GTEST_TEST_CLASS_NAME_(DeleteTest, DeleteFolderSharedAcrossTwoScanFolders_CorrectFileAndFolderAreDeletedFromCache); friend class GTEST_TEST_CLASS_NAME_(MetadataFileTest, MetadataFile_SourceFileExtensionDifferentCase); friend class AssetProcessorManagerTest; friend struct JobDependencyTest; friend struct ChainJobDependencyTest; friend struct DeleteTest; friend struct PathDependencyTest; friend struct DuplicateProductsTest; friend struct DuplicateProcessTest; friend struct AbsolutePathProductDependencyTest; friend struct WildcardSourceDependencyTest; explicit AssetProcessorManager_Test(AssetProcessor::PlatformConfiguration* config, QObject* parent = nullptr); ~AssetProcessorManager_Test() override; bool CheckJobKeyToJobRunKeyMap(AZStd::string jobKey); int CountDirtyBuilders() const { int numDirty = 0; for (const auto& element : m_builderDataCache) { if (element.second.m_isDirty) { ++numDirty; } } return numDirty; } bool IsBuilderDirty(const AZ::Uuid& builderBusId) const { auto finder = m_builderDataCache.find(builderBusId); if (finder == m_builderDataCache.end()) { return true; } return finder->second.m_isDirty; } void RecomputeDirtyBuilders() { // Run this twice so the test builder doesn't get counted as a "new" builder and bypass the modtime skipping ComputeBuilderDirty(); ComputeBuilderDirty(); } using AssetProcessorManager::m_stateData; using AssetProcessorManager::ComputeBuilderDirty; }; class AssetProcessorManagerTest : public AssetProcessor::AssetProcessorTest { public: AssetProcessorManagerTest(); virtual ~AssetProcessorManagerTest() { } // utility function. Blocks and runs the QT event pump for up to millisecondsMax and will break out as soon as the APM is idle. bool BlockUntilIdle(int millisecondsMax); protected: void SetUp() override; void TearDown() override; QTemporaryDir m_tempDir; AZStd::unique_ptr m_assetProcessorManager; AZStd::unique_ptr m_mockApplicationManager; AZStd::unique_ptr m_config; QString m_gameName; QDir m_normalizedCacheRootDir; AZStd::atomic_bool m_isIdling; QMetaObject::Connection m_idleConnection; struct StaticData { AZStd::string m_databaseLocation; ::testing::NiceMock m_databaseLocationListener; AZ::Entity* m_jobManagerEntity{}; AZ::ComponentDescriptor* m_descriptor{}; AZStd::unique_ptr m_serializeContext; }; AZStd::unique_ptr m_data; private: int m_argc; char** m_argv; AZStd::unique_ptr m_scopeDir; AZStd::unique_ptr m_qApp; }; struct AbsolutePathProductDependencyTest : public AssetProcessorManagerTest { void SetUp() override; AzToolsFramework::AssetDatabase::ProductDependencyDatabaseEntry SetAndReadAbsolutePathProductDependencyFromRelativePath( const AZStd::string& relativePath); AZStd::string BuildScanFolderRelativePath(const AZStd::string& relativePath) const; AzToolsFramework::AssetDatabase::ProductDatabaseEntry m_productToHaveDependency; const AssetProcessor::ScanFolderInfo* m_scanFolderInfo = nullptr; AZStd::string m_testPlatform = "SomePlatform"; }; struct PathDependencyTest : public AssetProcessorManagerTest { void SetUp() override; void TearDown() override; using OutputAssetSet = AZStd::vector>; struct TestAsset { TestAsset() = default; TestAsset(const char* name) : m_name(name) {} AZStd::string m_name; AZStd::vector m_products; }; void CaptureJobs(AZStd::vector& jobDetails, const char* sourceFilePath); bool ProcessAsset(TestAsset& asset, const OutputAssetSet& outputAssets, const AssetBuilderSDK::ProductPathDependencySet& dependencies = {}, const AZStd::string& folderPath = "subfolder1/", const AZStd::string& extension = ".txt"); void RunWildcardTest(bool useCorrectDatabaseSeparator, AssetBuilderSDK::ProductPathDependencyType pathDependencyType, bool buildDependenciesFirst); AssetProcessor::AssetDatabaseConnection* m_sharedConnection{}; }; struct DuplicateProcessTest : public PathDependencyTest { void SetUp() override; }; struct MultiplatformPathDependencyTest : public PathDependencyTest { void SetUp() override; }; struct WildcardSourceDependencyTest : AssetProcessorManagerTest { bool Test(const AZStd::string& dependencyPath, AZStd::vector& resolvedPaths); AZStd::vector FileAddedTest(const QString& path); void SetUp() override; }; struct MockBuilderInfoHandler : public AssetProcessor::AssetBuilderInfoBus::Handler { ~MockBuilderInfoHandler(); //! AssetProcessor::AssetBuilderInfoBus Interface void GetMatchingBuildersInfo(const AZStd::string& assetPath, AssetProcessor::BuilderInfoList& builderInfoList) override; void GetAllBuildersInfo(AssetProcessor::BuilderInfoList& builderInfoList) override; void CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response); void ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response); AssetBuilderSDK::AssetBuilderDesc CreateBuilderDesc(const QString& builderName, const QString& builderId, const AZStd::vector& builderPatterns); AssetBuilderSDK::AssetBuilderDesc m_builderDesc; QString m_jobFingerprint; QString m_dependencyFilePath; QString m_jobDependencyFilePath; int m_createJobsCount = 0; }; struct MetadataFileTest : public AssetProcessorManagerTest { void SetUp() override; }; struct FingerprintTest : public AssetProcessorManagerTest { void SetUp() override; void TearDown() override; void RunFingerprintTest(QString builderFingerprint, QString jobFingerprint, bool expectedResult); QString m_absolutePath; MockBuilderInfoHandler m_mockBuilderInfoHandler; AZStd::vector m_jobResults; }; struct JobDependencyTest : public PathDependencyTest { void SetUp() override; void TearDown() override; struct StaticData { MockBuilderInfoHandler m_mockBuilderInfoHandler; AZ::Uuid m_builderUuid; }; AZStd::unique_ptr m_data; }; struct MockMultiBuilderInfoHandler : public AssetProcessor::AssetBuilderInfoBus::Handler { ~MockMultiBuilderInfoHandler(); struct AssetBuilderExtraInfo { QString m_jobDependencyFilePath; }; //! AssetProcessor::AssetBuilderInfoBus Interface void GetMatchingBuildersInfo(const AZStd::string& assetPath, AssetProcessor::BuilderInfoList& builderInfoList) override; void GetAllBuildersInfo(AssetProcessor::BuilderInfoList& builderInfoList) override; void CreateJobs(AssetBuilderExtraInfo extraInfo, const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response); void ProcessJob(AssetBuilderExtraInfo extraInfo, const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response); void CreateBuilderDesc(const QString& builderName, const QString& builderId, const AZStd::vector& builderPatterns, AssetBuilderExtraInfo extraInfo); AZStd::vector m_builderDesc; AZStd::vector m_matcherBuilderPatterns; AZStd::unordered_map m_builderDescMap; int m_createJobsCount = 0; }; struct ChainJobDependencyTest : public PathDependencyTest { void SetUp() override; void TearDown() override; struct StaticData { MockMultiBuilderInfoHandler m_mockBuilderInfoHandler; AZStd::unique_ptr m_rcController; }; static constexpr int ChainLength = 10; AZStd::unique_ptr m_data; }; struct DuplicateProductsTest : public AssetProcessorManagerTest { void SetupDuplicateProductsTest(QString& sourceFile, QDir& tempPath, QString& productFile, AZStd::vector& jobDetails, AssetBuilderSDK::ProcessJobResponse& response, bool multipleOutputs, QString extension); };