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.
o3de/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.h

358 lines
14 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
*
*/
#pragma once
#include <AzTest/AzTest.h>
#include <AzCore/std/parallel/atomic.h>
#include <qcoreapplication.h>
#include "native/tests/AssetProcessorTest.h"
#include <AssetBuilderSDK/AssetBuilderSDK.h>
#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 <AssetManager/FileStateCache.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <QTemporaryDir>
#include <QMetaObject>
#include <AzCore/Jobs/JobContext.h>
#include <AzCore/Jobs/JobManager.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzToolsFramework/API/AssetDatabaseBus.h>
#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<AssetProcessorManager_Test> m_assetProcessorManager;
AZStd::unique_ptr<AssetProcessor::MockApplicationManager> m_mockApplicationManager;
AZStd::unique_ptr<AssetProcessor::PlatformConfiguration> 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<MockDatabaseLocationListener> m_databaseLocationListener;
AZ::Entity* m_jobManagerEntity{};
AZ::ComponentDescriptor* m_descriptor{};
AZStd::unique_ptr<AZ::SerializeContext> m_serializeContext;
};
AZStd::unique_ptr<StaticData> m_data;
private:
int m_argc;
char** m_argv;
AZStd::unique_ptr<UnitTestUtils::ScopedDir> m_scopeDir;
AZStd::unique_ptr<QCoreApplication> 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<AZStd::vector<const char*>>;
struct TestAsset
{
TestAsset() = default;
TestAsset(const char* name) : m_name(name) {}
AZStd::string m_name;
AZStd::vector<AZ::Data::AssetId> m_products;
};
void CaptureJobs(AZStd::vector<AssetProcessor::JobDetails>& 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<AZStd::string>& resolvedPaths);
AZStd::vector<AZStd::string> 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<AssetBuilderSDK::AssetBuilderPattern>& 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<AssetProcessor::JobDetails> m_jobResults;
};
struct JobDependencyTest
: public PathDependencyTest
{
void SetUp() override;
void TearDown() override;
struct StaticData
{
MockBuilderInfoHandler m_mockBuilderInfoHandler;
AZ::Uuid m_builderUuid;
};
AZStd::unique_ptr<StaticData> 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<AssetBuilderSDK::AssetBuilderPattern>& builderPatterns, AssetBuilderExtraInfo extraInfo);
AZStd::vector<AssetBuilderSDK::AssetBuilderDesc> m_builderDesc;
AZStd::vector<AssetUtilities::BuilderFilePatternMatcher> m_matcherBuilderPatterns;
AZStd::unordered_map<AZ::Uuid, AssetBuilderSDK::AssetBuilderDesc> m_builderDescMap;
int m_createJobsCount = 0;
};
struct ChainJobDependencyTest
: public PathDependencyTest
{
void SetUp() override;
void TearDown() override;
struct StaticData
{
MockMultiBuilderInfoHandler m_mockBuilderInfoHandler;
AZStd::unique_ptr<AssetProcessor::RCController> m_rcController;
};
static constexpr int ChainLength = 10;
AZStd::unique_ptr<StaticData> m_data;
};
struct DuplicateProductsTest
: public AssetProcessorManagerTest
{
void SetupDuplicateProductsTest(QString& sourceFile, QDir& tempPath, QString& productFile, AZStd::vector<AssetProcessor::JobDetails>& jobDetails, AssetBuilderSDK::ProcessJobResponse& response, bool multipleOutputs, QString extension);
};