3dec5d3b71
* LYN-2537 Moved the Engine and Editor folder to be within the EngineAssets folder * Fixed Documentation in bootstrap.cfg to correct the path to the user project specific registry file * Adding a newline to the output of AssetCatalog 'Registering asset..., but type is not set' message * Updating the AssetProcessorPlatformConfig.setreg Scan Folder to detect the @ENGINEROOT@/EngineAssets/Engine path for engine runtime assets and @ENGINEROOT@/EngineAssets/Editor path for engine tool assets * Updating references to Icons and other assets to account for moving the Engine and Editor folder under a single EngineAssets folder * Moving the Engine Settings Registry folder from Engine/Registry -> Registry * Removed the LY_PROJECT_CMAKE_PATH define as it is not portable to other locations. It is hard coded to the project location that was used for the CMake configuration. Furthermore it paths with backslashes within it are treated as escape characters and not a path separator * Updated the LyTestTools asset_processor.py script to copy the exclude.filetag from the EngineAssets/Engine directory now * Fixed Atom Shader Preprocessing when running using an External Project * Updated the TSGenerateAction.cpp to fix the build error with using a renamed variable * Updated the Install_Common.cmake ly_setup_others function to install the EngineAssets directory and the each of the Gem's Assets directory while maintaining the relative directory structure to the Engine Root Also updated the install step to install the Registry folder at the engine root * Fixed the copying of the Registry folder to be in the install root, instead of under a second 'Registry' folder * Moving the AssetProcessorPlatformConfig.setreg file over to the Registry folder * Updated the LyTestTools and C++ code to point that the new location of the AssetProcessorPlatformConfig.setreg file inside of the Registry folder * Renamed Test AssetProcessor*Config.ini files to have the .setreg extension * Converted the AssetProcessor test setreg files from ini format to json format using the SerializeContextTools convert-ini command * Updated the AssetProcessor CMakeLists.txt to copy over the test setreg files to the build folder * Updated the assetprocessor test file list to point at the renamed AsssetProcessor*Config setreg filenames * Removed the Output Prefix code from the AssetProcessor. The complexity that it brought to the AP code is not needed, as users can replicate the behavior by just moving there assets underneath a another folder, underneath the scan folder * Adding back support to read the AssetProcessorPlatformConfig.setreg file from the asset root. This is only needed for C++ UnitTests as they run in an environment where the accessing the Engine Settings Registry is not available * Updating the Install_common.cmake logic to copy any "Assets" folder to the install layout. The Script has also been updated to copy over the "Assets" folder in the Engine Root to the install layout instead of an "EngineAssets" folder * Updating References to EngineAssets source asset folder in code to be the Assets source folder * Moved the Engine Source Asset folder of 'EngineAssets' to a new folder name of 'Assets'. This is inline with the naming scheme we use for Gem asset folders * Adding the EngineFinder.cmake to the AutomatedTesting project to allow it to work in a project centric manner * Updating the LyTestTools copy_assets_to_project function to be able to copy assets with folders to the temporary project root Fixed an issue in LyTestTools where the temporary log directory could have shutil.rmtree being called twice on it leading to an exception which fails an automated test Updated the asset_procesor_gui_tests_2 AddScanFolder test to not use the output prefix, but instead place the source asset root into a subdirectory * Correct the AssetProcessorPlatformConfig Scan Folders for the EngineAssets directory to point at the Assets directory * Updated the asset procesor batch dependency test scan folder to point at the 'Assets' folder instead of 'EngineAssets'
372 lines
18 KiB
C++
372 lines
18 KiB
C++
/*
|
|
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
|
* its licensors.
|
|
*
|
|
* For complete copyright and license terms please see the LICENSE at the root of this
|
|
* distribution (the "License"). All use of this software is governed by the License,
|
|
* or, if provided, by the license below or the license accompanying this file. Do not
|
|
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
*
|
|
*/
|
|
|
|
#include <AzTest/AzTest.h>
|
|
#include <utilities/BatchApplicationManager.h>
|
|
#include <utilities/ApplicationServer.h>
|
|
#include <AzFramework/Asset/AssetSystemComponent.h>
|
|
#include <AzCore/UnitTest/TestTypes.h>
|
|
#include <connection/connectionManager.h>
|
|
#include <QCoreApplication>
|
|
#include <QTemporaryDir>
|
|
#include <AzFramework/Network/AssetProcessorConnection.h>
|
|
|
|
namespace AssetProcessorMessagesTests
|
|
{
|
|
using namespace testing;
|
|
using ::testing::NiceMock;
|
|
|
|
using namespace AssetProcessor;
|
|
using namespace AssetBuilderSDK;
|
|
|
|
static constexpr unsigned short AssetProcessorPort = static_cast<unsigned short>(888u);
|
|
|
|
class AssetProcessorMessages;
|
|
|
|
struct UnitTestBatchApplicationManager
|
|
: BatchApplicationManager
|
|
{
|
|
UnitTestBatchApplicationManager(int* argc, char*** argv, QObject* parent)
|
|
: BatchApplicationManager(argc, argv, parent)
|
|
{
|
|
|
|
}
|
|
|
|
friend class AssetProcessorMessages;
|
|
};
|
|
|
|
class AssetProcessorMessagesTestsMockDatabaseLocationListener : public AzToolsFramework::AssetDatabase::AssetDatabaseRequests::Bus::Handler
|
|
{
|
|
public:
|
|
MOCK_METHOD1(GetAssetDatabaseLocation, bool(AZStd::string&));
|
|
};
|
|
|
|
struct MockAssetCatalog : AssetProcessor::AssetCatalog
|
|
{
|
|
MockAssetCatalog(QObject* parent, AssetProcessor::PlatformConfiguration* platformConfiguration)
|
|
: AssetCatalog(parent, platformConfiguration)
|
|
{
|
|
}
|
|
|
|
|
|
AzFramework::AssetSystem::GetUnresolvedDependencyCountsResponse HandleGetUnresolvedDependencyCountsRequest(MessageData<AzFramework::AssetSystem::GetUnresolvedDependencyCountsRequest> messageData) override
|
|
{
|
|
m_called = true;
|
|
return AssetCatalog::HandleGetUnresolvedDependencyCountsRequest(messageData);
|
|
}
|
|
|
|
bool m_called = false;
|
|
};
|
|
|
|
struct MockAssetRequestHandler : AssetRequestHandler
|
|
{
|
|
bool InvokeHandler(MessageData<AzFramework::AssetSystem::BaseAssetProcessorMessage> message) override
|
|
{
|
|
m_invoked = true;
|
|
|
|
return AssetRequestHandler::InvokeHandler(message);
|
|
}
|
|
|
|
AZStd::atomic_bool m_invoked = false;
|
|
};
|
|
|
|
class AssetProcessorMessages
|
|
: public ::UnitTest::ScopedAllocatorSetupFixture
|
|
{
|
|
public:
|
|
void SetUp() override
|
|
{
|
|
#if !AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
|
|
AssetUtilities::ResetGameName();
|
|
|
|
m_temporarySourceDir = QDir(m_temporaryDir.path());
|
|
m_databaseLocation = m_temporarySourceDir.absoluteFilePath("test_database.sqlite").toUtf8().constData();
|
|
|
|
ON_CALL(m_databaseLocationListener, GetAssetDatabaseLocation(_))
|
|
.WillByDefault(
|
|
DoAll( // set the 0th argument ref (string) to the database location and return true.
|
|
SetArgReferee<0>(m_databaseLocation.c_str()),
|
|
Return(true)));
|
|
|
|
m_databaseLocationListener.BusConnect();
|
|
|
|
m_dbConn.OpenDatabase();
|
|
|
|
int argC = 0;
|
|
m_batchApplicationManager = AZStd::make_unique<UnitTestBatchApplicationManager>(&argC, nullptr, nullptr);
|
|
m_batchApplicationManager->BeforeRun();
|
|
|
|
// Override Game Name to be "AutomatedTesting"
|
|
AssetUtilities::ComputeProjectName("AutomatedTesting", true);
|
|
|
|
m_batchApplicationManager->m_platformConfiguration = new PlatformConfiguration();
|
|
m_batchApplicationManager->InitAssetProcessorManager();
|
|
|
|
m_assetCatalog = AZStd::make_unique<MockAssetCatalog>(nullptr, m_batchApplicationManager->m_platformConfiguration);
|
|
|
|
m_batchApplicationManager->m_assetCatalog = m_assetCatalog.get();
|
|
m_batchApplicationManager->InitRCController();
|
|
m_batchApplicationManager->InitFileStateCache();
|
|
m_batchApplicationManager->InitFileMonitor();
|
|
m_batchApplicationManager->InitApplicationServer();
|
|
m_batchApplicationManager->InitConnectionManager();
|
|
// Note this must be constructed after InitConnectionManager is called since it will interact with the connection manager
|
|
m_assetRequestHandler = new MockAssetRequestHandler();
|
|
m_batchApplicationManager->InitAssetRequestHandler(m_assetRequestHandler);
|
|
|
|
m_batchApplicationManager->m_fileWatcher.StartWatching();
|
|
|
|
QObject::connect(m_batchApplicationManager->m_connectionManager, &ConnectionManager::ConnectionError, [](unsigned /*connId*/, QString error)
|
|
{
|
|
AZ_Error("ConnectionManager", false, "%s", error.toUtf8().constData());
|
|
});
|
|
|
|
ASSERT_TRUE(m_batchApplicationManager->m_applicationServer->startListening(AssetProcessorPort));
|
|
|
|
using namespace AzFramework;
|
|
|
|
m_assetSystemComponent = AZStd::make_unique<AssetSystem::AssetSystemComponent>();
|
|
|
|
m_assetSystemComponent->Init();
|
|
m_assetSystemComponent->Activate();
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
RunNetworkRequest([]()
|
|
{
|
|
AZStd::string appBranchToken;
|
|
AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::CalculateBranchTokenForEngineRoot, appBranchToken);
|
|
|
|
AzFramework::AssetSystem::ConnectionSettings connectionSettings;
|
|
connectionSettings.m_assetProcessorIp = "127.0.0.1";
|
|
connectionSettings.m_assetProcessorPort = AssetProcessorPort;
|
|
connectionSettings.m_branchToken = appBranchToken;
|
|
connectionSettings.m_projectName = "AutomatedTesting";
|
|
connectionSettings.m_assetPlatform = "pc";
|
|
connectionSettings.m_connectionIdentifier = "UNITTEST";
|
|
connectionSettings.m_connectTimeout = AZStd::chrono::seconds(15);
|
|
connectionSettings.m_connectionDirection = AzFramework::AssetSystem::ConnectionSettings::ConnectionDirection::ConnectToAssetProcessor;
|
|
connectionSettings.m_waitUntilAssetProcessorIsReady = false;
|
|
connectionSettings.m_launchAssetProcessorOnFailedConnection = false;
|
|
|
|
bool result = false;
|
|
AzFramework::AssetSystemRequestBus::BroadcastResult(result,
|
|
&AzFramework::AssetSystemRequestBus::Events::EstablishAssetProcessorConnection, connectionSettings);
|
|
|
|
ASSERT_TRUE(result);
|
|
});
|
|
|
|
|
|
#endif // !AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
|
|
}
|
|
|
|
void TearDown() override
|
|
{
|
|
#if !AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
|
|
QEventLoop eventLoop;
|
|
|
|
QObject::connect(m_batchApplicationManager->m_connectionManager, &ConnectionManager::ReadyToQuit, &eventLoop, &QEventLoop::quit);
|
|
|
|
m_batchApplicationManager->m_connectionManager->QuitRequested();
|
|
|
|
eventLoop.exec();
|
|
|
|
m_assetSystemComponent->Deactivate();
|
|
m_batchApplicationManager->Destroy();
|
|
#endif // !AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
|
|
}
|
|
|
|
void RunNetworkRequest(AZStd::function<void()> func) const
|
|
{
|
|
AZStd::atomic_bool finished = false;
|
|
auto start = AZStd::chrono::monotonic_clock::now();
|
|
|
|
auto thread = AZStd::thread([&finished, &func]()
|
|
{
|
|
func();
|
|
finished = true;
|
|
}
|
|
);
|
|
|
|
constexpr int MaxWaitTime = 5;
|
|
while (!finished && AZStd::chrono::monotonic_clock::now() - start < AZStd::chrono::seconds(MaxWaitTime))
|
|
{
|
|
QCoreApplication::processEvents();
|
|
}
|
|
|
|
ASSERT_TRUE(finished) << "Timeout";
|
|
|
|
thread.join();
|
|
}
|
|
protected:
|
|
|
|
MockAssetRequestHandler* m_assetRequestHandler{}; // Not owned, AP will delete this pointer
|
|
QTemporaryDir m_temporaryDir;
|
|
AZStd::unique_ptr<UnitTestBatchApplicationManager> m_batchApplicationManager;
|
|
AZStd::unique_ptr<AzFramework::AssetSystem::AssetSystemComponent> m_assetSystemComponent;
|
|
NiceMock<AssetProcessorMessagesTestsMockDatabaseLocationListener> m_databaseLocationListener;
|
|
AZStd::unique_ptr<MockAssetCatalog> m_assetCatalog = nullptr;
|
|
QDir m_temporarySourceDir;
|
|
AZStd::string m_databaseLocation;
|
|
AssetDatabaseConnection m_dbConn;
|
|
};
|
|
|
|
struct MessagePair
|
|
{
|
|
AZStd::unique_ptr<AzFramework::AssetSystem::BaseAssetProcessorMessage> m_request;
|
|
AZStd::unique_ptr<AzFramework::AssetSystem::BaseAssetProcessorMessage> m_response;
|
|
};
|
|
|
|
#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
|
|
TEST_F(AssetProcessorMessages, DISABLED_All)
|
|
#else
|
|
TEST_F(AssetProcessorMessages, All)
|
|
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
|
|
{
|
|
// Test that we can successfully send network messages and have them arrive for processing
|
|
// For messages that have a response, it also verifies the response comes back
|
|
// Note that several harmless warnings will be triggered due to the messages not having any data set
|
|
using namespace AzFramework::AssetSystem;
|
|
using namespace AzToolsFramework::AssetSystem;
|
|
|
|
AZStd::vector<MessagePair> testMessages;
|
|
AZStd::unordered_map<int, AZStd::string> nameMap; // This is just for debugging, so we can output the name of failed messages
|
|
|
|
AZ::SerializeContext* serializeContext = nullptr;
|
|
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
|
|
|
|
auto addPairFunc = [&testMessages, &nameMap, serializeContext](auto* request, auto* response)
|
|
{
|
|
testMessages.emplace_back(MessagePair{
|
|
AZStd::unique_ptr<AZStd::remove_pointer_t<decltype(request)>>(request),
|
|
AZStd::unique_ptr<AZStd::remove_pointer_t<decltype(response)>>(response)
|
|
});
|
|
|
|
auto data = serializeContext->FindClassData(request->RTTI_GetType());
|
|
nameMap[request->GetMessageType()] = data->m_name;
|
|
};
|
|
|
|
auto addRequestFunc = [&testMessages, &nameMap, serializeContext](auto* request)
|
|
{
|
|
testMessages.emplace_back(MessagePair{AZStd::unique_ptr<AZStd::remove_pointer_t<decltype(request)>>(request), nullptr });
|
|
|
|
auto data = serializeContext->FindClassData(request->RTTI_GetType());
|
|
nameMap[request->GetMessageType()] = data->m_name;
|
|
};
|
|
|
|
addPairFunc(new GetFullSourcePathFromRelativeProductPathRequest(), new GetFullSourcePathFromRelativeProductPathResponse());
|
|
addPairFunc(new GetRelativeProductPathFromFullSourceOrProductPathRequest(), new GetRelativeProductPathFromFullSourceOrProductPathResponse());
|
|
addPairFunc(new SourceAssetInfoRequest(), new SourceAssetInfoResponse());
|
|
addPairFunc(new SourceAssetProductsInfoRequest(), new SourceAssetProductsInfoResponse());
|
|
addPairFunc(new GetScanFoldersRequest(), new GetScanFoldersResponse());
|
|
addPairFunc(new GetAssetSafeFoldersRequest(), new GetAssetSafeFoldersResponse());
|
|
addRequestFunc(new RegisterSourceAssetRequest());
|
|
addRequestFunc(new UnregisterSourceAssetRequest());
|
|
addPairFunc(new AssetInfoRequest(), new AssetInfoResponse());
|
|
addPairFunc(new AssetDependencyInfoRequest(), new AssetDependencyInfoResponse());
|
|
addRequestFunc(new RequestEscalateAsset());
|
|
addPairFunc(new RequestAssetStatus(), new ResponseAssetStatus());
|
|
|
|
RunNetworkRequest([&testMessages, &nameMap, this]()
|
|
{
|
|
for(auto&& pair : testMessages)
|
|
{
|
|
AZStd::string messageName = nameMap[pair.m_request->GetMessageType()];
|
|
|
|
m_assetRequestHandler->m_invoked = false;
|
|
|
|
if(pair.m_response)
|
|
{
|
|
EXPECT_TRUE(SendRequest(*pair.m_request.get(), *pair.m_response.get())) << "Message " << messageName.c_str() << " failed to send";
|
|
}
|
|
else
|
|
{
|
|
EXPECT_TRUE(SendRequest(*pair.m_request.get())) << "Message " << messageName.c_str() << " failed to send";
|
|
// Since there's no response, the above line will finish immediately, so we need to wait a little bit so the message can actually be sent
|
|
// before we check if it was received
|
|
// We'll wait a maximum of 5 seconds, checking periodically if the message was received, to avoid failing due to slow running test servers
|
|
constexpr int MaxWaitTimeSeconds = 5;
|
|
auto start = AZStd::chrono::monotonic_clock::now();
|
|
|
|
while (!m_assetRequestHandler->m_invoked && AZStd::chrono::monotonic_clock::now() - start < AZStd::chrono::seconds(MaxWaitTimeSeconds))
|
|
{
|
|
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(10));
|
|
}
|
|
}
|
|
|
|
EXPECT_TRUE(m_assetRequestHandler->m_invoked) << "Message " << messageName.c_str() << " was not received";
|
|
}
|
|
});
|
|
}
|
|
|
|
#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
|
|
TEST_F(AssetProcessorMessages, DISABLED_GetUnresolvedProductReferences_Succeeds)
|
|
#else
|
|
TEST_F(AssetProcessorMessages, GetUnresolvedProductReferences_Succeeds)
|
|
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
|
|
{
|
|
using namespace AzToolsFramework::AssetDatabase;
|
|
|
|
// Setup the database with all needed info
|
|
ScanFolderDatabaseEntry scanfolder1("scanfolder1", "scanfolder1", "scanfolder1");
|
|
ASSERT_TRUE(m_dbConn.SetScanFolder(scanfolder1));
|
|
|
|
SourceDatabaseEntry source1(scanfolder1.m_scanFolderID, "source1.png", AZ::Uuid::CreateRandom(), "Fingerprint");
|
|
SourceDatabaseEntry source2(scanfolder1.m_scanFolderID, "source2.png", AZ::Uuid::CreateRandom(), "Fingerprint");
|
|
ASSERT_TRUE(m_dbConn.SetSource(source1));
|
|
ASSERT_TRUE(m_dbConn.SetSource(source2));
|
|
|
|
JobDatabaseEntry job1(source1.m_sourceID, "jobkey", 1234, "pc", AZ::Uuid::CreateRandom(), AzToolsFramework::AssetSystem::JobStatus::Completed, 1111);
|
|
JobDatabaseEntry job2(source2.m_sourceID, "jobkey", 1234, "pc", AZ::Uuid::CreateRandom(), AzToolsFramework::AssetSystem::JobStatus::Completed, 2222);
|
|
ASSERT_TRUE(m_dbConn.SetJob(job1));
|
|
ASSERT_TRUE(m_dbConn.SetJob(job2));
|
|
|
|
ProductDatabaseEntry product1(job1.m_jobID, 5, "source1.product", AZ::Data::AssetType::CreateRandom());
|
|
ProductDatabaseEntry product2(job2.m_jobID, 15, "source2.product", AZ::Data::AssetType::CreateRandom());
|
|
ASSERT_TRUE(m_dbConn.SetProduct(product1));
|
|
ASSERT_TRUE(m_dbConn.SetProduct(product2));
|
|
|
|
ProductDependencyDatabaseEntry dependency1(product1.m_productID, AZ::Uuid::CreateNull(), 0, {}, "pc", 0, "somefileA.txt", ProductDependencyDatabaseEntry::DependencyType::ProductDep_SourceFile);
|
|
ProductDependencyDatabaseEntry dependency2(product1.m_productID, AZ::Uuid::CreateNull(), 0, {}, "pc", 0, "somefileB.txt", ProductDependencyDatabaseEntry::DependencyType::ProductDep_ProductFile);
|
|
ProductDependencyDatabaseEntry dependency3(product1.m_productID, AZ::Uuid::CreateNull(), 0, {}, "pc", 0, "somefileC.txt");
|
|
ProductDependencyDatabaseEntry dependency4(product1.m_productID, AZ::Uuid::CreateNull(), 0, {}, "pc", 0, ":somefileD.txt"); // Exclusion
|
|
ProductDependencyDatabaseEntry dependency5(product1.m_productID, AZ::Uuid::CreateNull(), 0, {}, "pc", 0, "somefileE*.txt"); // Wildcard
|
|
ASSERT_TRUE(m_dbConn.SetProductDependency(dependency1));
|
|
ASSERT_TRUE(m_dbConn.SetProductDependency(dependency2));
|
|
ASSERT_TRUE(m_dbConn.SetProductDependency(dependency3));
|
|
ASSERT_TRUE(m_dbConn.SetProductDependency(dependency4));
|
|
ASSERT_TRUE(m_dbConn.SetProductDependency(dependency5));
|
|
|
|
// Setup the asset catalog
|
|
AzFramework::AssetSystem::AssetNotificationMessage assetNotificationMessage("source1.product", AzFramework::AssetSystem::AssetNotificationMessage::NotificationType::AssetChanged, AZ::Data::AssetType::CreateRandom(), "pc");
|
|
assetNotificationMessage.m_assetId = AZ::Data::AssetId(source1.m_sourceGuid, product1.m_subID);
|
|
assetNotificationMessage.m_dependencies.push_back(AZ::Data::ProductDependency(AZ::Data::AssetId(source2.m_sourceGuid, product2.m_subID), {}));
|
|
|
|
m_assetCatalog->OnAssetMessage(assetNotificationMessage);
|
|
|
|
// Run the actual test
|
|
RunNetworkRequest([&source1, &product1]()
|
|
{
|
|
using namespace AzFramework;
|
|
|
|
AZ::u32 assetReferenceCount, pathReferenceCount;
|
|
AZ::Data::AssetId assetId = AZ::Data::AssetId(source1.m_sourceGuid, product1.m_subID);
|
|
AssetSystemRequestBus::Broadcast(&AssetSystemRequestBus::Events::GetUnresolvedProductReferences, assetId, assetReferenceCount, pathReferenceCount);
|
|
|
|
ASSERT_EQ(assetReferenceCount, 1);
|
|
ASSERT_EQ(pathReferenceCount, 3);
|
|
});
|
|
|
|
ASSERT_TRUE(m_assetCatalog->m_called);
|
|
}
|
|
}
|