Merge branch 'aws-lumberyard:development' into SerializeContextToolsConvertHandleAnyValidation

This commit is contained in:
lumberyard-employee-dm
2021-06-17 20:29:08 -05:00
committed by GitHub
2639 changed files with 258816 additions and 150125 deletions
@@ -15,14 +15,14 @@ android {
${SIGNING_CONFIGS}
compileSdkVersion sdkVer
buildToolsVersion buildToolsVer
ndkVersion ndkPlatformVer
lintOptions {
abortOnError false
checkReleaseBuilds false
}
defaultConfig {
minSdkVersion ndkPlatformVer
minSdkVersion minSdkVer
targetSdkVersion sdkVer
${NATIVE_CMAKE_SECTION_DEFAULT_CONFIG}
}
@@ -16,6 +16,5 @@
# For customization when using a Version Control System, please read the
# header note.
# ${GENERATION_TIMESTAMP}
ndk.dir=${ANDROID_NDK_PATH}
sdk.dir=${ANDROID_SDK_PATH}
${CMAKE_DIR_LINE}
@@ -12,10 +12,9 @@ buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.4'
classpath 'com.android.tools.build:gradle:${ANDROID_GRADLE_PLUGIN_VERSION}'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@@ -26,14 +25,14 @@ allprojects {
repositories {
google()
jcenter()
}
}
subprojects {
ext {
minSdkVer = ${MIN_SDK_VER}
sdkVer = ${SDK_VER}
ndkPlatformVer = ${NDK_PLATFORM_VER}
ndkPlatformVer = '${NDK_VERSION}'
buildToolsVer = '${SDK_BUILD_TOOL_VER}'
lyEngineRoot = '${LY_ENGINE_ROOT}'
}
@@ -3,7 +3,7 @@
"AssetProcessor": {
"Settings": {
"Platforms": {
"es3": "enabled"
"android": "enabled"
}
}
}
@@ -150,7 +150,7 @@ namespace AssetBundler
AzFramework::PlatformFlags platformFlags = GetEnabledPlatformFlags(m_data->m_testEngineRoot.c_str(), m_data->m_testEngineRoot.c_str(), DummyProjectName);
AzFramework::PlatformFlags hostPlatformFlag = AzFramework::PlatformHelper::GetPlatformFlag(AzToolsFramework::AssetSystem::GetHostAssetPlatform());
AzFramework::PlatformFlags expectedFlags = AzFramework::PlatformFlags::Platform_ES3 | AzFramework::PlatformFlags::Platform_IOS | AzFramework::PlatformFlags::Platform_PROVO | hostPlatformFlag;
AzFramework::PlatformFlags expectedFlags = AzFramework::PlatformFlags::Platform_ANDROID | AzFramework::PlatformFlags::Platform_IOS | AzFramework::PlatformFlags::Platform_PROVO | hostPlatformFlag;
ASSERT_EQ(platformFlags, expectedFlags);
}
+4 -4
View File
@@ -40,14 +40,14 @@ namespace AssetBundler
TEST_F(AssetBundlerBatchUtilsTest, SplitFilename_MacFile_OutputBaseNameAndPlatform)
{
AZStd::string filePath = "assetInfoFile_osx_gl.xml";
AZStd::string filePath = "assetInfoFile_mac.xml";
AZStd::string baseFilename;
AZStd::string platformIdentifier;
AzToolsFramework::SplitFilename(filePath, baseFilename, platformIdentifier);
ASSERT_EQ(baseFilename, "assetInfoFile");
ASSERT_EQ(platformIdentifier, "osx_gl");
ASSERT_EQ(platformIdentifier, "mac");
}
TEST_F(AssetBundlerBatchUtilsTest, SplitFilename_PcFile_OutputBaseNameAndPlatform)
@@ -64,14 +64,14 @@ namespace AssetBundler
TEST_F(AssetBundlerBatchUtilsTest, SplitFilename_MacFileWithUnderScoreInFileName_OutputBaseNameAndPlatform)
{
AZStd::string filePath = "assetInfoFile_test_osx_gl.xml";
AZStd::string filePath = "assetInfoFile_test_mac.xml";
AZStd::string baseFilename;
AZStd::string platformIdentifier;
AzToolsFramework::SplitFilename(filePath, baseFilename, platformIdentifier);
ASSERT_EQ(baseFilename, "assetInfoFile_test");
ASSERT_EQ(platformIdentifier, "osx_gl");
ASSERT_EQ(platformIdentifier, "mac");
}
TEST_F(AssetBundlerBatchUtilsTest, SplitFilename_PcFileWithUnderScoreInFileName_OutputBaseNameAndPlatform)
@@ -28,6 +28,10 @@ ly_add_target(
AZ::AzToolsFramework
)
if(LY_DEFAULT_PROJECT_PATH)
set_property(TARGET AssetBuilder APPEND PROPERTY VS_DEBUGGER_COMMAND_ARGUMENTS "--project-path=\"${LY_DEFAULT_PROJECT_PATH}\"")
endif()
# Aggregates all combined AssetBuilders into a single LY_ASSET_BUILDERS #define
get_property(asset_builders GLOBAL PROPERTY LY_ASSET_BUILDERS)
string (REPLACE ";" "," asset_builders "${asset_builders}")
@@ -78,17 +78,17 @@ namespace AssetBuilderSDK
{
return AssetBuilderSDK::Platform_PC;
}
if (azstricmp(newPlatformName, "es3") == 0)
if (azstricmp(newPlatformName, "android") == 0)
{
return AssetBuilderSDK::Platform_ES3;
return AssetBuilderSDK::Platform_ANDROID;
}
if (azstricmp(newPlatformName, "ios") == 0)
{
return AssetBuilderSDK::Platform_IOS;
}
if (azstricmp(newPlatformName, "osx_gl") == 0)
if (azstricmp(newPlatformName, "mac") == 0)
{
return AssetBuilderSDK::Platform_OSX;
return AssetBuilderSDK::Platform_MAC;
}
if (azstricmp(newPlatformName, "provo") == 0)
{
@@ -115,12 +115,12 @@ namespace AssetBuilderSDK
{
case AssetBuilderSDK::Platform_PC:
return "pc";
case AssetBuilderSDK::Platform_ES3:
return "es3";
case AssetBuilderSDK::Platform_ANDROID:
return "android";
case AssetBuilderSDK::Platform_IOS:
return "ios";
case AssetBuilderSDK::Platform_OSX:
return "osx_gl";
case AssetBuilderSDK::Platform_MAC:
return "mac";
case AssetBuilderSDK::Platform_PROVO:
return "provo";
case AssetBuilderSDK::Platform_SALEM:
@@ -148,15 +148,15 @@ namespace AssetBuilderSDK
{
Platform_NONE = 0x00,
Platform_PC = 0x01,
Platform_ES3 = 0x02,
Platform_ANDROID = 0x02,
Platform_IOS = 0x04,
Platform_OSX = 0x08,
Platform_MAC = 0x08,
Platform_PROVO = 0x20,
Platform_SALEM = 0x40,
Platform_JASPER = 0x80,
//! if you add a new platform entry to this enum, you must add it to allplatforms as well otherwise that platform would not be considered valid.
AllPlatforms = Platform_PC | Platform_ES3 | Platform_IOS | Platform_OSX | Platform_PROVO | Platform_SALEM | Platform_JASPER
AllPlatforms = Platform_PC | Platform_ANDROID | Platform_IOS | Platform_MAC | Platform_PROVO | Platform_SALEM | Platform_JASPER
};
#endif // defined(ENABLE_LEGACY_PLATFORMFLAGS_SUPPORT)
//! Map data structure to holder parameters that are passed into a job for ProcessJob requests.
@@ -503,7 +503,7 @@ namespace AssetBuilderSDK
AZ_CLASS_ALLOCATOR(PlatformInfo, AZ::SystemAllocator, 0);
AZ_TYPE_INFO(PlatformInfo, "{F7DA39A5-C319-4552-954B-3479E2454D3F}");
AZStd::string m_identifier; ///< like "pc" or "es3" or "ios"...
AZStd::string m_identifier; ///< like "pc" or "android" or "ios"...
AZStd::unordered_set<AZStd::string> m_tags; ///< The tags like "console" or "tools" on that platform
PlatformInfo() = default;
+5 -2
View File
@@ -125,6 +125,10 @@ ly_add_target(
AZ::AssetProcessorBatch.Static
)
if(LY_DEFAULT_PROJECT_PATH)
set_property(TARGET AssetProcessor AssetProcessorBatch APPEND PROPERTY VS_DEBUGGER_COMMAND_ARGUMENTS "--project-path=\"${LY_DEFAULT_PROJECT_PATH}\"")
endif()
# Adds the AssetProcessorBatch target as a C preprocessor define so that it can be used as a Settings Registry
# specialization in order to look up the generated .setreg which contains the dependencies
# specified for the target.
@@ -255,8 +259,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
ly_add_googletest(
NAME AZ::AssetProcessor.Tests
TEST_COMMAND $<TARGET_FILE:AZ::AssetProcessor.Tests> --unittest
TEST_COMMAND $<TARGET_FILE:AZ::AssetProcessor.Tests> --unittest --gtest_filter=-*.SUITE_sandbox*
)
endif()
@@ -11,9 +11,127 @@
*/
#include <native/FileWatcher/FileWatcher.h>
#include <QDirIterator>
#include <QHash>
#include <QMutex>
#include <unistd.h>
#include <sys/inotify.h>
static constexpr int s_handleToFolderMapLockTimeout = 1000; // 1 sec timeout for obtaining the handle to folder map lock
static constexpr size_t s_iNotifyMaxEntries = 1024 * 16; // Control the maximum number of entries (from inotify) that can be read at one time
static constexpr size_t s_iNotifyEventSize = sizeof(struct inotify_event);
static constexpr size_t s_iNotifyReadBufferSize = s_iNotifyMaxEntries * s_iNotifyEventSize;
struct FolderRootWatch::PlatformImplementation
{
PlatformImplementation() { }
PlatformImplementation() = default;
int m_iNotifyHandle = -1;
QMutex m_handleToFolderMapLock;
QHash<int, QString> m_handleToFolderMap;
bool Initialize()
{
if (m_iNotifyHandle < 0)
{
m_iNotifyHandle = inotify_init();
}
return (m_iNotifyHandle >= 0);
}
void Finalize()
{
if (m_iNotifyHandle >= 0)
{
if (!m_handleToFolderMapLock.tryLock(s_handleToFolderMapLockTimeout))
{
AZ_Error("FileWatcher", false, "Unable to obtain inotify handle lock on thread");
return;
}
QHashIterator<int, QString> iter(m_handleToFolderMap);
while (iter.hasNext())
{
iter.next();
int watchHandle = iter.key();
inotify_rm_watch(m_iNotifyHandle, watchHandle);
}
m_handleToFolderMap.clear();
m_handleToFolderMapLock.unlock();
::close(m_iNotifyHandle);
m_iNotifyHandle = -1;
}
}
void AddWatchFolder(QString folder)
{
if (m_iNotifyHandle >= 0)
{
// Clean up the path before accepting it as a watch folder
QString cleanPath = QDir::cleanPath(folder);
// Add the folder to watch and track it
int watchHandle = inotify_add_watch(m_iNotifyHandle,
cleanPath.toUtf8().constData(),
IN_CREATE | IN_CLOSE_WRITE | IN_DELETE | IN_DELETE_SELF | IN_MODIFY);
if (!m_handleToFolderMapLock.tryLock(s_handleToFolderMapLockTimeout))
{
AZ_Error("FileWatcher", false, "Unable to obtain inotify handle lock on thread");
return;
}
m_handleToFolderMap[watchHandle] = cleanPath;
m_handleToFolderMapLock.unlock();
// Add all the subfolders to watch and track them
QDirIterator dirIter(folder, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
while (dirIter.hasNext())
{
QString dirName = dirIter.next();
if (dirName.endsWith("/.") || dirName.endsWith("/.."))
{
continue;
}
int watchHandle = inotify_add_watch(m_iNotifyHandle,
dirName.toUtf8().constData(),
IN_CREATE | IN_CLOSE_WRITE | IN_DELETE | IN_DELETE_SELF | IN_MODIFY);
if (!m_handleToFolderMapLock.tryLock(s_handleToFolderMapLockTimeout))
{
AZ_Error("FileWatcher", false, "Unable to obtain inotify handle lock on thread");
return;
}
m_handleToFolderMap[watchHandle] = dirName;
m_handleToFolderMapLock.unlock();
}
}
}
void RemoveWatchFolder(int watchHandle)
{
if (m_iNotifyHandle >= 0)
{
if (!m_handleToFolderMapLock.tryLock(s_handleToFolderMapLockTimeout))
{
AZ_Error("FileWatcher", false, "Unable to obtain inotify handle lock on thread");
return;
}
QHash<int, QString>::iterator handleToRemove = m_handleToFolderMap.find(watchHandle);
if (handleToRemove != m_handleToFolderMap.end())
{
inotify_rm_watch(m_iNotifyHandle, watchHandle);
m_handleToFolderMap.erase(handleToRemove);
}
m_handleToFolderMapLock.unlock();
}
}
};
//////////////////////////////////////////////////////////////////////////////
@@ -36,17 +154,86 @@ FolderRootWatch::~FolderRootWatch()
bool FolderRootWatch::Start()
{
// TODO: Implement for Linux
return false;
// inotify will be used by linux to monitor file changes within directories under the root folder
if (!m_platformImpl->Initialize())
{
return false;
}
m_platformImpl->AddWatchFolder(m_root);
m_shutdownThreadSignal = false;
m_thread = std::thread([this]() { WatchFolderLoop(); });
return true;
}
void FolderRootWatch::Stop()
{
// TODO: Implement for Linux
m_shutdownThreadSignal = true;
m_platformImpl->Finalize();
if (m_thread.joinable())
{
m_thread.join(); // wait for the thread to finish
m_thread = std::thread(); //destroy
}
}
void FolderRootWatch::WatchFolderLoop()
{
// TODO: Implement for Linux
char eventBuffer[s_iNotifyReadBufferSize];
while (!m_shutdownThreadSignal)
{
ssize_t bytesRead = ::read(m_platformImpl->m_iNotifyHandle, eventBuffer, s_iNotifyReadBufferSize);
if (bytesRead < 0)
{
// Break out of the loop when the notify handle was closed (outside of this thread)
break;
}
else if (bytesRead > 0)
{
for (size_t index=0; index<bytesRead;)
{
struct inotify_event *event = ( struct inotify_event * ) &eventBuffer[ index ];
const char* eventName = event->name;
if (event->mask & (IN_CREATE | IN_DELETE | IN_MODIFY | IN_MOVE ))
{
QString pathStr = QString("%1%2%3").arg(m_platformImpl->m_handleToFolderMap[event->wd], QDir::separator(), event->name);
if (event->mask & (IN_CREATE | IN_MOVED_TO))
{
if ( event->mask & IN_ISDIR )
{
// New Directory, add it to the watch
m_platformImpl->AddWatchFolder(pathStr);
}
else
{
ProcessNewFileEvent(pathStr);
}
}
else if (event->mask & (IN_DELETE | IN_MOVED_FROM))
{
if (event->mask & IN_ISDIR)
{
// Directory Deleted, remove it from the watch
m_platformImpl->RemoveWatchFolder(event->wd);
}
else
{
ProcessDeleteFileEvent(pathStr);
}
}
else if ((event->mask & IN_MODIFY) && ((event->mask & IN_ISDIR) != IN_ISDIR))
{
ProcessModifyFileEvent(pathStr);
}
}
index += s_iNotifyEventSize + event->len;
}
}
}
}
@@ -1,134 +0,0 @@
/*
* 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 <native/FileWatcher/FileWatcher.h>
#include <AzCore/PlatformIncl.h>
struct FolderRootWatch::PlatformImplementation
{
PlatformImplementation() : m_directoryHandle(nullptr), m_ioHandle(nullptr) { }
HANDLE m_directoryHandle;
HANDLE m_ioHandle;
};
//////////////////////////////////////////////////////////////////////////////
/// FolderWatchRoot
FolderRootWatch::FolderRootWatch(const QString rootFolder)
: m_root(rootFolder)
, m_shutdownThreadSignal(false)
, m_fileWatcher(nullptr)
, m_platformImpl(new PlatformImplementation())
{
}
FolderRootWatch::~FolderRootWatch()
{
// Destructor is required in here since this file contains the definition of struct PlatformImplementation
Stop();
delete m_platformImpl;
}
bool FolderRootWatch::Start()
{
m_platformImpl->m_directoryHandle = ::CreateFileW(m_root.toStdWString().data(), FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, nullptr);
if (m_platformImpl->m_directoryHandle != INVALID_HANDLE_VALUE)
{
m_platformImpl->m_ioHandle = ::CreateIoCompletionPort(m_platformImpl->m_directoryHandle, nullptr, 1, 0);
if (m_platformImpl->m_ioHandle != INVALID_HANDLE_VALUE)
{
m_shutdownThreadSignal = false;
m_thread = std::thread(std::bind(&FolderRootWatch::WatchFolderLoop, this));
return true;
}
}
return false;
}
void FolderRootWatch::Stop()
{
m_shutdownThreadSignal = true;
CloseHandle(m_platformImpl->m_ioHandle);
m_platformImpl->m_ioHandle = nullptr;
if (m_thread.joinable())
{
m_thread.join(); // wait for the thread to finish
m_thread = std::thread(); //destroy
}
CloseHandle(m_platformImpl->m_directoryHandle);
m_platformImpl->m_directoryHandle = nullptr;
}
void FolderRootWatch::WatchFolderLoop()
{
FILE_NOTIFY_INFORMATION aFileNotifyInformationList[50000];
QString path;
OVERLAPPED aOverlapped;
LPOVERLAPPED pOverlapped;
DWORD dwByteCount;
ULONG_PTR ulKey;
while (!m_shutdownThreadSignal)
{
::memset(aFileNotifyInformationList, 0, sizeof(aFileNotifyInformationList));
::memset(&aOverlapped, 0, sizeof(aOverlapped));
if (::ReadDirectoryChangesW(m_platformImpl->m_directoryHandle, aFileNotifyInformationList, sizeof(aFileNotifyInformationList), true, FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_FILE_NAME, nullptr, &aOverlapped, nullptr))
{
//wait for up to a second for I/O to signal
dwByteCount = 0;
if (::GetQueuedCompletionStatus(m_platformImpl->m_ioHandle, &dwByteCount, &ulKey, &pOverlapped, INFINITE))
{
//if we are signaled to shutdown bypass
if (!m_shutdownThreadSignal && ulKey)
{
if (dwByteCount)
{
int offset = 0;
FILE_NOTIFY_INFORMATION* pFileNotifyInformation = aFileNotifyInformationList;
do
{
pFileNotifyInformation = (FILE_NOTIFY_INFORMATION*)((char*)pFileNotifyInformation + offset);
path.clear();
path.append(m_root);
path.append(QString::fromWCharArray(pFileNotifyInformation->FileName, pFileNotifyInformation->FileNameLength / 2));
QString file = QDir::toNativeSeparators(QDir::cleanPath(path));
switch (pFileNotifyInformation->Action)
{
case FILE_ACTION_ADDED:
case FILE_ACTION_RENAMED_NEW_NAME:
ProcessNewFileEvent(file);
break;
case FILE_ACTION_REMOVED:
case FILE_ACTION_RENAMED_OLD_NAME:
ProcessDeleteFileEvent(file);
break;
case FILE_ACTION_MODIFIED:
ProcessModifyFileEvent(file);
break;
}
offset = pFileNotifyInformation->NextEntryOffset;
} while (offset);
}
}
}
}
}
}
@@ -19,6 +19,7 @@ set(FILES
native/AssetManager/AssetRequestHandler.cpp
native/AssetManager/AssetRequestHandler.h
native/AssetManager/assetScanFolderInfo.h
native/AssetManager/assetScanFolderInfo.cpp
native/AssetManager/assetScanner.cpp
native/AssetManager/assetScanner.h
native/AssetManager/assetScannerWorker.cpp
@@ -655,6 +655,80 @@ namespace AssetProcessor
return true;
}
bool AssetCatalog::GenerateRelativeSourcePath(
const AZStd::string& sourcePath, AZStd::string& relativePath, AZStd::string& rootFolder)
{
QString normalizedSourcePath = AssetUtilities::NormalizeFilePath(sourcePath.c_str());
QDir inputPath(normalizedSourcePath);
QString scanFolder;
QString relativeName;
bool validResult = false;
AZ_TracePrintf(AssetProcessor::DebugChannel, "ProcessGenerateRelativeSourcePathRequest: %s...\n", sourcePath.c_str());
if (sourcePath.empty())
{
// For an empty input path, do nothing, we'll return an empty, invalid result.
// (We check fullPath instead of inputPath, because an empty fullPath actually produces "." for inputPath)
}
else if (inputPath.isAbsolute())
{
// For an absolute path, try to convert it to a relative path, based on the existing scan folders.
// To get the inputPath, we use absolutePath() instead of path() so that any . or .. entries get collapsed.
validResult = m_platformConfig->ConvertToRelativePath(inputPath.absolutePath(), relativeName, scanFolder);
}
else if (inputPath.isRelative())
{
// For a relative path, concatenate it with each scan folder, and see if a valid relative path emerges.
int scanFolders = m_platformConfig->GetScanFolderCount();
for (int scanIdx = 0; scanIdx < scanFolders; scanIdx++)
{
auto& scanInfo = m_platformConfig->GetScanFolderAt(scanIdx);
QDir possibleRoot(scanInfo.ScanPath());
QDir possibleAbsolutePath = possibleRoot.filePath(normalizedSourcePath);
// To get the inputPath, we use absolutePath() instead of path() so that any . or .. entries get collapsed.
if (m_platformConfig->ConvertToRelativePath(possibleAbsolutePath.absolutePath(), relativeName, scanFolder))
{
validResult = true;
break;
}
}
}
// The input has produced a valid relative path. However, the path might match multiple nested scan folders,
// so look to see if a higher-priority folder has a better match.
if (validResult)
{
QString overridingFile = m_platformConfig->GetOverridingFile(relativeName, scanFolder);
if (!overridingFile.isEmpty())
{
overridingFile = AssetUtilities::NormalizeFilePath(overridingFile);
validResult = m_platformConfig->ConvertToRelativePath(overridingFile, relativeName, scanFolder);
}
}
if (!validResult)
{
// if we are here it means we have failed to determine the relativePath, so we will send back the original path
AZ_TracePrintf(AssetProcessor::DebugChannel,
"GenerateRelativeSourcePath found no valid result, returning original path: %s...\n", sourcePath.c_str());
rootFolder.clear();
relativePath.clear();
relativePath = sourcePath;
return false;
}
relativePath = relativeName.toUtf8().data();
rootFolder = scanFolder.toUtf8().data();
AZ_Assert(!relativePath.empty(), "ConvertToRelativePath returned true, but relativePath is empty");
return true;
}
bool AssetCatalog::GetFullSourcePathFromRelativeProductPath(const AZStd::string& relPath, AZStd::string& fullSourcePath)
{
ProcessGetFullSourcePathFromRelativeProductPathRequest(relPath, fullSourcePath);
@@ -95,6 +95,12 @@ namespace AssetProcessor
const char* GetAbsoluteDevGameFolderPath() override;
const char* GetAbsoluteDevRootFolderPath() override;
bool GetRelativeProductPathFromFullSourceOrProductPath(const AZStd::string& fullPath, AZStd::string& relativeProductPath) override;
//! Given a partial or full source file path, respond with its relative path and the watch folder it is relative to.
//! The input source path does not need to exist, so this can be used for new files that haven't been saved yet.
bool GenerateRelativeSourcePath(
const AZStd::string& sourcePath, AZStd::string& relativePath, AZStd::string& watchFolder) override;
bool GetFullSourcePathFromRelativeProductPath(const AZStd::string& relPath, AZStd::string& fullSourcePath) override;
bool GetAssetInfoById(const AZ::Data::AssetId& assetId, const AZ::Data::AssetType& assetType, const AZStd::string& platformName, AZ::Data::AssetInfo& assetInfo, AZStd::string& rootFilePath) override;
bool GetSourceInfoBySourcePath(const char* sourcePath, AZ::Data::AssetInfo& assetInfo, AZStd::string& watchFolder) override;
@@ -104,6 +104,27 @@ namespace
return GetRelativeProductPathFromFullSourceOrProductPathResponse(relPathFound, relProductPath);
}
GenerateRelativeSourcePathResponse HandleGenerateRelativeSourcePathRequest(
MessageData<GenerateRelativeSourcePathRequest> messageData)
{
bool relPathFound = false;
AZStd::string relPath;
AZStd::string watchFolder;
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
relPathFound, &AzToolsFramework::AssetSystemRequestBus::Events::GenerateRelativeSourcePath,
messageData.m_message->m_sourcePath, relPath, watchFolder);
if (!relPathFound)
{
AZ_TracePrintf(
AssetProcessor::ConsoleChannel, "Could not find relative source path for the source file (%s).",
messageData.m_message->m_sourcePath.c_str());
}
return GenerateRelativeSourcePathResponse(relPathFound, relPath, watchFolder);
}
SourceAssetInfoResponse HandleSourceAssetInfoRequest(MessageData<SourceAssetInfoRequest> messageData)
{
SourceAssetInfoResponse response;
@@ -407,6 +428,7 @@ AssetRequestHandler::AssetRequestHandler()
m_requestRouter.RegisterMessageHandler(&HandleGetFullSourcePathFromRelativeProductPathRequest);
m_requestRouter.RegisterMessageHandler(&HandleGetRelativeProductPathFromFullSourceOrProductPathRequest);
m_requestRouter.RegisterMessageHandler(&HandleGenerateRelativeSourcePathRequest);
m_requestRouter.RegisterMessageHandler(&HandleSourceAssetInfoRequest);
m_requestRouter.RegisterMessageHandler(&HandleSourceAssetProductsInfoRequest);
m_requestRouter.RegisterMessageHandler(&HandleGetScanFoldersRequest);
@@ -445,10 +445,10 @@ namespace AssetProcessor
bool isExcludedDependency = dependencyPathSearch.starts_with(ExcludedDependenciesSymbol);
dependencyPathSearch = isExcludedDependency ? dependencyPathSearch.substr(1) : dependencyPathSearch;
bool isExactDependency = !AzFramework::StringFunc::Replace(dependencyPathSearch, '*', '%');
SanitizeForDatabase(dependencyPathSearch);
if (cleanedupDependency.m_dependencyType == AssetBuilderSDK::ProductPathDependencyType::ProductFile)
{
SanitizeForDatabase(dependencyPathSearch);
AzToolsFramework::AssetDatabase::ProductDatabaseEntryContainer productInfoContainer;
QString productNameWithPlatform = QString("%1%2%3").arg(platform.c_str(), AZ_CORRECT_DATABASE_SEPARATOR_STRING, dependencyPathSearch.c_str());
@@ -508,6 +508,10 @@ namespace AssetProcessor
}
else
{
// For source assets, the casing of the input path must be maintained. Just fix up the path separators.
AZStd::replace(dependencyPathSearch.begin(), dependencyPathSearch.end(), AZ_WRONG_DATABASE_SEPARATOR, AZ_CORRECT_DATABASE_SEPARATOR);
AzFramework::StringFunc::Replace(dependencyPathSearch, AZ_DOUBLE_CORRECT_DATABASE_SEPARATOR, AZ_CORRECT_DATABASE_SEPARATOR_STRING);
// See if path matches any source files
AzToolsFramework::AssetDatabase::SourceDatabaseEntryContainer sourceInfoContainer;
@@ -190,44 +190,55 @@ Please note that only those seed files will get updated that are active for your
void SourceFileRelocator::HandleMetaDataFiles(QStringList pathMatches, QHash<QString, int>& sourceIndexMap, const ScanFolderInfo* scanFolderInfo, SourceFileRelocationContainer& metadataFiles, bool excludeMetaDataFiles) const
{
QSet<QString> metaDataFileEntries;
for (QString file : pathMatches)
// Remove all the metadata files
if (excludeMetaDataFiles)
{
pathMatches.erase(AZStd::remove_if(pathMatches.begin(), pathMatches.end(), [this](const QString& file)
{
for (int idx = 0; idx < m_platformConfig->MetaDataFileTypesCount(); idx++)
{
const auto& [metadataType, extension] = m_platformConfig->GetMetaDataFileTypeAt(idx);
if (file.endsWith("." + metadataType, Qt::CaseInsensitive))
{
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Metadata file %s will be ignored because --excludeMetadataFiles was specified in the command line.\n",
file.toUtf8().constData());
return true;
}
}
return false;
}),
pathMatches.end());
}
for (const QString& file : pathMatches)
{
for (int idx = 0; idx < m_platformConfig->MetaDataFileTypesCount(); idx++)
{
QPair<QString, QString> metaInfo = m_platformConfig->GetMetaDataFileTypeAt(idx);
if (file.endsWith("." + metaInfo.first, Qt::CaseInsensitive))
const auto& [metadataType, extension] = m_platformConfig->GetMetaDataFileTypeAt(idx);
if (file.endsWith("." + metadataType, Qt::CaseInsensitive))
{
//it is a metadata file
if (excludeMetaDataFiles)
const QString normalizedFilePath = AssetUtilities::NormalizeFilePath(file);
if (!metaDataFileEntries.contains(normalizedFilePath))
{
AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Metadata file %s will be ignored because --excludeMetadataFiles was specified in the command line.\n",
file.toUtf8().constData());
break; // don't check it against other metafile entries, we've already ascertained its a metafile.
}
else
{
QString normalizedFilePath = AssetUtilities::NormalizeFilePath(file);
if (metaDataFileEntries.find(normalizedFilePath) == metaDataFileEntries.end())
{
SourceFileRelocationInfo metaDataFile(file.toUtf8().data(), scanFolderInfo);
metaDataFile.m_isMetaDataFile = true;
metadataFiles.emplace_back(metaDataFile);
metaDataFileEntries.insert(normalizedFilePath);
}
SourceFileRelocationInfo metaDataFile(file.toUtf8().data(), scanFolderInfo);
metaDataFile.m_isMetaDataFile = true;
metadataFiles.emplace_back(metaDataFile);
metaDataFileEntries.insert(normalizedFilePath);
}
}
else if (!excludeMetaDataFiles && (file.endsWith("." + metaInfo.second, Qt::CaseInsensitive) || metaInfo.second.isEmpty()))
else if (!excludeMetaDataFiles && (file.endsWith("." + extension, Qt::CaseInsensitive) || extension.isEmpty()))
{
// if we are here it implies that a metadata file might exists for this source file,
// add metadata file only if it exists and is not added already
AZStd::string metadataFilePath(file.toUtf8().data());
if (metaInfo.second.isEmpty())
if (extension.isEmpty())
{
metadataFilePath.append(AZStd::string::format(".%s", metaInfo.first.toUtf8().data()));
metadataFilePath.append(AZStd::string::format(".%s", metadataType.toUtf8().data()));
}
else
{
AZ::StringFunc::Path::ReplaceExtension(metadataFilePath, metaInfo.first.toUtf8().data());
AZ::StringFunc::Path::ReplaceExtension(metadataFilePath, metadataType.toUtf8().data());
};
// The metadata file can have a different case than the source file,
@@ -1697,7 +1697,10 @@ namespace AssetProcessor
if(productFileInfo.absoluteDir().entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot).empty())
{
productFileInfo.absoluteDir().rmdir(".");
const QDir productDir = productFileInfo.absoluteDir();
QDir parentDir = productDir;
parentDir.cdUp();
successfullyRemoved &= parentDir.rmdir(productDir.dirName());
}
if (successfullyRemoved)
@@ -57,6 +57,9 @@ namespace AzFramework
class GetRelativeProductPathFromFullSourceOrProductPathRequest;
class GetRelativeProductPathFromFullSourceOrProductPathResponse;
class GenerateRelativeSourcePathRequest;
class GenerateRelativeSourcePathResponse;
class GetFullSourcePathFromRelativeProductPathRequest;
class GetFullSourcePathFromRelativeProductPathResponse;
class AssetNotificationMessage;
@@ -104,6 +107,8 @@ namespace AssetProcessor
using GetAbsoluteAssetDatabaseLocationResponse = AzToolsFramework::AssetSystem::GetAbsoluteAssetDatabaseLocationResponse;
using GetRelativeProductPathFromFullSourceOrProductPathRequest = AzFramework::AssetSystem::GetRelativeProductPathFromFullSourceOrProductPathRequest;
using GetRelativeProductPathFromFullSourceOrProductPathResponse = AzFramework::AssetSystem::GetRelativeProductPathFromFullSourceOrProductPathResponse;
using GenerateRelativeSourcePathRequest = AzFramework::AssetSystem::GenerateRelativeSourcePathRequest;
using GenerateRelativeSourcePathResponse = AzFramework::AssetSystem::GenerateRelativeSourcePathResponse;
using GetFullSourcePathFromRelativeProductPathRequest = AzFramework::AssetSystem::GetFullSourcePathFromRelativeProductPathRequest;
using GetFullSourcePathFromRelativeProductPathResponse = AzFramework::AssetSystem::GetFullSourcePathFromRelativeProductPathResponse;
@@ -0,0 +1,42 @@
/*
* 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 <native/AssetManager/assetScanFolderInfo.h>
#include <native/utilities/assetUtils.h>
namespace AssetProcessor
{
ScanFolderInfo::ScanFolderInfo(
QString path,
QString displayName,
QString portableKey,
bool isRoot,
bool recurseSubFolders,
AZStd::vector<AssetBuilderSDK::PlatformInfo> platforms,
int order,
AZ::s64 scanFolderID,
bool canSaveNewAssets)
: m_scanPath(path)
, m_displayName(displayName)
, m_portableKey (portableKey)
, m_isRoot(isRoot)
, m_recurseSubFolders(recurseSubFolders)
, m_order(order)
, m_scanFolderID(scanFolderID)
, m_platforms(platforms)
, m_canSaveNewAssets(canSaveNewAssets)
{
m_scanPath = AssetUtilities::NormalizeFilePath(m_scanPath);
// note that m_scanFolderID is 0 unless its filled in from the DB.
}
} // end namespace AssetProcessor
@@ -33,19 +33,7 @@ namespace AssetProcessor
AZStd::vector<AssetBuilderSDK::PlatformInfo> platforms = AZStd::vector<AssetBuilderSDK::PlatformInfo>{},
int order = 0,
AZ::s64 scanFolderID = 0,
bool canSaveNewAssets = false)
: m_scanPath(path)
, m_displayName(displayName)
, m_portableKey (portableKey)
, m_isRoot(isRoot)
, m_recurseSubFolders(recurseSubFolders)
, m_order(order)
, m_scanFolderID(scanFolderID)
, m_platforms(platforms)
, m_canSaveNewAssets(canSaveNewAssets)
{
// note that m_scanFolderID is 0 unless its filled in from the DB.
}
bool canSaveNewAssets = false);
ScanFolderInfo() = default;
ScanFolderInfo(const ScanFolderInfo& other) = default;
@@ -224,20 +224,28 @@ namespace AssetProcessor
dbConn->SetScanFolder(newScanFolder);
}
// build some default configs.
void BuildConfig(const QDir& tempPath, AssetDatabaseConnection* dbConn, PlatformConfiguration& config)
virtual void AddScanFolders(
const QDir& tempPath, AssetDatabaseConnection* dbConn, PlatformConfiguration& config,
const AZStd::vector<AssetBuilderSDK::PlatformInfo>& platforms)
{
config.EnablePlatform({ "pc" ,{ "desktop", "renderer" } }, true);
config.EnablePlatform({ "es3" ,{ "mobile", "renderer" } }, true);
config.EnablePlatform({ "fandango" ,{ "console", "renderer" } }, false);
AZStd::vector<AssetBuilderSDK::PlatformInfo> platforms;
config.PopulatePlatformsForScanFolder(platforms);
// PATH DisplayName PortKey root recurse platforms order
AddScanFolder(ScanFolderInfo(tempPath.filePath("subfolder4"), "subfolder4", "subfolder4", false, false, platforms, -6), config, dbConn); // subfolder 4 overrides subfolder3
AddScanFolder(ScanFolderInfo(tempPath.filePath("subfolder3"), "subfolder3", "subfolder3", false, false, platforms, -5), config, dbConn); // subfolder 3 overrides subfolder2
AddScanFolder(ScanFolderInfo(tempPath.filePath("subfolder2"), "subfolder2", "subfolder2", false, true, platforms, -2), config, dbConn); // subfolder 2 overrides subfolder1
AddScanFolder(ScanFolderInfo(tempPath.filePath("subfolder1"), "subfolder1", "subfolder1", false, true, platforms, -1), config, dbConn); // subfolder1 overrides root
AddScanFolder(ScanFolderInfo(tempPath.absolutePath(), "temp", "tempfolder", true, false, platforms, 0), config, dbConn); // add the root
}
// build some default configs.
void BuildConfig(const QDir& tempPath, AssetDatabaseConnection* dbConn, PlatformConfiguration& config)
{
config.EnablePlatform({ "pc" ,{ "desktop", "renderer" } }, true);
config.EnablePlatform({ "android" ,{ "mobile", "renderer" } }, true);
config.EnablePlatform({ "fandango" ,{ "console", "renderer" } }, false);
AZStd::vector<AssetBuilderSDK::PlatformInfo> platforms;
config.PopulatePlatformsForScanFolder(platforms);
AddScanFolders(tempPath, dbConn, config, platforms);
config.AddMetaDataType("exportsettings", QString());
@@ -246,22 +254,22 @@ namespace AssetProcessor
AssetRecognizer rec;
AssetPlatformSpec specpc;
AssetPlatformSpec speces3;
AssetPlatformSpec specandroid;
speces3.m_extraRCParams = "somerandomparam";
specandroid.m_extraRCParams = "somerandomparam";
rec.m_name = "random files";
rec.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.random", AssetBuilderSDK::AssetBuilderPattern::Wildcard);
rec.m_platformSpecs.insert("pc", specpc);
config.AddRecognizer(rec);
specpc.m_extraRCParams = ""; // blank must work
speces3.m_extraRCParams = "testextraparams";
specandroid.m_extraRCParams = "testextraparams";
const char* builderTxt1Name = "txt files";
rec.m_name = builderTxt1Name;
rec.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.txt", AssetBuilderSDK::AssetBuilderPattern::Wildcard);
rec.m_platformSpecs.insert("pc", specpc);
rec.m_platformSpecs.insert("es3", speces3);
rec.m_platformSpecs.insert("android", specandroid);
config.AddRecognizer(rec);
@@ -272,7 +280,7 @@ namespace AssetProcessor
ignore_rec.m_name = "ignore files";
ignore_rec.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.ignore", AssetBuilderSDK::AssetBuilderPattern::Wildcard);
ignore_rec.m_platformSpecs.insert("pc", specpc);
ignore_rec.m_platformSpecs.insert("es3", ignore_spec);
ignore_rec.m_platformSpecs.insert("android", ignore_spec);
config.AddRecognizer(ignore_rec);
ExcludeAssetRecognizer excludeRecogniser;
@@ -359,7 +367,8 @@ namespace AssetProcessor
return false;
}
// Calls the GetFullSourcePathFromRelativeProductPath function and checks the return results, returning true if it matches both of the expected results
// Calls the GetFullSourcePathFromRelativeProductPath function and checks the return results, returning true if it matches both of
// the expected results
bool TestGetFullSourcePath(const QString& fileToCheck, const QDir& tempPath, bool expectToFind, const char* expectedPath)
{
bool fullPathfound = false;
@@ -531,6 +540,177 @@ namespace AssetProcessor
ASSERT_TRUE(TestGetRelativeProductPath(fileToCheck, true, { "aaa/basefile.txt" }));
}
class AssetCatalogTestRelativeSourcePath : public AssetCatalogTest
{
public:
QDir GetRoot()
{
// Return an OS-friendly absolute root directory for our tests ("C:/sourceRoot" or "/sourceRoot"). It doesn't
// need to exist, it just needs to be an absolute path.
return QDir::root().filePath("sourceRoot");
}
// Set up custom scan folders for the "relative source path" tests, so that we can try out specific combinations of watch folders
void AddScanFolders(
[[maybe_unused]] const QDir& tempPath, AssetDatabaseConnection* dbConn, PlatformConfiguration& config,
const AZStd::vector<AssetBuilderSDK::PlatformInfo>& platforms) override
{
QDir root = GetRoot();
// This will set up the following watch folders, in highest to lowest priority:
// /sourceRoot/recurseNested/nested (recurse)
// /sourceRoot/noRecurse (no recurse)
// /sourceRoot/recurseNotNested (recurse)
// /sourceRoot/recurseNested (recurse)
AddScanFolder(
ScanFolderInfo(root.filePath("recurseNested/nested"), "nested", "nested", false, true, platforms, -4), config, dbConn);
AddScanFolder(
ScanFolderInfo(root.filePath("noRecurse"), "noRecurse", "noRecurse", false, false, platforms, -3), config, dbConn);
AddScanFolder(
ScanFolderInfo(root.filePath("recurseNotNested"), "recurseNotNested", "recurseNotNested", false, true, platforms, -2),
config, dbConn);
AddScanFolder(
ScanFolderInfo(root.filePath("recurseNested"), "recurseNested", "recurseNested", false, true, platforms, -1),
config, dbConn);
}
// Calls the GenerateRelativeSourcePath function and validates that the results match the expected inputs.
void TestGetRelativeSourcePath(
const AZStd::string& sourcePath, bool expectedToFind, const AZStd::string& expectedPath, const AZStd::string& expectedRoot)
{
bool relPathFound = false;
AZStd::string relPath;
AZStd::string rootFolder;
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
relPathFound, &AzToolsFramework::AssetSystem::AssetSystemRequest::GenerateRelativeSourcePath, sourcePath,
relPath, rootFolder);
EXPECT_EQ(relPathFound, expectedToFind);
EXPECT_EQ(relPath, expectedPath);
EXPECT_EQ(rootFolder, expectedRoot);
}
};
TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_EmptySourcePath_ReturnsNoMatch)
{
// Test passes in an empty source path, which shouldn't produce a valid result.
// Input: empty source path
// Output: empty, not found result
TestGetRelativeSourcePath("", false, "", "");
}
TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_AbsolutePathOutsideWatchFolders_ReturnsNoMatch)
{
// Test passes in an invalid absolute source path, which shouldn't produce a valid result.
// Input: "/sourceRoot/noWatchFolder/test.txt"
// Output: not found result, which also returns the input as the relative file name
QDir watchFolder = GetRoot().filePath("noWatchFolder/");
QString fileToCheck = watchFolder.filePath("test.txt");
TestGetRelativeSourcePath(fileToCheck.toUtf8().constData(), false, fileToCheck.toUtf8().constData(), "");
}
TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_AbsolutePathUnderWatchFolder_ReturnsRelativePath)
{
// Test passes in a valid absolute source path, which should produce a valid relative path
// Input: "/sourceRoot/noRecurse/test.txt"
// Output: "test.txt" in folder "/sourceRoot/noRecurse/"
QDir watchFolder = GetRoot().filePath("noRecurse/");
QString fileToCheck = watchFolder.filePath("test.txt");
TestGetRelativeSourcePath(fileToCheck.toUtf8().constData(), true, "test.txt", watchFolder.path().toUtf8().constData());
}
TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_AbsolutePathUnderNestedWatchFolders_ReturnsRelativePath)
{
// Test passes in a valid absolute source path that matches a watch folder and a nested watch folder.
// The output relative path should match the nested folder, because the nested folder has a higher priority registered with the AP.
// Input: "/sourceRoot/recurseNested/nested/test.txt"
// Output: "test.txt" in folder "/sourceRoot/recurseNested/nested/"
QDir watchFolder = GetRoot().filePath("recurseNested/nested/");
QString fileToCheck = watchFolder.filePath("test.txt");
TestGetRelativeSourcePath(fileToCheck.toUtf8().constData(), true, "test.txt", watchFolder.path().toUtf8().constData());
}
TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_BareFileNameValidInWatchFolder_ReturnsHighestPriorityWatchFolder)
{
// Test passes in a simple file name. The output should be relative to the highest-priority watch folder.
// Input: "test.txt"
// Output: "test.txt" in folder "/sourceRoot/recurseNested/nested/"
QDir watchFolder = GetRoot().filePath("recurseNested/nested/");
TestGetRelativeSourcePath("test.txt", true, "test.txt", watchFolder.path().toUtf8().constData());
}
TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_RelativePathValidInWatchFolder_ReturnsHighestPriorityWatchFolder)
{
// Test passes in a relative path. The output should preserve the relative path, but list it as relative to the highest-priority
// watch folder.
// Input: "a/b/c/test.txt"
// Output: "a/b/c/test.txt" in folder "/sourceRoot/recurseNested/nested/"
QDir watchFolder = GetRoot().filePath("recurseNested/nested/");
TestGetRelativeSourcePath("a/b/c/test.txt", true, "a/b/c/test.txt", watchFolder.path().toUtf8().constData());
}
TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_RelativePathNotInWatchFolder_ReturnsNoMatch)
{
// Test passes in a relative path that "backs up" two directories. This will be invalid, because no matter which watch directory
// we start at, the result will be outside of any watch directory.
// Input: "../../test.txt"
// Output: not found result, which also returns the input as the relative file name
TestGetRelativeSourcePath("../../test.txt", false, "../../test.txt", "");
}
TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_RelativePathValidFromNestedWatchFolder_ReturnsOuterFolder)
{
// Test passes in a relative path that "backs up" one directory. This will produce a valid result, because we can back up from
// the "recurseNested/nested/" watch folder to "recurseNested", which is also a valid watch folder.
// Input: "../test.txt"
// Output: "test.txt" in folder "/sourceRoot/recurseNested"
QDir watchFolder = GetRoot().filePath("recurseNested/");
TestGetRelativeSourcePath("../test.txt", true, "test.txt", watchFolder.path().toUtf8().constData());
}
TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_RelativePathMovesToParentWatchFolder_ReturnsOuterFolder)
{
// Test passes in a relative path that backs up one directory and then forward into a directory. This will produce a valid
// result, because it can validly start in the highest-priority watch folder (recurseNested/nested), move back one into the
// outer watch folder (recurseNested), and then have a subdirectory within it.
// Note that it would also be valid to move from recurseNested to recurseNotNested, but that won't be the result of this test
// because that's a lower-priority match.
// Input: "../recurseNotNested/test.txt"
// Output: "recurseNotNested/test.txt" in folder "/sourceRoot/recurseNested/"
QDir watchFolder = GetRoot().filePath("recurseNested/");
TestGetRelativeSourcePath("../recurseNotNested/test.txt", true, "recurseNotNested/test.txt", watchFolder.path().toUtf8().constData());
}
TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_RelativePathMovesToSiblingWatchFolder_ReturnsSiblingFolder)
{
// Test passes in a relative path that backs up two directories and then forward into a directory. This will produce a valid
// result, because it can validly start in the recurseNested/nested folder, move back two folders, then forward into the sibling
// recurseNotNested folder. The result will be a relative path to the sibling folder.
// Input: "../../recurseNotNested/test.txt"
// Output: "test.txt" in folder "/sourceRoot/recurseNotNested/"
QDir watchFolder = GetRoot().filePath("recurseNotNested/");
TestGetRelativeSourcePath("../../recurseNotNested/test.txt", true, "test.txt", watchFolder.path().toUtf8().constData());
}
TEST_F(AssetCatalogTestRelativeSourcePath, GenerateRelativeSourcePath_RelativePathBacksOutOfWatchFolder_ReturnsNoMatch)
{
// Test passes in a relative path that adds a directory, then "backs up" three directories. This will be invalid, because no
// matter which watch directory we start at, the result will be outside of any watch directory.
// Input: "../test.txt"
// Output: "test.txt" in folder "/sourceRoot/recurseNested"
TestGetRelativeSourcePath("a/../../../test.txt", false, "a/../../../test.txt", "");
}
class AssetCatalogTest_GetFullSourcePath
: public AssetCatalogTest
{
@@ -912,7 +1092,7 @@ namespace AssetProcessor
{
AssetCatalogTest::SetUp();
m_platforms.push_back("pc");
m_platforms.push_back("es3");
m_platforms.push_back("android");
// 4 products for one platform, 1 product for the other.
m_platformToProductsForSourceWithDifferentProducts["pc"].push_back("subfolder3/basefilez.arc2");
@@ -920,7 +1100,7 @@ namespace AssetProcessor
m_platformToProductsForSourceWithDifferentProducts["pc"].push_back("subfolder3/basefile.arc2");
m_platformToProductsForSourceWithDifferentProducts["pc"].push_back("subfolder3/basefile.azm2");
m_platformToProductsForSourceWithDifferentProducts["es3"].push_back("subfolder3/es3exclusivefile.azm2");
m_platformToProductsForSourceWithDifferentProducts["android"].push_back("subfolder3/androidexclusivefile.azm2");
m_sourceFileWithDifferentProductsPerPlatform = AZ::Uuid::CreateString("{38032FC9-2838-4D6A-9DA0-79E5E4F20C1B}");
m_sourceFileWithDependency = AZ::Uuid::CreateString("{807C4174-1D19-42AD-B8BC-A59291D9388C}");
@@ -933,7 +1113,7 @@ namespace AssetProcessor
// resulting in image processing jobs having different products per platform. Because of this, the material jobs will then have different
// dependencies per platform, because each material will depend on a referenced texture and all of that texture's mipmaps.
// Add a source file with 4 products on pc, but 1 on es3
// Add a source file with 4 products on pc, but 1 on android
bool result = AddSourceAndJobForMultiplePlatforms(
"subfolder3",
"MultiplatformFile.txt",
@@ -948,7 +1128,7 @@ namespace AssetProcessor
result = AddSourceAndJobForMultiplePlatforms("subfolder3", "FileWithDependency.txt", &(m_data->m_dbConn), sourceFileWithSameProductsJobsPerPlatform, m_platforms, m_sourceFileWithDependency);
EXPECT_TRUE(result);
const AZStd::string fileWithDependencyProductPath = "subfolder3/es3exclusivefile.azm2";
const AZStd::string fileWithDependencyProductPath = "subfolder3/androidexclusivefile.azm2";
for (const AZStd::string& platform : m_platforms)
{
@@ -28,7 +28,7 @@ namespace AssetProcessorMessagesTests
using namespace AssetProcessor;
using namespace AssetBuilderSDK;
static constexpr unsigned short AssetProcessorPort = static_cast<unsigned short>(888u);
static constexpr unsigned short AssetProcessorPort{65535u};
class AssetProcessorMessages;
@@ -85,7 +85,6 @@ namespace AssetProcessorMessagesTests
public:
void SetUp() override
{
#if !AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
AssetUtilities::ResetGameName();
m_temporarySourceDir = QDir(m_temporaryDir.path());
@@ -166,12 +165,10 @@ namespace AssetProcessorMessagesTests
});
#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);
@@ -182,7 +179,6 @@ namespace AssetProcessorMessagesTests
m_assetSystemComponent->Deactivate();
m_batchApplicationManager->Destroy();
#endif // !AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
}
void RunNetworkRequest(AZStd::function<void()> func) const
@@ -207,6 +203,7 @@ namespace AssetProcessorMessagesTests
thread.join();
}
protected:
MockAssetRequestHandler* m_assetRequestHandler{}; // Not owned, AP will delete this pointer
@@ -226,11 +223,7 @@ namespace AssetProcessorMessagesTests
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
@@ -265,6 +258,9 @@ namespace AssetProcessorMessagesTests
addPairFunc(new GetFullSourcePathFromRelativeProductPathRequest(), new GetFullSourcePathFromRelativeProductPathResponse());
addPairFunc(new GetRelativeProductPathFromFullSourceOrProductPathRequest(), new GetRelativeProductPathFromFullSourceOrProductPathResponse());
addPairFunc(
new GenerateRelativeSourcePathRequest(),
new GenerateRelativeSourcePathResponse());
addPairFunc(new SourceAssetInfoRequest(), new SourceAssetInfoResponse());
addPairFunc(new SourceAssetProductsInfoRequest(), new SourceAssetProductsInfoResponse());
addPairFunc(new GetScanFoldersRequest(), new GetScanFoldersResponse());
@@ -308,11 +304,7 @@ namespace AssetProcessorMessagesTests
});
}
#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;
@@ -112,7 +112,6 @@ namespace UnitTests
SourceDatabaseEntry sourceFile8 = { m_data->m_scanFolder1.m_scanFolderID, "test.txt", AZ::Uuid::CreateRandom(), "AnalysisFingerprint" };
SourceDatabaseEntry sourceFile9 = { m_data->m_scanFolder1.m_scanFolderID, "duplicate/folder/file1.tif", AZ::Uuid::CreateRandom(), "AnalysisFingerprint" };
SourceDatabaseEntry sourceFile10 = { m_data->m_scanFolder1.m_scanFolderID, "folder/file.foo", AZ::Uuid::CreateRandom(), "AnalysisFingerprint" };
SourceDatabaseEntry sourceFile11 = { m_data->m_scanFolder1.m_scanFolderID, "testfolder/file.foo", AZ::Uuid::CreateRandom(), "AnalysisFingerprint" };
ASSERT_TRUE(m_data->m_connection->SetSource(sourceFile1));
ASSERT_TRUE(m_data->m_connection->SetSource(sourceFile2));
ASSERT_TRUE(m_data->m_connection->SetSource(sourceFile3));
@@ -123,7 +122,6 @@ namespace UnitTests
ASSERT_TRUE(m_data->m_connection->SetSource(sourceFile8));
ASSERT_TRUE(m_data->m_connection->SetSource(sourceFile9));
ASSERT_TRUE(m_data->m_connection->SetSource(sourceFile10));
ASSERT_TRUE(m_data->m_connection->SetSource(sourceFile11));
SourceFileDependencyEntry dependency1 = { AZ::Uuid::CreateRandom(), "subfolder1/somefile.tif", "subfolder1/otherfile.tif", SourceFileDependencyEntry::TypeOfDependency::DEP_SourceToSource, false };
SourceFileDependencyEntry dependency2 = { AZ::Uuid::CreateRandom(), "subfolder1/otherfile.tif", "otherfile.tif", SourceFileDependencyEntry::TypeOfDependency::DEP_JobToJob, false };
@@ -176,8 +174,6 @@ namespace UnitTests
ASSERT_TRUE(UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("dev/dummy/foo.metadataextension")));
ASSERT_TRUE(UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("dev/folder/file.foo")));
ASSERT_TRUE(UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("dev/folder/file.bar")));
ASSERT_TRUE(UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("dev/testfolder/file.foo")));
ASSERT_TRUE(UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath("dev/testfolder/File.bar")));
if (AZ::IO::FileIOBase::GetInstance() == nullptr)
{
@@ -487,20 +483,12 @@ namespace UnitTests
TestGetSourcesByPath("dev/", { }, false);
}
#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
TEST_F(SourceFileRelocatorTest, DISABLED_GetSources_MultipleScanFolders_Fails)
#else
TEST_F(SourceFileRelocatorTest, GetSources_MultipleScanFolders_Fails)
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
{
TestGetSourcesByPath("*", { }, false);
}
#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
TEST_F(SourceFileRelocatorTest, DISABLED_GetSources_PartialPath_FailsWithNoResults)
#else
TEST_F(SourceFileRelocatorTest, GetSources_PartialPath_FailsWithNoResults)
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
{
TestGetSourcesByPath("older/*", { }, false);
}
@@ -547,18 +535,6 @@ namespace UnitTests
TestGetSourcesByPath(filePath.toUtf8().constData(), { "folder/file.foo" }, true, true);
}
#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
TEST_F(SourceFileRelocatorTest, DISABLED_GetSources_HaveMetadataDifferentFileCase_AbsolutePath_Succeeds)
#else
TEST_F(SourceFileRelocatorTest, GetSources_HaveMetadataDifferentFileCase_AbsolutePath_Succeeds)
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
{
QDir tempPath(m_tempDir.path());
auto filePath = QDir(tempPath.absoluteFilePath(m_data->m_scanFolder1.m_scanFolder.c_str())).absoluteFilePath("testfolder/file.foo");
TestGetSourcesByPath(filePath.toUtf8().constData(), { "testfolder/file.foo", "testfolder/File.bar" }, true, false);
}
TEST_F(SourceFileRelocatorTest, GetMetaDataFile_SingleFileWildcard_Succeeds)
{
QDir tempPath(m_tempDir.path());
@@ -921,32 +897,46 @@ namespace UnitTests
ASSERT_FALSE(AZ::IO::FileIOBase::GetInstance()->Exists(filePath.toUtf8().constData()));
}
#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
TEST_F(SourceFileRelocatorTest, DISABLED_Delete_Real_Readonly_Fails)
#else
TEST_F(SourceFileRelocatorTest, Delete_Real_Readonly_Fails)
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
{
struct AutoResetDirectoryReadOnlyState
{
AutoResetDirectoryReadOnlyState(QString dirName)
: m_dirName(AZStd::move(dirName))
{
AZ::IO::SystemFile::SetWritable(m_dirName.toUtf8().constData(), false);
}
~AutoResetDirectoryReadOnlyState()
{
AZ::IO::SystemFile::SetWritable(m_dirName.toUtf8().constData(), true);
}
AZ_DISABLE_COPY_MOVE(AutoResetDirectoryReadOnlyState)
private:
QString m_dirName;
};
QDir tempPath(m_tempDir.path());
auto filePath = QDir(tempPath.absoluteFilePath(m_data->m_scanFolder1.m_scanFolder.c_str())).absoluteFilePath("duplicate/file1.tif");
ASSERT_TRUE(AZ::IO::FileIOBase::GetInstance()->Exists(filePath.toUtf8().constData()));
AutoResetDirectoryReadOnlyState readOnlyResetter(QFileInfo(filePath).absoluteDir().absolutePath());
AZ::IO::SystemFile::SetWritable(filePath.toUtf8().constData(), false);
auto result = m_data->m_reporter->Delete(filePath.toUtf8().constData(), false);
ASSERT_TRUE(result.IsSuccess());
EXPECT_TRUE(result.IsSuccess());
RelocationSuccess successResult = result.TakeValue();
ASSERT_EQ(successResult.m_moveSuccessCount, 0);
ASSERT_EQ(successResult.m_moveFailureCount, 1);
ASSERT_EQ(successResult.m_moveTotalCount, 1);
ASSERT_EQ(successResult.m_updateTotalCount, 0);
EXPECT_EQ(successResult.m_moveSuccessCount, 0);
EXPECT_EQ(successResult.m_moveFailureCount, 1);
EXPECT_EQ(successResult.m_moveTotalCount, 1);
EXPECT_EQ(successResult.m_updateTotalCount, 0);
ASSERT_TRUE(AZ::IO::FileIOBase::GetInstance()->Exists(filePath.toUtf8().constData()));
EXPECT_TRUE(AZ::IO::FileIOBase::GetInstance()->Exists(filePath.toUtf8().constData()));
}
TEST_F(SourceFileRelocatorTest, Delete_Real_WithDependencies_Fails)
@@ -28,7 +28,7 @@ namespace AssetProcessor
createJobsRequest.m_enabledPlatforms = {
{ "pc", {}
}, { "es3", {}
}, { "android", {}
}
};
ASSERT_EQ(createJobsRequest.GetEnabledPlatformsCount(), 2);
@@ -48,19 +48,19 @@ namespace AssetProcessor
ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(1), AssetBuilderSDK::Platform_NONE);
createJobsRequest.m_enabledPlatforms = {
{ "es3", {}
{ "android", {}
}
};
ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(0), AssetBuilderSDK::Platform_ES3);
ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(0), AssetBuilderSDK::Platform_ANDROID);
ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(1), AssetBuilderSDK::Platform_NONE);
createJobsRequest.m_enabledPlatforms = {
{ "pc", {}
}, { "es3", {}
}, { "android", {}
}
};
ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(0), AssetBuilderSDK::Platform_PC);
ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(1), AssetBuilderSDK::Platform_ES3);
ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(1), AssetBuilderSDK::Platform_ANDROID);
ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(2), AssetBuilderSDK::Platform_NONE);
createJobsRequest.m_enabledPlatforms = {
@@ -72,24 +72,24 @@ namespace AssetProcessor
createJobsRequest.m_enabledPlatforms = {
{ "pc", {}
}, { "es3", {}
}, { "android", {}
}, { "ios", {}
}, { "osx_gl", {}
}, { "mac", {}
}
};
ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(0), AssetBuilderSDK::Platform_PC);
ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(1), AssetBuilderSDK::Platform_ES3);
ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(1), AssetBuilderSDK::Platform_ANDROID);
ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(2), AssetBuilderSDK::Platform_IOS);
ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(3), AssetBuilderSDK::Platform_OSX);
ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(3), AssetBuilderSDK::Platform_MAC);
ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(4), AssetBuilderSDK::Platform_NONE);
createJobsRequest.m_enabledPlatforms = {
{ "pc", {}
}, { "es3", {}
}, { "android", {}
}
};
ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(0), AssetBuilderSDK::Platform_PC);
ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(1), AssetBuilderSDK::Platform_ES3);
ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(1), AssetBuilderSDK::Platform_ANDROID);
ASSERT_EQ(createJobsRequest.GetEnabledPlatformAt(2), AssetBuilderSDK::Platform_NONE);
// using a deprecated API should have generated warnings.
// but we can't test for it because these warnings are WarningOnce and some other unit test might have already triggered it
@@ -106,23 +106,23 @@ namespace AssetProcessor
}
};
ASSERT_TRUE(createJobsRequest.IsPlatformEnabled(AssetBuilderSDK::Platform_PC));
ASSERT_FALSE(createJobsRequest.IsPlatformEnabled(AssetBuilderSDK::Platform_ES3));
ASSERT_FALSE(createJobsRequest.IsPlatformEnabled(AssetBuilderSDK::Platform_ANDROID));
createJobsRequest.m_enabledPlatforms = {
{ "pc", {}
}, { "es3", {}
}, { "android", {}
}
};
ASSERT_TRUE(createJobsRequest.IsPlatformEnabled(AssetBuilderSDK::Platform_PC));
ASSERT_TRUE(createJobsRequest.IsPlatformEnabled(AssetBuilderSDK::Platform_ES3));
ASSERT_TRUE(createJobsRequest.IsPlatformEnabled(AssetBuilderSDK::Platform_ANDROID));
createJobsRequest.m_enabledPlatforms = {
{ "pc", {}
}, { "es3", {}
}, { "android", {}
}
};
ASSERT_TRUE(createJobsRequest.IsPlatformEnabled(AssetBuilderSDK::Platform_PC));
ASSERT_TRUE(createJobsRequest.IsPlatformEnabled(AssetBuilderSDK::Platform_ES3));
ASSERT_TRUE(createJobsRequest.IsPlatformEnabled(AssetBuilderSDK::Platform_ANDROID));
// using a deprecated API should have generated warnings.
// but we can't test for it because these warnings are WarningOnce and some other unit test might have already triggered it
}
@@ -133,9 +133,9 @@ namespace AssetProcessor
UnitTestUtils::AssertAbsorber absorb;
ASSERT_TRUE(createJobsRequest.IsPlatformValid(AssetBuilderSDK::Platform_PC));
ASSERT_TRUE(createJobsRequest.IsPlatformValid(AssetBuilderSDK::Platform_ES3));
ASSERT_TRUE(createJobsRequest.IsPlatformValid(AssetBuilderSDK::Platform_ANDROID));
ASSERT_TRUE(createJobsRequest.IsPlatformValid(AssetBuilderSDK::Platform_IOS));
ASSERT_TRUE(createJobsRequest.IsPlatformValid(AssetBuilderSDK::Platform_OSX));
ASSERT_TRUE(createJobsRequest.IsPlatformValid(AssetBuilderSDK::Platform_MAC));
ASSERT_TRUE(createJobsRequest.IsPlatformValid(AssetBuilderSDK::Platform_PROVO));
ASSERT_TRUE(createJobsRequest.IsPlatformValid(AssetBuilderSDK::Platform_SALEM));
ASSERT_TRUE(createJobsRequest.IsPlatformValid(AssetBuilderSDK::Platform_JASPER));
@@ -30,11 +30,7 @@ public:
friend class GTEST_TEST_CLASS_NAME_(MultiplatformPathDependencyTest, AssetProcessed_Impl_MultiplatformDependencies);
friend class GTEST_TEST_CLASS_NAME_(MultiplatformPathDependencyTest, AssetProcessed_Impl_MultiplatformDependencies_DeferredResolution);
#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
friend class GTEST_TEST_CLASS_NAME_(MultiplatformPathDependencyTest, DISABLED_AssetProcessed_Impl_MultiplatformDependencies_SourcePath);
#else
friend class GTEST_TEST_CLASS_NAME_(MultiplatformPathDependencyTest, AssetProcessed_Impl_MultiplatformDependencies_SourcePath);
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
friend class GTEST_TEST_CLASS_NAME_(AssetProcessorManagerTest, DeleteFolder_SignalsDeleteOfContainedFiles);
@@ -70,19 +66,11 @@ public:
friend class GTEST_TEST_CLASS_NAME_(AbsolutePathProductDependencyTest, UnresolvedProductPathDependency_AssetProcessedTwice_ValidatePathDependenciesMap);
friend class GTEST_TEST_CLASS_NAME_(AbsolutePathProductDependencyTest, UnresolvedSourceFileTypeProductPathDependency_DependencyHasNoProductOutput_ValidatePathDependenciesMap);
#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
friend class GTEST_TEST_CLASS_NAME_(ModtimeScanningTest, DISABLED_ModtimeSkipping_FileUnchanged_WithoutModtimeSkipping);
#else
friend class GTEST_TEST_CLASS_NAME_(ModtimeScanningTest, ModtimeSkipping_FileUnchanged_WithoutModtimeSkipping);
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
friend class GTEST_TEST_CLASS_NAME_(ModtimeScanningTest, ModtimeSkipping_FileUnchanged);
#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
friend class GTEST_TEST_CLASS_NAME_(ModtimeScanningTest, DISABLED_ModtimeSkipping_EnablePlatform_ShouldProcessFilesForPlatform);
#else
friend class GTEST_TEST_CLASS_NAME_(ModtimeScanningTest, ModtimeSkipping_EnablePlatform_ShouldProcessFilesForPlatform);
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
friend class GTEST_TEST_CLASS_NAME_(ModtimeScanningTest, ModtimeSkipping_ModifyFile);
friend class GTEST_TEST_CLASS_NAME_(ModtimeScanningTest, ModtimeSkipping_ModifyFile_AndThenRevert_ProcessesAgain);
@@ -2362,11 +2350,7 @@ TEST_F(PathDependencyTest, ChangeDependencies_Existing_ResolveCorrectly)
);
}
#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
TEST_F(PathDependencyTest, DISABLED_MixedPathDependencies_Existing_ResolveCorrectly)
#else
TEST_F(PathDependencyTest, MixedPathDependencies_Existing_ResolveCorrectly)
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
{
using namespace AssetProcessor;
using namespace AssetBuilderSDK;
@@ -2661,11 +2645,7 @@ TEST_F(MultiplatformPathDependencyTest, AssetProcessed_Impl_MultiplatformDepende
ASSERT_NE(SearchDependencies(dependencyContainer, asset1.m_products[0]), SearchDependencies(dependencyContainer, asset1.m_products[1]));
}
#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
TEST_F(MultiplatformPathDependencyTest, DISABLED_AssetProcessed_Impl_MultiplatformDependencies_SourcePath)
#else
TEST_F(MultiplatformPathDependencyTest, AssetProcessed_Impl_MultiplatformDependencies_SourcePath)
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
{
// One product will be pc, one will be console (order is non-deterministic)
TestAsset asset1("testAsset1");
@@ -3894,7 +3874,7 @@ void ModtimeScanningTest::ProcessAssetJobs()
for (const auto& processResult : m_data->m_processResults)
{
auto file = QDir(processResult.m_destinationPath).absoluteFilePath(processResult.m_jobEntry.m_databaseSourceName + ".arc1");
auto file = QDir(processResult.m_destinationPath).absoluteFilePath(processResult.m_jobEntry.m_databaseSourceName.toLower() + ".arc1");
m_data->m_productPaths.emplace(
QDir(processResult.m_jobEntry.m_watchFolderPath)
.absoluteFilePath(processResult.m_jobEntry.m_databaseSourceName)
@@ -3943,11 +3923,11 @@ void ModtimeScanningTest::ExpectWork(int createJobs, int processJobs)
{
ASSERT_TRUE(BlockUntilIdle(5000));
ASSERT_EQ(m_data->m_mockBuilderInfoHandler.m_createJobsCount, createJobs);
ASSERT_EQ(m_data->m_processResults.size(), processJobs);
ASSERT_FALSE(m_data->m_processResults[0].m_autoFail);
ASSERT_FALSE(m_data->m_processResults[1].m_autoFail);
ASSERT_EQ(m_data->m_deletedSources.size(), 0);
EXPECT_EQ(m_data->m_mockBuilderInfoHandler.m_createJobsCount, createJobs);
EXPECT_EQ(m_data->m_processResults.size(), processJobs);
EXPECT_FALSE(m_data->m_processResults[0].m_autoFail);
EXPECT_FALSE(m_data->m_processResults[1].m_autoFail);
EXPECT_EQ(m_data->m_deletedSources.size(), 0);
m_isIdling = false;
}
@@ -3975,11 +3955,7 @@ void ModtimeScanningTest::SetFileContents(QString filePath, QString contents)
file.close();
}
#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
TEST_F(ModtimeScanningTest, DISABLED_ModtimeSkipping_FileUnchanged_WithoutModtimeSkipping)
#else
TEST_F(ModtimeScanningTest, ModtimeSkipping_FileUnchanged_WithoutModtimeSkipping)
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
{
using namespace AzToolsFramework::AssetSystem;
@@ -4008,11 +3984,7 @@ TEST_F(ModtimeScanningTest, ModtimeSkipping_FileUnchanged)
ExpectNoWork();
}
#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
TEST_F(ModtimeScanningTest, DISABLED_ModtimeSkipping_EnablePlatform_ShouldProcessFilesForPlatform)
#else
TEST_F(ModtimeScanningTest, ModtimeSkipping_EnablePlatform_ShouldProcessFilesForPlatform)
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
{
using namespace AzToolsFramework::AssetSystem;
@@ -4020,15 +3992,15 @@ TEST_F(ModtimeScanningTest, ModtimeSkipping_EnablePlatform_ShouldProcessFilesFor
m_assetProcessorManager->m_allowModtimeSkippingFeature = true;
AssetUtilities::SetUseFileHashOverride(true, true);
// Enable es3 platform after the initial SetUp has already processed the files for pc
// Enable android platform after the initial SetUp has already processed the files for pc
QDir tempPath(m_tempDir.path());
AssetBuilderSDK::PlatformInfo es3Platform("es3", { "host", "renderer" });
m_config->EnablePlatform(es3Platform, true);
AssetBuilderSDK::PlatformInfo androidPlatform("android", { "host", "renderer" });
m_config->EnablePlatform(androidPlatform, true);
// There's no way to remove scanfolders and adding a new one after enabling the platform will cause the pc assets to build as well, which we don't want
// Instead we'll just const cast the vector and modify the enabled platforms for the scanfolder
auto& platforms = const_cast<AZStd::vector<AssetBuilderSDK::PlatformInfo>&>(m_config->GetScanFolderAt(0).GetPlatforms());
platforms.push_back(es3Platform);
platforms.push_back(androidPlatform);
// We need the builder fingerprints to be updated to reflect the newly enabled platform
m_assetProcessorManager->ComputeBuilderDirty();
@@ -4036,10 +4008,10 @@ TEST_F(ModtimeScanningTest, ModtimeSkipping_EnablePlatform_ShouldProcessFilesFor
QSet<AssetFileInfo> filePaths = BuildFileSet();
SimulateAssetScanner(filePaths);
ExpectWork(4, 2); // CreateJobs = 4, 2 files * 2 platforms. ProcessJobs = 2, just the es3 platform jobs (pc is already processed)
ExpectWork(4, 2); // CreateJobs = 4, 2 files * 2 platforms. ProcessJobs = 2, just the android platform jobs (pc is already processed)
ASSERT_TRUE(m_data->m_processResults[0].m_destinationPath.contains("es3"));
ASSERT_TRUE(m_data->m_processResults[1].m_destinationPath.contains("es3"));
ASSERT_TRUE(m_data->m_processResults[0].m_destinationPath.contains("android"));
ASSERT_TRUE(m_data->m_processResults[1].m_destinationPath.contains("android"));
}
TEST_F(ModtimeScanningTest, ModtimeSkipping_ModifyTimestamp)
@@ -4633,11 +4605,7 @@ TEST_F(AssetProcessorManagerTest, UpdateSourceFileDependenciesDatabase_WildcardM
dependList.clear();
}
#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
TEST_F(AssetProcessorManagerTest, DISABLED_RemoveSource_RemoveCacheFolderIfEmpty_Ok)
#else
TEST_F(AssetProcessorManagerTest, RemoveSource_RemoveCacheFolderIfEmpty_Ok)
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
{
using namespace AssetProcessor;
using namespace AssetBuilderSDK;
@@ -14,6 +14,7 @@
#include "native/tests/platformconfiguration/platformconfigurationtests.h"
#include <AzTest/AzTest.h>
#include <gmock/gmock.h>
const char TestAppRoot[] = "@exefolder@/testdata";
const char EmptyDummyProjectName[] = "EmptyDummyProject";
@@ -120,14 +121,14 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_Regular_Platforms)
// verify the data.
ASSERT_NE(config.GetPlatformByIdentifier(AzToolsFramework::AssetSystem::GetHostAssetPlatform()), nullptr);
ASSERT_NE(config.GetPlatformByIdentifier("es3"), nullptr);
ASSERT_NE(config.GetPlatformByIdentifier("android"), nullptr);
ASSERT_NE(config.GetPlatformByIdentifier("server"), nullptr);
ASSERT_TRUE(config.GetPlatformByIdentifier("es3")->HasTag("mobile"));
ASSERT_TRUE(config.GetPlatformByIdentifier("es3")->HasTag("renderer"));
ASSERT_TRUE(config.GetPlatformByIdentifier("es3")->HasTag("android"));
ASSERT_TRUE(config.GetPlatformByIdentifier("android")->HasTag("mobile"));
ASSERT_TRUE(config.GetPlatformByIdentifier("android")->HasTag("renderer"));
ASSERT_TRUE(config.GetPlatformByIdentifier("android")->HasTag("android"));
ASSERT_TRUE(config.GetPlatformByIdentifier("server")->HasTag("server"));
ASSERT_FALSE(config.GetPlatformByIdentifier("es3")->HasTag("server"));
ASSERT_FALSE(config.GetPlatformByIdentifier("android")->HasTag("server"));
ASSERT_FALSE(config.GetPlatformByIdentifier("server")->HasTag("renderer"));
}
@@ -238,33 +239,6 @@ TEST_F(PlatformConfigurationUnitTests_OnePCHostFixture, GetScanFolderForFile_Sub
EXPECT_STREQ(info->GetDisplayName().toUtf8().constData(), "Editor ScanFolder");
}
// note that in the case of GetOverridingFile, this SHOULD return the correct case if an override is found
// because its possible to override a file with another file with different case in a different scan folder
// such a situation is supposed to be very rare, so the cost of correcting the case is mitigated.
TEST_F(PlatformConfigurationUnitTests_OnePCHostFixture, GetOverridingFile_Exists_ReturnsCorrectCase)
{
using namespace AzToolsFramework::AssetSystem;
using namespace AssetProcessor;
// create two scan folders, since its order dependent, the ScanFolder1 is the "winner" in tie breakers (when they both contain same file relpath)
QString scanfolder1Path = m_tempPath.filePath("scanfolder1");
QString scanfolder2Path = m_tempPath.filePath("scanfolder2");
QString caseSensitiveDummyFileName = m_tempPath.absoluteFilePath("scanfolder1/TestCase.tXt");
QString differentCaseDummyFileName = m_tempPath.absoluteFilePath("scanfolder2/testcase.txt");
UnitTestUtils::CreateDummyFile(caseSensitiveDummyFileName, QString("testcase1\n"));
UnitTestUtils::CreateDummyFile(differentCaseDummyFileName, QString("testcase2\n"));
m_config->AddScanFolder(ScanFolderInfo(scanfolder1Path, "ScanFolder1", "sf1", false, true, m_platforms), true);
m_config->AddScanFolder(ScanFolderInfo(scanfolder2Path, "ScanFolder2", "sf2", false, true, m_platforms), true);
// Perform the test by asking it whether anyone overrides "testcase" (lowercase) in scanfolder 2.
QString overrider = m_config->GetOverridingFile("testcase.txt", scanfolder2Path);
ASSERT_FALSE(overrider.isEmpty());
// the result should be the real actual case of the file in scanfolder 1:
EXPECT_STREQ(overrider.toUtf8().constData(), caseSensitiveDummyFileName.toUtf8().constData());
}
TEST_F(PlatformConfigurationUnitTests_OnePCHostFixture, GetOverridingFile_ExistsButNotOverridden_ReturnsEmpty)
{
using namespace AzToolsFramework::AssetSystem;
@@ -360,7 +334,7 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_RegularScanfolder)
ASSERT_EQ(m_absorber.m_numErrorsAbsorbed, 0);
ASSERT_EQ(config.GetScanFolderCount(), 3); // the two, and then the one that has the same data as prior but different identifier.
QString scanName = AssetUtilities::ComputeProjectPath() + " Scan Folder";
QString scanName = AssetUtilities::ComputeProjectPath(true) + " Scan Folder";
ASSERT_EQ(config.GetScanFolderAt(0).GetDisplayName(), scanName);
ASSERT_EQ(config.GetScanFolderAt(0).RecurseSubFolders(), true);
ASSERT_EQ(config.GetScanFolderAt(0).GetOrder(), 0);
@@ -397,7 +371,7 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_RegularScanfolderP
AZStd::vector<AssetBuilderSDK::PlatformInfo> platforms = config.GetScanFolderAt(0).GetPlatforms();
ASSERT_EQ(platforms.size(), 4);
ASSERT_TRUE(AZStd::find(platforms.begin(), platforms.end(), AssetBuilderSDK::PlatformInfo(AzToolsFramework::AssetSystem::GetHostAssetPlatform(), AZStd::unordered_set<AZStd::string>{})) != platforms.end());
ASSERT_TRUE(AZStd::find(platforms.begin(), platforms.end(), AssetBuilderSDK::PlatformInfo("es3", AZStd::unordered_set<AZStd::string>{})) != platforms.end());
ASSERT_TRUE(AZStd::find(platforms.begin(), platforms.end(), AssetBuilderSDK::PlatformInfo("android", AZStd::unordered_set<AZStd::string>{})) != platforms.end());
ASSERT_TRUE(AZStd::find(platforms.begin(), platforms.end(), AssetBuilderSDK::PlatformInfo("ios", AZStd::unordered_set<AZStd::string>{})) != platforms.end());
ASSERT_TRUE(AZStd::find(platforms.begin(), platforms.end(), AssetBuilderSDK::PlatformInfo("server", AZStd::unordered_set<AZStd::string>{})) != platforms.end());
@@ -405,12 +379,12 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_RegularScanfolderP
platforms = config.GetScanFolderAt(1).GetPlatforms();
ASSERT_EQ(platforms.size(), 2);
ASSERT_TRUE(AZStd::find(platforms.begin(), platforms.end(), AssetBuilderSDK::PlatformInfo(AzToolsFramework::AssetSystem::GetHostAssetPlatform(), AZStd::unordered_set<AZStd::string>{})) != platforms.end());
ASSERT_TRUE(AZStd::find(platforms.begin(), platforms.end(), AssetBuilderSDK::PlatformInfo("es3", AZStd::unordered_set<AZStd::string>{})) != platforms.end());
ASSERT_TRUE(AZStd::find(platforms.begin(), platforms.end(), AssetBuilderSDK::PlatformInfo("android", AZStd::unordered_set<AZStd::string>{})) != platforms.end());
ASSERT_EQ(config.GetScanFolderAt(2).GetDisplayName(), QString("folder1output"));
platforms = config.GetScanFolderAt(2).GetPlatforms();
ASSERT_EQ(platforms.size(), 1);
ASSERT_TRUE(AZStd::find(platforms.begin(), platforms.end(), AssetBuilderSDK::PlatformInfo("es3", AZStd::unordered_set<AZStd::string>{})) != platforms.end());
ASSERT_TRUE(AZStd::find(platforms.begin(), platforms.end(), AssetBuilderSDK::PlatformInfo("android", AZStd::unordered_set<AZStd::string>{})) != platforms.end());
ASSERT_EQ(config.GetScanFolderAt(3).GetDisplayName(), QString("folder2output"));
platforms = config.GetScanFolderAt(3).GetPlatforms();
@@ -445,16 +419,12 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_RegularExcludes)
ASSERT_FALSE(config.IsFileExcluded("blahblah/Levels/blahblahhold/whatever.test"));
}
#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
TEST_F(PlatformConfigurationUnitTests, DISABLED_TestFailReadConfigFile_Recognizers)
#else
TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_Recognizers)
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
{
using namespace AzToolsFramework::AssetSystem;
using namespace AssetProcessor;
#if defined(AZ_PLATFORM_WINDOWS)
const char* platformWhichIsNotCurrentPlatform = "osx_gl";
#if defined(AZ_PLATFORM_WINDOWS) || defined(AZ_PLATFORM_LINUX)
const char* platformWhichIsNotCurrentPlatform = "mac";
#else
const char* platformWhichIsNotCurrentPlatform = "pc";
#endif
@@ -475,58 +445,74 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_Recognizers)
ASSERT_EQ(recogs["i_caf"].m_patternMatcher.GetBuilderPattern().m_pattern, "*.i_caf");
ASSERT_EQ(recogs["i_caf"].m_patternMatcher.GetBuilderPattern().m_type, AssetBuilderSDK::AssetBuilderPattern::Wildcard);
ASSERT_EQ(recogs["i_caf"].m_platformSpecs.size(), 2);
ASSERT_TRUE(recogs["i_caf"].m_platformSpecs.contains("es3"));
ASSERT_TRUE(recogs["i_caf"].m_platformSpecs.contains("android"));
ASSERT_TRUE(recogs["i_caf"].m_platformSpecs.contains(AzToolsFramework::AssetSystem::GetHostAssetPlatform()));
ASSERT_FALSE(recogs["i_caf"].m_platformSpecs.contains("server")); // server has been set to skip.
ASSERT_EQ(recogs["i_caf"].m_platformSpecs["es3"].m_extraRCParams, "mobile");
ASSERT_EQ(recogs["i_caf"].m_platformSpecs["android"].m_extraRCParams, "mobile");
ASSERT_EQ(recogs["i_caf"].m_platformSpecs[AzToolsFramework::AssetSystem::GetHostAssetPlatform()].m_extraRCParams, "defaultparams");
ASSERT_TRUE(recogs.contains("caf"));
ASSERT_TRUE(recogs["caf"].m_platformSpecs.contains("es3"));
ASSERT_TRUE(recogs["caf"].m_platformSpecs.contains("android"));
ASSERT_TRUE(recogs["caf"].m_platformSpecs.contains("server"));
ASSERT_TRUE(recogs["caf"].m_platformSpecs.contains(AzToolsFramework::AssetSystem::GetHostAssetPlatform()));
ASSERT_EQ(recogs["caf"].m_platformSpecs.size(), 3);
ASSERT_EQ(recogs["caf"].m_platformSpecs["es3"].m_extraRCParams, "rendererparams");
ASSERT_EQ(recogs["caf"].m_platformSpecs["android"].m_extraRCParams, "rendererparams");
ASSERT_EQ(recogs["caf"].m_platformSpecs[AzToolsFramework::AssetSystem::GetHostAssetPlatform()].m_extraRCParams, "rendererparams");
ASSERT_EQ(recogs["caf"].m_platformSpecs["server"].m_extraRCParams, "copy");
ASSERT_TRUE(recogs.contains("mov"));
ASSERT_TRUE(recogs["mov"].m_platformSpecs.contains("es3"));
ASSERT_TRUE(recogs["mov"].m_platformSpecs.contains("android"));
ASSERT_TRUE(recogs["mov"].m_platformSpecs.contains("server"));
ASSERT_TRUE(recogs["mov"].m_platformSpecs.contains(AzToolsFramework::AssetSystem::GetHostAssetPlatform()));
ASSERT_EQ(recogs["mov"].m_platformSpecs.size(), 3);
ASSERT_EQ(recogs["mov"].m_platformSpecs["es3"].m_extraRCParams, "platformspecificoverride");
ASSERT_EQ(recogs["mov"].m_platformSpecs["android"].m_extraRCParams, "platformspecificoverride");
ASSERT_EQ(recogs["mov"].m_platformSpecs[AzToolsFramework::AssetSystem::GetHostAssetPlatform()].m_extraRCParams, "rendererparams");
ASSERT_EQ(recogs["mov"].m_platformSpecs["server"].m_extraRCParams, "copy");
// the "rend" test makes sure that even if you dont specify 'params' its still there by default for all enabled platforms.
// (but platforms can override it)
ASSERT_TRUE(recogs.contains("rend"));
ASSERT_TRUE(recogs["rend"].m_platformSpecs.contains(AzToolsFramework::AssetSystem::GetHostAssetPlatform()));
ASSERT_TRUE(recogs["rend"].m_platformSpecs.contains("es3"));
ASSERT_TRUE(recogs["rend"].m_platformSpecs.contains("server"));
ASSERT_FALSE(recogs["rend"].m_platformSpecs.contains(platformWhichIsNotCurrentPlatform)); // this is not an enabled platform and should not be there.
ASSERT_EQ(recogs["rend"].m_platformSpecs.size(), 3);
ASSERT_EQ(recogs["rend"].m_platformSpecs[AzToolsFramework::AssetSystem::GetHostAssetPlatform()].m_extraRCParams, "rendererparams");
ASSERT_EQ(recogs["rend"].m_platformSpecs["es3"].m_extraRCParams, "rendererparams");
ASSERT_EQ(recogs["rend"].m_platformSpecs["server"].m_extraRCParams, ""); // default if not specified is empty string
EXPECT_THAT(
recogs["rend"].m_platformSpecs.keys(),
testing::AllOf(
testing::UnorderedElementsAre(
QString(AzToolsFramework::AssetSystem::GetHostAssetPlatform()),
QString("android"),
QString("server")
),
testing::Not(testing::Contains(platformWhichIsNotCurrentPlatform)) // this is not an enabled platform and should not be there.
)
);
EXPECT_EQ(recogs["rend"].m_platformSpecs[AzToolsFramework::AssetSystem::GetHostAssetPlatform()].m_extraRCParams, "rendererparams");
EXPECT_EQ(recogs["rend"].m_platformSpecs["android"].m_extraRCParams, "rendererparams");
EXPECT_EQ(recogs["rend"].m_platformSpecs["server"].m_extraRCParams, ""); // default if not specified is empty string
ASSERT_TRUE(recogs.contains("alldefault"));
ASSERT_TRUE(recogs["alldefault"].m_platformSpecs.contains(AzToolsFramework::AssetSystem::GetHostAssetPlatform()));
ASSERT_TRUE(recogs["alldefault"].m_platformSpecs.contains("es3"));
ASSERT_TRUE(recogs["alldefault"].m_platformSpecs.contains("server"));
ASSERT_FALSE(recogs["alldefault"].m_platformSpecs.contains(platformWhichIsNotCurrentPlatform)); // this is not an enabled platform and should not be there.
ASSERT_EQ(recogs["alldefault"].m_platformSpecs.size(), 3);
ASSERT_EQ(recogs["alldefault"].m_platformSpecs[AzToolsFramework::AssetSystem::GetHostAssetPlatform()].m_extraRCParams, "");
ASSERT_EQ(recogs["alldefault"].m_platformSpecs["es3"].m_extraRCParams, "");
ASSERT_EQ(recogs["alldefault"].m_platformSpecs["server"].m_extraRCParams, "");
EXPECT_THAT(
recogs["alldefault"].m_platformSpecs.keys(),
testing::AllOf(
testing::UnorderedElementsAre(
QString(AzToolsFramework::AssetSystem::GetHostAssetPlatform()),
QString("android"),
QString("server")
),
testing::Not(testing::Contains(platformWhichIsNotCurrentPlatform)) // this is not an enabled platform and should not be there.
)
);
EXPECT_EQ(recogs["alldefault"].m_platformSpecs[AzToolsFramework::AssetSystem::GetHostAssetPlatform()].m_extraRCParams, "");
EXPECT_EQ(recogs["alldefault"].m_platformSpecs["android"].m_extraRCParams, "");
EXPECT_EQ(recogs["alldefault"].m_platformSpecs["server"].m_extraRCParams, "");
ASSERT_TRUE(recogs.contains("skipallbutone"));
ASSERT_FALSE(recogs["skipallbutone"].m_platformSpecs.contains(AzToolsFramework::AssetSystem::GetHostAssetPlatform()));
ASSERT_FALSE(recogs["skipallbutone"].m_platformSpecs.contains("es3"));
ASSERT_TRUE(recogs["skipallbutone"].m_platformSpecs.contains("server")); // server is only one enabled (set to copy)
ASSERT_EQ(recogs["skipallbutone"].m_platformSpecs.size(), 1);
ASSERT_EQ(recogs["skipallbutone"].m_platformSpecs["server"].m_extraRCParams, "copy");
EXPECT_THAT(
recogs["skipallbutone"].m_platformSpecs.keys(),
testing::UnorderedElementsAre(
QString("server") // server is only one enabled (set to copy)
)
);
EXPECT_FALSE(recogs["skipallbutone"].m_platformSpecs.contains(AzToolsFramework::AssetSystem::GetHostAssetPlatform()));
EXPECT_FALSE(recogs["skipallbutone"].m_platformSpecs.contains("android"));
EXPECT_EQ(recogs["skipallbutone"].m_platformSpecs["server"].m_extraRCParams, "copy");
}
@@ -549,7 +535,7 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_Overrides)
// verify the data.
ASSERT_NE(config.GetPlatformByIdentifier(AzToolsFramework::AssetSystem::GetHostAssetPlatform()), nullptr);
ASSERT_NE(config.GetPlatformByIdentifier("es3"), nullptr);
ASSERT_NE(config.GetPlatformByIdentifier("android"), nullptr);
ASSERT_NE(config.GetPlatformByIdentifier("provo"), nullptr);
// this override swaps server with provo in that it turns ON provo, turns off server
ASSERT_EQ(config.GetPlatformByIdentifier("server"), nullptr); // this should be off due to overrides
@@ -566,11 +552,11 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_Overrides)
ASSERT_EQ(recogs["i_caf"].m_patternMatcher.GetBuilderPattern().m_pattern, "*.i_caf");
ASSERT_EQ(recogs["i_caf"].m_patternMatcher.GetBuilderPattern().m_type, AssetBuilderSDK::AssetBuilderPattern::Wildcard);
ASSERT_EQ(recogs["i_caf"].m_platformSpecs.size(), 3);
ASSERT_TRUE(recogs["i_caf"].m_platformSpecs.contains("es3"));
ASSERT_TRUE(recogs["i_caf"].m_platformSpecs.contains("android"));
ASSERT_TRUE(recogs["i_caf"].m_platformSpecs.contains("provo"));
ASSERT_TRUE(recogs["i_caf"].m_platformSpecs.contains(AzToolsFramework::AssetSystem::GetHostAssetPlatform()));
ASSERT_FALSE(recogs["i_caf"].m_platformSpecs.contains("server")); // server has been set to skip.
ASSERT_EQ(recogs["i_caf"].m_platformSpecs["es3"].m_extraRCParams, "mobile");
ASSERT_EQ(recogs["i_caf"].m_platformSpecs["android"].m_extraRCParams, "mobile");
ASSERT_EQ(recogs["i_caf"].m_platformSpecs[AzToolsFramework::AssetSystem::GetHostAssetPlatform()].m_extraRCParams, "defaultparams");
ASSERT_EQ(recogs["i_caf"].m_platformSpecs["provo"].m_extraRCParams, "copy");
@@ -137,10 +137,13 @@ TEST_F(AssetUtilitiesTest, UpdateToCorrectCase_ExistingFile_ReturnsTrue_Corrects
thingsToTry << "SomeFile.TxT";
thingsToTry << "otherfile.txt";
thingsToTry << "subfolder1/otherfile.txt";
#if defined(AZ_PLATFORM_WINDOWS)
thingsToTry << "subfolder2\\otherfile.txt";
thingsToTry << "subFolder3\\somefile.txt";
thingsToTry << "subFolder4\\subfolder6\\somefile.txt";
thingsToTry << "subFolder5\\subfolder7/someFile.txt";
#endif // AZ_PLATFORM_WINDOWS
thingsToTry << "specialFileName[.txt";
thingsToTry << "specialFileName].txt";
thingsToTry << "specialFileName!.txt";
@@ -14,6 +14,7 @@
#include "ProductAssetTreeItemData.h"
#include <AzCore/Component/TickBus.h>
#include <AzCore/IO/Path/Path.h>
#include <AzFramework/StringFunc/StringFunc.h>
namespace AssetProcessor
@@ -159,31 +160,33 @@ namespace AssetProcessor
return;
}
AZ::IO::Path productNamePath(product.m_productName, AZ::IO::PosixPathSeparator);
AZStd::vector<AZStd::string> tokens;
AzFramework::StringFunc::Tokenize(product.m_productName.c_str(), tokens, AZ_CORRECT_DATABASE_SEPARATOR, false, true);
if (tokens.empty())
if (productNamePath.empty())
{
AZ_Warning("AssetProcessor", false, "Product id %d has an invalid name: %s", product.m_productID, product.m_productName.c_str());
return;
}
AssetTreeItem* parentItem = m_root.get();
AZStd::string fullFolderName;
for (int i = 0; i < tokens.size() - 1; ++i)
AZ::IO::Path currentFullFolderPath;
const AZ::IO::PathView filename = productNamePath.Filename();
const AZ::IO::PathView fullPathWithoutFilename = productNamePath.RemoveFilename();
AZStd::fixed_string<AZ::IO::MaxPathLength> currentPath;
for (auto pathIt = fullPathWithoutFilename.begin(); pathIt != fullPathWithoutFilename.end(); ++pathIt)
{
AzFramework::StringFunc::AssetDatabasePath::Join(fullFolderName.c_str(), tokens[i].c_str(), fullFolderName);
AssetTreeItem* nextParent = parentItem->GetChildFolder(tokens[i].c_str());
currentPath = pathIt->FixedMaxPathString();
currentFullFolderPath /= currentPath;
AssetTreeItem* nextParent = parentItem->GetChildFolder(currentPath.c_str());
if (!nextParent)
{
if (!modelIsResetting)
{
QModelIndex parentIndex = parentItem == m_root.get() ? QModelIndex() : createIndex(parentItem->GetRow(), 0, parentItem);
QModelIndex parentIndex = createIndex(parentItem->GetRow(), 0, parentItem);
beginInsertRows(parentIndex, parentItem->getChildCount(), parentItem->getChildCount());
}
nextParent = parentItem->CreateChild(ProductAssetTreeItemData::MakeShared(nullptr, fullFolderName, tokens[i].c_str(), true, AZ::Uuid::CreateNull()));
m_productToTreeItem[fullFolderName] = nextParent;
nextParent = parentItem->CreateChild(ProductAssetTreeItemData::MakeShared(nullptr, currentFullFolderPath.Native(), currentPath.c_str(), true, AZ::Uuid::CreateNull()));
m_productToTreeItem[currentFullFolderPath.Native()] = nextParent;
// m_productIdToTreeItem is not used for folders, folders don't have product IDs.
if (!modelIsResetting)
@@ -205,12 +208,12 @@ namespace AssetProcessor
if (!modelIsResetting)
{
QModelIndex parentIndex = parentItem == m_root.get() ? QModelIndex() : createIndex(parentItem->GetRow(), 0, parentItem);
QModelIndex parentIndex = createIndex(parentItem->GetRow(), 0, parentItem);
beginInsertRows(parentIndex, parentItem->getChildCount(), parentItem->getChildCount());
}
AZStd::shared_ptr<ProductAssetTreeItemData> productItemData =
ProductAssetTreeItemData::MakeShared(&product, product.m_productName, tokens[tokens.size() - 1].c_str(), false, sourceId);
ProductAssetTreeItemData::MakeShared(&product, product.m_productName, AZStd::fixed_string<AZ::IO::MaxPathLength>(filename.Native()).c_str(), false, sourceId);
m_productToTreeItem[product.m_productName] =
parentItem->CreateChild(productItemData);
m_productIdToTreeItem[product.m_productID] = m_productToTreeItem[product.m_productName];
@@ -63,8 +63,7 @@ namespace AssetProcessor
}
auto fullPath = AZ::IO::Path(scanFolder.m_scanFolder) / source.m_sourceName;
AZ::IO::Path fullPath = AZ::IO::Path(scanFolder.m_scanFolder, AZ::IO::PosixPathSeparator) / source.m_sourceName;
// It's common for Open 3D Engine game projects and scan folders to be in a subfolder
// of the engine install. To improve readability of the source files, strip out
@@ -78,34 +77,35 @@ namespace AssetProcessor
AzFramework::StringFunc::Replace(fullPath.Native(), m_assetRoot.absolutePath().toUtf8(), "");
}
AZStd::vector<AZStd::string> tokens;
AzFramework::StringFunc::Tokenize(fullPath.c_str(), tokens, AZ_CORRECT_DATABASE_SEPARATOR, false, true);
if (tokens.empty())
if (fullPath.empty())
{
AZ_Warning("AssetProcessor", false, "Source id %s has an invalid name: %s",
source.m_sourceGuid.ToString<AZStd::string>().c_str(), source.m_sourceName.c_str());
AZ_Warning(
"AssetProcessor", false, "Source id %s has an invalid name: %s", source.m_sourceGuid.ToString<AZStd::string>().c_str(),
source.m_sourceName.c_str());
return;
}
QModelIndex newIndicesStart;
AssetTreeItem* parentItem = m_root.get();
AZStd::string fullFolderName;
for (int i = 0; i < tokens.size() - 1; ++i)
AZ::IO::Path currentFullFolderPath;
const AZ::IO::PathView filename = fullPath.Filename();
const AZ::IO::PathView fullPathWithoutFilename = fullPath.RemoveFilename();
AZStd::fixed_string<AZ::IO::MaxPathLength> currentPath;
for (auto pathIt = fullPathWithoutFilename.begin(); pathIt != fullPathWithoutFilename.end(); ++pathIt)
{
AzFramework::StringFunc::AssetDatabasePath::Join(fullFolderName.c_str(), tokens[i].c_str(), fullFolderName);
AssetTreeItem* nextParent = parentItem->GetChildFolder(tokens[i].c_str());
currentPath = pathIt->FixedMaxPathString();
currentFullFolderPath /= currentPath;
AssetTreeItem* nextParent = parentItem->GetChildFolder(currentPath.c_str());
if (!nextParent)
{
if (!modelIsResetting)
{
QModelIndex parentIndex = parentItem == m_root.get() ? QModelIndex() : createIndex(parentItem->GetRow(), 0, parentItem);
QModelIndex parentIndex = createIndex(parentItem->GetRow(), 0, parentItem);
beginInsertRows(parentIndex, parentItem->getChildCount(), parentItem->getChildCount());
}
nextParent = parentItem->CreateChild(SourceAssetTreeItemData::MakeShared(nullptr, nullptr, fullFolderName, tokens[i].c_str(), true));
m_sourceToTreeItem[fullFolderName] = nextParent;
nextParent = parentItem->CreateChild(SourceAssetTreeItemData::MakeShared(nullptr, nullptr, currentFullFolderPath.Native(), currentPath.c_str(), true));
m_sourceToTreeItem[currentFullFolderPath.Native()] = nextParent;
// Folders don't have source IDs, don't add to m_sourceIdToTreeItem
if (!modelIsResetting)
{
@@ -117,12 +117,12 @@ namespace AssetProcessor
if (!modelIsResetting)
{
QModelIndex parentIndex = parentItem == m_root.get() ? QModelIndex() : createIndex(parentItem->GetRow(), 0, parentItem);
QModelIndex parentIndex = createIndex(parentItem->GetRow(), 0, parentItem);
beginInsertRows(parentIndex, parentItem->getChildCount(), parentItem->getChildCount());
}
m_sourceToTreeItem[source.m_sourceName] =
parentItem->CreateChild(SourceAssetTreeItemData::MakeShared(&source, &scanFolder, source.m_sourceName, tokens[tokens.size() - 1].c_str(), false));
parentItem->CreateChild(SourceAssetTreeItemData::MakeShared(&source, &scanFolder, source.m_sourceName, AZStd::fixed_string<AZ::IO::MaxPathLength>(filename.Native()).c_str(), false));
m_sourceIdToTreeItem[source.m_sourceID] = m_sourceToTreeItem[source.m_sourceName];
if (!modelIsResetting)
{
@@ -51,6 +51,8 @@ namespace AssetProcessor
public:
using GetRelativeProductPathFromFullSourceOrProductPathRequest = AzFramework::AssetSystem::GetRelativeProductPathFromFullSourceOrProductPathRequest;
using GetRelativeProductPathFromFullSourceOrProductPathResponse = AzFramework::AssetSystem::GetRelativeProductPathFromFullSourceOrProductPathResponse;
using GenerateRelativeSourcePathRequest = AzFramework::AssetSystem::GenerateRelativeSourcePathRequest;
using GenerateRelativeSourcePathResponse = AzFramework::AssetSystem::GenerateRelativeSourcePathResponse;
using GetFullSourcePathFromRelativeProductPathRequest = AzFramework::AssetSystem::GetFullSourcePathFromRelativeProductPathRequest;
using GetFullSourcePathFromRelativeProductPathResponse = AzFramework::AssetSystem::GetFullSourcePathFromRelativeProductPathResponse;
};
@@ -88,34 +90,34 @@ namespace AssetProcessor
//AZ_TracePrintf("test", "-------------------------\n");
}
void ComputeFingerprints(unsigned int& fingerprintForPC, unsigned int& fingerprintForES3, PlatformConfiguration& config, QString scanFolderPath, QString relPath)
void ComputeFingerprints(unsigned int& fingerprintForPC, unsigned int& fingerprintForANDROID, PlatformConfiguration& config, QString scanFolderPath, QString relPath)
{
QString extraInfoForPC;
QString extraInfoForES3;
QString extraInfoForANDROID;
RecognizerPointerContainer output;
QString filePath = scanFolderPath + "/" + relPath;
config.GetMatchingRecognizers(filePath, output);
for (const AssetRecognizer* assetRecogniser : output)
{
extraInfoForPC.append(assetRecogniser->m_platformSpecs["pc"].m_extraRCParams);
extraInfoForES3.append(assetRecogniser->m_platformSpecs["es3"].m_extraRCParams);
extraInfoForANDROID.append(assetRecogniser->m_platformSpecs["android"].m_extraRCParams);
extraInfoForPC.append(assetRecogniser->m_version);
extraInfoForES3.append(assetRecogniser->m_version);
extraInfoForANDROID.append(assetRecogniser->m_version);
}
//Calculating fingerprints for the file for pc and es3 platforms
//Calculating fingerprints for the file for pc and android platforms
AZ::Uuid sourceId = AZ::Uuid("{2206A6E0-FDBC-45DE-B6FE-C2FC63020BD5}");
JobEntry jobEntryPC(scanFolderPath, relPath, relPath, 0, { "pc", {"desktop", "renderer"} }, "", 0, 1, sourceId);
JobEntry jobEntryES3(scanFolderPath, relPath, relPath, 0, { "es3", {"mobile", "renderer"} }, "", 0, 2, sourceId);
JobEntry jobEntryANDROID(scanFolderPath, relPath, relPath, 0, { "android", {"mobile", "renderer"} }, "", 0, 2, sourceId);
JobDetails jobDetailsPC;
jobDetailsPC.m_extraInformationForFingerprinting = extraInfoForPC.toUtf8().constData();
jobDetailsPC.m_jobEntry = jobEntryPC;
JobDetails jobDetailsES3;
jobDetailsES3.m_extraInformationForFingerprinting = extraInfoForES3.toUtf8().constData();
jobDetailsES3.m_jobEntry = jobEntryES3;
JobDetails jobDetailsANDROID;
jobDetailsANDROID.m_extraInformationForFingerprinting = extraInfoForANDROID.toUtf8().constData();
jobDetailsANDROID.m_jobEntry = jobEntryANDROID;
fingerprintForPC = AssetUtilities::GenerateFingerprint(jobDetailsPC);
fingerprintForES3 = AssetUtilities::GenerateFingerprint(jobDetailsES3);
fingerprintForANDROID = AssetUtilities::GenerateFingerprint(jobDetailsANDROID);
}
}
@@ -173,6 +175,16 @@ namespace AssetProcessor
UNIT_TEST_EXPECT_FALSE(gameName.isEmpty());
// should create cache folder in the root, and read everything from there.
// There is a sub-case of handling mixed cases, but is only supported on case-insensitive filesystems.
#if defined(AZ_PLATFORM_LINUX)
// Linux is case-sensitive, so 'basefile.txt' will stay the same case as the other subfolder versions
constexpr const char* subfolder3BaseFilePath = "subfolder3/basefile.txt";
constexpr int expectedLegacyAssetIdCount = 1;
#else
constexpr const char* subfolder3BaseFilePath = "subfolder3/BaseFile.txt";
constexpr int expectedLegacyAssetIdCount = 2;
#endif
QSet<QString> expectedFiles;
// set up some interesting files:
expectedFiles << tempPath.absoluteFilePath("rootfile2.txt");
@@ -183,7 +195,9 @@ namespace AssetProcessor
expectedFiles << tempPath.absoluteFilePath("subfolder2/aaa/bbb/basefile.txt");
expectedFiles << tempPath.absoluteFilePath("subfolder2/aaa/bbb/ccc/basefile.txt");
expectedFiles << tempPath.absoluteFilePath("subfolder2/aaa/bbb/ccc/ddd/basefile.txt");
expectedFiles << tempPath.absoluteFilePath("subfolder3/BaseFile.txt"); // note the case upper here
expectedFiles << tempPath.absoluteFilePath(subfolder3BaseFilePath);
expectedFiles << tempPath.absoluteFilePath("subfolder8/a/b/c/test.txt");
// subfolder3 is not recursive so none of these should show up in any scan or override check
@@ -240,7 +254,7 @@ namespace AssetProcessor
PlatformConfiguration config;
config.EnablePlatform({ "pc",{ "desktop", "renderer" } }, true);
config.EnablePlatform({ "es3",{ "mobile", "renderer" } }, true);
config.EnablePlatform({ "android",{ "mobile", "renderer" } }, true);
config.EnablePlatform({ "fandago",{ "console", "renderer" } }, false);
AZStd::vector<AssetBuilderSDK::PlatformInfo> platforms;
config.PopulatePlatformsForScanFolder(platforms);
@@ -259,9 +273,9 @@ namespace AssetProcessor
AssetRecognizer rec;
AssetPlatformSpec specpc;
AssetPlatformSpec speces3;
AssetPlatformSpec specandroid;
speces3.m_extraRCParams = "somerandomparam";
specandroid.m_extraRCParams = "somerandomparam";
rec.m_name = "random files";
rec.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.random", AssetBuilderSDK::AssetBuilderPattern::Wildcard);
rec.m_platformSpecs.insert("pc", specpc);
@@ -269,13 +283,13 @@ namespace AssetProcessor
UNIT_TEST_EXPECT_TRUE(mockAppManager.RegisterAssetRecognizerAsBuilder(rec));
specpc.m_extraRCParams = ""; // blank must work
speces3.m_extraRCParams = "testextraparams";
specandroid.m_extraRCParams = "testextraparams";
const char* builderTxt1Name = "txt files";
rec.m_name = builderTxt1Name;
rec.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.txt", AssetBuilderSDK::AssetBuilderPattern::Wildcard);
rec.m_platformSpecs.insert("pc", specpc);
rec.m_platformSpecs.insert("es3", speces3);
rec.m_platformSpecs.insert("android", specandroid);
config.AddRecognizer(rec);
@@ -305,21 +319,21 @@ namespace AssetProcessor
rec.m_testLockSource = false;
specpc.m_extraRCParams = "pcparams";
speces3.m_extraRCParams = "es3params";
specandroid.m_extraRCParams = "androidparams";
rec.m_name = "xxx files";
rec.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.xxx", AssetBuilderSDK::AssetBuilderPattern::Wildcard);
rec.m_platformSpecs.insert("pc", specpc);
rec.m_platformSpecs.insert("es3", speces3);
rec.m_platformSpecs.insert("android", specandroid);
config.AddRecognizer(rec);
mockAppManager.RegisterAssetRecognizerAsBuilder(rec);
// two recognizers for the same pattern.
rec.m_name = "xxx files 2 (builder2)";
specpc.m_extraRCParams = "pcparams2";
speces3.m_extraRCParams = "es3params2";
specandroid.m_extraRCParams = "androidparams2";
rec.m_platformSpecs.insert("pc", specpc);
rec.m_platformSpecs.insert("es3", speces3);
rec.m_platformSpecs.insert("android", specandroid);
config.AddRecognizer(rec);
mockAppManager.RegisterAssetRecognizerAsBuilder(rec);
@@ -330,7 +344,7 @@ namespace AssetProcessor
ignore_rec.m_name = "ignore files";
ignore_rec.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.ignore", AssetBuilderSDK::AssetBuilderPattern::Wildcard);
ignore_rec.m_platformSpecs.insert("pc", specpc);
ignore_rec.m_platformSpecs.insert("es3", ignore_spec);
ignore_rec.m_platformSpecs.insert("android", ignore_spec);
config.AddRecognizer(ignore_rec);
mockAppManager.RegisterAssetRecognizerAsBuilder(ignore_rec);
@@ -432,7 +446,7 @@ namespace AssetProcessor
sortAssetToProcessResultList(processResults);
UNIT_TEST_EXPECT_TRUE(processResults.size() == 1); // 1, since we have one recognizer for .ignore, but the 'es3' platform is marked as skip
UNIT_TEST_EXPECT_TRUE(processResults.size() == 1); // 1, since we have one recognizer for .ignore, but the 'android' platform is marked as skip
UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "pc"));
@@ -455,16 +469,16 @@ namespace AssetProcessor
sortAssetToProcessResultList(processResults);
UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and es3,since we have two recognizer for .txt file
UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and android,since we have two recognizer for .txt file
UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == processResults[1].m_jobEntry.m_platformInfo.m_identifier);
UNIT_TEST_EXPECT_TRUE(processResults[2].m_jobEntry.m_platformInfo.m_identifier == processResults[3].m_jobEntry.m_platformInfo.m_identifier);
UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3"));
UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "es3"));
UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android"));
UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "android"));
UNIT_TEST_EXPECT_TRUE((processResults[2].m_jobEntry.m_platformInfo.m_identifier == "pc"));
UNIT_TEST_EXPECT_TRUE((processResults[3].m_jobEntry.m_platformInfo.m_identifier == "pc"));
QList<int> es3JobsIndex;
QList<int> androidJobsIndex;
QList<int> pcJobsIndex;
for (int checkIdx = 0; checkIdx < 4; ++checkIdx)
{
@@ -662,19 +676,19 @@ namespace AssetProcessor
// ---------- test successes ----------
QStringList es3outs;
es3outs.push_back(cacheRoot.filePath(QString("es3/basefile.arc1")));
es3outs.push_back(cacheRoot.filePath(QString("es3/basefile.arc2")));
QStringList androidouts;
androidouts.push_back(cacheRoot.filePath(QString("android/basefile.arc1")));
androidouts.push_back(cacheRoot.filePath(QString("android/basefile.arc2")));
// feed it the messages its waiting for (create the files)
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(es3outs[0], "products."));
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(es3outs[1], "products."))
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(androidouts[0], "products."));
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(androidouts[1], "products."))
//Invoke Asset Processed for es3 platform , txt files job description
//Invoke Asset Processed for android platform , txt files job description
AssetBuilderSDK::ProcessJobResponse response;
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs[0].toUtf8().constData(), AZ::Uuid::CreateNull(), 1));
response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs[1].toUtf8().constData(), AZ::Uuid::CreateNull(), 2));
response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts[0].toUtf8().constData(), AZ::Uuid::CreateNull(), 1));
response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts[1].toUtf8().constData(), AZ::Uuid::CreateNull(), 2));
// make sure legacy SubIds get stored in the DB and in asset response messages.
// also make sure they don't get filed for the wrong asset.
@@ -693,8 +707,8 @@ namespace AssetProcessor
UNIT_TEST_EXPECT_TRUE(changedInputResults.size() == 1);
// always RELATIVE, always with the product name.
UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_platform == "es3");
UNIT_TEST_EXPECT_TRUE(assetMessages[1].m_platform == "es3");
UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_platform == "android");
UNIT_TEST_EXPECT_TRUE(assetMessages[1].m_platform == "android");
UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_data == "basefile.arc1");
UNIT_TEST_EXPECT_TRUE(assetMessages[1].m_data == "basefile.arc2");
UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_type == AzFramework::AssetSystem::AssetNotificationMessage::AssetChanged);
@@ -793,14 +807,14 @@ namespace AssetProcessor
changedInputResults.clear();
assetMessages.clear();
es3outs.clear();
es3outs.push_back(cacheRoot.filePath(QString("es3/basefile.azm")));
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(es3outs[0], "products."));
androidouts.clear();
androidouts.push_back(cacheRoot.filePath(QString("android/basefile.azm")));
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(androidouts[0], "products."));
//Invoke Asset Processed for es3 platform , txt files2 job description
//Invoke Asset Processed for android platform , txt files2 job description
response.m_outputProducts.clear();
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs[0].toUtf8().constData()));
response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts[0].toUtf8().constData()));
QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[1].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response));
@@ -812,7 +826,7 @@ namespace AssetProcessor
UNIT_TEST_EXPECT_TRUE(changedInputResults.size() == 1);
// always RELATIVE, always with the product name.
UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_platform == "es3");
UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_platform == "android");
UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_data == "basefile.azm");
changedInputResults.clear();
@@ -1002,11 +1016,11 @@ namespace AssetProcessor
sortAssetToProcessResultList(processResults);
// --------- same result as above ----------
UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and es3,since we have two recognizer for .txt file
UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and android,since we have two recognizer for .txt file
UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == processResults[1].m_jobEntry.m_platformInfo.m_identifier);
UNIT_TEST_EXPECT_TRUE(processResults[2].m_jobEntry.m_platformInfo.m_identifier == processResults[3].m_jobEntry.m_platformInfo.m_identifier);
UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3"));
UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "es3"));
UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android"));
UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "android"));
UNIT_TEST_EXPECT_TRUE((processResults[2].m_jobEntry.m_platformInfo.m_identifier == "pc"));
UNIT_TEST_EXPECT_TRUE((processResults[3].m_jobEntry.m_platformInfo.m_identifier == "pc"));
UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_computedFingerprint != 0);
@@ -1025,25 +1039,25 @@ namespace AssetProcessor
// this time make different products:
QStringList oldes3outs;
QStringList oldandroidouts;
QStringList oldpcouts;
oldes3outs = es3outs;
oldandroidouts = androidouts;
oldpcouts.append(pcouts);
QStringList es3outs2;
QStringList androidouts2;
QStringList pcouts2;
es3outs.clear();
androidouts.clear();
pcouts.clear();
es3outs.push_back(cacheRoot.filePath(QString("es3/basefilea.arc1")));
es3outs2.push_back(cacheRoot.filePath(QString("es3/basefilea.azm")));
// note that the ES3 outs have changed
androidouts.push_back(cacheRoot.filePath(QString("android/basefilea.arc1")));
androidouts2.push_back(cacheRoot.filePath(QString("android/basefilea.azm")));
// note that the android outs have changed
// but the pc outs are still the same.
pcouts.push_back(cacheRoot.filePath(QString("pc/basefile.arc1")));
pcouts2.push_back(cacheRoot.filePath(QString("pc/basefile.azm")));
// feed it the messages its waiting for (create the files)
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(es3outs[0], "newfile."));
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(androidouts[0], "newfile."));
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(pcouts[0], "newfile."));
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(es3outs2[0], "newfile."));
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(androidouts2[0], "newfile."));
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(pcouts2[0], "newfile."));
QCoreApplication::processEvents(QEventLoop::AllEvents | QEventLoop::WaitForMoreEvents, 50);
@@ -1055,12 +1069,12 @@ namespace AssetProcessor
response.m_outputProducts.clear();
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs[0].toUtf8().constData()));
response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts[0].toUtf8().constData()));
QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[0].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response));
response.m_outputProducts.clear();
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs2[0].toUtf8().constData()));
response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts2[0].toUtf8().constData()));
QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[1].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response));
response.m_outputProducts.clear();
@@ -1083,12 +1097,12 @@ namespace AssetProcessor
// The files removed should be the ones we did not emit this time
// note that order isn't guarantee but an example output it this
// [0] Removed: ES3, basefile.arc1
// [1] Removed: ES3, basefile.arc2
// [2] Changed: ES3, basefilea.arc1 (added)
// [0] Removed: ANDROID, basefile.arc1
// [1] Removed: ANDROID, basefile.arc2
// [2] Changed: ANDROID, basefilea.arc1 (added)
// [3] Removed: ES3, basefile.azm
// [4] Changed: ES3, basefilea.azm (added)
// [3] Removed: ANDROID, basefile.azm
// [4] Changed: ANDROID, basefilea.azm (added)
// [5] changed: PC, basefile.arc1 (changed)
// [6] changed: PC, basefile.azm (changed)
@@ -1110,18 +1124,18 @@ namespace AssetProcessor
if (element.m_data == "basefilea.arc1")
{
UNIT_TEST_EXPECT_TRUE(element.m_type == AzFramework::AssetSystem::AssetNotificationMessage::AssetChanged);
UNIT_TEST_EXPECT_TRUE(element.m_platform == "es3");
UNIT_TEST_EXPECT_TRUE(element.m_platform == "android");
}
if (element.m_data == "basefile.arc2")
{
UNIT_TEST_EXPECT_TRUE(element.m_type == AzFramework::AssetSystem::AssetNotificationMessage::AssetRemoved);
UNIT_TEST_EXPECT_TRUE(element.m_platform == "es3");
UNIT_TEST_EXPECT_TRUE(element.m_platform == "android");
}
}
// original products must no longer exist since it should have found and deleted them!
for (QString outFile: oldes3outs)
for (QString outFile: oldandroidouts)
{
UNIT_TEST_EXPECT_FALSE(QFile::exists(outFile));
}
@@ -1145,11 +1159,11 @@ namespace AssetProcessor
sortAssetToProcessResultList(processResults);
// --------- same result as above ----------
UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // pc and es3
UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // pc and android
UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == processResults[1].m_jobEntry.m_platformInfo.m_identifier);
UNIT_TEST_EXPECT_TRUE(processResults[2].m_jobEntry.m_platformInfo.m_identifier == processResults[3].m_jobEntry.m_platformInfo.m_identifier);
UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3"));
UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "es3"));
UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android"));
UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "android"));
UNIT_TEST_EXPECT_TRUE((processResults[2].m_jobEntry.m_platformInfo.m_identifier == "pc"));
UNIT_TEST_EXPECT_TRUE((processResults[3].m_jobEntry.m_platformInfo.m_identifier == "pc"));
UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_computedFingerprint != 0);
@@ -1169,12 +1183,12 @@ namespace AssetProcessor
response.m_outputProducts.clear();
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs[0].toUtf8().constData()));
response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts[0].toUtf8().constData()));
QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[0].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response));
response.m_outputProducts.clear();
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs2[0].toUtf8().constData()));
response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts2[0].toUtf8().constData()));
QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[1].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response));
response.m_outputProducts.clear();
@@ -1205,11 +1219,11 @@ namespace AssetProcessor
sortAssetToProcessResultList(processResults);
// --------- same result as above ----------
UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and es3,since we have two recognizer for .txt file
UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and android,since we have two recognizer for .txt file
UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == processResults[1].m_jobEntry.m_platformInfo.m_identifier);
UNIT_TEST_EXPECT_TRUE(processResults[2].m_jobEntry.m_platformInfo.m_identifier == processResults[3].m_jobEntry.m_platformInfo.m_identifier);
UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3"));
UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "es3"));
UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android"));
UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "android"));
UNIT_TEST_EXPECT_TRUE((processResults[2].m_jobEntry.m_platformInfo.m_identifier == "pc"));
UNIT_TEST_EXPECT_TRUE((processResults[3].m_jobEntry.m_platformInfo.m_identifier == "pc"));
UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_computedFingerprint != 0);
@@ -1220,12 +1234,12 @@ namespace AssetProcessor
response.m_outputProducts.clear();
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs[0].toUtf8().constData()));
response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts[0].toUtf8().constData()));
QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[0].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response));
response.m_outputProducts.clear();
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs2[0].toUtf8().constData()));
response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts2[0].toUtf8().constData()));
QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[1].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response));
response.m_outputProducts.clear();
@@ -1243,9 +1257,9 @@ namespace AssetProcessor
// deleting the fingerprint file should not have erased the products
UNIT_TEST_EXPECT_TRUE(QFile::exists(pcouts[0]));
UNIT_TEST_EXPECT_TRUE(QFile::exists(es3outs[0]));
UNIT_TEST_EXPECT_TRUE(QFile::exists(androidouts[0]));
UNIT_TEST_EXPECT_TRUE(QFile::exists(pcouts2[0]));
UNIT_TEST_EXPECT_TRUE(QFile::exists(es3outs2[0]));
UNIT_TEST_EXPECT_TRUE(QFile::exists(androidouts2[0]));
changedInputResults.clear();
assetMessages.clear();
@@ -1304,9 +1318,9 @@ namespace AssetProcessor
}
UNIT_TEST_EXPECT_FALSE(QFile::exists(pcouts[0]));
UNIT_TEST_EXPECT_FALSE(QFile::exists(es3outs[0]));
UNIT_TEST_EXPECT_FALSE(QFile::exists(androidouts[0]));
UNIT_TEST_EXPECT_FALSE(QFile::exists(pcouts2[0]));
UNIT_TEST_EXPECT_FALSE(QFile::exists(es3outs2[0]));
UNIT_TEST_EXPECT_FALSE(QFile::exists(androidouts2[0]));
changedInputResults.clear();
assetMessages.clear();
@@ -1321,28 +1335,28 @@ namespace AssetProcessor
sortAssetToProcessResultList(processResults);
// --------- same result as above ----------
UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and es3,since we have two recognizer for .txt file
UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and android,since we have two recognizer for .txt file
UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == processResults[1].m_jobEntry.m_platformInfo.m_identifier);
UNIT_TEST_EXPECT_TRUE(processResults[2].m_jobEntry.m_platformInfo.m_identifier == processResults[3].m_jobEntry.m_platformInfo.m_identifier);
UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3"));
UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "es3"));
UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android"));
UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "android"));
UNIT_TEST_EXPECT_TRUE((processResults[2].m_jobEntry.m_platformInfo.m_identifier == "pc"));
UNIT_TEST_EXPECT_TRUE((processResults[3].m_jobEntry.m_platformInfo.m_identifier == "pc"));
UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_computedFingerprint != 0);
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(es3outs[0], "newfile."));
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(es3outs2[0], "newfile."));
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(androidouts[0], "newfile."));
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(androidouts2[0], "newfile."));
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(pcouts2[0], "newfile."));
// send both done messages simultaneously!
response.m_outputProducts.clear();
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs[0].toUtf8().constData()));
response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts[0].toUtf8().constData()));
QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[0].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response));
response.m_outputProducts.clear();
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs2[0].toUtf8().constData()));
response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts2[0].toUtf8().constData()));
QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[1].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response));
// send one failure only for PC :
@@ -1420,12 +1434,12 @@ namespace AssetProcessor
UNIT_TEST_EXPECT_TRUE(changedInputResults.size() == 3);
UNIT_TEST_EXPECT_TRUE(assetMessages.size() == 3);
// which should be for the ES3:
// which should be for the ANDROID:
UNIT_TEST_EXPECT_TRUE(AssetUtilities::NormalizeFilePath(changedInputResults[0].first) == absolutePath);
// always RELATIVE, always with the product name.
UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_data == "basefilea.arc1" || assetMessages[0].m_data == "basefilea.azm");
UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_platform == "es3");
UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_platform == "android");
for (auto& payload : payloadList)
{
@@ -1519,35 +1533,36 @@ namespace AssetProcessor
// -------------- override test -----------------
// set up by letting it compile basefile.txt from 3:
absolutePath = AssetUtilities::NormalizeFilePath(tempPath.absoluteFilePath("subfolder3/BaseFile.txt"));
absolutePath = AssetUtilities::NormalizeFilePath(tempPath.absoluteFilePath(subfolder3BaseFilePath));
QMetaObject::invokeMethod(&apm, "AssessModifiedFile", Qt::QueuedConnection, Q_ARG(QString, absolutePath));
UNIT_TEST_EXPECT_TRUE(BlockUntil(idling, 5000));
sortAssetToProcessResultList(processResults);
// --------- same result as above ----------
UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and es3,since we have two recognizer for .txt file
UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and android,since we have two recognizer for .txt file
UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == processResults[1].m_jobEntry.m_platformInfo.m_identifier);
UNIT_TEST_EXPECT_TRUE(processResults[2].m_jobEntry.m_platformInfo.m_identifier == processResults[3].m_jobEntry.m_platformInfo.m_identifier);
UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3"));
UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "es3"));
UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android"));
UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "android"));
UNIT_TEST_EXPECT_TRUE((processResults[2].m_jobEntry.m_platformInfo.m_identifier == "pc"));
UNIT_TEST_EXPECT_TRUE((processResults[3].m_jobEntry.m_platformInfo.m_identifier == "pc"));
UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_computedFingerprint != 0);
es3outs.clear();
es3outs2.clear();
androidouts.clear();
androidouts2.clear();
pcouts.clear();
pcouts2.clear();
es3outs.push_back(cacheRoot.filePath(QString("es3/basefilez.arc2")));
es3outs2.push_back(cacheRoot.filePath(QString("es3/basefileaz.azm2")));
// note that the ES3 outs have changed
androidouts.push_back(cacheRoot.filePath(QString("android/basefilez.arc2")));
androidouts2.push_back(cacheRoot.filePath(QString("android/basefileaz.azm2")));
// note that the android outs have changed
// but the pc outs are still the same.
pcouts.push_back(cacheRoot.filePath(QString("pc/basefile.arc2")));
pcouts2.push_back(cacheRoot.filePath(QString("pc/basefile.azm2")));
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(es3outs[0], "newfile."));
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(androidouts[0], "newfile."));
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(pcouts[0], "newfile."));
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(es3outs2[0], "newfile."));
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(androidouts2[0], "newfile."));
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(pcouts2[0], "newfile."));
changedInputResults.clear();
assetMessages.clear();
@@ -1555,12 +1570,12 @@ namespace AssetProcessor
// send all the done messages simultaneously:
response.m_outputProducts.clear();
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs[0].toUtf8().constData(), AZ::Uuid::CreateNull(), 1));
response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts[0].toUtf8().constData(), AZ::Uuid::CreateNull(), 1));
QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[0].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response));
response.m_outputProducts.clear();
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(es3outs2[0].toUtf8().constData(), AZ::Uuid::CreateNull(), 2));
response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(androidouts2[0].toUtf8().constData(), AZ::Uuid::CreateNull(), 2));
QMetaObject::invokeMethod(&apm, "AssetProcessed", Qt::QueuedConnection, Q_ARG(JobEntry, processResults[1].m_jobEntry), Q_ARG(AssetBuilderSDK::ProcessJobResponse, response));
response.m_outputProducts.clear();
@@ -1581,8 +1596,7 @@ namespace AssetProcessor
UNIT_TEST_EXPECT_TRUE(assetMessages.size() == 4);
for (auto element : assetMessages)
{
// because the source asset had UPPER CASE in it, we should have multiple legacy IDs
UNIT_TEST_EXPECT_TRUE(element.m_legacyAssetIds.size() == 2);
UNIT_TEST_EXPECT_TRUE(element.m_legacyAssetIds.size() == expectedLegacyAssetIdCount);
}
// ------------- setup complete, now do the test...
@@ -1605,7 +1619,7 @@ namespace AssetProcessor
// delete the highest priority override file and ensure that it generates tasks
// for the next highest priority! Basically, deleting this file should "reveal" the file underneath it in the other subfolder
QString deletedFile = tempPath.absoluteFilePath("subfolder3/BaseFile.txt");
QString deletedFile = tempPath.absoluteFilePath(subfolder3BaseFilePath);
QString expectedReplacementInputFile = AssetUtilities::NormalizeFilePath(tempPath.absoluteFilePath("subfolder2/basefile.txt"));
UNIT_TEST_EXPECT_TRUE(QFile::remove(deletedFile));
@@ -1619,12 +1633,17 @@ namespace AssetProcessor
sortAssetToProcessResultList(processResults);
#if defined(AZ_PLATFORM_LINUX)
// On Linux, because of we cannot change the case of the source file, the job fingerprint is not updated due the case-switch so
// there will be actually nothing to process
UNIT_TEST_EXPECT_TRUE(processResults.size() == 0);
#else
// --------- same result as above ----------
UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and es3,since we have two recognizer for .txt file
UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // 2 each for pc and android,since we have two recognizer for .txt file
UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == processResults[1].m_jobEntry.m_platformInfo.m_identifier);
UNIT_TEST_EXPECT_TRUE(processResults[2].m_jobEntry.m_platformInfo.m_identifier == processResults[3].m_jobEntry.m_platformInfo.m_identifier);
UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3"));
UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "es3"));
UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android"));
UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "android"));
UNIT_TEST_EXPECT_TRUE((processResults[2].m_jobEntry.m_platformInfo.m_identifier == "pc"));
UNIT_TEST_EXPECT_TRUE((processResults[3].m_jobEntry.m_platformInfo.m_identifier == "pc"));
UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_computedFingerprint != 0);
@@ -1639,15 +1658,15 @@ namespace AssetProcessor
UNIT_TEST_EXPECT_TRUE(processFile1.startsWith(platformFolder));
UNIT_TEST_EXPECT_TRUE(processResults[checkIdx].m_jobEntry.m_computedFingerprint != 0);
}
#endif // defined(AZ_PLATFORM_LINUX)
relativePathFromWatchFolder = "somefile.xxx";
watchFolderPath = tempPath.absoluteFilePath("subfolder3");
absolutePath = watchFolderPath + "/" + relativePathFromWatchFolder;
unsigned int fingerprintForPC = 0;
unsigned int fingerprintForES3 = 0;
unsigned int fingerprintForANDROID = 0;
ComputeFingerprints(fingerprintForPC, fingerprintForES3, config, watchFolderPath, relativePathFromWatchFolder);
ComputeFingerprints(fingerprintForPC, fingerprintForANDROID, config, watchFolderPath, relativePathFromWatchFolder);
processResults.clear();
QMetaObject::invokeMethod(&apm, "AssessModifiedFile", Qt::QueuedConnection, Q_ARG(QString, absolutePath));
@@ -1655,11 +1674,11 @@ namespace AssetProcessor
sortAssetToProcessResultList(processResults);
UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // // 2 each for pc and es3,since we have two recognizer for .xxx file
UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // // 2 each for pc and android,since we have two recognizer for .xxx file
UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == processResults[1].m_jobEntry.m_platformInfo.m_identifier);
UNIT_TEST_EXPECT_TRUE(processResults[2].m_jobEntry.m_platformInfo.m_identifier == processResults[3].m_jobEntry.m_platformInfo.m_identifier);
UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3"));
UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "es3"));
UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android"));
UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "android"));
UNIT_TEST_EXPECT_TRUE((processResults[2].m_jobEntry.m_platformInfo.m_identifier == "pc"));
UNIT_TEST_EXPECT_TRUE((processResults[3].m_jobEntry.m_platformInfo.m_identifier == "pc"));
@@ -1681,11 +1700,11 @@ namespace AssetProcessor
// we never actually submitted any fingerprints or indicated success, so the same number of jobs should occur as before
sortAssetToProcessResultList(processResults);
UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // // 2 each for pc and es3,since we have two recognizer for .xxx file
UNIT_TEST_EXPECT_TRUE(processResults.size() == 4); // // 2 each for pc and android,since we have two recognizer for .xxx file
UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == processResults[1].m_jobEntry.m_platformInfo.m_identifier);
UNIT_TEST_EXPECT_TRUE(processResults[2].m_jobEntry.m_platformInfo.m_identifier == processResults[3].m_jobEntry.m_platformInfo.m_identifier);
UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3"));
UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "es3"));
UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android"));
UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "android"));
UNIT_TEST_EXPECT_TRUE((processResults[2].m_jobEntry.m_platformInfo.m_identifier == "pc"));
UNIT_TEST_EXPECT_TRUE((processResults[3].m_jobEntry.m_platformInfo.m_identifier == "pc"));
@@ -1705,7 +1724,7 @@ namespace AssetProcessor
// now re-perform the same test, this time only the pc ones should re-appear.
// this should happen because we're changing the extra params, which should be part of the fingerprint
// if this unit test fails, check to make sure that the extra params are being ingested into the fingerprint computation functions
// and also make sure that the jobs that are for the remaining es3 platform don't change.
// and also make sure that the jobs that are for the remaining android platform don't change.
// store the UUID so that we can insert the new one with the same UUID
AZStd::shared_ptr<InternalMockBuilder> builderTxt2Builder;
@@ -1743,12 +1762,12 @@ namespace AssetProcessor
// ---------------------
unsigned int newfingerprintForPC = 0;
unsigned int newfingerprintForES3 = 0;
unsigned int newfingerprintForANDROID = 0;
ComputeFingerprints(newfingerprintForPC, newfingerprintForES3, config, watchFolderPath, relativePathFromWatchFolder);
ComputeFingerprints(newfingerprintForPC, newfingerprintForANDROID, config, watchFolderPath, relativePathFromWatchFolder);
UNIT_TEST_EXPECT_TRUE(newfingerprintForPC != fingerprintForPC);//Fingerprints should be different
UNIT_TEST_EXPECT_TRUE(newfingerprintForES3 == fingerprintForES3);//Fingerprints are same
UNIT_TEST_EXPECT_TRUE(newfingerprintForANDROID == fingerprintForANDROID);//Fingerprints are same
config.RemoveRecognizer("xxx files 2 (builder2)");
mockAppManager.UnRegisterAssetRecognizerAsBuilder("xxx files 2 (builder2)");
@@ -1763,18 +1782,18 @@ namespace AssetProcessor
absolutePath = AssetUtilities::NormalizeFilePath(absolutePath);
QMetaObject::invokeMethod(&apm, "AssessModifiedFile", Qt::QueuedConnection, Q_ARG(QString, absolutePath));
UNIT_TEST_EXPECT_TRUE(BlockUntil(idling, 5000));
UNIT_TEST_EXPECT_TRUE(processResults.size() == 2); // pc and es3
UNIT_TEST_EXPECT_TRUE(processResults.size() == 2); // pc and android
UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier != processResults[1].m_jobEntry.m_platformInfo.m_identifier);
UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "pc") || (processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3"));
UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "pc") || (processResults[1].m_jobEntry.m_platformInfo.m_identifier == "es3"));
UNIT_TEST_EXPECT_TRUE((processResults[0].m_jobEntry.m_platformInfo.m_identifier == "pc") || (processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android"));
UNIT_TEST_EXPECT_TRUE((processResults[1].m_jobEntry.m_platformInfo.m_identifier == "pc") || (processResults[1].m_jobEntry.m_platformInfo.m_identifier == "android"));
unsigned int newfingerprintForPCAfterVersionChange = 0;
unsigned int newfingerprintForES3AfterVersionChange = 0;
unsigned int newfingerprintForANDROIDAfterVersionChange = 0;
ComputeFingerprints(newfingerprintForPCAfterVersionChange, newfingerprintForES3AfterVersionChange, config, watchFolderPath, relativePathFromWatchFolder);
ComputeFingerprints(newfingerprintForPCAfterVersionChange, newfingerprintForANDROIDAfterVersionChange, config, watchFolderPath, relativePathFromWatchFolder);
UNIT_TEST_EXPECT_TRUE((newfingerprintForPCAfterVersionChange != fingerprintForPC) || (newfingerprintForPCAfterVersionChange != newfingerprintForPC));//Fingerprints should be different
UNIT_TEST_EXPECT_TRUE((newfingerprintForES3AfterVersionChange != fingerprintForES3) || (newfingerprintForES3AfterVersionChange != newfingerprintForES3));//Fingerprints should be different
UNIT_TEST_EXPECT_TRUE((newfingerprintForANDROIDAfterVersionChange != fingerprintForANDROID) || (newfingerprintForANDROIDAfterVersionChange != newfingerprintForANDROID));//Fingerprints should be different
//------Test for Files which are excluded
processResults.clear();
@@ -1919,7 +1938,7 @@ namespace AssetProcessor
UNIT_TEST_EXPECT_TRUE(processResults.size() == 0); // nothing to process
// we are aware that 4 products went missing (es3 and pc versions of the 2 files since we renamed the SOURCE folder)
// we are aware that 4 products went missing (android and pc versions of the 2 files since we renamed the SOURCE folder)
UNIT_TEST_EXPECT_TRUE(assetMessages.size() == 4);
for (auto element : assetMessages)
{
@@ -2178,8 +2197,8 @@ namespace AssetProcessor
UNIT_TEST_EXPECT_TRUE(assetMessages[2].m_assetId != AZ::Data::AssetId());
UNIT_TEST_EXPECT_TRUE(assetMessages[3].m_assetId != AZ::Data::AssetId());
UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_platform == "es3");
UNIT_TEST_EXPECT_TRUE(assetMessages[1].m_platform == "es3");
UNIT_TEST_EXPECT_TRUE(assetMessages[0].m_platform == "android");
UNIT_TEST_EXPECT_TRUE(assetMessages[1].m_platform == "android");
UNIT_TEST_EXPECT_TRUE(assetMessages[2].m_platform == "pc");
UNIT_TEST_EXPECT_TRUE(assetMessages[3].m_platform == "pc");
@@ -2212,12 +2231,12 @@ namespace AssetProcessor
mockAppManager.UnRegisterAllBuilders();
AssetRecognizer abt_rec1;
AssetPlatformSpec abt_speces3;
AssetPlatformSpec abt_specandroid;
abt_rec1.m_name = "UnitTestTextBuilder1";
abt_rec1.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.txt", AssetBuilderSDK::AssetBuilderPattern::Wildcard);
//abt_rec1.m_regexp.setPatternSyntax(QRegExp::Wildcard);
//abt_rec1.m_regexp.setPattern("*.txt");
abt_rec1.m_platformSpecs.insert("es3", speces3);
abt_rec1.m_platformSpecs.insert("android", specandroid);
mockAppManager.RegisterAssetRecognizerAsBuilder(abt_rec1);
AssetRecognizer abt_rec2;
@@ -2266,8 +2285,8 @@ namespace AssetProcessor
sortAssetToProcessResultList(processResults);
UNIT_TEST_EXPECT_TRUE(processResults.size() == 2); // 1 for pc and es3
UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == "es3");
UNIT_TEST_EXPECT_TRUE(processResults.size() == 2); // 1 for pc and android
UNIT_TEST_EXPECT_TRUE(processResults[0].m_jobEntry.m_platformInfo.m_identifier == "android");
UNIT_TEST_EXPECT_TRUE(processResults[1].m_jobEntry.m_platformInfo.m_identifier == "pc");
UNIT_TEST_EXPECT_TRUE(QString::compare(processResults[0].m_jobEntry.GetAbsoluteSourcePath(), absolutePath, Qt::CaseInsensitive) == 0);
UNIT_TEST_EXPECT_TRUE(QString::compare(processResults[1].m_jobEntry.GetAbsoluteSourcePath(), absolutePath, Qt::CaseInsensitive) == 0);
@@ -2719,7 +2738,12 @@ namespace AssetProcessor
{
AssetBuilderSDK::JobDescriptor secondDescriptor = descriptor;
secondDescriptor.m_jobKey = "yyy";
#if defined(AZ_PLATFORM_WINDOWS)
sourceFileDependency.m_sourceFileDependencyPath = "some\\random/Folders/FILEa.TxT";
#else
sourceFileDependency.m_sourceFileDependencyPath = "some/random/folders/FileA.txt";
#endif // defined(AZ_PLATFORM_WINDOWS)
// ... declare a job dependency on job A ('FileA.txt', 'xxx', platform)
AssetBuilderSDK::JobDependency jobDependency("xxx", platformInfo.m_identifier.c_str(), AssetBuilderSDK::JobDependencyType::Fingerprint, sourceFileDependency);
secondDescriptor.m_jobDependencyList.push_back(jobDependency);
@@ -2803,11 +2827,11 @@ namespace AssetProcessor
QDir cacheRoot;
UNIT_TEST_EXPECT_TRUE(AssetUtilities::ComputeProjectCacheRoot(cacheRoot));
QString productFileAPath = cacheRoot.filePath(QString("pc/FileAProduct.txt"));
QString productFileBPath = cacheRoot.filePath(QString("pc/FileBProduct1.txt"));
QString product2FileBPath = cacheRoot.filePath(QString("pc/FileBProduct2.txt"));
QString productFileCPath = cacheRoot.filePath(QString("pc/FileCProduct.txt"));
QString product2FileCPath = cacheRoot.filePath(QString("pc/FileCProduct2.txt"));
QString productFileAPath = cacheRoot.filePath(QString("pc/fileaproduct.txt"));
QString productFileBPath = cacheRoot.filePath(QString("pc/filebproduct1.txt"));
QString product2FileBPath = cacheRoot.filePath(QString("pc/filebproduct2.txt"));
QString productFileCPath = cacheRoot.filePath(QString("pc/filecproduct.txt"));
QString product2FileCPath = cacheRoot.filePath(QString("pc/filecproduct2.txt"));
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(sourceFileAPath, ""));
UNIT_TEST_EXPECT_TRUE(CreateDummyFile(sourceFileBPath, ""));
@@ -17,16 +17,16 @@ void ConnectionUnitTest::StartTest()
m_testConnection.SetAssetPlatformsString("pc");
AzFramework::AssetSystem::AssetNotificationMessage testMessage;
EXPECT_CALL(m_testConnection, Send(testing::_, testing::_)).Times(0);
m_testConnection.SendPerPlatform(0, testMessage, "osx_gl");
m_testConnection.SendPerPlatform(0, testMessage, "mac");
EXPECT_CALL(m_testConnection, Send(testing::_, testing::_)).Times(1);
m_testConnection.SendPerPlatform(0, testMessage, "pc");
m_testConnection.SetAssetPlatformsString("pc,es3");
m_testConnection.SetAssetPlatformsString("pc,android");
EXPECT_CALL(m_testConnection, Send(testing::_, testing::_)).Times(1);
m_testConnection.SendPerPlatform(0, testMessage, "pc");
EXPECT_CALL(m_testConnection, Send(testing::_, testing::_)).Times(0);
m_testConnection.SendPerPlatform(0, testMessage, "osx_gl");
m_testConnection.SendPerPlatform(0, testMessage, "mac");
EXPECT_CALL(m_testConnection, Send(testing::_, testing::_)).Times(1);
m_testConnection.SendPerPlatform(0, testMessage, "es3");
m_testConnection.SendPerPlatform(0, testMessage, "android");
EXPECT_CALL(m_testConnection, Send(testing::_, testing::_)).Times(0);
// Intended partial string match test - shouldn't send
m_testConnection.SendPerPlatform(0, testMessage, "es");
@@ -106,7 +106,7 @@ void FileWatcherUnitTestRunner::StartTest()
AZ_TracePrintf(AssetProcessor::DebugChannel, "Waiting for remaining notifications: %d \n", outstandingFiles.count());
}
if (outstandingFiles.count() > 0)
if (outstandingFiles.count() > 0)
{
#if defined(AZ_ENABLE_TRACING)
AZ_TracePrintf(AssetProcessor::DebugChannel, "Timed out waiting for file changes: %d / %d missed\n", outstandingFiles.count(), maxFiles);
@@ -226,7 +226,9 @@ void FileWatcherUnitTestRunner::StartTest()
UNIT_TEST_EXPECT_TRUE(fileAddCalled);
UNIT_TEST_EXPECT_TRUE(fileRemoveCalled);
UNIT_TEST_EXPECT_TRUE(fileModifiedCalled); // modified should be called on the folder that the file lives in
#if defined(AZ_PLATFORM_WINDOWS)
UNIT_TEST_EXPECT_TRUE(fileModifiedCalled); // modified should be called on the folder that the file lives in (Only on Windows)
#endif // AZ_PLATFORM_WINDOWS
UNIT_TEST_EXPECT_TRUE(QDir::toNativeSeparators(fileRemoveName).toLower() == QDir::toNativeSeparators(originalName).toLower());
UNIT_TEST_EXPECT_TRUE(QDir::toNativeSeparators(fileAddName).toLower() == QDir::toNativeSeparators(newName1).toLower());
@@ -249,7 +251,10 @@ void FileWatcherUnitTestRunner::StartTest()
// the new1 was "removed" and the new2 was "added"
UNIT_TEST_EXPECT_TRUE(fileAddCalled);
UNIT_TEST_EXPECT_TRUE(fileRemoveCalled);
UNIT_TEST_EXPECT_TRUE(fileModifiedCalled); // modified should be called on the folder that the file lives in
#if defined(AZ_PLATFORM_WINDOWS)
UNIT_TEST_EXPECT_TRUE(fileModifiedCalled); // modified should be called on the folder that the file lives in (Only on Windows)
#endif // AZ_PLATFORM_WINDOWS
UNIT_TEST_EXPECT_TRUE(QDir::toNativeSeparators(fileRemoveName).toLower() == QDir::toNativeSeparators(newName1).toLower());
UNIT_TEST_EXPECT_TRUE(QDir::toNativeSeparators(fileAddName).toLower() == QDir::toNativeSeparators(newName2).toLower());
@@ -270,11 +275,15 @@ void FileWatcherUnitTestRunner::StartTest()
// the new1 was "removed" and the new2 was "added"
UNIT_TEST_EXPECT_TRUE(fileAddCalled);
UNIT_TEST_EXPECT_TRUE(fileRemoveCalled);
UNIT_TEST_EXPECT_TRUE(fileModifiedCalled); // modified should be called on the folder that the file lives in
#if defined(AZ_PLATFORM_WINDOWS)
UNIT_TEST_EXPECT_TRUE(fileModifiedCalled); // modified should be called on the folder that the file lives in (Only on Windows)
#endif // AZ_PLATFORM_WINDOWS
UNIT_TEST_EXPECT_TRUE(QDir::toNativeSeparators(fileRemoveName).toLower() == QDir::toNativeSeparators(newName2).toLower());
UNIT_TEST_EXPECT_TRUE(QDir::toNativeSeparators(fileAddName).toLower() == QDir::toNativeSeparators(newName3).toLower());
// final test... make sure that renaming a DIRECTORY works too
#if !defined(AZ_PLATFORM_LINUX)
// final test... make sure that renaming a DIRECTORY works too.
// Note that linux does not get any callbacks if just the directory is renamed (from inotify)
QDir renamer;
fileAddCalled = false;
fileRemoveCalled = false;
@@ -297,7 +306,7 @@ void FileWatcherUnitTestRunner::StartTest()
UNIT_TEST_EXPECT_TRUE(QDir::toNativeSeparators(fileRemoveName).toLower() == QDir::toNativeSeparators(tempDirPath.absoluteFilePath("dir3")).toLower());
UNIT_TEST_EXPECT_TRUE(QDir::toNativeSeparators(fileAddName).toLower() == QDir::toNativeSeparators(tempDirPath.absoluteFilePath("dir4")).toLower());
#endif // AZ_PLATFORM_LINUX
QObject::disconnect(connectionRemove);
QObject::disconnect(connectionAdd);
@@ -63,7 +63,7 @@ namespace AssetProcessor
size_t SendPerPlatform(unsigned int serial, const AzFramework::AssetSystem::BaseAssetProcessorMessage& message, const QString& platform) override
{
if (QString::compare(platform, "pc", Qt::CaseInsensitive) == 0 || QString::compare(platform, "es3", Qt::CaseInsensitive) == 0)
if (QString::compare(platform, "pc", Qt::CaseInsensitive) == 0 || QString::compare(platform, "android", Qt::CaseInsensitive) == 0)
{
return Send(serial, message);
}
@@ -72,7 +72,7 @@ namespace AssetProcessor
size_t SendRawPerPlatform(unsigned int type, unsigned int serial, const QByteArray& data, const QString& platform) override
{
if (QString::compare(platform, "pc", Qt::CaseInsensitive) == 0 || QString::compare(platform, "es3", Qt::CaseInsensitive) == 0)
if (QString::compare(platform, "pc", Qt::CaseInsensitive) == 0 || QString::compare(platform, "android", Qt::CaseInsensitive) == 0)
{
return SendRaw(type, serial, data);
}
@@ -64,7 +64,7 @@ void PlatformConfigurationTests::StartTest()
PlatformConfiguration config;
config.EnablePlatform({ "pc",{ "desktop", "host" } }, true);
config.EnablePlatform({ "es3",{ "mobile", "android" } }, true);
config.EnablePlatform({ "android",{ "mobile", "android" } }, true);
config.EnablePlatform({ "fandago",{ "console" } }, false);
AZStd::vector<AssetBuilderSDK::PlatformInfo> platforms;
config.PopulatePlatformsForScanFolder(platforms);
@@ -88,15 +88,15 @@ void PlatformConfigurationTests::StartTest()
AssetRecognizer rec;
AssetPlatformSpec specpc;
AssetPlatformSpec speces3;
AssetPlatformSpec specandroid;
AssetPlatformSpec specfandago;
specpc.m_extraRCParams = ""; // blank must work
speces3.m_extraRCParams = "testextraparams";
specandroid.m_extraRCParams = "testextraparams";
rec.m_name = "txt files";
rec.m_patternMatcher = AssetBuilderSDK::FilePatternMatcher("*.txt", AssetBuilderSDK::AssetBuilderPattern::Wildcard);
rec.m_platformSpecs.insert("pc", specpc);
rec.m_platformSpecs.insert("es3", speces3);
rec.m_platformSpecs.insert("android", specandroid);
rec.m_platformSpecs.insert("fandago", specfandago);
config.AddRecognizer(rec);
@@ -111,7 +111,7 @@ void PlatformConfigurationTests::StartTest()
UNIT_TEST_EXPECT_TRUE(config.GetEnabledPlatforms().size() == 2);
UNIT_TEST_EXPECT_TRUE(config.GetEnabledPlatforms()[0].m_identifier == "pc");
UNIT_TEST_EXPECT_TRUE(config.GetEnabledPlatforms()[1].m_identifier == "es3");
UNIT_TEST_EXPECT_TRUE(config.GetEnabledPlatforms()[1].m_identifier == "android");
UNIT_TEST_EXPECT_TRUE(config.GetScanFolderCount() == 11);
UNIT_TEST_EXPECT_FALSE(config.GetScanFolderAt(0).IsRoot());
@@ -239,14 +239,14 @@ void RCcontrollerUnitTests::RunRCControllerTests()
createdJobs.push_back(job);
}
// double them up for "es3" to make sure that platform is respected
// double them up for "android" to make sure that platform is respected
for (QString name : tempJobNames)
{
AZ::Uuid uuidOfSource = AZ::Uuid::CreateName(name.toUtf8().constData());
RCJob* job0 = new RCJob(rcJobListModel);
AssetProcessor::JobDetails jobDetails;
jobDetails.m_jobEntry.m_databaseSourceName = jobDetails.m_jobEntry.m_pathRelativeToWatchFolder = name;
jobDetails.m_jobEntry.m_platformInfo = { "es3" ,{ "mobile", "renderer" } };
jobDetails.m_jobEntry.m_platformInfo = { "android" ,{ "mobile", "renderer" } };
jobDetails.m_jobEntry.m_jobKey = "Compile Other Stuff";
jobDetails.m_jobEntry.m_sourceFileUUID = uuidOfSource;
job0->Init(jobDetails);
@@ -490,7 +490,7 @@ void RCcontrollerUnitTests::RunRCControllerTests()
UNIT_TEST_EXPECT_FALSE(gotJobsInQueueCall);
// submit same job but different platform:
details.m_jobEntry = JobEntry("d:/test", "test1.txt", "test1.txt", AZ::Uuid("{7954065D-CFD1-4666-9E4C-3F36F417C7AC}"), { "es3" ,{ "mobile", "renderer" } }, "Test Job", 1234, 3, sourceId);
details.m_jobEntry = JobEntry("d:/test", "test1.txt", "test1.txt", AZ::Uuid("{7954065D-CFD1-4666-9E4C-3F36F417C7AC}"), { "android" ,{ "mobile", "renderer" } }, "Test Job", 1234, 3, sourceId);
m_rcController.JobSubmitted(details);
QCoreApplication::processEvents(QEventLoop::AllEvents);
@@ -545,10 +545,13 @@ void RCcontrollerUnitTests::RunRCControllerTests()
rcJob.SetCheckExclusiveLock(true);
rcJob.Start();
#if defined(AZ_PLATFORM_WINDOWS)
// on windows, opening a file for reading locks it
// but on other platforms, this is not the case.
// we only expect work to begin when we can gain an exclusive lock on this file.
UNIT_TEST_EXPECT_FALSE(UnitTestUtils::BlockUntil(beginWork, 5000));
#if defined(AZ_PLATFORM_WINDOWS)
// Once we release the file, it should process normally
lockFileTest.close();
#else
@@ -54,7 +54,7 @@ namespace UnitTestUtils
{
void SleepForMinimumFileSystemTime()
{
// note that on OSX, the file system has a resolution of 1 second, and since we're using modtime for a bunch of things,
// note that on Mac, the file system has a resolution of 1 second, and since we're using modtime for a bunch of things,
// not the actual hash files, we have to wait different amount depending on the OS.
#ifdef AZ_PLATFORM_WINDOWS
int milliseconds = 1;
@@ -227,8 +227,9 @@ void UtilitiesUnitTests::StartTest()
#else
int handle = open(lockTestFileName.toUtf8().constData(), O_RDONLY | O_EXLOCK | O_NONBLOCK);
#endif // AZ_PLATFORM_WINDOWS
UNIT_TEST_EXPECT_FALSE(AssetUtilities::CheckCanLock(lockTestFileName));
#if defined(AZ_PLATFORM_WINDOWS)
UNIT_TEST_EXPECT_FALSE(AssetUtilities::CheckCanLock(lockTestFileName));
lockTestFile.close();
#else
if (handle != -1)
@@ -622,13 +622,14 @@ bool ApplicationManager::Activate()
{
if (!AssetUtilities::ComputeAssetRoot(m_systemRoot))
{
AZ_Error(AssetProcessor::ConsoleChannel, false, "Unable to compute the asset root for the project, this application cannot launch until this is fixed.");
return false;
}
auto projectName = AssetUtilities::ComputeProjectName();
if (projectName.isEmpty())
{
AZ_Error(AssetProcessor::ConsoleChannel, false, "Unable to detect name of current game project. Is bootstrap.cfg appropriately configured?");
AZ_Error(AssetProcessor::ConsoleChannel, false, "Unable to detect name of current game project. Configure your game project name to launch this application.");
return false;
}
@@ -1191,6 +1191,7 @@ bool ApplicationManagerBase::Activate()
QDir projectCache;
if (!AssetUtilities::ComputeProjectCacheRoot(projectCache))
{
AZ_Error("AssetProcessor", false, "Could not compute project cache root, please configure your project correctly to launch Asset Processor.");
return false;
}
@@ -1200,22 +1201,27 @@ bool ApplicationManagerBase::Activate()
// Shutdown if the disk has less than 128MB of free space
if (!CheckSufficientDiskSpace(projectCache.absolutePath(), 128 * 1024 * 1024, true))
{
// CheckSufficientDiskSpace reports an error if disk space is low.
return false;
}
bool appInited = InitApplicationServer();
if (!appInited)
{
AZ_Error(
"AssetProcessor", false, "InitApplicationServer failed, something internal to Asset Processor has failed, please report this to support if you encounter this error.");
return false;
}
if (!InitAssetDatabase())
{
// AssetDatabaseConnection::OpenDatabase reports any errors it encounters.
return false;
}
if (!ApplicationManager::Activate())
{
// ApplicationManager::Activate() reports any errors it encounters.
return false;
}
@@ -1230,6 +1236,7 @@ bool ApplicationManagerBase::Activate()
m_isCurrentlyLoadingGems = true;
if (!ActivateModules())
{
// ActivateModules reports any errors it encounters.
m_isCurrentlyLoadingGems = false;
return false;
}
@@ -1299,6 +1306,7 @@ bool ApplicationManagerBase::Activate()
{
if (!m_applicationServer->startListening())
{
// startListening reports any errors it encounters.
return false;
}
}
@@ -507,8 +507,13 @@ namespace AssetUtilities
return QString::fromUtf8(s_projectName.c_str(), aznumeric_cast<int>(s_projectName.size()));
}
QString ComputeProjectPath()
QString ComputeProjectPath(bool resetCachedProjectPath/*=false*/)
{
if (resetCachedProjectPath)
{
// Clear any cached value if reset was requested
s_projectPath.clear();
}
if (s_projectPath.empty())
{
// Check command-line args first
@@ -104,7 +104,8 @@ namespace AssetUtilities
QString ComputeProjectName(QString projectNameOverride = QString(), bool force = false);
//! Determine the absolute path of the current project
QString ComputeProjectPath();
//! The path computed path will be cached on subsequent calls unless resetCachedProjectPath=true
QString ComputeProjectPath(bool resetCachedProjectPath = false);
//! Reads the allowed list directly from the bootstrap file
QString ReadAllowedlistFromSettingsRegistry(QString initialFolder = QString());
@@ -5,11 +5,11 @@
"Platform pc": {
"tags": "tools,renderer"
},
"Platform osx_gl": {
"Platform mac": {
"tags": "tools,renderer"
},
"Platforms": {
"es3": "enabled"
"android": "enabled"
},
"ScanFolder Game": {
"watch": "@PROJECTROOT@",
@@ -5,7 +5,7 @@
"Platform pc": {
"tags": "tools,renderer"
},
"Platform osx_gl": {
"Platform mac": {
"tags": "tools,renderer"
},
"RC i_caf": {
@@ -5,7 +5,7 @@
"Platform pc": {
"tags": "tools,renderer"
},
"Platform osx_gl": {
"Platform mac": {
"tags": "tools,renderer"
},
"ScanFolder Game": {
@@ -5,17 +5,17 @@
"Platform pc": {
"tags": "tools,renderer"
},
"Platform es3": {
"Platform android": {
"tags": "android,mobile,renderer"
},
"Platform osx_gl": {
"Platform mac": {
"tags": "tools,renderer"
},
"Platform server": {
"tags": "server"
},
"Platforms": {
"es3": "enabled",
"android": "enabled",
"server": "enabled"
},
"Jobs": {
@@ -56,7 +56,7 @@
"glob": "*.i_caf",
"params": "defaultparams",
"server": "skip",
"es3": "mobile",
"android": "mobile",
"priority": 5,
"checkServer": true
},
@@ -68,7 +68,7 @@
"RC mov": {
"glob": "*.mov",
"params": "copy",
"es3": "platformspecificoverride",
"android": "platformspecificoverride",
"renderer": "rendererparams"
},
"RC rend": {
@@ -5,13 +5,13 @@
"Platform pc": {
"tags": "tools,renderer"
},
"Platform es3": {
"Platform android": {
"tags": "android,mobile,renderer"
},
"Platform ios": {
"tags": "mobile,renderer"
},
"Platform osx_gl": {
"Platform mac": {
"tags": "tools,renderer"
},
"Platform server": {
@@ -21,7 +21,7 @@
"tags": "console,renderer"
},
"Platforms": {
"es3": "enabled",
"android": "enabled",
"ios": "enabled",
"server": "enabled"
},
@@ -54,14 +54,14 @@
"display": "folder1output",
"recursive": 1,
"order": 50000,
"include": "es3"
"include": "android"
},
"ScanFolder Folder2": {
"watch": "@ENGINEROOT@/Folder2",
"display": "folder2output",
"recursive": 1,
"order": 60000,
"exclude": "es3"
"exclude": "android"
},
"ScanFolder Folder3": {
"watch": "@ENGINEROOT@/Folder3",
@@ -80,7 +80,7 @@
"glob": "*.i_caf",
"params": "defaultparams",
"server": "skip",
"es3": "mobile",
"android": "mobile",
"test": "copy",
"priority": 5
},
@@ -92,7 +92,7 @@
"RC mov": {
"glob": "*.mov",
"params": "copy",
"es3": "platformspecificoverride",
"android": "platformspecificoverride",
"renderer": "rendererparams"
},
"RC rend": {
+20 -16
View File
@@ -9,12 +9,12 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME})
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME})
include(${pal_dir}/platform_traits_${PAL_PLATFORM_NAME_LOWERCASE}.cmake)
include(${pal_dir}/platform_traits_${PAL_PLATFORM_NAME_LOWERCASE}.cmake)
if(PAL_TRAIT_AZTESTRUNNER_SUPPORTED AND NOT LY_MONOLITHIC_GAME)
ly_add_target(
NAME AzTestRunner ${PAL_TRAIT_AZTESTRUNNER_LAUNCHER_TYPE}
NAMESPACE AZ
@@ -32,19 +32,23 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
AZ::AzTest
AZ::AzFramework
)
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
ly_add_target(
NAME AzTestRunner.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
NAMESPACE AZ
FILES_CMAKE
aztestrunner_test_files.cmake
BUILD_DEPENDENCIES
PRIVATE
AZ::AzTest
)
ly_add_target(
NAME AzTestRunner.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
NAMESPACE AZ
FILES_CMAKE
aztestrunner_test_files.cmake
BUILD_DEPENDENCIES
PRIVATE
AZ::AzTest
)
ly_add_googletest(
NAME AZ::AzTestRunner.Tests
)
ly_add_googletest(
NAME AZ::AzTestRunner.Tests
)
endif()
endif()
@@ -9,5 +9,5 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(PAL_TRAIT_AZTESTRUNNER_SUPPORTED TRUE)
set(PAL_TRAIT_AZTESTRUNNER_LAUNCHER_TYPE MODULE)
@@ -9,5 +9,5 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(PAL_TRAIT_AZTESTRUNNER_SUPPORTED TRUE)
set(PAL_TRAIT_AZTESTRUNNER_LAUNCHER_TYPE EXECUTABLE)
@@ -9,5 +9,5 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(PAL_TRAIT_AZTESTRUNNER_SUPPORTED TRUE)
set(PAL_TRAIT_AZTESTRUNNER_LAUNCHER_TYPE EXECUTABLE)
@@ -9,5 +9,5 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(PAL_TRAIT_AZTESTRUNNER_SUPPORTED TRUE)
set(PAL_TRAIT_AZTESTRUNNER_LAUNCHER_TYPE EXECUTABLE)
@@ -9,5 +9,5 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(PAL_TRAIT_AZTESTRUNNER_SUPPORTED TRUE)
set(PAL_TRAIT_AZTESTRUNNER_LAUNCHER_TYPE EXECUTABLE)
+1 -1
View File
@@ -552,7 +552,7 @@ GridHubComponent::OnMemberJoined([[maybe_unused]] GridMate::GridSession* session
switch( member->GetPlatformId() )
{
case AZ::PlatformID::PLATFORM_WINDOWS_64:
case AZ::PlatformID::PLATFORM_APPLE_OSX:
case AZ::PlatformID::PLATFORM_APPLE_MAC:
{
GridMate::string localMachineName = GridMate::Utils::GetMachineAddress();
if( member->GetMachineName() == localMachineName )
+58 -9
View File
@@ -20,13 +20,11 @@ if (NOT python_package_name)
message(WARNING "Python was not found in the package assocation list. Did someone call ly_associate_package(xxxxxxx Python) ?")
endif()
ly_add_target(
NAME ProjectManager APPLICATION
OUTPUT_NAME o3de
NAME ProjectManager.Static STATIC
NAMESPACE AZ
AUTOMOC
AUTOUIC
AUTORCC
FILES_CMAKE
project_manager_files.cmake
Platform/${PAL_PLATFORM_NAME}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake
@@ -37,11 +35,8 @@ ly_add_target(
PRIVATE
PY_PACKAGE="${python_package_name}"
INCLUDE_DIRECTORIES
PUBLIC
.
PRIVATE
Source
BUILD_DEPENDENCIES
PRIVATE
3rdParty::Qt::Core
@@ -51,6 +46,60 @@ ly_add_target(
3rdParty::pybind11
AZ::AzCore
AZ::AzFramework
AZ::AzToolsFramework
AZ::AzQtComponents
)
)
ly_add_target(
NAME ProjectManager APPLICATION
OUTPUT_NAME o3de
NAMESPACE AZ
AUTORCC
FILES_CMAKE
project_manager_app_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
Source
BUILD_DEPENDENCIES
PRIVATE
3rdParty::Qt::Core
3rdParty::Qt::Concurrent
3rdParty::Qt::Widgets
3rdParty::Python
3rdParty::pybind11
AZ::AzCore
AZ::AzFramework
AZ::AzQtComponents
AZ::ProjectManager.Static
)
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
ly_add_target(
NAME ProjectManager.Tests EXECUTABLE
NAMESPACE AZ
AUTORCC
FILES_CMAKE
project_manager_tests_files.cmake
Platform/${PAL_PLATFORM_NAME}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}_tests_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
Source
Platform/${PAL_PLATFORM_NAME}
BUILD_DEPENDENCIES
PRIVATE
3rdParty::Qt::Core
3rdParty::Qt::Concurrent
3rdParty::Qt::Widgets
3rdParty::Python
3rdParty::pybind11
AZ::AzTest
AZ::AzFramework
AZ::AzFrameworkTestShared
AZ::ProjectManager.Static
)
ly_add_googletest(
NAME AZ::ProjectManager.Tests
TEST_COMMAND $<TARGET_FILE:AZ::ProjectManager.Tests> --unittest
)
endif()
@@ -0,0 +1,15 @@
#
# 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.
#
set(FILES
ProjectManager_Test_Traits_Platform.h
ProjectManager_Test_Traits_Linux.h
)
@@ -0,0 +1,15 @@
/*
* 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.
*
*/
#pragma once
#define AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS true
@@ -0,0 +1,15 @@
/*
* 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.
*
*/
#pragma once
#include <ProjectManager_Test_Traits_Linux.h>
@@ -0,0 +1,15 @@
#
# 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.
#
set(FILES
ProjectManager_Test_Traits_Platform.h
ProjectManager_Test_Traits_Mac.h
)
@@ -0,0 +1,15 @@
/*
* 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.
*
*/
#pragma once
#define AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS false
@@ -0,0 +1,15 @@
/*
* 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.
*
*/
#pragma once
#include <ProjectManager_Test_Traits_Mac.h>
@@ -0,0 +1,15 @@
#
# 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.
#
set(FILES
ProjectManager_Test_Traits_Platform.h
ProjectManager_Test_Traits_Windows.h
)
@@ -0,0 +1,15 @@
/*
* 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.
*
*/
#pragma once
#include <ProjectManager_Test_Traits_Windows.h>
@@ -0,0 +1,15 @@
/*
* 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.
*
*/
#pragma once
#define AZ_TRAIT_DISABLE_FAILED_PROJECT_MANAGER_TESTS false
@@ -0,0 +1,5 @@
<svg width="24" height="64" viewBox="0 0 24 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(0,40)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 3H11V11H3V13H11V21H13V13H21V11H13V3Z" fill="white"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 246 B

@@ -0,0 +1,5 @@
<svg width="24" height="64" viewBox="0 0 24 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(0,40)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 3H11V11H3V13H11V21H13V13H21V11H13V3Z" fill="#1e70eb"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 248 B

@@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.02554 10L9 15.0362V18L0 9L9 0V2.98885L4 8H18V10H4.02554Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 228 B

@@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.02554 10L9 15.0362V18L0 9L9 0V2.98885L4 8H18V10H4.02554Z" fill="#1e70eb"/>
</svg>

After

Width:  |  Height:  |  Size: 230 B

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 17L15 14H9L12 17Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 190 B

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f82f22df64b93d4bec91e56b60efa3d5ce2915ce388a2dc627f1ab720678e3d5
size 334987
oid sha256:4a5881b8d6cfbc4ceefb14ab96844484fe19407ee030824768f9fcce2f729d35
size 2949
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8358f4dad9878c662b9819b2b346622af691eb45f8eddc28fff79a50650ae6cf
size 2503
@@ -0,0 +1,5 @@
<svg width="24" height="64" viewBox="0 0 24 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(0,40)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.98407 4H2V6V7V19.8145H2.07539L6.02059 7.99123H19V6H11.328L9.98407 4ZM19 19.7473L22.0766 10H7.05436L3.68763 19.8329H18.973L18.9788 19.8145H19V19.7473Z" fill="white"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 358 B

@@ -0,0 +1,5 @@
<svg width="24" height="64" viewBox="0 0 24 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(0,40)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.98407 4H2V6V7V19.8145H2.07539L6.02059 7.99123H19V6H11.328L9.98407 4ZM19 19.7473L22.0766 10H7.05436L3.68763 19.8329H18.973L18.9788 19.8145H19V19.7473Z" fill="#1e70eb"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 360 B

@@ -4,6 +4,13 @@
</qresource>
<qresource prefix="/">
<file>Add.svg</file>
<file>AddOffset.svg</file>
<file>AddOffset_Hover.svg</file>
<file>ArrowBack.svg</file>
<file>ArrowBack_Hover.svg</file>
<file>build.svg</file>
<file>FolderOffset.svg</file>
<file>FolderOffset_Hover.svg</file>
<file>Select_Folder.svg</file>
<file>o3de_editor.ico</file>
<file>Windows.svg</file>
@@ -12,10 +19,17 @@
<file>Linux.svg</file>
<file>macOS.svg</file>
<file>DefaultProjectImage.png</file>
<file>DefaultTemplate.png</file>
<file>ArrowDownLine.svg</file>
<file>ArrowUpLine.svg</file>
<file>o3de.svg</file>
<file>menu.svg</file>
<file>menu_hover.svg</file>
<file>Backgrounds/FirstTimeBackgroundImage.jpg</file>
<file>ArrowDownLine.svg</file>
<file>ArrowUpLine.svg</file>
<file>CarrotArrowDown.svg</file>
<file>Summary.svg</file>
<file>WindowClose.svg</file>
</qresource>
</RCC>
@@ -1,29 +1,74 @@
/************** General (MainWindow) **************/
QMainWindow {
background-color: #333333;
background:#131313 url(:/o3de.svg) no-repeat top left;
/* position the logo using padding and background-origin, Qt does not support background-position pixels */
background-origin:content;
padding:25px 16px;
margin:0;
}
#ScreensCtrl {
min-width:1200px;
min-height:800px;
}
QPushButton:focus {
outline: none;
border:1px solid #1e70eb;
}
QTabBar {
background-color: transparent;
}
QTabWidget::tab-bar
{
left: 78px; /* make room for the logo */
}
QTabBar::tab {
height:82px;
background-color: transparent;
font-size:24px;
min-width:100px;
margin-right:40px;
border-bottom: 3px solid transparent;
}
QTabBar::tab:text
{
text-align:left;
}
QTabWidget::pane {
background-color: #333333;
border:0 none;
}
QTabBar::tab:selected
{
border-bottom: 3px solid #1e70eb;
color: #1e70eb;
}
QTabBar::tab:hover
{
color: #1e70eb;
}
QTabBar::tab:pressed
{
color: #0e60eb;
}
/************** General (Forms) **************/
#formLineEditWidget,
#formBrowseEditWidget {
max-width: 780px;
max-width: 890px;
}
#formFrame {
max-width: 720px;
max-width: 840px;
background-color: #444444;
border:1px solid #dddddd;
border-radius: 4px;
padding: 0px 10px 2px 6px;
margin-top:10px;
margin-left:30px;
margin-left:50px;
}
#formFrame[Focus="true"] {
@@ -59,15 +104,382 @@ QPushButton:focus {
padding-top: -4px;
}
#formErrorLabel {
color: #ec3030;
font-size: 14px;
margin-left: 40px;
margin-left: 50px;
}
#formTitleLabel {
font-size:21px;
color:#ffffff;
margin: 10px 0 10px 30px;
margin: 24px 0 10px 50px;
}
/************** General (Modal windows) **************/
#header {
background-color:#111111;
min-height:80px;
max-height:80px;
}
#header QPushButton {
/* settings min/max lets us use a fixed size */
min-width: 24px;
max-width: 24px;
min-height: 24px;
max-height: 24px;
margin: 20px 10px 0px 10px;
background:transparent url(:/ArrowBack.svg) no-repeat center;
background-origin:content;
qproperty-flat: true;
qproperty-iconSize: 50px;
}
#header QPushButton:focus {
border:none;
}
#header QPushButton:hover,
#header QPushButton:pressed {
background:transparent url(:/ArrowBack_Hover.svg) no-repeat center;
}
#headerTitle {
font-size:14px;
text-align:left;
margin:0;
padding-top:10px;
padding-bottom:-5px;
min-height:15px;
max-height:15px;
}
#headerSubTitle {
font-size:24px;
text-align:left;
margin:0;
min-height:42px;
max-height:42px;
}
#body {
background-color:#333333;
}
#footer {
/* settings min/max lets us use a fixed size */
min-width: 50px;
min-height:54px;
max-height:54px;
}
#footer > QPushButton {
qproperty-flat: true;
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 #0095f2, stop: 1.0 #1e70eb);
border-radius: 3px;
min-height: 28px;
max-height: 28px;
min-width: 150px;
margin-right:30px;
}
#footer > QPushButton:hover {
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 #10A5f2, stop: 1.0 #2e80eb);
}
#footer > QPushButton:pressed {
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 #0085e2, stop: 1.0 #0e60db);
}
#footer > QPushButton[secondary="true"] {
margin-right: 10px;
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 #888888, stop: 1.0 #555555);
}
#footer > QPushButton[secondary="true"]:hover {
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 #999999, stop: 1.0 #666666);
}
#footer > QPushButton[secondary="true"]:pressed {
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 #555555, stop: 1.0 #777777);
}
/************** Project Settings **************/
#projectSettings {
margin-top:42px;
}
#projectTemplate {
margin: 55px 0 0 50px;
}
#projectTemplateLabel {
font-size:16px;
font-weight:100;
}
#projectTemplateDetailsLabel {
font-size:14px;
min-height:40px;
margin-bottom:20px;
}
#projectTemplateDetails {
background-color:#444444;
max-width:20%;
min-width:240px;
margin-left:30px;
}
#projectTemplateDetails #displayName,
#projectTemplateDetails #includedGemsTitle {
font-size:18px;
}
#projectTemplateDetails #moreGems {
font-size:14px;
margin-top:20px;
}
#projectTemplateDetails #includedGemsTitle {
margin-top:5px;
margin-bottom:5px;
}
#projectTemplateDetails #summary {
padding-bottom:0px;
border-bottom:2px solid #555555;
min-height:80px;
qproperty-alignment: AlignTop;
}
#projectTemplateDetails #browseCatalog {
margin:5px 0px 15px 0px;
}
#projectTemplate QPushButton {
qproperty-flat: true;
min-width: 96px;
max-width: 96px;
min-height: 160px;
max-height: 160px;
}
#projectTemplate #templateLabel {
qproperty-alignment: AlignCenter;
}
#projectTemplate QPushButton #templateImage {
border:3px solid transparent;
border-radius: 4px;
}
#projectTemplate QPushButton[Checked="true"] #templateImage {
border:3px solid #1e70eb;
}
#projectTemplate QPushButton[Checked="true"] #templateLabel {
font-weight:bold;
}
#projectTemplate QPushButton:hover {
background-color: #444444;
}
#projectTemplate QPushButton:focus {
outline: none;
border:none;
}
#projectSettingsTab::tab-bar {
left: 60px;
}
#projectSettingsTabBar::tab {
height:50px;
}
#projectSettingsTab::tab-bar > QPushButton {
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 #888888, stop: 1.0 #555555);
qproperty-flat: true;
margin-right:30px;
margin-bottom:12px;
margin-top:0px;
min-width:170px;
max-width:170px;
min-height:26px;
max-height:26px;
border-radius: 3px;
text-align:center;
font-size:13px;
}
#projectSettingsTab::tab-bar > QPushButton:hover {
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 #999999, stop: 1.0 #666666);
}
#projectSettingsTab::tab-bar > QPushButton:pressed {
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 #555555, stop: 1.0 #777777);
}
#projectSettingsTopFrame {
background-color:#1E252F;
}
/************** Projects **************/
#firstTimeContent > #titleLabel {
font-size:60px;
margin:73px 0px 0px 0px;
qproperty-indent: 0;
}
#firstTimeContent > #introLabel {
font-size:14px;
margin:10px 0 60px 0;
qproperty-indent: 0;
}
#firstTimeContent > QPushButton {
min-width: 210px;
max-width: 210px;
min-height: 276px;
max-height: 276px;
qproperty-flat: true;
background-origin:content;
font-size:14px;
border: 1px solid #ffffff;
}
#firstTimeContent > QPushButton:hover {
border: 1px solid #1e70eb;
color: #1e70eb;
}
#firstTimeContent > QPushButton:pressed {
border: 1px solid #0e60eb;
color: #0e60eb;
}
#createProjectButton {
background:rgba(0,0,0,180) url(:/AddOffset.svg) no-repeat center center;
}
#createProjectButton:hover,
#createProjectButton:pressed {
background:rgba(0,0,0,180) url(:/AddOffset_Hover.svg) no-repeat center center;
}
#addProjectButton {
background:rgba(0,0,0,180) url(:/FolderOffset.svg) no-repeat center center;
}
#addProjectButton:hover,
#addProjectButton:pressed {
background:rgba(0,0,0,180) url(:/FolderOffset_Hover.svg) no-repeat center center;
}
#projectsContent > QFrame {
margin-top:60px;
}
#projectsContent > QFrame > #titleLabel {
font-size:24px;
qproperty-indent: 0;
}
#projectsContent > QScrollArea {
margin-top:40px;
margin-bottom:5px;
}
#projectButton > #labelButton {
border:1px solid white;
}
#projectButton > #labelButton:hover,
#projectButton > #labelButton:pressed {
border:1px solid #1e70eb;
}
#projectButton > QFrame {
margin-top:6px;
}
#projectButton > QFrame > QLabel {
font-weight:bold;
font-size:14px;
qproperty-indent: 0;
}
#projectMenuButton {
qproperty-flat: true;
background:transparent url(:/menu.svg) no-repeat center center;
max-width:30px;
min-width:30px;
max-height:14px;
min-height:14px;
}
#projectsContent > QFrame > #newProjectButton {
min-width:150px;
max-width:150px;
min-height:26px;
max-height:26px;
}
#labelButtonOverlay {
background-color: rgba(50,50,50,200);
min-width:210px;
max-width:210px;;
min-height:278px;
max-height:278px;
}
QProgressBar {
border: none;
background-color: transparent;
padding: 0px;
min-height: 14px;
font-size: 2px;
}
QProgressBar::chunk {
background-color: #1E70EB;
}
/************** Gem Catalog **************/
#GemCatalogTitle {
font-size: 18px;
}
#GemCatalogCart {
background-color: #555555;
}
#GemCatalogCartCountLabel {
font-size: 12px;
background-color: #4285F4;
border-radius: 3px;
}
#GemCatalogCartOverlaySectionLabel {
font-weight: 600;
}
/************** Gem Catalog (Inspector) **************/
#GemCatalogInspector {
background-color: #444444;
}
/************** Gem Catalog (Filter/left pane) **************/
#GemCatalogFilterWidget {
background-color: #444444;
}
#GemCatalogHeaderWidget {
background-color: #1E252F;
}
#GemCatalogFilterCategoryTitle {
font-size: 12px;
font-weight: 600;
}
@@ -0,0 +1 @@
IDI_ICON1 ICON DISCARDABLE "o3de_editor.ico"
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.5714 6.7619H16.7619C16.7619 4.13333 14.6286 2 12 2C9.37145 2 7.23811 4.13333 7.23811 6.7619H3.42859V22H20.5714V6.7619ZM12 3.90476C13.581 3.90476 14.8572 5.18095 14.8572 6.7619H9.14287C9.14287 5.18095 10.4191 3.90476 12 3.90476ZM13.7997 11.7424L12.0288 9.24095H11.9712L10.2003 11.7424H13.7997ZM5.00002 12.4335L10.9158 19.4063L9.03253 12.4335H5.00002ZM10.14 12.4335L11.9356 19.5147C11.9548 19.5189 11.974 19.521 11.9959 19.521C12.0178 19.521 12.0398 19.5189 12.0617 19.5147L13.86 12.4335H10.14ZM14.9675 12.4335H18.8547H19L13.0842 19.398L14.9675 12.4335ZM8.26457 9.24095H11.075L9.52787 11.4253L8.26457 9.24095ZM8.83789 11.7424L7.5193 9.46191L5.01921 11.7424H5.14804H8.83789ZM18.9808 11.7424H18.8547H15.1512L16.4286 9.41397L18.9808 11.7424ZM15.6558 9.24095H12.9252L14.4647 11.4146L15.6558 9.24095Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 966 B

@@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.5507 19.5506L19.5213 2.58008L21.289 4.34785L4.31846 21.3184L2.5507 19.5506Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.28706 2.65015L21.2576 19.6207L19.4899 21.3885L2.51929 4.41791L4.28706 2.65015Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 394 B

@@ -0,0 +1,11 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="3" y="16" width="5" height="5" fill="white"/>
<rect x="9.5" y="16" width="5" height="5" fill="white"/>
<rect x="16" y="16" width="5" height="5" fill="white"/>
<rect x="3" y="9.5" width="5" height="5" fill="white"/>
<rect x="9.5" y="9.5" width="5" height="5" fill="white"/>
<rect x="19.415" y="8.58496" width="5" height="5" transform="rotate(60 19.415 8.58496)" fill="white"/>
<rect x="3" y="3" width="5" height="5" fill="white"/>
<rect x="8.58496" y="6.41504" width="5" height="5" transform="rotate(-60 8.58496 6.41504)" fill="white"/>
<rect x="19.5" y="0.964844" width="5" height="5" transform="rotate(45 19.5 0.964844)" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 749 B

@@ -0,0 +1,5 @@
<svg width="24" height="16" viewBox="0 0 24 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1.3335" y="2.66675" width="13.3333" height="1.33333" fill="white"/>
<rect x="1.3335" y="7.33325" width="13.3333" height="1.33333" fill="white"/>
<rect x="1.3335" y="12" width="13.3333" height="1.33333" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 329 B

@@ -0,0 +1,5 @@
<svg width="24" height="16" viewBox="0 0 24 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1.3335" y="2.66675" width="13.3333" height="1.33333" fill="#1e70eb"/>
<rect x="1.3335" y="7.33325" width="13.3333" height="1.33333" fill="#1e70eb"/>
<rect x="1.3335" y="12" width="13.3333" height="1.33333" fill="#1e70eb"/>
</svg>

After

Width:  |  Height:  |  Size: 335 B

@@ -0,0 +1,3 @@
<svg width="38" height="32" viewBox="0 0 38 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M33.0335 27.6364C30.2006 30.4503 26.4498 32 22.4729 32H22.4719L22.4007 31.9999C18.3948 31.9813 14.6226 30.396 11.7792 27.5358C7.98134 23.7148 6.67845 18.3374 7.86239 13.445H13.291C12.8522 14.5772 12.6238 15.7937 12.6313 17.0465C12.6466 19.6692 13.682 22.1248 15.5465 23.9605C17.4134 25.7987 19.8692 26.811 22.462 26.811L22.4897 26.8108C25.0998 26.8025 27.555 25.7736 29.4067 23.9136C31.2794 22.0326 32.3023 19.5505 32.2872 16.9246C32.2718 14.2963 31.2187 11.8234 29.3215 9.96131C27.4791 8.153 25.0581 7.16098 22.4942 7.16098C22.4558 7.16098 22.4177 7.16127 22.379 7.16171C20.9514 7.17701 19.5695 7.49947 18.3061 8.09355V2.5551C19.6438 2.16896 21.0401 1.96569 22.4658 1.96569L22.5394 1.96598C26.5556 1.98507 30.3331 3.58486 33.1758 6.47058C35.976 9.31332 37.5035 13.0777 37.4771 17.0703C37.4508 21.0641 35.8727 24.8164 33.0335 27.6364ZM1.73437 2.98423H4.71974V0H1.73437V2.98423ZM0 11.9513H4.68388V7.26925H0V11.9513ZM7.65831 10.0451H14.8796V2.82656H7.65831V10.0451Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c042fce57915fc749abc7b37de765fd697c3c4d7de045a3d44805aa0ce29901a
size 107016
oid sha256:d717f77fe01f45df934a61bbc215e5322447d21e16f3cebcf2a02f148178f266
size 106449
@@ -0,0 +1,186 @@
/*
* 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 <Application.h>
#include <ProjectUtils.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/Utils/Utils.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzFramework/Logging/LoggingComponent.h>
#include <AzQtComponents/Utilities/HandleDpiAwareness.h>
#include <AzQtComponents/Components/StyleManager.h>
#include <AzQtComponents/Components/WindowDecorationWrapper.h>
#include <QApplication>
#include <QDir>
#include <QMessageBox>
namespace O3DE::ProjectManager
{
Application::~Application()
{
TearDown();
}
bool Application::Init(bool interactive)
{
constexpr const char* applicationName { "O3DE" };
QApplication::setOrganizationName(applicationName);
QApplication::setOrganizationDomain("o3de.org");
QCoreApplication::setApplicationName(applicationName);
QCoreApplication::setApplicationVersion("1.0");
// Use the LogComponent for non-dev logging log
RegisterComponentDescriptor(AzFramework::LogComponent::CreateDescriptor());
// set the log alias to .o3de/Logs instead of the default user/logs
AZ::IO::FixedMaxPath path = AZ::Utils::GetO3deLogsDirectory();
// DevWriteStorage is where the event log is written during development
m_settingsRegistry->Set(AZ::SettingsRegistryMergeUtils::FilePathKey_DevWriteStorage, path.LexicallyNormal().Native());
// Save event logs to .o3de/Logs/eventlogger/EventLogO3DE.azsl
m_settingsRegistry->Set(AZ::SettingsRegistryMergeUtils::BuildTargetNameKey, applicationName);
Start(AzFramework::Application::Descriptor());
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
QLocale::setDefault(QLocale(QLocale::English, QLocale::UnitedStates));
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
AzQtComponents::Utilities::HandleDpiAwareness(AzQtComponents::Utilities::SystemDpiAware);
// Create the actual Qt Application - this needs to happen before using QMessageBox
m_app.reset(new QApplication(*GetArgC(), *GetArgV()));
if(!InitLog(applicationName))
{
AZ_Warning("ProjectManager", false, "Failed to init logging");
}
m_pythonBindings = AZStd::make_unique<PythonBindings>(GetEngineRoot());
if (!m_pythonBindings || !m_pythonBindings->PythonStarted())
{
if (interactive)
{
QMessageBox::critical(nullptr, QObject::tr("Failed to start Python"),
QObject::tr("This tool requires an O3DE engine with a Python runtime, "
"but either Python is missing or mis-configured. Please rename "
"your python/runtime folder to python/runtime_bak, then run "
"python/get_python.bat to restore the Python runtime folder."));
}
return false;
}
const AZ::CommandLine* commandLine = GetCommandLine();
AZ_Assert(commandLine, "Failed to get command line");
ProjectManagerScreen startScreen = ProjectManagerScreen::Projects;
if (size_t screenSwitchCount = commandLine->GetNumSwitchValues("screen"); screenSwitchCount > 0)
{
QString screenOption = commandLine->GetSwitchValue("screen", screenSwitchCount - 1).c_str();
ProjectManagerScreen screen = ProjectUtils::GetProjectManagerScreen(screenOption);
if (screen != ProjectManagerScreen::Invalid)
{
startScreen = screen;
}
}
AZ::IO::FixedMaxPath projectPath;
if (size_t projectSwitchCount = commandLine->GetNumSwitchValues("project-path"); projectSwitchCount > 0)
{
projectPath = commandLine->GetSwitchValue("project-path", projectSwitchCount - 1).c_str();
}
m_mainWindow.reset(new ProjectManagerWindow(nullptr, projectPath, startScreen));
return true;
}
bool Application::InitLog(const char* logName)
{
if (!m_entity)
{
// override the log alias to the O3de Logs directory instead of the default project user/Logs folder
AZ::IO::FixedMaxPath path = AZ::Utils::GetO3deLogsDirectory();
AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
AZ_Assert(fileIO, "Failed to get FileIOBase instance");
fileIO->SetAlias("@log@", path.LexicallyNormal().Native().c_str());
// this entity exists because we need a home for LogComponent
// and cannot use the system entity because we need to be able to call SetLogFileBaseName
// so the log will be named O3DE.log
m_entity = aznew AZ::Entity("Application Entity");
if (m_entity)
{
AzFramework::LogComponent* logger = aznew AzFramework::LogComponent();
AZ_Assert(logger, "Failed to create LogComponent");
logger->SetLogFileBaseName(logName);
m_entity->AddComponent(logger);
m_entity->Init();
m_entity->Activate();
}
}
return m_entity != nullptr;
}
void Application::TearDown()
{
if (m_entity)
{
m_entity->Deactivate();
delete m_entity;
m_entity = nullptr;
}
m_pythonBindings.reset();
m_mainWindow.reset();
m_app.reset();
}
bool Application::Run()
{
// Set up the Style Manager
AzQtComponents::StyleManager styleManager(qApp);
styleManager.initialize(qApp, GetEngineRoot());
// setup stylesheets and hot reloading
AZ::IO::FixedMaxPath engineRoot(GetEngineRoot());
QDir rootDir(engineRoot.c_str());
const auto pathOnDisk = rootDir.absoluteFilePath("Code/Tools/ProjectManager/Resources");
const auto qrcPath = QStringLiteral(":/ProjectManager/style");
AzQtComponents::StyleManager::addSearchPaths("style", pathOnDisk, qrcPath, engineRoot);
// set stylesheet after creating the main window or their styles won't get updated
AzQtComponents::StyleManager::setStyleSheet(m_mainWindow.data(), QStringLiteral("style:ProjectManager.qss"));
// the decoration wrapper is intended to remember window positioning and sizing
auto wrapper = new AzQtComponents::WindowDecorationWrapper();
wrapper->setGuest(m_mainWindow.data());
wrapper->show();
m_mainWindow->show();
qApp->setQuitOnLastWindowClosed(true);
// Run the application
return qApp->exec();
}
}
@@ -0,0 +1,48 @@
/*
* 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.
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzFramework/Application/Application.h>
#include <QCoreApplication>
#include <PythonBindings.h>
#include <ProjectManagerWindow.h>
#endif
namespace AZ
{
class Entity;
}
namespace O3DE::ProjectManager
{
class Application
: public AzFramework::Application
{
public:
using AzFramework::Application::Application;
virtual ~Application();
bool Init(bool interactive = true);
bool Run();
void TearDown();
private:
bool InitLog(const char* logName);
AZStd::unique_ptr<PythonBindings> m_pythonBindings;
QSharedPointer<QCoreApplication> m_app;
QSharedPointer<ProjectManagerWindow> m_mainWindow;
AZ::Entity* m_entity = nullptr;
};
}
@@ -14,11 +14,17 @@
#include <ScreensCtrl.h>
#include <PythonBindingsInterface.h>
#include <NewProjectSettingsScreen.h>
#include <ScreenHeaderWidget.h>
#include <GemCatalog/GemCatalogScreen.h>
#include <QDialogButtonBox>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QPushButton>
#include <QMessageBox>
#include <QStackedWidget>
#include <QLabel>
#include <QSizePolicy>
namespace O3DE::ProjectManager
{
@@ -26,29 +32,44 @@ namespace O3DE::ProjectManager
: ScreenWidget(parent)
{
QVBoxLayout* vLayout = new QVBoxLayout();
vLayout->setContentsMargins(0,0,0,0);
m_header = new ScreenHeader(this);
m_header->setTitle(tr("Create a New Project"));
m_header->setSubTitle(tr("Enter Project Details"));
connect(m_header->backButton(), &QPushButton::clicked, this, &CreateProjectCtrl::HandleBackButton);
vLayout->addWidget(m_header);
m_stack = new QStackedWidget(this);
m_stack->setObjectName("body");
m_stack->setSizePolicy(QSizePolicy(QSizePolicy::Preferred,QSizePolicy::Expanding));
m_newProjectSettingsScreen = new NewProjectSettingsScreen(this);
m_stack->addWidget(m_newProjectSettingsScreen);
m_gemCatalogScreen = new GemCatalogScreen(this);
m_stack->addWidget(m_gemCatalogScreen);
vLayout->addWidget(m_stack);
QDialogButtonBox* buttons = new QDialogButtonBox();
buttons->setObjectName("footer");
vLayout->addWidget(buttons);
#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED
connect(m_newProjectSettingsScreen, &ScreenWidget::ChangeScreenRequest, this, &CreateProjectCtrl::OnChangeScreenRequest);
m_secondaryButton = buttons->addButton(tr("Back"), QDialogButtonBox::RejectRole);
m_secondaryButton->setProperty("secondary", true);
m_secondaryButton->setVisible(false);
connect(m_secondaryButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandleSecondaryButton);
Update();
#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED
m_primaryButton = buttons->addButton(tr("Create Project"), QDialogButtonBox::ApplyRole);
connect(m_primaryButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandlePrimaryButton);
setLayout(vLayout);
m_screensCtrl = new ScreensCtrl();
vLayout->addWidget(m_screensCtrl);
QDialogButtonBox* backNextButtons = new QDialogButtonBox();
vLayout->addWidget(backNextButtons);
m_backButton = backNextButtons->addButton(tr("Back"), QDialogButtonBox::RejectRole);
m_nextButton = backNextButtons->addButton(tr("Next"), QDialogButtonBox::ApplyRole);
connect(m_backButton, &QPushButton::pressed, this, &CreateProjectCtrl::HandleBackButton);
connect(m_nextButton, &QPushButton::pressed, this, &CreateProjectCtrl::HandleNextButton);
m_screensOrder =
{
ProjectManagerScreen::NewProjectSettings,
ProjectManagerScreen::GemCatalog
};
m_screensCtrl->BuildScreens(m_screensOrder);
m_screensCtrl->ForceChangeToScreen(ProjectManagerScreen::NewProjectSettings, false);
UpdateNextButtonText();
}
ProjectManagerScreen CreateProjectCtrl::GetScreenEnum()
@@ -56,75 +77,148 @@ namespace O3DE::ProjectManager
return ProjectManagerScreen::CreateProject;
}
void CreateProjectCtrl::NotifyCurrentScreen()
{
ScreenWidget* currentScreen = reinterpret_cast<ScreenWidget*>(m_stack->currentWidget());
if (currentScreen)
{
currentScreen->NotifyCurrentScreen();
}
}
void CreateProjectCtrl::HandleBackButton()
{
if (!m_screensCtrl->GotoPreviousScreen())
if (m_stack->currentIndex() > 0)
{
#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED
PreviousScreen();
#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED
}
else
{
emit GotoPreviousScreenRequest();
}
}
#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED
void CreateProjectCtrl::HandleSecondaryButton()
{
if (m_stack->currentIndex() > 0)
{
// return to Project Settings page
PreviousScreen();
}
else
{
UpdateNextButtonText();
// Configure Gems
NextScreen();
}
}
void CreateProjectCtrl::HandleNextButton()
void CreateProjectCtrl::Update()
{
ScreenWidget* currentScreen = m_screensCtrl->GetCurrentScreen();
ProjectManagerScreen screenEnum = currentScreen->GetScreenEnum();
auto screenOrderIter = m_screensOrder.begin();
for (; screenOrderIter != m_screensOrder.end(); ++screenOrderIter)
if (m_stack->currentWidget() == m_gemCatalogScreen)
{
if (*screenOrderIter == screenEnum)
{
++screenOrderIter;
break;
}
}
if (screenEnum == ProjectManagerScreen::NewProjectSettings)
{
auto newProjectScreen = reinterpret_cast<NewProjectSettingsScreen*>(currentScreen);
if (newProjectScreen)
{
if (!newProjectScreen->Validate())
{
QMessageBox::critical(this, tr("Invalid project settings"), tr("Invalid project settings"));
return;
}
m_projectInfo = newProjectScreen->GetProjectInfo();
m_projectTemplatePath = newProjectScreen->GetProjectTemplatePath();
}
}
if (screenOrderIter != m_screensOrder.end())
{
m_screensCtrl->ChangeToScreen(*screenOrderIter);
UpdateNextButtonText();
m_header->setSubTitle(tr("Configure project with Gems"));
m_secondaryButton->setVisible(false);
}
else
{
auto result = PythonBindingsInterface::Get()->CreateProject(m_projectTemplatePath, m_projectInfo);
m_header->setSubTitle(tr("Enter Project Details"));
m_secondaryButton->setVisible(true);
m_secondaryButton->setText(tr("Configure Gems"));
}
}
void CreateProjectCtrl::OnChangeScreenRequest(ProjectManagerScreen screen)
{
if (screen == ProjectManagerScreen::GemCatalog)
{
HandleSecondaryButton();
}
else
{
emit ChangeScreenRequest(screen);
}
}
void CreateProjectCtrl::NextScreen()
{
if (m_stack->currentIndex() < m_stack->count())
{
if(CurrentScreenIsValid())
{
m_stack->setCurrentIndex(m_stack->currentIndex() + 1);
QString projectTemplatePath = m_newProjectSettingsScreen->GetProjectTemplatePath();
m_gemCatalogScreen->ReinitForProject(projectTemplatePath + "/Template", /*isNewProject=*/true);
Update();
}
else
{
QMessageBox::warning(this, tr("Invalid project settings"), tr("Please correct the indicated project settings and try again."));
}
}
}
void CreateProjectCtrl::PreviousScreen()
{
// we don't require the current screen to be valid when moving back
if (m_stack->currentIndex() > 0)
{
m_stack->setCurrentIndex(m_stack->currentIndex() - 1);
Update();
}
}
#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED
void CreateProjectCtrl::HandlePrimaryButton()
{
CreateProject();
}
bool CreateProjectCtrl::CurrentScreenIsValid()
{
if (m_stack->currentWidget() == m_newProjectSettingsScreen)
{
return m_newProjectSettingsScreen->Validate();
}
return true;
}
void CreateProjectCtrl::CreateProject()
{
if (m_newProjectSettingsScreen->Validate())
{
ProjectInfo projectInfo = m_newProjectSettingsScreen->GetProjectInfo();
QString projectTemplatePath = m_newProjectSettingsScreen->GetProjectTemplatePath();
auto result = PythonBindingsInterface::Get()->CreateProject(projectTemplatePath, projectInfo);
if (result.IsSuccess())
{
// adding gems is not implemented yet because we don't know what targets to add or how to add them
emit ChangeScreenRequest(ProjectManagerScreen::ProjectsHome);
// automatically register the project
PythonBindingsInterface::Get()->AddProject(projectInfo.m_path);
#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED
m_gemCatalogScreen->EnableDisableGemsForProject(projectInfo.m_path);
#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED
projectInfo.m_needsBuild = true;
emit NotifyBuildProject(projectInfo);
emit ChangeScreenRequest(ProjectManagerScreen::Projects);
}
else
{
QMessageBox::critical(this, tr("Project creation failed"), tr("Failed to create project."));
}
}
}
void CreateProjectCtrl::UpdateNextButtonText()
{
QString nextButtonText = tr("Next");
if (m_screensCtrl->GetCurrentScreen()->GetScreenEnum() == ProjectManagerScreen::GemCatalog)
else
{
nextButtonText = tr("Create Project");
QMessageBox::warning(this, tr("Invalid project settings"), tr("Please correct the indicated project settings and try again."));
}
m_nextButton->setText(nextButtonText);
}
} // namespace O3DE::ProjectManager
@@ -12,15 +12,23 @@
#pragma once
#if !defined(Q_MOC_RUN)
#include "ProjectInfo.h"
#include <ScreenWidget.h>
#include <ScreensCtrl.h>
#include <QPushButton>
#include <ProjectInfo.h>
#endif
// due to current limitations, customizing template Gems is disabled
#define TEMPLATE_GEM_CONFIGURATION_ENABLED
QT_FORWARD_DECLARE_CLASS(QStackedWidget)
QT_FORWARD_DECLARE_CLASS(QPushButton)
QT_FORWARD_DECLARE_CLASS(QLabel)
namespace O3DE::ProjectManager
{
QT_FORWARD_DECLARE_CLASS(ScreenHeader)
QT_FORWARD_DECLARE_CLASS(NewProjectSettingsScreen)
QT_FORWARD_DECLARE_CLASS(GemCatalogScreen)
class CreateProjectCtrl
: public ScreenWidget
{
@@ -28,21 +36,41 @@ namespace O3DE::ProjectManager
explicit CreateProjectCtrl(QWidget* parent = nullptr);
~CreateProjectCtrl() = default;
ProjectManagerScreen GetScreenEnum() override;
void NotifyCurrentScreen() override;
protected slots:
void HandleBackButton();
void HandleNextButton();
void HandlePrimaryButton();
#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED
void OnChangeScreenRequest(ProjectManagerScreen screen);
void HandleSecondaryButton();
#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED
private:
void UpdateNextButtonText();
#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED
void Update();
void NextScreen();
void PreviousScreen();
#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED
ScreensCtrl* m_screensCtrl;
QPushButton* m_backButton;
QPushButton* m_nextButton;
QVector<ProjectManagerScreen> m_screensOrder;
bool CurrentScreenIsValid();
void CreateProject();
QStackedWidget* m_stack = nullptr;
ScreenHeader* m_header = nullptr;
QPushButton* m_primaryButton = nullptr;
#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED
QPushButton* m_secondaryButton = nullptr;
#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED
QString m_projectTemplatePath;
ProjectInfo m_projectInfo;
NewProjectSettingsScreen* m_newProjectSettingsScreen = nullptr;
GemCatalogScreen* m_gemCatalogScreen = nullptr;
};
} // namespace O3DE::ProjectManager
@@ -25,7 +25,7 @@ namespace O3DE::ProjectManager
EngineSettingsScreen::EngineSettingsScreen(QWidget* parent)
: ScreenWidget(parent)
{
auto* layout = new QVBoxLayout(this);
auto* layout = new QVBoxLayout();
layout->setAlignment(Qt::AlignTop);
setObjectName("engineSettingsScreen");
@@ -82,6 +82,16 @@ namespace O3DE::ProjectManager
return ProjectManagerScreen::EngineSettings;
}
QString EngineSettingsScreen::GetTabText()
{
return tr("Engine");
}
bool EngineSettingsScreen::IsTab()
{
return true;
}
void EngineSettingsScreen::OnTextChanged()
{
// save engine settings
@@ -26,7 +26,10 @@ namespace O3DE::ProjectManager
public:
explicit EngineSettingsScreen(QWidget* parent = nullptr);
~EngineSettingsScreen() = default;
ProjectManagerScreen GetScreenEnum() override;
QString GetTabText() override;
bool IsTab() override;
protected slots:
void OnTextChanged();
@@ -1,95 +0,0 @@
/*
* 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 <FirstTimeUseScreen.h>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QIcon>
#include <QSpacerItem>
namespace O3DE::ProjectManager
{
FirstTimeUseScreen::FirstTimeUseScreen(QWidget* parent)
: ScreenWidget(parent)
{
QVBoxLayout* vLayout = new QVBoxLayout();
setLayout(vLayout);
vLayout->setContentsMargins(s_contentMargins, s_contentMargins, s_contentMargins, s_contentMargins);
QLabel* titleLabel = new QLabel(this);
titleLabel->setText(tr("Ready. Set. Create!"));
titleLabel->setStyleSheet("font-size: 60px");
vLayout->addWidget(titleLabel);
QLabel* introLabel = new QLabel(this);
introLabel->setTextFormat(Qt::AutoText);
introLabel->setText(tr("<html><head/><body><p>Welcome to O3DE! Start something new by creating a project. Not sure what to create? </p><p>Explore what\342\200\231s available by downloading our sample project.</p></body></html>"));
introLabel->setStyleSheet("font-size: 14px");
vLayout->addWidget(introLabel);
QHBoxLayout* buttonLayout = new QHBoxLayout();
buttonLayout->setSpacing(s_buttonSpacing);
m_createProjectButton = CreateLargeBoxButton(QIcon(":/Add.svg"), tr("Create Project"), this);
m_createProjectButton->setIconSize(QSize(s_iconSize, s_iconSize));
buttonLayout->addWidget(m_createProjectButton);
m_addProjectButton = CreateLargeBoxButton(QIcon(":/Select_Folder.svg"), tr("Add a Project"), this);
m_addProjectButton->setIconSize(QSize(s_iconSize, s_iconSize));
buttonLayout->addWidget(m_addProjectButton);
QSpacerItem* buttonSpacer = new QSpacerItem(s_spacerSize, s_spacerSize, QSizePolicy::Expanding, QSizePolicy::Minimum);
buttonLayout->addItem(buttonSpacer);
vLayout->addItem(buttonLayout);
QSpacerItem* verticalSpacer = new QSpacerItem(s_spacerSize, s_spacerSize, QSizePolicy::Minimum, QSizePolicy::Expanding);
vLayout->addItem(verticalSpacer);
// Using border-image allows for scaling options background-image does not support
setStyleSheet("O3DE--ProjectManager--ScreenWidget { border-image: url(:/Backgrounds/FirstTimeBackgroundImage.jpg) repeat repeat; }");
connect(m_createProjectButton, &QPushButton::pressed, this, &FirstTimeUseScreen::HandleNewProjectButton);
connect(m_addProjectButton, &QPushButton::pressed, this, &FirstTimeUseScreen::HandleAddProjectButton);
}
ProjectManagerScreen FirstTimeUseScreen::GetScreenEnum()
{
return ProjectManagerScreen::FirstTimeUse;
}
void FirstTimeUseScreen::HandleNewProjectButton()
{
emit ResetScreenRequest(ProjectManagerScreen::CreateProject);
emit ChangeScreenRequest(ProjectManagerScreen::CreateProject);
}
void FirstTimeUseScreen::HandleAddProjectButton()
{
emit ChangeScreenRequest(ProjectManagerScreen::ProjectsHome);
}
QPushButton* FirstTimeUseScreen::CreateLargeBoxButton(const QIcon& icon, const QString& text, QWidget* parent)
{
QPushButton* largeBoxButton = new QPushButton(icon, text, parent);
largeBoxButton->setFixedSize(s_boxButtonWidth, s_boxButtonHeight);
largeBoxButton->setFlat(true);
largeBoxButton->setFocusPolicy(Qt::FocusPolicy::NoFocus);
largeBoxButton->setStyleSheet("QPushButton { font-size: 14px; background-color: rgba(0, 0, 0, 191); }");
return largeBoxButton;
}
} // namespace O3DE::ProjectManager
@@ -1,49 +0,0 @@
/*
* 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.
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <ScreenWidget.h>
#endif
QT_FORWARD_DECLARE_CLASS(QIcon)
QT_FORWARD_DECLARE_CLASS(QPushButton)
namespace O3DE::ProjectManager
{
class FirstTimeUseScreen
: public ScreenWidget
{
public:
explicit FirstTimeUseScreen(QWidget* parent = nullptr);
~FirstTimeUseScreen() = default;
ProjectManagerScreen GetScreenEnum() override;
protected slots:
void HandleNewProjectButton();
void HandleAddProjectButton();
private:
QPushButton* CreateLargeBoxButton(const QIcon& icon, const QString& text, QWidget* parent = nullptr);
QPushButton* m_createProjectButton;
QPushButton* m_addProjectButton;
inline constexpr static int s_contentMargins = 80;
inline constexpr static int s_buttonSpacing = 30;
inline constexpr static int s_iconSize = 24;
inline constexpr static int s_spacerSize = 20;
inline constexpr static int s_boxButtonWidth = 210;
inline constexpr static int s_boxButtonHeight = 280;
};
} // namespace O3DE::ProjectManager
@@ -78,6 +78,14 @@ namespace O3DE::ProjectManager
m_errorLabel->setText(labelText);
}
void FormLineEditWidget::setErrorLabelVisible(bool visible)
{
m_errorLabel->setVisible(visible);
m_frame->setProperty("Valid", !visible);
refreshStyle();
}
QLineEdit* FormLineEditWidget::lineEdit() const
{
return m_lineEdit;
@@ -39,6 +39,7 @@ namespace O3DE::ProjectManager
//! Set the error message for to display when invalid.
void setErrorLabelText(const QString& labelText);
void setErrorLabelVisible(bool visible);
//! Returns a pointer to the underlying LineEdit.
QLineEdit* lineEdit() const;
@@ -10,40 +10,253 @@
*
*/
#include <AzQtComponents/Components/SearchLineEdit.h>
#include <GemCatalog/GemCatalogHeaderWidget.h>
#include <QHBoxLayout>
#include <QMouseEvent>
#include <QLabel>
#include <QPushButton>
#include <TagWidget.h>
namespace O3DE::ProjectManager
{
GemCatalogHeaderWidget::GemCatalogHeaderWidget(GemSortFilterProxyModel* filterProxyModel, QWidget* parent)
CartOverlayWidget::CartOverlayWidget(GemModel* gemModel, QWidget* parent)
: QWidget(parent)
, m_gemModel(gemModel)
{
setObjectName("GemCatalogCart");
m_layout = new QVBoxLayout();
m_layout->setSpacing(0);
m_layout->setMargin(0);
m_layout->setAlignment(Qt::AlignTop);
setLayout(m_layout);
QHBoxLayout* hLayout = new QHBoxLayout();
QPushButton* closeButton = new QPushButton();
closeButton->setFlat(true);
closeButton->setFocusPolicy(Qt::NoFocus);
closeButton->setIcon(QIcon(":/WindowClose.svg"));
connect(closeButton, &QPushButton::clicked, this, [=]
{
deleteLater();
});
hLayout->addSpacerItem(new QSpacerItem(10, 0, QSizePolicy::Expanding));
hLayout->addWidget(closeButton);
m_layout->addLayout(hLayout);
// enabled
{
m_enabledWidget = new QWidget();
m_enabledWidget->setFixedWidth(s_width);
m_layout->addWidget(m_enabledWidget);
QVBoxLayout* layout = new QVBoxLayout();
layout->setAlignment(Qt::AlignTop);
m_enabledWidget->setLayout(layout);
m_enabledLabel = new QLabel();
m_enabledLabel->setObjectName("GemCatalogCartOverlaySectionLabel");
layout->addWidget(m_enabledLabel);
m_enabledTagContainer = new TagContainerWidget();
layout->addWidget(m_enabledTagContainer);
}
// disabled
{
m_disabledWidget = new QWidget();
m_disabledWidget->setFixedWidth(s_width);
m_layout->addWidget(m_disabledWidget);
QVBoxLayout* layout = new QVBoxLayout();
layout->setAlignment(Qt::AlignTop);
m_disabledWidget->setLayout(layout);
m_disabledLabel = new QLabel();
m_disabledLabel->setObjectName("GemCatalogCartOverlaySectionLabel");
layout->addWidget(m_disabledLabel);
m_disabledTagContainer = new TagContainerWidget();
layout->addWidget(m_disabledTagContainer);
}
setWindowFlags(Qt::FramelessWindowHint | Qt::Dialog);
Update();
connect(gemModel, &GemModel::dataChanged, this, [=]
{
Update();
});
}
void CartOverlayWidget::Update()
{
const QVector<QModelIndex> toBeAdded = m_gemModel->GatherGemsToBeAdded();
if (toBeAdded.isEmpty())
{
m_enabledWidget->hide();
}
else
{
m_enabledTagContainer->Update(ConvertFromModelIndices(toBeAdded));
m_enabledLabel->setText(QString("%1 %2").arg(QString::number(toBeAdded.size()), tr("Gems to be enabled")));
m_enabledWidget->show();
}
const QVector<QModelIndex> toBeRemoved = m_gemModel->GatherGemsToBeRemoved();
if (toBeRemoved.isEmpty())
{
m_disabledWidget->hide();
}
else
{
m_disabledTagContainer->Update(ConvertFromModelIndices(toBeRemoved));
m_disabledLabel->setText(QString("%1 %2").arg(QString::number(toBeRemoved.size()), tr("Gems to be disabled")));
m_disabledWidget->show();
}
}
QStringList CartOverlayWidget::ConvertFromModelIndices(const QVector<QModelIndex>& gems) const
{
QStringList gemNames;
gemNames.reserve(gems.size());
for (const QModelIndex& modelIndex : gems)
{
gemNames.push_back(GemModel::GetName(modelIndex));
}
return gemNames;
}
CartButton::CartButton(GemModel* gemModel, QWidget* parent)
: QWidget(parent)
, m_gemModel(gemModel)
{
m_layout = new QHBoxLayout();
m_layout->setMargin(0);
setLayout(m_layout);
QPushButton* iconButton = new QPushButton();
iconButton->setFlat(true);
iconButton->setFocusPolicy(Qt::NoFocus);
iconButton->setIcon(QIcon(":/Summary.svg"));
iconButton->setFixedSize(s_iconSize, s_iconSize);
connect(iconButton, &QPushButton::clicked, this, &CartButton::ShowOverlay);
m_layout->addWidget(iconButton);
m_countLabel = new QLabel();
m_countLabel->setObjectName("GemCatalogCartCountLabel");
m_countLabel->setFixedHeight(s_iconSize - 1); // Compensate for the empty icon space by using a slightly smaller label height.
m_layout->addWidget(m_countLabel);
m_dropDownButton = new QPushButton();
m_dropDownButton->setFlat(true);
m_dropDownButton->setFocusPolicy(Qt::NoFocus);
m_dropDownButton->setIcon(QIcon(":/CarrotArrowDown.svg"));
m_dropDownButton->setFixedSize(s_arrowDownIconSize, s_arrowDownIconSize);
connect(m_dropDownButton, &QPushButton::clicked, this, &CartButton::ShowOverlay);
m_layout->addWidget(m_dropDownButton);
// Adjust the label text whenever the model gets updated.
connect(gemModel, &GemModel::dataChanged, [=]
{
const QVector<QModelIndex> toBeAdded = m_gemModel->GatherGemsToBeAdded();
const QVector<QModelIndex> toBeRemoved = m_gemModel->GatherGemsToBeRemoved();
const int count = toBeAdded.size() + toBeRemoved.size();
m_countLabel->setText(QString::number(count));
m_dropDownButton->setVisible(!toBeAdded.isEmpty() || !toBeRemoved.isEmpty());
// Automatically close the overlay window in case there are no gems to be enabled or disabled anymore.
if (m_cartOverlay && toBeAdded.isEmpty() && toBeRemoved.isEmpty())
{
m_cartOverlay->deleteLater();
m_cartOverlay = nullptr;
}
});
}
void CartButton::mousePressEvent([[maybe_unused]] QMouseEvent* event)
{
ShowOverlay();
}
void CartButton::ShowOverlay()
{
const QVector<QModelIndex> toBeAdded = m_gemModel->GatherGemsToBeAdded();
const QVector<QModelIndex> toBeRemoved = m_gemModel->GatherGemsToBeRemoved();
if (toBeAdded.isEmpty() && toBeRemoved.isEmpty())
{
return;
}
if (m_cartOverlay)
{
// Directly delete the former overlay before creating the new one.
// Don't use deleteLater() here. This might overwrite the new overlay pointer
// depending on the event queue.
delete m_cartOverlay;
}
m_cartOverlay = new CartOverlayWidget(m_gemModel, this);
connect(m_cartOverlay, &QWidget::destroyed, this, [=]
{
// Reset the overlay pointer on destruction to prevent dangling pointers.
m_cartOverlay = nullptr;
});
m_cartOverlay->show();
const QPoint parentPos = m_dropDownButton->mapToParent(m_dropDownButton->pos());
const QPoint globalPos = m_dropDownButton->mapToGlobal(m_dropDownButton->pos());
const QPoint offset(-4, 10);
m_cartOverlay->setGeometry(globalPos.x() - parentPos.x() - m_cartOverlay->width() + width() + offset.x(),
globalPos.y() + offset.y(),
m_cartOverlay->width(),
m_cartOverlay->height());
}
CartButton::~CartButton()
{
// Make sure the overlay window is automatically closed in case the gem catalog is destroyed.
if (m_cartOverlay)
{
m_cartOverlay->deleteLater();
}
}
GemCatalogHeaderWidget::GemCatalogHeaderWidget(GemModel* gemModel, GemSortFilterProxyModel* filterProxyModel, QWidget* parent)
: QFrame(parent)
{
QHBoxLayout* hLayout = new QHBoxLayout();
hLayout->setAlignment(Qt::AlignLeft);
hLayout->setMargin(0);
hLayout->setContentsMargins(10, 7, 10, 7);
setLayout(hLayout);
setStyleSheet("background-color: #1E252F;");
setObjectName("GemCatalogHeaderWidget");
setFixedHeight(s_height);
QLabel* titleLabel = new QLabel(tr("Gem Catalog"));
titleLabel->setStyleSheet("font-size: 21px;");
titleLabel->setObjectName("GemCatalogTitle");
hLayout->addWidget(titleLabel);
hLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding));
AzQtComponents::SearchLineEdit* filterLineEdit = new AzQtComponents::SearchLineEdit();
filterLineEdit->setStyleSheet("background-color: #DDDDDD;");
connect(filterLineEdit, &QLineEdit::textChanged, this, [=](const QString& text)
m_filterLineEdit = new AzQtComponents::SearchLineEdit();
m_filterLineEdit->setStyleSheet("background-color: #DDDDDD;");
connect(m_filterLineEdit, &QLineEdit::textChanged, this, [=](const QString& text)
{
filterProxyModel->SetSearchString(text);
});
hLayout->addWidget(filterLineEdit);
hLayout->addWidget(m_filterLineEdit);
hLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding));
hLayout->addSpacerItem(new QSpacerItem(220, 0, QSizePolicy::Fixed));
setFixedHeight(60);
hLayout->addSpacerItem(new QSpacerItem(75, 0, QSizePolicy::Fixed));
CartButton* cartButton = new CartButton(gemModel);
hLayout->addWidget(cartButton);
}
void GemCatalogHeaderWidget::ReinitForProject()
{
m_filterLineEdit->setText({});
}
} // namespace O3DE::ProjectManager
@@ -13,19 +13,81 @@
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzQtComponents/Components/SearchLineEdit.h>
#include <GemCatalog/GemModel.h>
#include <GemCatalog/GemSortFilterProxyModel.h>
#include <TagWidget.h>
#include <QFrame>
#include <QLabel>
#include <QDialog>
#include <QMoveEvent>
#include <QVBoxLayout>
#endif
namespace O3DE::ProjectManager
{
class CartOverlayWidget
: public QWidget
{
Q_OBJECT // AUTOMOC
public:
CartOverlayWidget(GemModel* gemModel, QWidget* parent = nullptr);
void Update();
private:
QStringList ConvertFromModelIndices(const QVector<QModelIndex>& gems) const;
QVBoxLayout* m_layout = nullptr;
GemModel* m_gemModel = nullptr;
QWidget* m_enabledWidget = nullptr;
QLabel* m_enabledLabel = nullptr;
TagContainerWidget* m_enabledTagContainer = nullptr;
QWidget* m_disabledWidget = nullptr;
QLabel* m_disabledLabel = nullptr;
TagContainerWidget* m_disabledTagContainer = nullptr;
inline constexpr static int s_width = 240;
};
class CartButton
: public QWidget
{
Q_OBJECT // AUTOMOC
public:
CartButton(GemModel* gemModel, QWidget* parent = nullptr);
~CartButton();
void ShowOverlay();
private:
void mousePressEvent(QMouseEvent* event) override;
GemModel* m_gemModel = nullptr;
QHBoxLayout* m_layout = nullptr;
QLabel* m_countLabel = nullptr;
QPushButton* m_dropDownButton = nullptr;
CartOverlayWidget* m_cartOverlay = nullptr;
inline constexpr static int s_iconSize = 24;
inline constexpr static int s_arrowDownIconSize = 8;
};
class GemCatalogHeaderWidget
: public QFrame
{
Q_OBJECT // AUTOMOC
public:
explicit GemCatalogHeaderWidget(GemSortFilterProxyModel* filterProxyModel, QWidget* parent = nullptr);
explicit GemCatalogHeaderWidget(GemModel* gemModel, GemSortFilterProxyModel* filterProxyModel, QWidget* parent = nullptr);
~GemCatalogHeaderWidget() = default;
void ReinitForProject();
private:
AzQtComponents::SearchLineEdit* m_filterLineEdit = nullptr;
inline constexpr static int s_height = 60;
};
} // namespace O3DE::ProjectManager
@@ -12,16 +12,14 @@
#include <GemCatalog/GemCatalogScreen.h>
#include <PythonBindingsInterface.h>
#include <GemCatalog/GemCatalogHeaderWidget.h>
#include <GemCatalog/GemListHeaderWidget.h>
#include <GemCatalog/GemSortFilterProxyModel.h>
#include <GemCatalog/GemFilterWidget.h>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QTimer>
//#define USE_TESTGEMDATA
#include <PythonBindingsInterface.h>
#include <QMessageBox>
namespace O3DE::ProjectManager
{
@@ -29,47 +27,32 @@ namespace O3DE::ProjectManager
: ScreenWidget(parent)
{
m_gemModel = new GemModel(this);
GemSortFilterProxyModel* proxyModel = new GemSortFilterProxyModel(m_gemModel, this);
m_proxModel = new GemSortFilterProxyModel(m_gemModel, this);
QVBoxLayout* vLayout = new QVBoxLayout();
vLayout->setMargin(0);
vLayout->setSpacing(0);
setLayout(vLayout);
GemCatalogHeaderWidget* headerWidget = new GemCatalogHeaderWidget(proxyModel);
vLayout->addWidget(headerWidget);
m_headerWidget = new GemCatalogHeaderWidget(m_gemModel, m_proxModel);
vLayout->addWidget(m_headerWidget);
QHBoxLayout* hLayout = new QHBoxLayout();
hLayout->setMargin(0);
vLayout->addLayout(hLayout);
m_gemListView = new GemListView(proxyModel, proxyModel->GetSelectionModel(), this);
m_gemListView = new GemListView(m_proxModel, m_proxModel->GetSelectionModel(), this);
m_gemInspector = new GemInspector(m_gemModel, this);
m_gemInspector->setFixedWidth(320);
m_gemInspector->setFixedWidth(240);
// Start: Temporary gem test data
#ifdef USE_TESTGEMDATA
QVector<GemInfo> testGemData = GenerateTestData();
for (const GemInfo& gemInfo : testGemData)
{
m_gemModel->AddGem(gemInfo);
}
#else
// End: Temporary gem test data
auto result = PythonBindingsInterface::Get()->GetGems();
if (result.IsSuccess())
{
for (auto gemInfo : result.GetValue())
{
m_gemModel->AddGem(gemInfo);
}
}
#endif
QWidget* filterWidget = new QWidget(this);
filterWidget->setFixedWidth(240);
m_filterWidgetLayout = new QVBoxLayout();
m_filterWidgetLayout->setMargin(0);
m_filterWidgetLayout->setSpacing(0);
filterWidget->setLayout(m_filterWidgetLayout);
GemFilterWidget* filterWidget = new GemFilterWidget(proxyModel);
filterWidget->setFixedWidth(250);
GemListHeaderWidget* listHeaderWidget = new GemListHeaderWidget(proxyModel);
GemListHeaderWidget* listHeaderWidget = new GemListHeaderWidget(m_proxModel);
QVBoxLayout* middleVLayout = new QVBoxLayout();
middleVLayout->setMargin(0);
@@ -80,98 +63,114 @@ namespace O3DE::ProjectManager
hLayout->addWidget(filterWidget);
hLayout->addLayout(middleVLayout);
hLayout->addWidget(m_gemInspector);
proxyModel->InvalidateFilter();
}
QVector<GemInfo> GemCatalogScreen::GenerateTestData()
void GemCatalogScreen::ReinitForProject(const QString& projectPath, bool isNewProject)
{
QVector<GemInfo> result;
m_gemModel->clear();
FillModel(projectPath, isNewProject);
GemInfo gem("EMotion FX",
"O3DE Foundation",
"EMFX is a real-time character animation system. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
(GemInfo::Android | GemInfo::iOS | GemInfo::macOS | GemInfo::Windows | GemInfo::Linux),
true);
gem.m_directoryLink = "C:/";
gem.m_documentationLink = "http://www.amazon.com";
gem.m_dependingGemUuids = QStringList({"EMotionFX", "Atom"});
gem.m_conflictingGemUuids = QStringList({"Vegetation", "Camera", "ScriptCanvas", "CloudCanvas", "Networking"});
gem.m_types = (GemInfo::Code | GemInfo::Asset);
gem.m_version = "v1.01";
gem.m_lastUpdatedDate = "24th April 2021";
gem.m_binarySizeInKB = 40;
gem.m_features = QStringList({"Animation", "Assets", "Physics"});
gem.m_gemOrigin = GemInfo::O3DEFoundation;
result.push_back(gem);
if (m_filterWidget)
{
m_filterWidget->hide();
m_filterWidget->deleteLater();
}
gem.m_name = "Atom";
gem.m_creator = "O3DE Seattle";
gem.m_summary = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
gem.m_platforms = (GemInfo::Android | GemInfo::Windows | GemInfo::Linux | GemInfo::macOS);
gem.m_isAdded = true;
gem.m_directoryLink = "C:/";
gem.m_documentationLink = "https://aws.amazon.com/gametech/";
gem.m_dependingGemUuids = QStringList({"EMotionFX", "Core", "AudioSystem", "Camera", "Particles"});
gem.m_conflictingGemUuids = QStringList({"CloudCanvas", "NovaNet"});
gem.m_version = "v2.31";
gem.m_lastUpdatedDate = "24th November 2020";
gem.m_features = QStringList({"Assets", "Rendering", "UI", "VR", "Debug", "Environment"});
gem.m_binarySizeInKB = 2087;
result.push_back(gem);
m_proxModel->ResetFilters();
m_filterWidget = new GemFilterWidget(m_proxModel);
m_filterWidgetLayout->addWidget(m_filterWidget);
gem.m_name = "Physics";
gem.m_creator = "O3DE London";
gem.m_summary = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
gem.m_platforms = (GemInfo::Android | GemInfo::Linux | GemInfo::macOS);
gem.m_isAdded = true;
gem.m_directoryLink = "C:/";
gem.m_documentationLink = "https://aws.amazon.com/gametech/";
gem.m_dependingGemUuids = QStringList({"GraphCanvas", "ExpressionEvaluation", "UI Lib", "Multiplayer", "GameStateSamples"});
gem.m_conflictingGemUuids = QStringList({"Cloud Canvas", "EMotion FX", "Streaming", "MessagePopup", "Cloth", "Graph Canvas", "Twitch Integration"});
gem.m_version = "v1.5.102145";
gem.m_lastUpdatedDate = "1st January 2021";
gem.m_binarySizeInKB = 2000000;
gem.m_features = QStringList({"Physics", "Gameplay", "Debug", "Assets"});
result.push_back(gem);
m_headerWidget->ReinitForProject();
result.push_back(O3DE::ProjectManager::GemInfo("Certificate Manager",
"O3DE Irvine",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
GemInfo::Windows,
false));
connect(m_gemModel, &GemModel::dataChanged, m_filterWidget, &GemFilterWidget::ResetGemStatusFilter);
result.push_back(O3DE::ProjectManager::GemInfo("Cloud Gem Framework",
"O3DE Seattle",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
GemInfo::iOS | GemInfo::Linux,
false));
// Select the first entry after everything got correctly sized
QTimer::singleShot(200, [=]{
QModelIndex firstModelIndex = m_gemListView->model()->index(0,0);
m_gemListView->selectionModel()->select(firstModelIndex, QItemSelectionModel::ClearAndSelect);
});
}
result.push_back(O3DE::ProjectManager::GemInfo("Cloud Gem Core",
"O3DE Foundation",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
GemInfo::Android | GemInfo::Windows | GemInfo::Linux,
true));
void GemCatalogScreen::FillModel(const QString& projectPath, bool isNewProject)
{
AZ::Outcome<QVector<GemInfo>, AZStd::string> allGemInfosResult;
if (isNewProject)
{
allGemInfosResult = PythonBindingsInterface::Get()->GetEngineGemInfos();
}
else
{
allGemInfosResult = PythonBindingsInterface::Get()->GetAllGemInfos(projectPath);
}
result.push_back(O3DE::ProjectManager::GemInfo("Gestures",
"O3DE Foundation",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
GemInfo::Android | GemInfo::Windows | GemInfo::Linux,
false));
if (allGemInfosResult.IsSuccess())
{
// Add all available gems to the model.
const QVector<GemInfo> allGemInfos = allGemInfosResult.GetValue();
for (const GemInfo& gemInfo : allGemInfos)
{
m_gemModel->AddGem(gemInfo);
}
result.push_back(O3DE::ProjectManager::GemInfo("Effects System",
"O3DE Foundation",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
GemInfo::Android | GemInfo::Windows | GemInfo::Linux,
true));
// Gather enabled gems for the given project.
auto enabledGemNamesResult = PythonBindingsInterface::Get()->GetEnabledGemNames(projectPath);
if (enabledGemNamesResult.IsSuccess())
{
const QVector<AZStd::string> enabledGemNames = enabledGemNamesResult.GetValue();
for (const AZStd::string& enabledGemName : enabledGemNames)
{
const QModelIndex modelIndex = m_gemModel->FindIndexByNameString(enabledGemName.c_str());
if (modelIndex.isValid())
{
GemModel::SetWasPreviouslyAdded(*m_gemModel, modelIndex, true);
GemModel::SetIsAdded(*m_gemModel, modelIndex, true);
}
else
{
AZ_Warning("ProjectManager::GemCatalog", false,
"Cannot find entry for gem with name '%s'. The CMake target name probably does not match the specified name in the gem.json.",
enabledGemName.c_str());
}
}
}
else
{
QMessageBox::critical(nullptr, tr("Operation failed"), QString("Cannot retrieve enabled gems for project %1.\n\nError:\n%2").arg(projectPath, enabledGemNamesResult.GetError().c_str()));
}
}
else
{
QMessageBox::critical(nullptr, tr("Operation failed"), QString("Cannot retrieve gems for %1.\n\nError:\n%2").arg(projectPath, allGemInfosResult.GetError().c_str()));
}
}
result.push_back(O3DE::ProjectManager::GemInfo("Microphone",
"O3DE Foundation",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus euismod ligula vitae dui dictum, a sodales dolor luctus. Sed id elit dapibus, finibus neque sed, efficitur mi. Nam facilisis ligula at eleifend pellentesque. Praesent non ex consectetur, blandit tellus in, venenatis lacus. Duis nec neque in urna ullamcorper euismod id eu leo. Nam efficitur dolor sed odio vehicula venenatis. Suspendisse nec est non velit commodo cursus in sit amet dui. Ut bibendum nisl et libero hendrerit dapibus. Vestibulum ultrices ullamcorper urna, placerat porttitor est lobortis in. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer a magna ac tellus sollicitudin porttitor. Phasellus lobortis viverra justo id bibendum. Etiam ac pharetra risus. Nulla vitae justo nibh. Nulla viverra leo et molestie interdum. Duis sit amet bibendum nulla, sit amet vehicula augue.",
GemInfo::Android | GemInfo::Windows | GemInfo::Linux,
false));
void GemCatalogScreen::EnableDisableGemsForProject(const QString& projectPath)
{
IPythonBindings* pythonBindings = PythonBindingsInterface::Get();
QVector<QModelIndex> toBeAdded = m_gemModel->GatherGemsToBeAdded();
QVector<QModelIndex> toBeRemoved = m_gemModel->GatherGemsToBeRemoved();
return result;
for (const QModelIndex& modelIndex : toBeAdded)
{
const QString gemPath = GemModel::GetPath(modelIndex);
const AZ::Outcome<void, AZStd::string> result = pythonBindings->AddGemToProject(gemPath, projectPath);
if (!result.IsSuccess())
{
QMessageBox::critical(nullptr, "Operation failed",
QString("Cannot add gem %1 to project.\n\nError:\n%2").arg(GemModel::GetName(modelIndex), result.GetError().c_str()));
}
}
for (const QModelIndex& modelIndex : toBeRemoved)
{
const QString gemPath = GemModel::GetPath(modelIndex);
const AZ::Outcome<void, AZStd::string> result = pythonBindings->RemoveGemFromProject(gemPath, projectPath);
if (!result.IsSuccess())
{
QMessageBox::critical(nullptr, "Operation failed",
QString("Cannot remove gem %1 from project.\n\nError:\n%2").arg(GemModel::GetName(modelIndex), result.GetError().c_str()));
}
}
}
ProjectManagerScreen GemCatalogScreen::GetScreenEnum()
@@ -14,9 +14,12 @@
#if !defined(Q_MOC_RUN)
#include <ScreenWidget.h>
#include <GemCatalog/GemCatalogHeaderWidget.h>
#include <GemCatalog/GemFilterWidget.h>
#include <GemCatalog/GemListView.h>
#include <GemCatalog/GemInspector.h>
#include <GemCatalog/GemModel.h>
#include <GemCatalog/GemSortFilterProxyModel.h>
#endif
namespace O3DE::ProjectManager
@@ -29,11 +32,18 @@ namespace O3DE::ProjectManager
~GemCatalogScreen() = default;
ProjectManagerScreen GetScreenEnum() override;
void ReinitForProject(const QString& projectPath, bool isNewProject);
void EnableDisableGemsForProject(const QString& projectPath);
private:
QVector<GemInfo> GenerateTestData();
void FillModel(const QString& projectPath, bool isNewProject);
GemListView* m_gemListView = nullptr;
GemInspector* m_gemInspector = nullptr;
GemModel* m_gemModel = nullptr;
GemCatalogHeaderWidget* m_headerWidget = nullptr;
GemSortFilterProxyModel* m_proxModel = nullptr;
QVBoxLayout* m_filterWidgetLayout = nullptr;
GemFilterWidget* m_filterWidget = nullptr;
};
} // namespace O3DE::ProjectManager
@@ -26,6 +26,7 @@ namespace O3DE::ProjectManager
const QVector<QString>& elementNames,
const QVector<int>& elementCounts,
bool showAllLessButton,
bool collapsed,
int defaultShowCount,
QWidget* parent)
: QWidget(parent)
@@ -40,10 +41,10 @@ namespace O3DE::ProjectManager
QHBoxLayout* collapseLayout = new QHBoxLayout();
m_collapseButton = new QPushButton();
m_collapseButton->setCheckable(true);
m_collapseButton->setChecked(collapsed);
m_collapseButton->setFlat(true);
m_collapseButton->setFocusPolicy(Qt::NoFocus);
m_collapseButton->setFixedWidth(s_collapseButtonSize);
m_collapseButton->setStyleSheet("border: 0px; border-radius: 0px;");
connect(m_collapseButton, &QPushButton::clicked, this, [=]()
{
UpdateCollapseState();
@@ -52,7 +53,7 @@ namespace O3DE::ProjectManager
// Category title
QLabel* headerLabel = new QLabel(header);
headerLabel->setStyleSheet("font-size: 11pt;");
headerLabel->setObjectName("GemCatalogFilterCategoryTitle");
collapseLayout->addWidget(headerLabel);
vLayout->addLayout(collapseLayout);
@@ -79,14 +80,14 @@ namespace O3DE::ProjectManager
elementWidget->setLayout(elementLayout);
QCheckBox* checkbox = new QCheckBox(elementNames[i]);
checkbox->setStyleSheet("font-size: 11pt;");
checkbox->setStyleSheet("font-size: 12px;");
m_buttonGroup->addButton(checkbox);
elementLayout->addWidget(checkbox);
elementLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding));
QLabel* countLabel = new QLabel(QString::number(elementCounts[i]));
countLabel->setStyleSheet("font-size: 11pt; background-color: #333333; border-radius: 3px; color: #94D2FF;");
countLabel->setStyleSheet("font-size: 12px; background-color: #333333; border-radius: 3px; color: #94D2FF;");
elementLayout->addWidget(countLabel);
m_elementWidgets.push_back(elementWidget);
@@ -110,6 +111,8 @@ namespace O3DE::ProjectManager
}
}
vLayout->addSpacing(5);
// Separating line
QFrame* hLine = new QFrame();
hLine->setFrameShape(QFrame::HLine);
@@ -177,10 +180,17 @@ namespace O3DE::ProjectManager
return m_buttonGroup;
}
bool FilterCategoryWidget::IsCollapsed()
{
return m_collapseButton->isChecked();
}
GemFilterWidget::GemFilterWidget(GemSortFilterProxyModel* filterProxyModel, QWidget* parent)
: QScrollArea(parent)
, m_filterProxyModel(filterProxyModel)
{
setObjectName("GemCatalogFilterWidget");
m_gemModel = m_filterProxyModel->GetSourceModel();
setWidgetResizable(true);
@@ -190,20 +200,106 @@ namespace O3DE::ProjectManager
QWidget* mainWidget = new QWidget();
setWidget(mainWidget);
m_mainLayout = new QVBoxLayout();
m_mainLayout->setAlignment(Qt::AlignTop);
mainWidget->setLayout(m_mainLayout);
QVBoxLayout* mainLayout = new QVBoxLayout();
mainLayout->setAlignment(Qt::AlignTop);
mainWidget->setLayout(mainLayout);
QLabel* filterByLabel = new QLabel("Filter by");
filterByLabel->setStyleSheet("font-size: 15pt;");
m_mainLayout->addWidget(filterByLabel);
filterByLabel->setStyleSheet("font-size: 16px;");
mainLayout->addWidget(filterByLabel);
QWidget* filterSection = new QWidget(this);
mainLayout->addWidget(filterSection);
m_filterLayout = new QVBoxLayout();
m_filterLayout->setAlignment(Qt::AlignTop);
m_filterLayout->setContentsMargins(0, 0, 0, 0);
filterSection->setLayout(m_filterLayout);
ResetGemStatusFilter();
AddGemOriginFilter();
AddTypeFilter();
AddPlatformFilter();
AddFeatureFilter();
}
void GemFilterWidget::ResetGemStatusFilter()
{
QVector<QString> elementNames;
QVector<int> elementCounts;
const int totalGems = m_gemModel->rowCount();
const int selectedGemTotal = m_gemModel->TotalAddedGems();
elementNames.push_back(GemSortFilterProxyModel::GetGemStatusString(GemSortFilterProxyModel::GemStatus::Unselected));
elementCounts.push_back(totalGems - selectedGemTotal);
elementNames.push_back(GemSortFilterProxyModel::GetGemStatusString(GemSortFilterProxyModel::GemStatus::Selected));
elementCounts.push_back(selectedGemTotal);
bool wasCollapsed = false;
if (m_statusFilter)
{
wasCollapsed = m_statusFilter->IsCollapsed();
}
FilterCategoryWidget* filterWidget =
new FilterCategoryWidget("Status", elementNames, elementCounts, /*showAllLessButton=*/false, /*collapsed*/wasCollapsed);
if (m_statusFilter)
{
m_filterLayout->replaceWidget(m_statusFilter, filterWidget);
}
else
{
m_filterLayout->addWidget(filterWidget);
}
m_statusFilter->deleteLater();
m_statusFilter = filterWidget;
const GemSortFilterProxyModel::GemStatus currentFilterState = m_filterProxyModel->GetGemStatus();
const QList<QAbstractButton*> buttons = m_statusFilter->GetButtonGroup()->buttons();
for (int statusFilterIndex = 0; statusFilterIndex < buttons.size(); ++statusFilterIndex)
{
const GemSortFilterProxyModel::GemStatus gemStatus = static_cast<GemSortFilterProxyModel::GemStatus>(statusFilterIndex);
QAbstractButton* button = buttons[statusFilterIndex];
if (static_cast<GemSortFilterProxyModel::GemStatus>(statusFilterIndex) == currentFilterState)
{
button->setChecked(true);
}
connect(
button, &QAbstractButton::toggled, this,
[=](bool checked)
{
GemSortFilterProxyModel::GemStatus filterStatus = m_filterProxyModel->GetGemStatus();
if (checked)
{
if (filterStatus == GemSortFilterProxyModel::GemStatus::NoFilter)
{
filterStatus = gemStatus;
}
else
{
filterStatus = GemSortFilterProxyModel::GemStatus::NoFilter;
}
}
else
{
if (filterStatus != gemStatus)
{
filterStatus = static_cast<GemSortFilterProxyModel::GemStatus>(!gemStatus);
}
else
{
filterStatus = GemSortFilterProxyModel::GemStatus::NoFilter;
}
}
m_filterProxyModel->SetGemStatus(filterStatus);
});
}
}
void GemFilterWidget::AddGemOriginFilter()
{
QVector<QString> elementNames;
@@ -230,7 +326,7 @@ namespace O3DE::ProjectManager
}
FilterCategoryWidget* filterWidget = new FilterCategoryWidget("Provider", elementNames, elementCounts, /*showAllLessButton=*/false);
m_mainLayout->addWidget(filterWidget);
m_filterLayout->addWidget(filterWidget);
const QList<QAbstractButton*> buttons = filterWidget->GetButtonGroup()->buttons();
for (int i = 0; i < buttons.size(); ++i)
@@ -280,7 +376,7 @@ namespace O3DE::ProjectManager
}
FilterCategoryWidget* filterWidget = new FilterCategoryWidget("Type", elementNames, elementCounts, /*showAllLessButton=*/false);
m_mainLayout->addWidget(filterWidget);
m_filterLayout->addWidget(filterWidget);
const QList<QAbstractButton*> buttons = filterWidget->GetButtonGroup()->buttons();
for (int i = 0; i < buttons.size(); ++i)
@@ -330,7 +426,7 @@ namespace O3DE::ProjectManager
}
FilterCategoryWidget* filterWidget = new FilterCategoryWidget("Supported Platforms", elementNames, elementCounts, /*showAllLessButton=*/false);
m_mainLayout->addWidget(filterWidget);
m_filterLayout->addWidget(filterWidget);
const QList<QAbstractButton*> buttons = filterWidget->GetButtonGroup()->buttons();
for (int i = 0; i < buttons.size(); ++i)
@@ -385,8 +481,8 @@ namespace O3DE::ProjectManager
}
FilterCategoryWidget* filterWidget = new FilterCategoryWidget("Features", elementNames, elementCounts,
/*showAllLessButton=*/true, /*defaultShowCount=*/5);
m_mainLayout->addWidget(filterWidget);
/*showAllLessButton=*/true, false, /*defaultShowCount=*/5);
m_filterLayout->addWidget(filterWidget);
const QList<QAbstractButton*> buttons = filterWidget->GetButtonGroup()->buttons();
for (int i = 0; i < buttons.size(); ++i)
@@ -37,11 +37,14 @@ namespace O3DE::ProjectManager
const QVector<QString>& elementNames,
const QVector<int>& elementCounts,
bool showAllLessButton = true,
bool collapsed = false,
int defaultShowCount = 4,
QWidget* parent = nullptr);
QButtonGroup* GetButtonGroup();
bool IsCollapsed();
private:
void UpdateCollapseState();
void UpdateSeeMoreLess();
@@ -66,14 +69,18 @@ namespace O3DE::ProjectManager
explicit GemFilterWidget(GemSortFilterProxyModel* filterProxyModel, QWidget* parent = nullptr);
~GemFilterWidget() = default;
public slots:
void ResetGemStatusFilter();
private:
void AddGemOriginFilter();
void AddTypeFilter();
void AddPlatformFilter();
void AddFeatureFilter();
QVBoxLayout* m_mainLayout = nullptr;
QVBoxLayout* m_filterLayout = nullptr;
GemModel* m_gemModel = nullptr;
GemSortFilterProxyModel* m_filterProxyModel = nullptr;
FilterCategoryWidget* m_statusFilter = nullptr;
};
} // namespace O3DE::ProjectManager

Some files were not shown because too many files have changed in this diff Show More