Просмотр исходного кода

{lyn12587} Asset cache server feature configuration (#9667)

* {lyn12587} Asset cache server feature configuration

* Asset cache server feature configuration now can be done in a setreg file
* removed legacy RC classes and tests
* Asset cache server will create sub folders when storing
* Amazon/AssetProcessor/Settings/Server/enableCacheServer to start ACS feature

Signed-off-by: Allen Jackson <[email protected]>

* Added unit test for the ACS mode
Updated assetUtils.cpp for testing purposes


Signed-off-by: Allen Jackson <[email protected]>

* clearing ACS at start of each unit test

Signed-off-by: Allen Jackson <[email protected]>
Allen Jackson 3 лет назад
Родитель
Сommit
82d78d6618

+ 1 - 0
Code/Tools/AssetProcessor/assetprocessor_test_files.cmake

@@ -61,6 +61,7 @@ set(FILES
     native/tests/AssetProcessorMessagesTests.cpp
     native/tests/ApplicationManagerTests.cpp
     native/tests/ApplicationManagerTests.h
+    native/unittests/AssetCacheServerUnitTests.cpp
     native/unittests/AssetProcessingStateDataUnitTests.cpp
     native/unittests/AssetProcessingStateDataUnitTests.h
     native/unittests/AssetProcessorManagerUnitTests.cpp

+ 35 - 0
Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.cpp

@@ -2158,6 +2158,8 @@ namespace AssetProcessor
         }
         else
         {
+            UpdateForCacheServer(jobDetails);
+
             // macOS requires that the cacheRootDir to not be all lowercase, otherwise file copies will not work correctly.
             // So use the lowerCasePath string to capture the parts that need to be lower case while keeping the cache root
             // mixed case.
@@ -2182,6 +2184,39 @@ namespace AssetProcessor
         return true;
     }
 
+    void AssetProcessorManager::UpdateForCacheServer(JobDetails& jobDetails)
+    {
+        if (AssetUtilities::ServerAddress().isEmpty())
+        {
+            // Asset Cache Server mode feature is turned off
+            return;
+        }
+        else if (!m_platformConfig)
+        {
+            AZ_Error(AssetProcessor::ConsoleChannel, m_platformConfig, "Platform not configured. Called too soon?");
+            return;
+        }
+
+        auto& cacheRecognizerContainer = m_platformConfig->GetAssetCacheRecognizerContainer();
+        for(auto&& cacheRecognizer : cacheRecognizerContainer)
+        {
+            auto matchingPatternIt = AZStd::find_if(
+                jobDetails.m_assetBuilderDesc.m_patterns.begin(),
+                jobDetails.m_assetBuilderDesc.m_patterns.end(),
+                [cacheRecognizer](const AssetBuilderSDK::AssetBuilderPattern& pattern)
+                {
+                    return cacheRecognizer.m_patternMatcher.GetBuilderPattern().m_type == pattern.m_type &&
+                           cacheRecognizer.m_patternMatcher.GetBuilderPattern().m_pattern == pattern.m_pattern;
+                }
+            );
+
+            if (matchingPatternIt != jobDetails.m_assetBuilderDesc.m_patterns.end())
+            {
+                jobDetails.m_checkServer = cacheRecognizer.m_checkServer;
+            }
+        }
+    }
+
     void AssetProcessorManager::CheckDeletedCacheFolder(QString normalizedPath)
     {
         QDir checkDir(normalizedPath);

+ 2 - 0
Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.h

@@ -407,6 +407,8 @@ namespace AssetProcessor
         //! Analyzes and forward the job to the RCController if the job requires processing
         void ProcessJob(JobDetails& jobDetails);
 
+        void UpdateForCacheServer(JobDetails& jobDetails);
+
         AssetProcessor::PlatformConfiguration* m_platformConfig = nullptr;
 
         bool m_queuedExamination = false;

+ 8 - 175
Code/Tools/AssetProcessor/native/resourcecompiler/RCBuilder.cpp

@@ -97,79 +97,6 @@ namespace AssetProcessor
         }
     }
 
-    // don't make this too high, its basically how slowly the app responds to a job finishing.
-    // this basically puts a hard cap on how many RC jobs can execute per second, since at 10ms per job (minimum), with 8 cores, thats a max
-    // of 800 jobs per second that can possibly run.  However, the actual time it takes to launch RC.EXE is far, far longer than 10ms, so this is not a bad number for now...
-    const int NativeLegacyRCCompiler::s_maxSleepTime = 10;
-
-    // You have up to 60 minutes to finish processing an asset.
-    // This was increased from 10 to account for PVRTC compression
-    // taking up to an hour for large normal map textures, and should
-    // be reduced again once we move to the ASTC compression format, or
-    // find another solution to reduce processing times to be reasonable.
-    const unsigned int NativeLegacyRCCompiler::s_jobMaximumWaitTime = 1000 * 60 * 60;
-
-    NativeLegacyRCCompiler::Result::Result(int exitCode, bool crashed, const QString& outputDir)
-        : m_exitCode(exitCode)
-        , m_crashed(crashed)
-        , m_outputDir(outputDir)
-    {
-    }
-
-    NativeLegacyRCCompiler::NativeLegacyRCCompiler()
-        : m_resourceCompilerInitialized(false)
-        , m_requestedQuit(false)
-    {
-    }
-    
-    bool NativeLegacyRCCompiler::Initialize()
-    {
-        this->m_resourceCompilerInitialized = true;
-        return true;
-    }
-
-    bool NativeLegacyRCCompiler::Execute(
-        [[maybe_unused]] const QString& inputFile,
-        [[maybe_unused]] const QString& watchFolder,
-        [[maybe_unused]] const QString& platformIdentifier,
-        [[maybe_unused]] const QString& params,
-        [[maybe_unused]] const QString& dest,
-        [[maybe_unused]] const AssetBuilderSDK::JobCancelListener* jobCancelListener,
-        [[maybe_unused]] Result& result) const
-    {
-        // running RC.EXE is deprecated.
-        AZ_Error("RC Builder", false, "running RC.EXE is deprecated");
-
-        return false;
-    }
-
-    QString NativeLegacyRCCompiler::BuildCommand(const QString& inputFile, const QString& watchFolder, const QString& platformIdentifier, const QString& params, const QString& dest)
-    {
-        QString cmdLine;
-        if (!dest.isEmpty())
-        {
-            QString projectPath = AssetUtilities::ComputeProjectPath();
-
-            int portNumber = 0;
-            ApplicationServerBus::BroadcastResult(portNumber, &ApplicationServerBus::Events::GetServerListeningPort);
-
-            AZStd::string appBranchToken;
-            AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::CalculateBranchTokenForEngineRoot, appBranchToken);
-            cmdLine = QString("\"%1\" --platform=%2 %3 --unattended=true --project-path=\"%4\" --watchfolder=\"%6\" --targetroot=\"%5\" --logprefix=\"%5/\" --port=%7 --branchtoken=\"%8\"");
-            cmdLine = cmdLine.arg(inputFile, platformIdentifier, params, projectPath, dest, watchFolder).arg(portNumber).arg(appBranchToken.c_str());
-        }
-        else
-        {
-            cmdLine = QString("\"%1\" --platform=%2 %3").arg(inputFile, platformIdentifier, params);
-        }
-        return cmdLine;
-    }
-
-    void NativeLegacyRCCompiler::RequestQuit()
-    {
-        this->m_requestedQuit = true;
-    }
-
     BuilderIdAndName::BuilderIdAndName(QString builderName, QString builderId, Type type, QString rcParam /*=QString("")*/)
         : m_builderName(builderName)
         , m_builderId(builderId)
@@ -261,7 +188,6 @@ namespace AssetProcessor
     //! unit testing.
     InternalRecognizerBasedBuilder::InternalRecognizerBasedBuilder(QHash<QString, BuilderIdAndName> inputBuilderByIdMap, AZ::Uuid internalBuilderUuid)
         : m_isShuttingDown(false)
-        , m_rcCompiler(new NativeLegacyRCCompiler())
         , m_internalRecognizerBuilderUuid(internalBuilderUuid)
     {
         for (BuilderIdAndName builder : inputBuilderByIdMap)
@@ -311,7 +237,6 @@ namespace AssetProcessor
     void InternalRecognizerBasedBuilder::ShutDown()
     {
         m_isShuttingDown = true;
-        this->m_rcCompiler->RequestQuit();
     }
 
     bool InternalRecognizerBasedBuilder::FindRC(QString& rcAbsolutePathOut)
@@ -330,7 +255,7 @@ namespace AssetProcessor
     bool InternalRecognizerBasedBuilder::Initialize(const RecognizerConfiguration& recognizerConfig)
     {
         InitializeAssetRecognizers(recognizerConfig.GetAssetRecognizerContainer());
-        return m_rcCompiler->Initialize();
+        return true;
     }
 
 
@@ -674,27 +599,6 @@ namespace AssetProcessor
                 AZ_TracePrintf(AssetProcessor::DebugChannel, "Job ID %lld Failed, encountered an invalid 'skip' parameter during job processing\n", AssetProcessor::GetThreadLocalJobId());
                 response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
             }
-            else
-            {
-                // If the job fails due to a networking issue, we will attempt to retry RetriesForJobNetworkError times
-                int retryCount = 0;
-
-                do
-                {
-                    ++retryCount;
-                    ProcessLegacyRCJob(request, rcParam, assetRecognizer->m_productAssetType, jobCancelListener, response);
-
-                    // If a lost connection occured, prepare for a retry using an exponential backoff policy
-                    if ((response.m_resultCode == AssetBuilderSDK::ProcessJobResult_NetworkIssue) && (retryCount <= AssetProcessor::RetriesForJobLostConnection))
-                    {
-                        const int delay = 1 << (retryCount-1);
-                        AZStd::this_thread::sleep_for(AZStd::chrono::seconds(delay));
-                    }
-                    AZ_Warning("RC Builder", response.m_resultCode != AssetBuilderSDK::ProcessJobResult_NetworkIssue, "RC.exe reported a network connection issue.  %s",
-                        retryCount <= AssetProcessor::RetriesForJobLostConnection ? "Attempting to retry job." : "Maximum retry attempts exceeded, giving up.");
-                } while (response.m_resultCode == AssetBuilderSDK::ProcessJobResult_NetworkIssue && retryCount <= AssetProcessor::RetriesForJobLostConnection);
-
-            }
 
             if (jobCancelListener.IsCancelled())
             {
@@ -710,50 +614,12 @@ namespace AssetProcessor
         }
     }
 
-    void InternalRecognizerBasedBuilder::CreateLegacyRCJob(const AssetBuilderSDK::CreateJobsRequest& request, QString rcParam, AssetBuilderSDK::CreateJobsResponse& response)
+    void InternalRecognizerBasedBuilder::CreateLegacyRCJob(
+        [[maybe_unused]] const AssetBuilderSDK::CreateJobsRequest& request,
+        [[maybe_unused]] QString rcParam,
+        AssetBuilderSDK::CreateJobsResponse& response)
     {
-        const char* requestFileName = "createjobsRequest.xml";
-        const char* responseFileName = "createjobsResponse.xml";
-        const char* createJobsParam = "/createjobs";
-
-        QDir watchDir(request.m_watchFolder.c_str());
-        auto normalizedPath = watchDir.absoluteFilePath(request.m_sourceFile.c_str());
-
-        QString workFolder;
-
-        if (!AssetUtilities::CreateTempWorkspace(workFolder))
-        {
-            AZ_TracePrintf(AssetProcessor::DebugChannel, "Failed to create temporary workspace");
-            return;
-        }
-
-        QString watchFolder = request.m_watchFolder.c_str();
-        NativeLegacyRCCompiler::Result  rcResult;
-
-        QDir workDir(workFolder);
-        QString requestPath = workDir.absoluteFilePath(requestFileName);
-        QString responsePath = workDir.absoluteFilePath(responseFileName);
-
-        if (!AZ::Utils::SaveObjectToFile(requestPath.toStdString().c_str(), AZ::DataStream::ST_XML, &request))
-        {
-            AZ_TracePrintf(AssetProcessor::DebugChannel, "Failed to write CreateJobsRequest to file %s", requestPath.toStdString().c_str());
-            return;
-        }
-
-        QString params = QString("%1=\"%2\"").arg(createJobsParam).arg(requestPath);
-
-        //Platform and platform id are hard coded to PC because it doesn't matter, the actual platform info is in the CreateJobsRequest
-        if ((!this->m_rcCompiler->Execute(normalizedPath, watchFolder, "pc", rcParam.append(params), workFolder, nullptr, rcResult)) || (rcResult.m_exitCode != 0))
-        {
-            AZ_TracePrintf(AssetProcessor::DebugChannel, "Job ID %lld Failed with exit code %d\n", AssetProcessor::GetThreadLocalJobId(), rcResult.m_exitCode);
-            response.m_result = AssetBuilderSDK::CreateJobsResultCode::Failed;
-            return;
-        }
-
-        if (AZ::Utils::LoadObjectFromFileInPlace(responsePath.toStdString().c_str(), response))
-        {
-            workDir.removeRecursively();
-        }
+        response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;
     }
 
     bool InternalRecognizerBasedBuilder::SaveProcessJobRequestFile(const char* requestFileDir, const char* requestFileName, const AssetBuilderSDK::ProcessJobRequest& request)
@@ -804,7 +670,6 @@ namespace AssetProcessor
         QString platformIdentifier = request.m_jobDescription.GetPlatformIdentifier().c_str();
         QString dest = request.m_tempDirPath.c_str();
         QString watchFolder = request.m_watchFolder.c_str();
-        NativeLegacyRCCompiler::Result  rcResult;
 
         QDir workDir(dest);
 
@@ -814,21 +679,6 @@ namespace AssetProcessor
             return;
         }
 
-        if ((!this->m_rcCompiler->Execute(inputFile, watchFolder, platformIdentifier, rcParam, dest, &jobCancelListener, rcResult)) || (rcResult.m_exitCode != 0))
-        {
-            AZ_TracePrintf(AssetProcessor::DebugChannel, "Job ID %lld Failed with exit code %d\n", AssetProcessor::GetThreadLocalJobId(), rcResult.m_exitCode);
-            if (jobCancelListener.IsCancelled())
-            {
-                response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
-                return;
-            }
-            else if (rcResult.m_crashed)
-            {
-                response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Crashed;
-                return;
-            }
-        }
-
         // did the rc Compiler output a response file?
         bool responseFromRCCompiler = false;
         if (!LoadProcessJobResponseFile(dest.toUtf8().constData(), AssetBuilderSDK::s_processJobResponseFileName, response, responseFromRCCompiler))
@@ -839,17 +689,8 @@ namespace AssetProcessor
 
         if (!responseFromRCCompiler)
         {
-            if(rcResult.m_exitCode != 0)
-            {
-                // RC didn't crash and didn't write a response, but returned a failure code
-                response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
-                return;
-            }
-            else
-            {
-                // if the response was NOT loaded from a response file, we assume success (since RC did not crash or anything)
-                response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
-            }
+            // if the response was NOT loaded from a response file, we assume success (since RC did not crash or anything)
+            response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
         }
 
         if (jobCancelListener.IsCancelled())
@@ -1044,14 +885,6 @@ namespace AssetProcessor
         }
         // Temporary solution to get around the fact that we don't have job dependencies
         TempSolution_TouchCopyJobActivity();
-
-        if (outputProductDependencies)
-        {
-            // Launch the external process when it requires the old cry code to parse a specific type of asset
-            QString rcParam = QString("/copyonly /outputproductdependencies /targetroot");
-            rcParam = QString("%1=\"%2\"").arg(rcParam).arg(request.m_tempDirPath.c_str());
-            ProcessLegacyRCJob(request, rcParam, productAssetType, jobCancelListener, response);
-        }
     }
 
     QFileInfoList InternalRecognizerBasedBuilder::GetFilesInDirectory(const QString& directoryPath)

+ 0 - 39
Code/Tools/AssetProcessor/native/resourcecompiler/RCBuilder.h

@@ -14,44 +14,6 @@
 
 namespace AssetProcessor
 {
-    struct RCCompiler
-    {
-        //! RC.exe execution result
-        struct Result
-        {
-            Result(int exitCode, bool crashed, const QString& outputDir);
-            Result() = default;
-            int     m_exitCode = 1;
-            bool    m_crashed = false;
-            QString m_outputDir;
-        };
-
-        virtual ~RCCompiler() = default;
-        virtual bool Initialize() = 0;
-        virtual bool Execute(const QString& inputFile, const QString& watchFolder, const QString& platformIdentifier, const QString& params,
-            const QString& dest, const AssetBuilderSDK::JobCancelListener* jobCancelListener, Result& result) const = 0;
-        virtual void RequestQuit() = 0;
-    };
-
-    //! Worker class to handle shell execution of the legacy rc.exe compiler
-    class NativeLegacyRCCompiler
-        : public RCCompiler
-    {
-    public:
-        NativeLegacyRCCompiler();
-
-        bool Initialize() override;
-        bool Execute(const QString& inputFile, const QString& watchFolder, const QString& platformIdentifier, const QString& params, const QString& dest,
-            const AssetBuilderSDK::JobCancelListener* jobCancelListener, Result& result) const override;
-        static QString BuildCommand(const QString& inputFile, const QString& watchFolder, const QString& platformIdentifier, const QString& params, const QString& dest);
-        void RequestQuit()  override;
-    private:
-        static const int            s_maxSleepTime;
-        static const unsigned int   s_jobMaximumWaitTime;
-        bool                        m_resourceCompilerInitialized;
-        volatile bool               m_requestedQuit;
-    };
-
     //! Internal structure that consolidates a internal builder id, its name, and its custom fixed rc param match
     class BuilderIdAndName
     {
@@ -191,7 +153,6 @@ namespace AssetProcessor
         // returns false only if there is a critical failure.
         virtual bool LoadProcessJobResponseFile(const char* responseFileDir, const char* responseFileName, AssetBuilderSDK::ProcessJobResponse& response, bool& responseLoaded);
 
-        AZStd::unique_ptr<RCCompiler>           m_rcCompiler;
         volatile bool                           m_isShuttingDown;
         InternalRecognizerContainer             m_assetRecognizerDictionary;
 

+ 3 - 744
Code/Tools/AssetProcessor/native/tests/resourcecompiler/RCBuilderTest.cpp

@@ -9,470 +9,6 @@
 #include "RCBuilderTest.h"
 
 
-
-TEST_F(RCBuilderTest, CreateBuilderDesc_CreateBuilder_Valid)
-{
-    AssetBuilderSDK::AssetBuilderPattern    pattern;
-    pattern.m_pattern = "*.foo";
-    AZStd::vector<AssetBuilderSDK::AssetBuilderPattern> builderPatterns;
-    builderPatterns.push_back(pattern);
-
-    TestInternalRecognizerBasedBuilder  test(new MockRCCompiler());
-
-    AssetBuilderSDK::AssetBuilderDesc result = test.CreateBuilderDesc(this->GetBuilderID(), builderPatterns);
-
-    ASSERT_EQ(this->GetBuilderName(), result.m_name);
-    ASSERT_EQ(this->GetBuilderUUID(), result.m_busId);
-    ASSERT_EQ(false, result.IsExternalBuilder());
-    ASSERT_TRUE(result.m_patterns.size() == 1);
-    ASSERT_EQ(result.m_patterns[0].m_pattern, pattern.m_pattern);
-}
-
-TEST_F(RCBuilderTest, Shutdown_NormalShutdown_Requested)
-{
-    MockRCCompiler*                     mockRC = new MockRCCompiler();
-    TestInternalRecognizerBasedBuilder  test(mockRC);
-
-    test.ShutDown();
-
-    ASSERT_EQ(mockRC->m_request_quit, 1);
-}
-
-
-TEST_F(RCBuilderTest, Initialize_StandardInitializationWithDuplicateAndInvalidRecognizers_Valid)
-{
-    MockRCCompiler*                     mockRC = new MockRCCompiler();
-    TestInternalRecognizerBasedBuilder  test(mockRC);
-    MockRecognizerConfiguration         configuration;
-
-    // 3 Asset recognizers, 1 duplicate & 1 without platform should result in only 1 InternalAssetRecognizer
-
-    // Good spec
-    AssetRecognizer     good;
-    good.m_name = "Good";
-    good.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.foo", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard);
-    AssetPlatformSpec   good_spec;
-    good_spec.m_extraRCParams = "/i";
-    good.m_platformSpecs["pc"] = good_spec;
-
-    // No Platform spec
-    AssetRecognizer     no_platform;
-    no_platform.m_name = "No Platform";
-    no_platform.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.ccc", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard);
-
-    // Duplicate
-    AssetRecognizer     duplicate(good.m_name, good.m_testLockSource, good.m_priority, good.m_isCritical, good.m_supportsCreateJobs, good.m_patternMatcher, good.m_version, good.m_productAssetType, good.m_outputProductDependencies);
-    duplicate.m_platformSpecs["pc"] = good_spec;
-
-    configuration.m_recognizerContainer["good"] = good;
-    configuration.m_recognizerContainer["no_platform"] = no_platform;
-    configuration.m_recognizerContainer["duplicate"] = duplicate;
-
-    bool initialization_result = test.Initialize(configuration);
-    ASSERT_TRUE(initialization_result);
-
-    ASSERT_EQ(mockRC->m_initialize, 1);
-
-    AZStd::vector<AssetBuilderSDK::PlatformInfo> platformInfos;
-    AZStd::unordered_set<AZStd::string> tags;
-    tags.insert("tools");
-    tags.insert("desktop");
-    platformInfos.emplace_back(AssetBuilderSDK::PlatformInfo("pc", tags));
-
-    InternalRecognizerPointerContainer  good_recognizers;
-    bool good_recognizers_found = test.GetMatchingRecognizers(platformInfos, "test.foo", good_recognizers);
-    ASSERT_TRUE(good_recognizers_found); // Should find at least 1
-    ASSERT_EQ(good_recognizers.size(), 1);  // 1, not 2 since the duplicates should be removed
-    ASSERT_EQ(good_recognizers.at(0)->m_name, good.m_name); // Match the same recognizer
-
-
-    InternalRecognizerPointerContainer  bad_recognizers;
-    bool no_recognizers_found = !test.GetMatchingRecognizers(platformInfos, "test.ccc", good_recognizers);
-    ASSERT_TRUE(no_recognizers_found);
-    ASSERT_EQ(bad_recognizers.size(), 0);  // 1, not 2 since the duplicates should be removed
-
-    ASSERT_EQ(m_errorAbsorber->m_numWarningsAbsorbed, 1); // this should be the "duplicate builder" warning.
-    ASSERT_EQ(m_errorAbsorber->m_numErrorsAbsorbed, 0);
-    ASSERT_EQ(m_errorAbsorber->m_numAssertsAbsorbed, 0);
-}
-
-
-TEST_F(RCBuilderTest, CreateJobs_CreateSingleJobStandard_Valid)
-{
-    MockRCCompiler*                     mockRC = new MockRCCompiler();
-    TestInternalRecognizerBasedBuilder  test(mockRC);
-
-    MockRecognizerConfiguration         configuration;
-
-    AssetRecognizer     good;
-    good.m_name = "Good";
-    good.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.foo", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard);
-    AssetPlatformSpec   good_spec;
-    good_spec.m_extraRCParams = "/i";
-    good.m_platformSpecs["pc"] = good_spec;
-    configuration.m_recognizerContainer["good"] = good;
-
-
-    bool initialization_result = test.Initialize(configuration);
-    ASSERT_TRUE(initialization_result);
-
-    AssetBuilderSDK::CreateJobsRequest  request;
-    AssetBuilderSDK::CreateJobsResponse response;
-
-    request.m_watchFolder = "c:\temp";
-    request.m_sourceFile = "test.foo";
-    request.m_enabledPlatforms = { AssetBuilderSDK::PlatformInfo("pc", { "desktop", "renderer" }) };
-    AssetProcessor::BUILDER_ID_RC.GetUuid(request.m_builderid);
-
-    test.CreateJobs(request, response);
-
-    ASSERT_EQ(response.m_result, AssetBuilderSDK::CreateJobsResultCode::Success);
-    ASSERT_EQ(response.m_createJobOutputs.size(), 1);
-
-    AssetBuilderSDK::JobDescriptor descriptor = response.m_createJobOutputs.at(0);
-    ASSERT_EQ(descriptor.GetPlatformIdentifier(), "pc");
-    ASSERT_FALSE(descriptor.m_critical);
-}
-
-TEST_F(RCBuilderTest, CreateJobs_CreateMultiplesJobStandard_Valid)
-{
-    MockRCCompiler*                     mockRC = new MockRCCompiler();
-    TestInternalRecognizerBasedBuilder  test(mockRC);
-
-    MockRecognizerConfiguration         configuration;
-
-    AssetRecognizer         standard_AR_RC;
-    const AZStd::string     job_key_rc = "RCjob";
-    {
-        standard_AR_RC.m_name = "RCjob";
-        standard_AR_RC.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.foo", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard);
-        AssetPlatformSpec   good_rc_spec;
-        good_rc_spec.m_extraRCParams = "/i";
-        standard_AR_RC.m_platformSpecs["pc"] = good_rc_spec;
-    }
-    configuration.m_recognizerContainer["rc_foo"] = standard_AR_RC;
-
-    AssetRecognizer         standard_AR_Copy;
-    const AZStd::string     job_key_copy = "Copyjob";
-    {
-        standard_AR_Copy.m_name = QString(job_key_copy.c_str());
-        standard_AR_Copy.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.foo", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard);
-        AssetPlatformSpec   good_copy_spec;
-        good_copy_spec.m_extraRCParams = "copy";
-        standard_AR_Copy.m_platformSpecs["pc"] = good_copy_spec;
-    }
-    configuration.m_recognizerContainer["copy_foo"] = standard_AR_Copy;
-
-
-    bool initialization_result = test.Initialize(configuration);
-    ASSERT_TRUE(initialization_result);
-
-    // Request is for the copy builder
-    {
-        AssetBuilderSDK::CreateJobsRequest  request_copy;
-        AssetBuilderSDK::CreateJobsResponse response_copy;
-
-        request_copy.m_watchFolder = "c:\temp";
-        request_copy.m_sourceFile = "test.foo";
-
-        request_copy.m_enabledPlatforms = { AssetBuilderSDK::PlatformInfo("pc",{ "desktop", "renderer" }) };
-        AssetProcessor::BUILDER_ID_COPY.GetUuid(request_copy.m_builderid);
-        test.CreateJobs(request_copy, response_copy);
-
-        ASSERT_EQ(response_copy.m_result, AssetBuilderSDK::CreateJobsResultCode::Success);
-        ASSERT_EQ(response_copy.m_createJobOutputs.size(), 1);
-
-        AssetBuilderSDK::JobDescriptor descriptor = response_copy.m_createJobOutputs.at(0);
-        ASSERT_EQ(descriptor.GetPlatformIdentifier(), "pc");
-        ASSERT_EQ(descriptor.m_jobKey.compare(job_key_copy), 0);
-        ASSERT_TRUE(descriptor.m_critical);
-    }
-
-    // Request is for the rc builder
-    {
-        AssetBuilderSDK::CreateJobsRequest  request_rc;
-        AssetBuilderSDK::CreateJobsResponse response_rc;
-
-        request_rc.m_watchFolder = "c:\temp";
-        request_rc.m_sourceFile = "test.foo";
-        request_rc.m_enabledPlatforms = { AssetBuilderSDK::PlatformInfo("pc",{ "desktop", "renderer" }) };
-        AssetProcessor::BUILDER_ID_RC.GetUuid(request_rc.m_builderid);
-        test.CreateJobs(request_rc, response_rc);
-
-        ASSERT_EQ(response_rc.m_result, AssetBuilderSDK::CreateJobsResultCode::Success);
-        ASSERT_EQ(response_rc.m_createJobOutputs.size(), 1);
-
-        AssetBuilderSDK::JobDescriptor descriptor = response_rc.m_createJobOutputs.at(0);
-        ASSERT_EQ(descriptor.GetPlatformIdentifier(), "pc");
-        ASSERT_EQ(descriptor.m_jobKey.compare(job_key_rc), 0);
-        ASSERT_FALSE(descriptor.m_critical);
-    }
-
-}
-
-TEST_F(RCBuilderTest, CreateJobs_CreateSingleJobCopy_Valid)
-{
-    MockRCCompiler*                     mockRC = new MockRCCompiler();
-    TestInternalRecognizerBasedBuilder  test(mockRC);
-
-    MockRecognizerConfiguration     configuration;
-
-    AssetRecognizer     copy;
-    copy.m_name = "Copy";
-    copy.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.copy", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard);
-    AssetPlatformSpec   copy_spec;
-    copy_spec.m_extraRCParams = "copy";
-    copy.m_platformSpecs["pc"] = copy_spec;
-    configuration.m_recognizerContainer["copy"] = copy;
-
-
-
-    bool initialization_result = test.Initialize(configuration);
-    ASSERT_TRUE(initialization_result);
-
-    AssetBuilderSDK::CreateJobsRequest  request;
-    AssetBuilderSDK::CreateJobsResponse response;
-
-    request.m_watchFolder = "c:\temp";
-    request.m_sourceFile = "test.copy";
-    request.m_enabledPlatforms = { AssetBuilderSDK::PlatformInfo("pc",{ "desktop", "renderer" }) };
-    AssetProcessor::BUILDER_ID_COPY.GetUuid(request.m_builderid);
-
-    test.CreateJobs(request, response);
-
-    ASSERT_EQ(response.m_result, AssetBuilderSDK::CreateJobsResultCode::Success);
-    ASSERT_EQ(response.m_createJobOutputs.size(), 1);
-
-    AssetBuilderSDK::JobDescriptor descriptor = response.m_createJobOutputs.at(0);
-    ASSERT_EQ(descriptor.GetPlatformIdentifier(), "pc");
-    ASSERT_TRUE(descriptor.m_critical);
-}
-
-TEST_F(RCBuilderTest, CreateJobs_CreateSingleJobStandardSkip_Valid)
-{
-    MockRCCompiler*                     mockRC = new MockRCCompiler();
-    TestInternalRecognizerBasedBuilder  test(mockRC);
-
-    MockRecognizerConfiguration     configuration;
-
-    {
-        AssetRecognizer     skip;
-        skip.m_name = "Skip";
-        skip.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.skip", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard);
-        AssetPlatformSpec   skip_spec;
-        skip_spec.m_extraRCParams = "skip";
-        skip.m_platformSpecs["pc"] = skip_spec;
-        configuration.m_recognizerContainer["skip"] = skip;
-    }
-
-
-    bool initialization_result = test.Initialize(configuration);
-    ASSERT_TRUE(initialization_result);
-
-    AssetBuilderSDK::CreateJobsRequest  request;
-    AssetBuilderSDK::CreateJobsResponse response;
-
-    request.m_watchFolder = "c:\temp";
-    request.m_sourceFile = "test.skip";
-    request.m_enabledPlatforms = { AssetBuilderSDK::PlatformInfo("pc",{ "desktop", "renderer" }) };
-    request.m_builderid = this->GetBuilderUUID();
-
-    test.CreateJobs(request, response);
-
-    ASSERT_EQ(response.m_result, AssetBuilderSDK::CreateJobsResultCode::Success);
-    ASSERT_EQ(response.m_createJobOutputs.size(), 0);
-}
-
-
-TEST_F(RCBuilderTest, CreateJobs_CreateSingleJobStandard_Failed)
-{
-    MockRCCompiler*                     mockRC = new MockRCCompiler();
-    TestInternalRecognizerBasedBuilder  test(mockRC);
-
-    MockRecognizerConfiguration     configuration;
-
-    AssetRecognizer     good;
-    good.m_name = "Good";
-    good.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.foo", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard);
-    AssetPlatformSpec   good_spec;
-    good_spec.m_extraRCParams = "/i";
-    good.m_platformSpecs["pc"] = good_spec;
-    configuration.m_recognizerContainer["good"] = good;
-
-
-    bool initialization_result = test.Initialize(configuration);
-    ASSERT_TRUE(initialization_result);
-
-    AssetBuilderSDK::CreateJobsRequest  request;
-    AssetBuilderSDK::CreateJobsResponse response;
-
-    request.m_watchFolder = "c:\temp";
-    request.m_sourceFile = "test.ccc";
-    request.m_enabledPlatforms = { AssetBuilderSDK::PlatformInfo("pc",{ "desktop", "renderer" }) };
-    request.m_builderid = this->GetBuilderUUID();
-
-    test.CreateJobs(request, response);
-
-    ASSERT_EQ(response.m_result, AssetBuilderSDK::CreateJobsResultCode::Failed);
-    ASSERT_EQ(response.m_createJobOutputs.size(), 0);
-}
-
-TEST_F(RCBuilderTest, CreateJobs_CreateSingleJobStandard_ShuttingDown)
-{
-    MockRCCompiler*                     mockRC = new MockRCCompiler();
-    TestInternalRecognizerBasedBuilder  test(mockRC);
-
-    MockRecognizerConfiguration     configuration;
-
-    AssetRecognizer     good;
-    good.m_name = "Good";
-    good.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.foo", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard);
-    AssetPlatformSpec   good_spec;
-    good_spec.m_extraRCParams = "/i";
-    good.m_platformSpecs["pc"] = good_spec;
-    configuration.m_recognizerContainer["good"] = good;
-
-
-    bool initialization_result = test.Initialize(configuration);
-    ASSERT_TRUE(initialization_result);
-
-    test.ShutDown();
-
-    AssetBuilderSDK::CreateJobsRequest  request;
-    AssetBuilderSDK::CreateJobsResponse response;
-
-    request.m_watchFolder = "c:\temp";
-    request.m_sourceFile = "test.ccc";
-    request.m_enabledPlatforms = { AssetBuilderSDK::PlatformInfo("pc",{ "desktop", "renderer" }) };
-    request.m_builderid = this->GetBuilderUUID();
-
-    test.CreateJobs(request, response);
-
-    ASSERT_EQ(response.m_result, AssetBuilderSDK::CreateJobsResultCode::ShuttingDown);
-    ASSERT_EQ(response.m_createJobOutputs.size(), 0);
-}
-
-TEST_F(RCBuilderTest, CreateJobs_CreateSingleJobBadJobRequest1_Failed)
-{
-    MockRCCompiler*                     mockRC = new MockRCCompiler();
-    TestInternalRecognizerBasedBuilder  test(mockRC);
-
-    MockRecognizerConfiguration     configuration;
-
-    AssetRecognizer     good;
-    good.m_name = "Good";
-    good.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.foo", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard);
-    AssetPlatformSpec   good_spec;
-    good_spec.m_extraRCParams = "/i";
-    good.m_platformSpecs["pc"] = good_spec;
-    configuration.m_recognizerContainer["good"] = good;
-
-
-    bool initialization_result = test.Initialize(configuration);
-    ASSERT_TRUE(initialization_result);
-
-    AssetBuilderSDK::CreateJobsRequest  request;
-    AssetBuilderSDK::CreateJobsResponse response;
-
-    request.m_sourceFile = "test.ccc";
-    request.m_enabledPlatforms = { AssetBuilderSDK::PlatformInfo("pc",{ "desktop", "renderer" }) };
-    request.m_builderid = this->GetBuilderUUID();
-
-    test.CreateJobs(request, response);
-
-    ASSERT_EQ(response.m_result, AssetBuilderSDK::CreateJobsResultCode::Failed);
-    ASSERT_EQ(response.m_createJobOutputs.size(), 0);
-}
-
-TEST_F(RCBuilderTest, ProcessLegacyRCJob_ProcessStandardSingleJob_Failed)
-{
-    AZ::Uuid                            assetTypeUUid = AZ::Uuid::CreateRandom();
-    MockRCCompiler*                     mockRC = new MockRCCompiler();
-    TestInternalRecognizerBasedBuilder  test(mockRC);
-
-    MockRecognizerConfiguration         configuration;
-
-    // Create the test job
-    AssetBuilderSDK::ProcessJobRequest request = CreateTestJobRequest("file.c", false, "pc", 1);
-
-    bool initialization_result = test.Initialize(configuration);
-    ASSERT_TRUE(initialization_result);
-
-    // Case 1: execution failed
-    mockRC->SetResultResultExecute(NativeLegacyRCCompiler::Result(1, true, ""));
-    mockRC->SetResultExecute(false);
-    AssetBuilderSDK::ProcessJobResponse responseCrashed;
-    AssetBuilderSDK::JobCancelListener jobCancelListener(request.m_jobId);
-    test.TestProcessLegacyRCJob(request, "/i", assetTypeUUid, jobCancelListener, responseCrashed);
-    ASSERT_EQ(responseCrashed.m_resultCode, AssetBuilderSDK::ProcessJobResultCode::ProcessJobResult_Crashed);
-
-    // case 2: result code from execution non-zero
-    mockRC->SetResultExecute(true);
-    mockRC->SetResultResultExecute(NativeLegacyRCCompiler::Result(1, false, ""));
-    AssetBuilderSDK::ProcessJobResponse responseFailed;
-    test.TestProcessLegacyRCJob(request, "/i", assetTypeUUid, jobCancelListener, responseFailed);
-    ASSERT_EQ(responseFailed.m_resultCode, AssetBuilderSDK::ProcessJobResultCode::ProcessJobResult_Failed);
-}
-
-
-TEST_F(RCBuilderTest, ProcessLegacyRCJob_ProcessStandardSingleJob_Valid)
-{
-    AZ::Uuid                            assetTypeUUid = AZ::Uuid::CreateRandom();
-    MockRCCompiler*                     mockRC = new MockRCCompiler();
-    TestInternalRecognizerBasedBuilder  test(mockRC);
-
-    MockRecognizerConfiguration         configuration;
-
-    // Create the test job
-    AssetBuilderSDK::ProcessJobRequest request = CreateTestJobRequest("file.c", false, "pc");
-
-
-    test.AddTestFileInfo("c:\\temp\\file.a").AddTestFileInfo("c:\\temp\\file.b");
-
-
-    bool initialization_result = test.Initialize(configuration);
-    ASSERT_TRUE(initialization_result);
-
-    mockRC->SetResultResultExecute(NativeLegacyRCCompiler::Result(0, false, "c:\\temp"));
-    mockRC->SetResultExecute(true);
-    AssetBuilderSDK::ProcessJobResponse response;
-    AssetBuilderSDK::JobCancelListener jobCancelListener(request.m_jobId);
-    test.TestProcessLegacyRCJob(request, "/i", assetTypeUUid, jobCancelListener, response);
-    ASSERT_EQ(response.m_resultCode, AssetBuilderSDK::ProcessJobResultCode::ProcessJobResult_Success);
-
-    ASSERT_EQ(response.m_outputProducts.size(), 2); // file.c->(file.a, file.b)
-}
-
-
-TEST_F(RCBuilderTest, ProcessLegacyRCJob_ProcessCopySingleJob_Valid)
-{
-    AZStd::string                       name = "test";
-    AZ::Uuid                            assetTypeUUid = AZ::Uuid::CreateRandom();
-    MockRCCompiler*                     mockRC = new MockRCCompiler();
-    TestInternalRecognizerBasedBuilder  test(mockRC);
-
-    MockRecognizerConfiguration         configuration;
-
-    // Create the test job
-    AssetBuilderSDK::ProcessJobRequest request = CreateTestJobRequest("file.c", false, "pc");
-
-    bool initialization_result = test.Initialize(configuration);
-    ASSERT_TRUE(initialization_result);
-
-    mockRC->SetResultResultExecute(NativeLegacyRCCompiler::Result(0, false, "c:\\temp"));
-    mockRC->SetResultExecute(true);
-    AssetBuilderSDK::ProcessJobResponse response;
-    AssetBuilderSDK::JobCancelListener jobCancelListener(request.m_jobId);
-    test.TestProcessCopyJob(request, assetTypeUUid, jobCancelListener, response);
-    ASSERT_EQ(response.m_resultCode, AssetBuilderSDK::ProcessJobResultCode::ProcessJobResult_Success);
-
-    ASSERT_EQ(response.m_outputProducts.size(), 1); // file.c->(file.a, file.b)
-    AssetBuilderSDK::JobProduct resultJobProd = response.m_outputProducts.at(0);
-    ASSERT_EQ(resultJobProd.m_productAssetType, assetTypeUUid);
-    ASSERT_EQ(resultJobProd.m_productFileName, request.m_fullPath);
-}
-
-
 TEST_F(RCBuilderTest, MatchTempFileToSkip_SkipRCFiles_true)
 {
     const char* rc_skip_fileNames[] = {
@@ -501,281 +37,6 @@ TEST_F(RCBuilderTest, MatchTempFileToSkip_SkipRCFiles_false)
     }
 }
 
-
-
-TEST_F(RCBuilderTest, ProcessJob_ProcessStandardRCSingleJob_Valid)
-{
-    MockRCCompiler*                     mockRC = new MockRCCompiler();
-    TestInternalRecognizerBasedBuilder  test(mockRC);
-    MockRecognizerConfiguration         configuration;
-
-
-    // Create a dummy test recognizer
-    AZ::u32 recID = test.AddTestRecognizer(this->GetBuilderID(),QString("/i"), "pc");
-
-    // Create the test job
-    AssetBuilderSDK::ProcessJobRequest request = CreateTestJobRequest("test.tif", false, "pc");
-    request.m_jobDescription.m_jobParameters[recID] = "/i";
-
-
-    test.AddTestFileInfo("c:\\temp\\file.a").AddTestFileInfo("c:\\temp\\file.b");
-
-    bool initialization_result = test.Initialize(configuration);
-    ASSERT_TRUE(initialization_result);
-
-    mockRC->SetResultResultExecute(NativeLegacyRCCompiler::Result(0, false, "c:\\temp"));
-    mockRC->SetResultExecute(true);
-    AssetBuilderSDK::ProcessJobResponse response;
-    test.TestProcessJob(request, response);
-    ASSERT_EQ(response.m_resultCode, AssetBuilderSDK::ProcessJobResultCode::ProcessJobResult_Success);
-    ASSERT_EQ(response.m_outputProducts.size(), 2); // file.c->(file.a, file.b)
-}
-
-
-TEST_F(RCBuilderTest, ProcessJob_ProcessStandardRCSingleJob_Failed)
-{
-    MockRCCompiler*                     mockRC = new MockRCCompiler();
-    TestInternalRecognizerBasedBuilder  test(mockRC);
-    MockRecognizerConfiguration         configuration;
-
-
-    // Create a dummy test recognizer
-    AZ::u32 recID = test.AddTestRecognizer(this->GetBuilderID(), QString("/i"), "pc");
-
-    // Create the test job
-    AssetBuilderSDK::ProcessJobRequest request = CreateTestJobRequest("test.tif", false, "pc");
-    request.m_jobDescription.m_jobParameters[recID] = "/i";
-
-    test.AddTestFileInfo("c:\\temp\\file.a").AddTestFileInfo("c:\\temp\\file.b");
-
-    bool initialization_result = test.Initialize(configuration);
-    ASSERT_TRUE(initialization_result);
-
-    mockRC->SetResultResultExecute(NativeLegacyRCCompiler::Result(1, false, "c:\\temp"));
-    mockRC->SetResultExecute(true);
-    AssetBuilderSDK::ProcessJobResponse response;
-    test.TestProcessJob(request, response);
-    ASSERT_EQ(response.m_resultCode, AssetBuilderSDK::ProcessJobResultCode::ProcessJobResult_Failed);
-}
-
-
-TEST_F(RCBuilderTest, ProcessJob_ProcessStandardCopySingleJob_Valid)
-{
-    MockRCCompiler*                     mockRC = new MockRCCompiler();
-    TestInternalRecognizerBasedBuilder  test(mockRC);
-    MockRecognizerConfiguration         configuration;
-
-
-    // Create a dummy test recognizer
-    AZ::u32 recID = test.AddTestRecognizer(this->GetBuilderID(), QString("copy"), "pc");
-
-    // Create the test job
-    AssetBuilderSDK::ProcessJobRequest request = CreateTestJobRequest("test.tif", true, "pc");
-    request.m_jobDescription.m_jobParameters[recID] = "copy";
-
-    bool initialization_result = test.Initialize(configuration);
-    ASSERT_TRUE(initialization_result);
-
-    mockRC->SetResultExecute(true);
-    AssetBuilderSDK::ProcessJobResponse response;
-    test.TestProcessJob(request, response);
-    ASSERT_EQ(response.m_resultCode, AssetBuilderSDK::ProcessJobResultCode::ProcessJobResult_Success);
-    ASSERT_EQ(response.m_outputProducts.size(), 1); // test.
-    ASSERT_TRUE(response.m_outputProducts[0].m_productFileName.find("test.tif") != AZStd::string::npos);
-}
-
-TEST_F(RCBuilderTest, ProcessJob_ProcessStandardSkippedSingleJob_Invalid)
-{
-    MockRCCompiler*                     mockRC = new MockRCCompiler();
-    TestInternalRecognizerBasedBuilder  test(mockRC);
-    MockRecognizerConfiguration         configuration;
-
-
-    // Create a dummy test recognizer
-    AZ::u32 recID = test.AddTestRecognizer(this->GetBuilderID(), QString("skip"), "pc");
-
-    // Create the test job
-    AssetBuilderSDK::ProcessJobRequest request = CreateTestJobRequest("test.tif", true, "pc");
-    request.m_jobDescription.m_jobParameters[recID] = "copy";
-
-    bool initialization_result = test.Initialize(configuration);
-    ASSERT_TRUE(initialization_result);
-
-    mockRC->SetResultExecute(true);
-    AssetBuilderSDK::ProcessJobResponse response;
-    test.TestProcessJob(request, response);
-    ASSERT_EQ(response.m_resultCode, AssetBuilderSDK::ProcessJobResultCode::ProcessJobResult_Failed);
-}
-
-
-TEST_F(RCBuilderTest, TestProcessRCResultFolder_LegacySystem)
-{
-    TestInternalRecognizerBasedBuilder  test(new MockRCCompiler());
-    test.AddTestFileInfo("file.dds")
-        .AddTestFileInfo("file.caf")
-        .AddTestFileInfo("file.png")
-        .AddTestFileInfo("rc_createdfiles.txt")
-        .AddTestFileInfo("rc_log.log")
-        .AddTestFileInfo("rc_log_warnings.log")
-        .AddTestFileInfo("rc_log_errors.log")
-        .AddTestFileInfo("ProcessJobRequest.xml")
-        .AddTestFileInfo("ProcessJobResponse.xml");
-
-    AZ::Uuid productGUID = AZ::Uuid("{60554E3C-D8D5-4429-AC77-740F0ED46193}");
-
-    AssetBuilderSDK::ProcessJobResponse response;
-    test.TestProcessRCResultFolder("c:\\temp", productGUID, false, response);
-
-    ASSERT_EQ(response.m_resultCode, AssetBuilderSDK::ProcessJobResultCode::ProcessJobResult_Success);
-
-    // we expect it to have ignored most of the file cruft.
-    ASSERT_EQ(response.m_outputProducts.size(), 3);
-
-    AZStd::string fileJoined;
-
-    AzFramework::StringFunc::Path::Join("c:\\temp", "file.dds", fileJoined);
-    ASSERT_EQ(response.m_outputProducts[0].m_productFileName, fileJoined);
-    ASSERT_EQ(response.m_outputProducts[0].m_productAssetType, productGUID);
-    ASSERT_EQ(response.m_outputProducts[0].m_productSubID, 0x00000000);
-    ASSERT_EQ(response.m_outputProducts[0].m_legacySubIDs.size(), 0);
-
-    AzFramework::StringFunc::Path::Join("c:\\temp", "file.caf", fileJoined);
-    ASSERT_EQ(response.m_outputProducts[1].m_productFileName, fileJoined);
-    ASSERT_EQ(response.m_outputProducts[1].m_productAssetType, productGUID);
-    ASSERT_EQ(response.m_outputProducts[1].m_productSubID, (AZ_CRC("file.caf", 0x91277b80) & 0x0000FFFF)); // legacy subids are just the lower 16 bits of the crc of filename.
-    ASSERT_EQ(response.m_outputProducts[1].m_legacySubIDs.size(), 0);
-    
-    AzFramework::StringFunc::Path::Join("c:\\temp", "file.png", fileJoined);
-    ASSERT_EQ(response.m_outputProducts[2].m_productFileName, fileJoined);
-    ASSERT_EQ(response.m_outputProducts[2].m_productAssetType, productGUID);
-    ASSERT_EQ(response.m_outputProducts[2].m_productSubID, (AZ_CRC("file.png", 0x7fd84af0) & 0x0000FFFF));
-    ASSERT_EQ(response.m_outputProducts[2].m_legacySubIDs.size(), 0);
-}
-
-TEST_F(RCBuilderTest, TestProcessRCResultFolder_Fail_Fail)
-{
-    TestInternalRecognizerBasedBuilder  test(new MockRCCompiler());
-    AssetBuilderSDK::ProcessJobResponse response;
-    response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
-    
-    AZ::Uuid productGUID = AZ::Uuid("{60554E3C-D8D5-4429-AC77-740F0ED46193}");
-    test.TestProcessRCResultFolder("c:\\temp", productGUID, true, response);
-    ASSERT_EQ(response.m_resultCode, AssetBuilderSDK::ProcessJobResult_Failed);
-}
-
-TEST_F(RCBuilderTest, TestProcessRCResultFolder_Succeed_NothingBuilt)
-{
-    TestInternalRecognizerBasedBuilder  test(new MockRCCompiler());
-    AssetBuilderSDK::ProcessJobResponse response;
-    response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
-    
-    AZ::Uuid productGUID = AZ::Uuid("{60554E3C-D8D5-4429-AC77-740F0ED46193}");
-    test.TestProcessRCResultFolder("c:\\temp", productGUID, true, response);
-    ASSERT_EQ(response.m_resultCode, AssetBuilderSDK::ProcessJobResult_Success);
-}
-
-TEST_F(RCBuilderTest, TestProcessRCResultFolder_Fail_BadName)
-{
-    TestInternalRecognizerBasedBuilder  test(new MockRCCompiler());
-    AssetBuilderSDK::ProcessJobResponse response;
-    response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
-
-    AZ::Uuid productGUID = AZ::Uuid("{60554E3C-D8D5-4429-AC77-740F0ED46193}");
-    // note: empty name on next line
-    response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct("", productGUID, 1234));
-    test.TestProcessRCResultFolder("c:\\temp", productGUID, true, response);
-    ASSERT_EQ(response.m_resultCode, AssetBuilderSDK::ProcessJobResult_Failed);
-}
-
-TEST_F(RCBuilderTest, TestProcessRCResultFolder_Fail_DuplicateFile)
-{
-    m_errorAbsorber->m_debugMessages = true;
-    TestInternalRecognizerBasedBuilder  test(new MockRCCompiler());
-    AssetBuilderSDK::ProcessJobResponse response;
-    test.AddTestFileInfo("file.dds");
-    AZ::Uuid productGUID = AZ::Uuid("{60554E3C-D8D5-4429-AC77-740F0ED46193}");
-    response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
-    response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct("file.dds", productGUID, 1234));
-    response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct("file.dds", productGUID, 5679));
-    test.TestProcessRCResultFolder("c:\\temp", productGUID, true, response);
-    m_errorAbsorber->AssertErrors(1);
-    ASSERT_EQ(response.m_resultCode, AssetBuilderSDK::ProcessJobResult_Failed);
-}
-
-TEST_F(RCBuilderTest, TestProcessRCResultFolder_Fail_DuplicateSubID)
-{
-    TestInternalRecognizerBasedBuilder  test(new MockRCCompiler());
-    AssetBuilderSDK::ProcessJobResponse response;
-    test.AddTestFileInfo("file.dds")
-        .AddTestFileInfo("file.caf");
-    AZ::Uuid productGUID = AZ::Uuid("{60554E3C-D8D5-4429-AC77-740F0ED46193}");
-
-    response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
-    response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct("file.dds", productGUID, 1234));
-    response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct("file.caf", productGUID, 1234));
-    test.TestProcessRCResultFolder("c:\\temp", productGUID, true, response);
-    ASSERT_EQ(m_errorAbsorber->m_numErrorsAbsorbed, 1);
-    ASSERT_EQ(response.m_resultCode, AssetBuilderSDK::ProcessJobResult_Failed);
-}
-
-
-TEST_F(RCBuilderTest, TestProcessRCResultFolder_WithResponseFromRC)
-{
-    TestInternalRecognizerBasedBuilder  test(new MockRCCompiler());
-    test.AddTestFileInfo("file.dds")
-        .AddTestFileInfo("file.caf")
-        .AddTestFileInfo("file.png")
-        .AddTestFileInfo("rc_createdfiles.txt")
-        .AddTestFileInfo("rc_log.log")
-        .AddTestFileInfo("rc_log_warnings.log")
-        .AddTestFileInfo("rc_log_errors.log")
-        .AddTestFileInfo("ProcessJobRequest.xml")
-        .AddTestFileInfo("ProcessJobResponse.xml");
-
-    AZ::Uuid productGUID = AZ::Uuid::CreateNull(); // this is to make sure that it doesn't matter what we pass in
-    AZ::Uuid actualGUID = AZ::Uuid("{60554E3C-D8D5-4429-AC77-740F0ED46193}");
-
-    AssetBuilderSDK::ProcessJobResponse response;
-
-    response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct("file.dds", actualGUID, 1234));
-    response.m_outputProducts.back().m_legacySubIDs.push_back(3333);
-    response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct("file.caf", actualGUID, 3456));
-    response.m_outputProducts.back().m_legacySubIDs.push_back(2222);
-    response.m_outputProducts.back().m_legacySubIDs.push_back((AZ_CRC("file.caf", 0x91277b80) & 0x0000FFFF)); // push back the existing one to make sure no dupes.
-    response.m_resultCode = AssetBuilderSDK::ProcessJobResultCode::ProcessJobResult_Success;
-
-    // in this test we pretend the response was actually populated by the builder and make sure it populates the legacy IDs correctly
-    // 1. there should actually BE legacy IDs
-    // 2. there should be no duplicate IDs (legacy IDs should not duplicate ACTUAL ids)
-    // 3. there should be no duplicate Legacy IDs (legacy IDs should not duplicate each other)
-    // 4. if we provide legacy Ids, they should be used in addition to the automatic ones.
-
-    test.TestProcessRCResultFolder("c:\\temp", productGUID, true, response);
-
-    ASSERT_EQ(response.m_resultCode, AssetBuilderSDK::ProcessJobResultCode::ProcessJobResult_Success);
-
-    // we expect it to only have accepted the products we specified.
-    ASSERT_EQ(response.m_outputProducts.size(), 2);
-
-    AZStd::string fileJoined;
-
-    AzFramework::StringFunc::Path::Join("c:\\temp", "file.dds", fileJoined);
-    ASSERT_EQ(response.m_outputProducts[0].m_productFileName, fileJoined);
-    ASSERT_EQ(response.m_outputProducts[0].m_productAssetType, actualGUID);
-    ASSERT_EQ(response.m_outputProducts[0].m_productSubID, 1234);
-    ASSERT_EQ(response.m_outputProducts[0].m_legacySubIDs.size(), 2); // it must include our new one AND the zero that it would have generated before.
-    ASSERT_EQ(response.m_outputProducts[0].m_legacySubIDs[0], 3333);
-    ASSERT_EQ(response.m_outputProducts[0].m_legacySubIDs[1], 0);
-
-    AzFramework::StringFunc::Path::Join("c:\\temp", "file.caf", fileJoined);
-    ASSERT_EQ(response.m_outputProducts[1].m_productFileName, fileJoined);
-    ASSERT_EQ(response.m_outputProducts[1].m_productAssetType, actualGUID);
-    ASSERT_EQ(response.m_outputProducts[1].m_productSubID, 3456); // legacy subids are just the lower 16 bits of the crc of filename.
-    ASSERT_EQ(response.m_outputProducts[1].m_legacySubIDs.size(), 2); // we only expect the one legacy, no dupes!
-    ASSERT_EQ(response.m_outputProducts[1].m_legacySubIDs[0], 2222);
-    ASSERT_EQ(response.m_outputProducts[1].m_legacySubIDs[1], (AZ_CRC("file.caf", 0x91277b80) & 0x0000FFFF));
-}
-
 class MockBuilderListener : public AssetBuilderSDK::AssetBuilderBus::Handler
 {
 public:
@@ -799,15 +60,13 @@ public:
     // A utility function which feeds in the version and asset type to the builder, fingerprints it, and returns the fingerprint
     AZStd::string BuildFingerprint(int versionNumber, AZ::Uuid builderProductType)
     {
-        MockRCCompiler*                     mockRC = new MockRCCompiler();
-        TestInternalRecognizerBasedBuilder  test(mockRC);
-
-        MockRecognizerConfiguration         configuration;
+        TestInternalRecognizerBasedBuilder test;
+        MockRecognizerConfiguration configuration;
 
         AssetPlatformSpec   good_spec;
         good_spec.m_extraRCParams = "/i";
 
-        AssetRecognizer     good;
+        AssetRecognizer good;
         good.m_name = "Good";
         good.m_version = static_cast<char>(versionNumber);
         good.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.foo", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard);

+ 6 - 66
Code/Tools/AssetProcessor/native/tests/resourcecompiler/RCBuilderTest.h

@@ -21,63 +21,6 @@ extern const BuilderIdAndName BUILDER_ID_COPY;
 extern const BuilderIdAndName BUILDER_ID_RC;
 extern const BuilderIdAndName BUILDER_ID_SKIP;
 
-class MockRCCompiler
-    : public AssetProcessor::RCCompiler
-{
-public:
-    MockRCCompiler()
-        : m_executeResultResult(0, false, "c:\temp")
-    {
-    }
-
-    bool Initialize() override
-    {
-        m_initialize++;
-        return m_initializeResult;
-    }
-    bool Execute([[maybe_unused]] const QString& inputFile, [[maybe_unused]] const QString& watchFolder, [[maybe_unused]] const QString& platformIdentifier, [[maybe_unused]] const QString& params, [[maybe_unused]] const QString& dest, 
-        [[maybe_unused]] const AssetBuilderSDK::JobCancelListener* jobCancelListener, Result& result) const override
-    {
-        m_execute++;
-        result = m_executeResultResult;
-        return m_executeResult;
-    }
-    void RequestQuit() override
-    {
-        m_request_quit++;
-    }
-
-    void ResetCounters()
-    {
-        this->m_initialize = 0;
-        this->m_execute = 0;
-        this->m_request_quit = 0;
-    }
-
-    void SetResultInitialize(bool result)
-    {
-        m_initializeResult = result;
-    }
-
-    void SetResultExecute(bool result)
-    {
-        m_executeResult = result;
-    }
-
-    void SetResultResultExecute(Result result)
-    {
-        m_executeResultResult = result;
-    }
-
-    bool m_initializeResult = true;
-    bool m_executeResult = true;
-    Result m_executeResultResult;
-    mutable int m_initialize = 0;
-    mutable int m_execute = 0;
-    mutable int m_request_quit = 0;
-};
-
-
 struct MockRecognizerConfiguration
     : public RecognizerConfiguration
 {
@@ -87,6 +30,12 @@ struct MockRecognizerConfiguration
     {
         return m_recognizerContainer;
     }
+
+    const RecognizerContainer& GetAssetCacheRecognizerContainer() const override
+    {
+        return m_recognizerContainer;
+    }
+
     const ExcludeRecognizerContainer& GetExcludeAssetRecognizerContainer() const override
     {
         return m_excludeContainer;
@@ -99,15 +48,6 @@ struct MockRecognizerConfiguration
 struct TestInternalRecognizerBasedBuilder
     : public InternalRecognizerBasedBuilder
 {
-    TestInternalRecognizerBasedBuilder(RCCompiler* rcCompiler = nullptr)
-        : InternalRecognizerBasedBuilder()
-    {
-        if (rcCompiler != nullptr)
-        {
-            m_rcCompiler.reset(rcCompiler);
-        }
-    }
-
     bool FindRC([[maybe_unused]] QString& rcPathOut) override
     {
         return true;

+ 218 - 0
Code/Tools/AssetProcessor/native/unittests/AssetCacheServerUnitTests.cpp

@@ -0,0 +1,218 @@
+/*
+ * 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 <AzCore/Serialization/Json/JsonSerializationSettings.h>
+#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
+#include <AzCore/Settings/SettingsRegistryVisitorUtils.h>
+#include <AzCore/UnitTest/Mocks/MockSettingsRegistry.h>
+#include <AzCore/UnitTest/TestTypes.h>
+#include <AzTest/Utils.h>
+#include <AzToolsFramework/Archive/ArchiveAPI.h>
+#include <native/resourcecompiler/rcjob.h>
+#include <native/utilities/AssetServerHandler.h>
+#include <native/utilities/assetUtils.h>
+#include <native/unittests/UnitTestRunner.h>
+#include <QStandardPaths>
+#include <QDir>
+
+namespace UnitTest
+{
+    struct MockArchiveCommandsBusHandler
+        : public AzToolsFramework::ArchiveCommandsBus::Handler
+    {
+        MockArchiveCommandsBusHandler()
+        {
+            AzToolsFramework::ArchiveCommandsBus::Handler::BusConnect();
+        }
+
+        ~MockArchiveCommandsBusHandler()
+        {
+            AzToolsFramework::ArchiveCommandsBus::Handler::BusDisconnect();
+        }
+
+        MOCK_METHOD2(CreateArchive, std::future<bool>(const AZStd::string&, const AZStd::string&));
+        MOCK_METHOD2(ExtractArchive, std::future<bool>(const AZStd::string&, const AZStd::string&));
+        MOCK_METHOD3(ExtractFile, std::future<bool>(const AZStd::string&, const AZStd::string&, const AZStd::string&));
+        MOCK_METHOD2(ListFilesInArchive, bool(const AZStd::string&, AZStd::vector<AZStd::string>&));
+        MOCK_METHOD3(AddFileToArchive, std::future<bool>(const AZStd::string&, const AZStd::string&, const AZStd::string&));
+        MOCK_METHOD3(AddFilesToArchive, std::future<bool>(const AZStd::string&, const AZStd::string&, const AZStd::string&));
+    };
+
+    class AssetServerHandlerUnitTest
+        : public AllocatorsFixture
+    {
+
+    public:
+        AssetServerHandlerUnitTest()
+        {
+            AZ::SettingsRegistry::Register(&m_mockSettingsRegistry);
+            m_tempFolder = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
+            m_fakeSourceFile = QString(m_tempFolder + m_fakeFullname + ".zip");
+            AssetUtilities::ResetServerAddress();
+            AssetUtilities::ResetServerMode();
+        }
+
+        ~AssetServerHandlerUnitTest()
+        {
+            RemoveMockAssetArchive();
+            AssetUtilities::ResetServerAddress();
+            AssetUtilities::ResetServerMode();
+            AZ::SettingsRegistry::Unregister(&m_mockSettingsRegistry);
+        }
+
+        void MockSettingsRegistry()
+        {
+            auto getString = [this](AZStd::string& result, AZStd::string_view input) -> bool
+            {
+                if (input == "/Amazon/AssetProcessor/Settings/Server/cacheServerAddress")
+                {
+                    result = this->m_tempFolder.toUtf8().toStdString().c_str();
+                }
+                return true;
+            };
+            ON_CALL(m_mockSettingsRegistry, Get(::testing::An<AZStd::string&>(), ::testing::_)).WillByDefault(getString);
+
+            auto getBool = [this](bool& result, AZStd::string_view input) -> bool
+            {
+                if (input == "/Amazon/AssetProcessor/Settings/Server/enableCacheServer")
+                {
+                    result = this->m_enableServer;
+                }
+                return true;
+            };
+            ON_CALL(m_mockSettingsRegistry, Get(::testing::An<bool&>(), ::testing::_)).WillByDefault(getBool);
+        }
+
+        void CreateMockAssetArchive()
+        {
+            QFileInfo fileInfo(m_fakeSourceFile);
+            if (!fileInfo.absoluteDir().exists())
+            {
+                EXPECT_TRUE(fileInfo.absoluteDir().mkpath("."));
+            }
+            QFile tempFile(m_fakeSourceFile);
+            tempFile.open(QIODevice::OpenModeFlag::NewOnly);
+        }
+
+        void RemoveMockAssetArchive()
+        {
+            QFile tempFile(m_fakeSourceFile);
+            tempFile.remove();
+        }
+
+        UnitTestUtils::AssertAbsorber m_assertAbsorber;
+        AZ::MockSettingsRegistry m_mockSettingsRegistry;
+        MockArchiveCommandsBusHandler m_mockArchiveCommandsBusHandler;
+        QString m_tempFolder;
+        QString m_fakeSourceFile;
+        bool m_enableServer = false;
+        const char* m_fakeFullname = "/mock_cache/asset_server_key";
+        const char* m_fakeFilename = "asset_server_key";
+    };
+
+    TEST_F(AssetServerHandlerUnitTest, AssetCacheServer_UnConfiguredToRunAsServer_SetsFalse)
+    {
+        EXPECT_CALL(m_mockSettingsRegistry, Get(::testing::An<AZStd::string&>(), ::testing::_)).Times(1);
+
+        AssetProcessor::AssetServerHandler assetServerHandler;
+        EXPECT_FALSE(assetServerHandler.IsServerAddressValid());
+    }
+
+    TEST_F(AssetServerHandlerUnitTest, AssetCacheServer_ConfiguredToRunAsServer_Works)
+    {
+        m_enableServer = true;
+        MockSettingsRegistry();
+
+        EXPECT_CALL(m_mockSettingsRegistry, Get(::testing::An<AZStd::string&>(), ::testing::_)).Times(1);
+        EXPECT_CALL(m_mockSettingsRegistry, Get(::testing::An<bool&>(), ::testing::_)).Times(1);
+
+        AssetProcessor::AssetServerHandler assetServerHandler;
+        EXPECT_TRUE(assetServerHandler.IsServerAddressValid());
+        EXPECT_TRUE(AssetUtilities::InServerMode());
+    }
+
+    TEST_F(AssetServerHandlerUnitTest, AssetCacheServer_ConfiguredToRunAsClient_Works)
+    {
+        m_enableServer = false;
+        MockSettingsRegistry();
+
+        EXPECT_CALL(m_mockSettingsRegistry, Get(::testing::An<AZStd::string&>(), ::testing::_)).Times(1);
+        EXPECT_CALL(m_mockSettingsRegistry, Get(::testing::An<bool&>(), ::testing::_)).Times(1);
+
+        AssetProcessor::AssetServerHandler assetServerHandler;
+        EXPECT_TRUE(assetServerHandler.IsServerAddressValid());
+        EXPECT_FALSE(AssetUtilities::InServerMode());
+    }
+
+    TEST_F(AssetServerHandlerUnitTest, AssetCacheServer_ServerStoresZipFile_Works)
+    {
+        m_enableServer = true;
+        MockSettingsRegistry();
+        RemoveMockAssetArchive();
+
+        auto createArchive = [this](const AZStd::string& archivePath, const AZStd::string&) -> std::future<bool>
+        {
+            AZStd::string targetFilename = AZStd::string(this->m_fakeFilename) + ".zip";
+            EXPECT_TRUE(archivePath.ends_with(targetFilename));
+            return std::async(std::launch::async, [] { return true; });
+        };
+        ON_CALL(m_mockArchiveCommandsBusHandler, CreateArchive(::testing::_, ::testing::_)).WillByDefault(createArchive);
+
+        EXPECT_CALL(m_mockSettingsRegistry, Get(::testing::An<AZStd::string&>(), ::testing::_)).Times(1);
+        EXPECT_CALL(m_mockSettingsRegistry, Get(::testing::An<bool&>(), ::testing::_)).Times(1);
+        EXPECT_CALL(m_mockArchiveCommandsBusHandler, CreateArchive(::testing::_, ::testing::_)).Times(1);
+
+        QObject parent{};
+        AssetProcessor::JobDetails jobDetails;
+        jobDetails.m_jobEntry.m_jobKey = "ACS_Test";
+        AssetProcessor::RCJob rcJob {&parent};
+        rcJob.Init(jobDetails);
+        AZ::ComponentDescriptor::StringWarningArray sourceFileList;
+        AssetProcessor::BuilderParams builderParams {&rcJob};
+        builderParams.m_serverKey = m_fakeFilename;
+        builderParams.m_processJobRequest.m_sourceFile = (m_tempFolder + m_fakeFullname).toUtf8().toStdString().c_str();
+
+        AssetProcessor::AssetServerHandler assetServerHandler;
+        EXPECT_TRUE(assetServerHandler.IsServerAddressValid());
+        EXPECT_TRUE(AssetUtilities::InServerMode());
+        EXPECT_TRUE(assetServerHandler.StoreJobResult(builderParams, sourceFileList));
+    }
+
+    TEST_F(AssetServerHandlerUnitTest, AssetCacheServer_ClientReadsZipFile_Works)
+    {
+        m_enableServer = false;
+        MockSettingsRegistry();
+        CreateMockAssetArchive();
+
+        auto extractArchive = [this](const AZStd::string& archivePath, const AZStd::string& tempFolder) -> std::future<bool>
+        {
+            AZStd::string targetFilename = AZStd::string(this->m_fakeFilename) + ".zip";
+            EXPECT_TRUE(archivePath.ends_with(targetFilename));
+            AZ_UNUSED(tempFolder);
+            return std::async(std::launch::async, [] { return true; });
+        };
+        ON_CALL(m_mockArchiveCommandsBusHandler, ExtractArchive(::testing::_, ::testing::_)).WillByDefault(extractArchive);
+
+        EXPECT_CALL(m_mockSettingsRegistry, Get(::testing::An<AZStd::string&>(), ::testing::_)).Times(1);
+        EXPECT_CALL(m_mockSettingsRegistry, Get(::testing::An<bool&>(), ::testing::_)).Times(1);
+        EXPECT_CALL(m_mockArchiveCommandsBusHandler, ExtractArchive(::testing::_, ::testing::_)).Times(1);
+
+        QObject parent{};
+        AssetProcessor::JobDetails jobDetails;
+        jobDetails.m_jobEntry.m_jobKey = "ACS_Test";
+        AssetProcessor::RCJob rcJob{ &parent };
+        rcJob.Init(jobDetails);
+        AssetProcessor::BuilderParams builderParams{ &rcJob };
+        builderParams.m_serverKey = m_fakeFilename;
+        builderParams.m_processJobRequest.m_sourceFile = (m_tempFolder + m_fakeFullname).toUtf8().toStdString().c_str();
+
+        AssetProcessor::AssetServerHandler assetServerHandler;
+        EXPECT_TRUE(assetServerHandler.IsServerAddressValid());
+        EXPECT_FALSE(AssetUtilities::InServerMode());
+        EXPECT_TRUE(assetServerHandler.RetrieveJobResult(builderParams));
+    }
+}

+ 6 - 58
Code/Tools/AssetProcessor/native/unittests/MockApplicationManager.cpp

@@ -28,77 +28,25 @@ namespace AssetProcessor
         {
             return m_container;
         }
-        const ExcludeRecognizerContainer& GetExcludeAssetRecognizerContainer() const override
-        {
-            return m_excludeContainer;
-        }
-
-        RecognizerContainer m_container;
-        ExcludeRecognizerContainer m_excludeContainer;
-    };
-    class MockRCCompiler
-        : public AssetProcessor::RCCompiler
-    {
-    public:
-        MockRCCompiler()
-            : m_executeResultResult(0, false, "c:\temp")
-        {
-        }
 
-        bool Initialize() override
-        {
-            m_initialize++;
-            return m_initializeResult;
-        }
-        bool Execute([[maybe_unused]] const QString& inputFile, [[maybe_unused]] const QString& watchFolder, [[maybe_unused]] const QString& platformIdentifier, [[maybe_unused]] const QString& params, [[maybe_unused]] const QString& dest, 
-            [[maybe_unused]] const AssetBuilderSDK::JobCancelListener* jobCancelListener, Result& result) const override
-        {
-            m_execute++;
-            result = m_executeResultResult;
-            return m_executeResult;
-        }
-        void RequestQuit() override
+        const RecognizerContainer& GetAssetCacheRecognizerContainer() const override
         {
-            m_request_quit++;
-        }
-
-        void ResetCounters()
-        {
-            this->m_initialize = 0;
-            this->m_execute = 0;
-            this->m_request_quit = 0;
-        }
-
-        void SetResultInitialize(bool result)
-        {
-            m_initializeResult = result;
-        }
-
-        void SetResultExecute(bool result)
-        {
-            m_executeResult = result;
+            return m_container;
         }
 
-        void SetResultResultExecute(Result result)
+        const ExcludeRecognizerContainer& GetExcludeAssetRecognizerContainer() const override
         {
-            m_executeResultResult = result;
+            return m_excludeContainer;
         }
 
-        bool m_initializeResult = true;
-        bool m_executeResult = true;
-        Result m_executeResultResult;
-        mutable int m_initialize = 0;
-        mutable int m_execute = 0;
-        mutable int m_request_quit = 0;
+        RecognizerContainer m_container;
+        ExcludeRecognizerContainer m_excludeContainer;
     };
 
-
-
     InternalMockBuilder::InternalMockBuilder(const QHash<QString, BuilderIdAndName>& inputBuilderNameByIdMap)
         : InternalRecognizerBasedBuilder(inputBuilderNameByIdMap,AZ::Uuid::CreateRandom())
         , m_createJobCallsCount(0)
     {
-        this->m_rcCompiler.reset(new MockRCCompiler());
     }
 
     InternalMockBuilder::~InternalMockBuilder()

+ 12 - 0
Code/Tools/AssetProcessor/native/utilities/AssetServerHandler.cpp

@@ -138,6 +138,18 @@ namespace AssetProcessor
             return false;
         }
 
+        // make sub-folders if needed
+        QFileInfo fileInfo(archiveAbsFilePath);
+        if (!fileInfo.absoluteDir().exists())
+        {
+            if (!fileInfo.absoluteDir().mkpath("."))
+            {
+                AZ_Error(AssetProcessor::DebugChannel, false, "Could not make archive folder %s !",
+                    fileInfo.absoluteDir().absolutePath().toUtf8().data());
+                return false;
+            }
+        }
+
         AZ_TracePrintf(AssetProcessor::DebugChannel, "Creating archive for job (%s, %s, %s) with fingerprint (%u).\n",
             builderParams.m_rcJob->GetJobEntry().m_pathRelativeToWatchFolder.toUtf8().data(), builderParams.m_rcJob->GetJobKey().toUtf8().data(),
             builderParams.m_rcJob->GetPlatformInfo().m_identifier.c_str(), builderParams.m_rcJob->GetOriginalFingerprint());

+ 190 - 2
Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.cpp

@@ -683,6 +683,181 @@ namespace AssetProcessor
         }
     }
 
+    // Find the current AssetRecognizer identified by the top entry in the name stack
+    AssetRecognizer* ACSVisitor::CurrentAssetRecognizer()
+    {
+        if (m_nameStack.empty())
+        {
+            return nullptr;
+        }
+
+        AZStd::string_view nameView = m_nameStack.top();
+        auto rcName = QString::fromUtf8(nameView.data(), aznumeric_cast<int>(nameView.size()));
+
+        auto assetRecognizerEntryIt = AZStd::find_if(m_assetRecognizers.rbegin(), m_assetRecognizers.rend(),
+            [&rcName](const AssetRecognizer& assetRecognizer)
+            {
+                return assetRecognizer.m_name == rcName;
+            });
+        if (assetRecognizerEntryIt == m_assetRecognizers.rend())
+        {
+            return nullptr;
+        }
+        return &(*assetRecognizerEntryIt);
+    }
+
+    AZ::SettingsRegistryInterface::VisitResponse ACSVisitor::Traverse(AZStd::string_view jsonPath, AZStd::string_view valueName,
+        AZ::SettingsRegistryInterface::VisitAction action, AZ::SettingsRegistryInterface::Type)
+    {
+        constexpr AZStd::string_view ACSNamePrefix = "ACS ";
+        constexpr AZ::SettingsRegistryInterface::FixedValueString key(AssetProcessorSettingsKey);
+        switch (action)
+        {
+        case AZ::SettingsRegistryInterface::VisitAction::Begin:
+        {
+            if (jsonPath == key + "/Server")
+            {
+                return AZ::SettingsRegistryInterface::VisitResponse::Continue;
+            }
+            if (valueName.starts_with(ACSNamePrefix))
+            {
+                AZStd::string name = valueName.substr(ACSNamePrefix.size());
+                m_nameStack.push(name);
+
+                AssetRecognizer& assetRecognizer = m_assetRecognizers.emplace_back();
+                assetRecognizer.m_name = QString::fromUtf8(name.c_str(), aznumeric_cast<int>(name.size()));
+            }
+        }
+        break;
+        case AZ::SettingsRegistryInterface::VisitAction::End:
+        {
+            if (valueName.starts_with(ACSNamePrefix))
+            {
+                AZ_Assert(!m_nameStack.empty(), "RC name stack should not be empty. More stack pops, than pushes");
+                m_nameStack.pop();
+            }
+        }
+        break;
+
+        default:
+            break;
+        }
+
+        return AZ::SettingsRegistryInterface::VisitResponse::Continue;
+    }
+
+    void ACSVisitor::Visit([[maybe_unused]] AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, bool value)
+    {
+        auto* assetRecognizer = CurrentAssetRecognizer();
+        if (!assetRecognizer)
+        {
+            return;
+        }
+        else if (valueName == "lockSource")
+        {
+            assetRecognizer->m_testLockSource = value;
+        }
+        else if (valueName == "critical")
+        {
+            assetRecognizer->m_isCritical = value;
+        }
+        else if (valueName == "checkServer")
+        {
+            assetRecognizer->m_checkServer = value;
+        }
+        else if (valueName == "supportsCreateJobs")
+        {
+            assetRecognizer->m_supportsCreateJobs = value;
+        }
+        else if (valueName == "outputProductDependencies")
+        {
+            assetRecognizer->m_outputProductDependencies = value;
+        }
+    }
+
+    void ACSVisitor::Visit([[maybe_unused]] AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZ::s64 value)
+    {
+        auto* assetRecognizer = CurrentAssetRecognizer();
+        if (!assetRecognizer)
+        {
+            return;
+        }
+        else if (valueName == "priority")
+        {
+            assetRecognizer->m_priority = aznumeric_cast<int>(value);
+        }
+    }
+
+    void ACSVisitor::Visit([[maybe_unused]] AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value)
+    {
+        auto* assetRecognizer = CurrentAssetRecognizer();
+        if (!assetRecognizer)
+        {
+            return;
+        }
+
+        // The "pattern" and "glob" entries were previously parsed by QSettings which un-escapes the values
+        // To compensate for it the AssetProcessorPlatformConfig.ini was escaping the
+        // backslash character used to escape other characters, therefore causing a "double escape"
+        // situation
+        auto UnescapePattern = [](AZStd::string_view pattern)
+        {
+            constexpr AZStd::string_view backslashEscape = R"(\\)";
+            AZStd::string unescapedResult;
+            while (!pattern.empty())
+            {
+                size_t pos = pattern.find(backslashEscape);
+                if (pos != AZStd::string_view::npos)
+                {
+                    unescapedResult += pattern.substr(0, pos);
+                    unescapedResult += '\\';
+                    // Move the pattern string after the double backslash characters
+                    pattern = pattern.substr(pos + backslashEscape.size());
+                }
+                else
+                {
+                    unescapedResult += pattern;
+                    pattern = {};
+                }
+            }
+
+            return unescapedResult;
+        };
+
+        if (valueName == "pattern")
+        {
+            if (!value.empty())
+            {
+                const auto patternType = AssetBuilderSDK::AssetBuilderPattern::Regex;
+                assetRecognizer->m_patternMatcher = AssetBuilderSDK::FilePatternMatcher(UnescapePattern(value), patternType);
+            }
+        }
+        else if (valueName == "glob")
+        {
+            // Add the glob pattern if it the matter matcher doesn't already contain a valid regex pattern
+            if (!assetRecognizer->m_patternMatcher.IsValid())
+            {
+                const auto patternType = AssetBuilderSDK::AssetBuilderPattern::Wildcard;
+                assetRecognizer->m_patternMatcher = AssetBuilderSDK::FilePatternMatcher(UnescapePattern(value), patternType);
+            }
+        }
+        else if (valueName == "version")
+        {
+            assetRecognizer->m_version = QString::fromUtf8(value.data(), aznumeric_cast<int>(value.size()));
+        }
+        else if (valueName == "productAssetType")
+        {
+            if (!value.empty())
+            {
+                AZ::Uuid productAssetType{ value.data(), value.size() };
+                if (!productAssetType.IsNull())
+                {
+                    assetRecognizer->m_productAssetType = productAssetType;
+                }
+            }
+        }
+    }
+ 
     const char AssetConfigPlatformDir[] = "AssetProcessorConfig/";
     const char AssetProcessorPlatformConfigFileName[] = "AssetProcessorPlatformConfig.ini";
 
@@ -1171,6 +1346,14 @@ namespace AssetProcessor
             }
         }
 
+        ACSVisitor acsVistor;
+        AZ::SettingsRegistryInterface::FixedValueString key(AssetProcessor::AssetProcessorSettingsKey);
+        settingsRegistry->Visit(acsVistor, key + "/Server");
+        for (auto&& acsRecognizer : acsVistor.m_assetRecognizers)
+        {
+            m_assetCacheServerRecognizers[acsRecognizer.m_name] = AZStd::move(acsRecognizer);
+        }
+
         return true;
     }
 
@@ -1722,12 +1905,17 @@ namespace AssetProcessor
 
     const RecognizerContainer& PlatformConfiguration::GetAssetRecognizerContainer() const
     {
-        return this->m_assetRecognizers;
+        return m_assetRecognizers;
+    }
+
+    const RecognizerContainer& PlatformConfiguration::GetAssetCacheRecognizerContainer() const
+    {
+        return m_assetCacheServerRecognizers;
     }
 
     const ExcludeRecognizerContainer& PlatformConfiguration::GetExcludeAssetRecognizerContainer() const
     {
-        return this->m_excludeAssetRecognizers;
+        return m_excludeAssetRecognizers;
     }
 
     void AssetProcessor::PlatformConfiguration::AddExcludeRecognizer(const ExcludeAssetRecognizer& recogniser)

+ 23 - 0
Code/Tools/AssetProcessor/native/utilities/PlatformConfiguration.h

@@ -120,6 +120,7 @@ namespace AssetProcessor
     struct RecognizerConfiguration
     {
         virtual const RecognizerContainer& GetAssetRecognizerContainer() const = 0;
+        virtual const RecognizerContainer& GetAssetCacheRecognizerContainer() const = 0;
         virtual const ExcludeRecognizerContainer& GetExcludeAssetRecognizerContainer() const = 0;
     };
 
@@ -195,6 +196,25 @@ namespace AssetProcessor
         const AZStd::vector<AssetBuilderSDK::PlatformInfo>& m_enabledPlatforms;
     };
 
+    //! This vistor reads in the Asset Cache Server configuration elements from the settings registry
+    struct ACSVisitor
+        : AZ::SettingsRegistryInterface::Visitor
+    {
+        AZ::SettingsRegistryInterface::VisitResponse Traverse(AZStd::string_view jsonPath, AZStd::string_view valueName,
+            AZ::SettingsRegistryInterface::VisitAction action, AZ::SettingsRegistryInterface::Type) override;
+
+        using AZ::SettingsRegistryInterface::Visitor::Visit;
+        void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, bool value) override;
+        void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZ::s64 value) override;
+        void Visit(AZStd::string_view path, AZStd::string_view valueName, AZ::SettingsRegistryInterface::Type, AZStd::string_view value) override;
+
+        AZStd::vector<AssetRecognizer> m_assetRecognizers;
+    private:
+        AssetRecognizer* CurrentAssetRecognizer();
+
+        AZStd::stack<AZStd::string> m_nameStack;
+    };
+
     /** Reads the platform ini configuration file to determine
     * platforms for which assets needs to be build
     */
@@ -331,6 +351,8 @@ namespace AssetProcessor
 
         const RecognizerContainer& GetAssetRecognizerContainer() const override;
 
+        const RecognizerContainer& GetAssetCacheRecognizerContainer() const override;
+
         const ExcludeRecognizerContainer& GetExcludeAssetRecognizerContainer() const override;
 
         /** returns true if the config is valid.
@@ -367,6 +389,7 @@ namespace AssetProcessor
     private:
         AZStd::vector<AssetBuilderSDK::PlatformInfo> m_enabledPlatforms;
         RecognizerContainer m_assetRecognizers;
+        RecognizerContainer m_assetCacheServerRecognizers;
         ExcludeRecognizerContainer m_excludeAssetRecognizers;
         AZStd::vector<AssetProcessor::ScanFolderInfo> m_scanFolders;
         QList<QPair<QString, QString> > m_metaDataFileTypes;

+ 56 - 18
Code/Tools/AssetProcessor/native/utilities/assetUtils.cpp

@@ -237,6 +237,7 @@ namespace AssetUtilities
     int s_truncateFingerprintTimestampPrecision{ 1 };
     AZStd::optional<bool> s_fileHashOverride{};
     AZStd::optional<bool> s_fileHashSetting{};
+    AZStd::optional<bool> s_serverMode{};
 
     void SetTruncateFingerprintTimestamp(int precision)
     {
@@ -551,37 +552,75 @@ namespace AssetUtilities
 
     bool InServerMode()
     {
-        static bool s_serverMode = CheckServerMode();
-        return s_serverMode;
+        if (s_serverMode.has_value())
+        {
+            return s_serverMode.value();
+        }
+        s_serverMode = AZStd::make_optional<bool>(CheckServerMode());
+        return s_serverMode.value();
+    }
+
+    void ResetServerMode()
+    {
+        if (s_serverMode.has_value())
+        {
+            s_serverMode.reset();
+        }
     }
 
     bool CheckServerMode()
     {
-        QStringList args = QCoreApplication::arguments();
-        for (const QString& arg : args)
+        bool inServerMode = false;
+        if (QCoreApplication::instance())
         {
-            if (arg.contains("/server", Qt::CaseInsensitive) || arg.contains("--server", Qt::CaseInsensitive))
+            QStringList args = QCoreApplication::arguments();
+            for (const QString& arg : args)
             {
-                bool isValid = false;
-                AssetProcessor::AssetServerBus::BroadcastResult(isValid, &AssetProcessor::AssetServerBusTraits::IsServerAddressValid);
-                if (isValid)
+                if (arg.contains("/server", Qt::CaseInsensitive) || arg.contains("--server", Qt::CaseInsensitive))
                 {
-                    AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Asset Processor is running in server mode.\n");
-                    return true;
+                    inServerMode = true;
+                    break;
                 }
-                else
+            }
+        }
+
+        if (!inServerMode)
+        {
+            auto settingsRegistry = AZ::SettingsRegistry::Get();
+            if (settingsRegistry)
+            {
+                bool enableAssetCacheServerMode = false;
+                AZ::SettingsRegistryInterface::FixedValueString key(AssetProcessor::AssetProcessorSettingsKey);
+                if (settingsRegistry->Get(enableAssetCacheServerMode, key + "/Server/enableCacheServer"))
                 {
-                    AZ_Warning(AssetProcessor::ConsoleChannel, false, "Invalid server address, please check the AssetProcessorPlatformConfig.setreg file"
-                        " to ensure that the address is correct. Asset Processor won't be running in server mode.");
+                    inServerMode = enableAssetCacheServerMode;
                 }
-
-                break;
             }
         }
 
+        if (inServerMode)
+        {
+            bool isServerAddressValid = false;
+            AssetProcessor::AssetServerBus::BroadcastResult(isServerAddressValid, &AssetProcessor::AssetServerBusTraits::IsServerAddressValid);
+            if (isServerAddressValid)
+            {
+                AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Asset Processor is running in server mode.\n");
+                return true;
+            }
+            else
+            {
+                AZ_Warning(AssetProcessor::ConsoleChannel, false,
+                    "Invalid server address, please check the AssetProcessorPlatformConfig.setreg file"
+                    " to ensure that the address is correct. Asset Processor won't be running in server mode.");
+            }
+        }
         return false;
     }
 
+    void ResetServerAddress()
+    {
+        s_assetServerAddress.clear();
+    }
 
     QString ServerAddress()
     {
@@ -617,10 +656,9 @@ namespace AssetUtilities
                 + "/Server/cacheServerAddress"))
             {
                 AZ_TracePrintf(AssetProcessor::DebugChannel, "Server Address: %s\n", address.c_str());
+                s_assetServerAddress = address;
+                return QString::fromUtf8(address.data(), aznumeric_cast<int>(address.size()));
             }
-            s_assetServerAddress = address;
-
-            return QString::fromUtf8(address.data(), aznumeric_cast<int>(address.size()));
         }
 
         return QString();

+ 6 - 0
Code/Tools/AssetProcessor/native/utilities/assetUtils.h

@@ -85,12 +85,18 @@ namespace AssetUtilities
     //! Checks to see if the asset processor is running in server mode
     bool InServerMode();
 
+    //! Clears the server flag
+    void ResetServerMode();
+
     //! Checks the args for the server parameter, returns true if found otherwise false.
     bool CheckServerMode();
 
     //! Reads the server address from the config file.
     QString ServerAddress();
 
+    //! Clears the string holding the server address for the Cache Server mode
+    void ResetServerAddress();
+
     bool ShouldUseFileHashing();
 
     //! Determine the name of the current project - for example, AutomatedTesting