diff --git a/Code/Framework/AzFramework/AzFramework/Asset/AssetProcessorMessages.h b/Code/Framework/AzFramework/AzFramework/Asset/AssetProcessorMessages.h index c15f75e3e7..f4ef1a1ca2 100644 --- a/Code/Framework/AzFramework/AzFramework/Asset/AssetProcessorMessages.h +++ b/Code/Framework/AzFramework/AzFramework/Asset/AssetProcessorMessages.h @@ -166,7 +166,7 @@ namespace AzFramework { public: AZ_CLASS_ALLOCATOR(RequestEscalateAsset, AZ::OSAllocator, 0); - AZ_RTTI(RequestAssetStatus, "{E95C5422-5F00-478B-A984-C041DE70484F}", BaseAssetProcessorMessage); + AZ_RTTI(RequestEscalateAsset, "{E95C5422-5F00-478B-A984-C041DE70484F}", BaseAssetProcessorMessage); static void Reflect(AZ::ReflectContext* context); static constexpr unsigned int MessageType = AZ_CRC("AssetSystem::RequestEscalateAsset", 0x1894d94e); diff --git a/Code/Framework/AzTest/AzTest/Platform/Linux/AzTest_Traits_Linux.h b/Code/Framework/AzTest/AzTest/Platform/Linux/AzTest_Traits_Linux.h index 855b4fe416..64816f75b2 100644 --- a/Code/Framework/AzTest/AzTest/Platform/Linux/AzTest_Traits_Linux.h +++ b/Code/Framework/AzTest/AzTest/Platform/Linux/AzTest_Traits_Linux.h @@ -22,7 +22,7 @@ #define AZ_TRAIT_DISABLE_FAILED_AP_CONNECTION_TESTS true #define AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS true -#define AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS true + #define AZ_TRAIT_DISABLE_FAILED_ATOM_RPI_TESTS true #define AZ_TRAIT_DISABLE_FAILED_ARCHIVE_TESTS true #define AZ_TRAIT_DISABLE_FAILED_ZERO_COLOR_CONVERSION_TEST true diff --git a/Code/Tools/AssetProcessor/CMakeLists.txt b/Code/Tools/AssetProcessor/CMakeLists.txt index 2db888218b..5e5ede1397 100644 --- a/Code/Tools/AssetProcessor/CMakeLists.txt +++ b/Code/Tools/AssetProcessor/CMakeLists.txt @@ -259,8 +259,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) ly_add_googletest( NAME AZ::AssetProcessor.Tests - TEST_COMMAND $ --unittest + TEST_COMMAND $ --unittest --gtest_filter=-*.SUITE_sandbox* ) - endif() diff --git a/Code/Tools/AssetProcessor/Platform/Linux/native/FileWatcher/FileWatcher_linux.cpp b/Code/Tools/AssetProcessor/Platform/Linux/native/FileWatcher/FileWatcher_linux.cpp index 5029db6603..6e71897ab7 100644 --- a/Code/Tools/AssetProcessor/Platform/Linux/native/FileWatcher/FileWatcher_linux.cpp +++ b/Code/Tools/AssetProcessor/Platform/Linux/native/FileWatcher/FileWatcher_linux.cpp @@ -11,9 +11,127 @@ */ #include +#include +#include +#include + +#include +#include + + +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 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 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::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; indexname; + + 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; + } + } + } } diff --git a/Code/Tools/AssetProcessor/Platform/Linux/native/FileWatcher/FileWatcher_win.cpp b/Code/Tools/AssetProcessor/Platform/Linux/native/FileWatcher/FileWatcher_win.cpp deleted file mode 100644 index fbcfe140fd..0000000000 --- a/Code/Tools/AssetProcessor/Platform/Linux/native/FileWatcher/FileWatcher_win.cpp +++ /dev/null @@ -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 - -#include - -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); - } - } - } - } - } -} - diff --git a/Code/Tools/AssetProcessor/native/AssetManager/PathDependencyManager.cpp b/Code/Tools/AssetProcessor/native/AssetManager/PathDependencyManager.cpp index de8bde9b05..2345c9e285 100644 --- a/Code/Tools/AssetProcessor/native/AssetManager/PathDependencyManager.cpp +++ b/Code/Tools/AssetProcessor/native/AssetManager/PathDependencyManager.cpp @@ -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; diff --git a/Code/Tools/AssetProcessor/native/AssetManager/SourceFileRelocator.cpp b/Code/Tools/AssetProcessor/native/AssetManager/SourceFileRelocator.cpp index 49c7ef7f20..c0300b2b2a 100644 --- a/Code/Tools/AssetProcessor/native/AssetManager/SourceFileRelocator.cpp +++ b/Code/Tools/AssetProcessor/native/AssetManager/SourceFileRelocator.cpp @@ -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& sourceIndexMap, const ScanFolderInfo* scanFolderInfo, SourceFileRelocationContainer& metadataFiles, bool excludeMetaDataFiles) const { QSet 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 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, diff --git a/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.cpp b/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.cpp index 47f79d9709..25ec36c888 100644 --- a/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.cpp +++ b/Code/Tools/AssetProcessor/native/AssetManager/assetProcessorManager.cpp @@ -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) diff --git a/Code/Tools/AssetProcessor/native/tests/AssetProcessorMessagesTests.cpp b/Code/Tools/AssetProcessor/native/tests/AssetProcessorMessagesTests.cpp index 04949c82a9..8879697faa 100644 --- a/Code/Tools/AssetProcessor/native/tests/AssetProcessorMessagesTests.cpp +++ b/Code/Tools/AssetProcessor/native/tests/AssetProcessorMessagesTests.cpp @@ -28,7 +28,7 @@ namespace AssetProcessorMessagesTests using namespace AssetProcessor; using namespace AssetBuilderSDK; - static constexpr unsigned short AssetProcessorPort = static_cast(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 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 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 @@ -311,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; diff --git a/Code/Tools/AssetProcessor/native/tests/SourceFileRelocatorTests.cpp b/Code/Tools/AssetProcessor/native/tests/SourceFileRelocatorTests.cpp index 2731aa6c5a..e9f18a442a 100644 --- a/Code/Tools/AssetProcessor/native/tests/SourceFileRelocatorTests.cpp +++ b/Code/Tools/AssetProcessor/native/tests/SourceFileRelocatorTests.cpp @@ -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) diff --git a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp index bd99cf7a94..6cd18d534b 100644 --- a/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp +++ b/Code/Tools/AssetProcessor/native/tests/assetmanager/AssetProcessorManagerTest.cpp @@ -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; @@ -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; diff --git a/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp b/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp index 9c80b267eb..4a8e1a7e1c 100644 --- a/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp +++ b/Code/Tools/AssetProcessor/native/tests/platformconfiguration/platformconfigurationtests.cpp @@ -14,6 +14,7 @@ #include "native/tests/platformconfiguration/platformconfigurationtests.h" #include +#include const char TestAppRoot[] = "@exefolder@/testdata"; const char EmptyDummyProjectName[] = "EmptyDummyProject"; @@ -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); @@ -445,15 +419,11 @@ 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) +#if defined(AZ_PLATFORM_WINDOWS) || defined(AZ_PLATFORM_LINUX) const char* platformWhichIsNotCurrentPlatform = "mac"; #else const char* platformWhichIsNotCurrentPlatform = "pc"; @@ -502,31 +472,47 @@ TEST_F(PlatformConfigurationUnitTests, TestFailReadConfigFile_Recognizers) // 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("android")); - 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["android"].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("android")); - 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["android"].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("android")); - 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"); } diff --git a/Code/Tools/AssetProcessor/native/tests/utilities/assetUtilsTest.cpp b/Code/Tools/AssetProcessor/native/tests/utilities/assetUtilsTest.cpp index 5ed392d12b..796bca2af2 100644 --- a/Code/Tools/AssetProcessor/native/tests/utilities/assetUtilsTest.cpp +++ b/Code/Tools/AssetProcessor/native/tests/utilities/assetUtilsTest.cpp @@ -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"; diff --git a/Code/Tools/AssetProcessor/native/unittests/AssetProcessorManagerUnitTests.cpp b/Code/Tools/AssetProcessor/native/unittests/AssetProcessorManagerUnitTests.cpp index 079cdb7c66..efeec5d7c1 100644 --- a/Code/Tools/AssetProcessor/native/unittests/AssetProcessorManagerUnitTests.cpp +++ b/Code/Tools/AssetProcessor/native/unittests/AssetProcessorManagerUnitTests.cpp @@ -175,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 expectedFiles; // set up some interesting files: expectedFiles << tempPath.absoluteFilePath("rootfile2.txt"); @@ -185,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 @@ -1521,7 +1533,8 @@ 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)); @@ -1583,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... @@ -1607,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)); @@ -1621,6 +1633,11 @@ 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 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); @@ -1641,7 +1658,7 @@ 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; @@ -2721,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); @@ -2805,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, "")); diff --git a/Code/Tools/AssetProcessor/native/unittests/FileWatcherUnitTests.cpp b/Code/Tools/AssetProcessor/native/unittests/FileWatcherUnitTests.cpp index 785ea8efa6..f6fb21e4e7 100644 --- a/Code/Tools/AssetProcessor/native/unittests/FileWatcherUnitTests.cpp +++ b/Code/Tools/AssetProcessor/native/unittests/FileWatcherUnitTests.cpp @@ -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); diff --git a/Code/Tools/AssetProcessor/native/unittests/RCcontrollerUnitTests.cpp b/Code/Tools/AssetProcessor/native/unittests/RCcontrollerUnitTests.cpp index 02b98e5e33..38c1377b46 100644 --- a/Code/Tools/AssetProcessor/native/unittests/RCcontrollerUnitTests.cpp +++ b/Code/Tools/AssetProcessor/native/unittests/RCcontrollerUnitTests.cpp @@ -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 diff --git a/Code/Tools/AssetProcessor/native/unittests/UtilitiesUnitTests.cpp b/Code/Tools/AssetProcessor/native/unittests/UtilitiesUnitTests.cpp index 1eada874d9..24433b550c 100644 --- a/Code/Tools/AssetProcessor/native/unittests/UtilitiesUnitTests.cpp +++ b/Code/Tools/AssetProcessor/native/unittests/UtilitiesUnitTests.cpp @@ -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) diff --git a/Code/Tools/AssetProcessor/native/utilities/assetUtils.cpp b/Code/Tools/AssetProcessor/native/utilities/assetUtils.cpp index b152e5ed4f..19423358b5 100644 --- a/Code/Tools/AssetProcessor/native/utilities/assetUtils.cpp +++ b/Code/Tools/AssetProcessor/native/utilities/assetUtils.cpp @@ -507,8 +507,13 @@ namespace AssetUtilities return QString::fromUtf8(s_projectName.c_str(), aznumeric_cast(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 diff --git a/Code/Tools/AssetProcessor/native/utilities/assetUtils.h b/Code/Tools/AssetProcessor/native/utilities/assetUtils.h index 201911eb96..694498e423 100644 --- a/Code/Tools/AssetProcessor/native/utilities/assetUtils.h +++ b/Code/Tools/AssetProcessor/native/utilities/assetUtils.h @@ -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());