From b2bafc44aba56d80868b9976b10d04a7312d72bf Mon Sep 17 00:00:00 2001 From: Steve Pham <82231385+spham-amzn@users.noreply.github.com> Date: Thu, 10 Jun 2021 11:04:18 -0700 Subject: [PATCH] LYN-2705: Remove 'AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS' trait for Linux (#1235) * Fix Delete_Real_Readonly_Fails to mark parent directory as read-only as well Read-only files in Windows cannot be deleted. The previous version of this code relied on that fact, and would attempt to delete a file even when `skipReadOnly = true`, relying on the OS to refuse to delete the file if it is read only. On Linux, it is the writable state of the *directory* that determines if a file can be deleted or not. This fixes the test to set up the correct situation where a file deletion would fail. * Remove excluded items from a vector before iterating over it Removing items from the `pathMatches` `QStringList` while iterating over it was causing a segfault on Linux. This change separates out the item removal from the item iteration, which allows the item iteration loop to use a range-for loop instead of directly manipulating iterators. * Remove invalid test that asserts a file's metadata file can have differing file casing This test is asserting that a given source file and its accompanying metadata file can have the the same name but differing case. This is really testing whether or not the underlying filesystem that those files live on is case sensitive or not. The 99% chance is that users are using the default filesystem that their host OS gives them, NTFS on Windows, EXT* on Linux, and APFS on Mac. Even though NTFS is case-insensitive by default, it [can be configured per-directory](https://devblogs.microsoft.com/commandline/improved-per-directory-case-sensitivity-support-in-wsl/). APFS as well can be configured to be case-sensitive. For users with case sensitive filesystems, this test makes no sense. We could extend this test to inspect the case-sensitivity of the underlying filesystem, but then it is just testing the filesystem's behavior, which seems out of scope of this test. * Use a non-priviliged port for the Asset Processor tests From https://www.w3.org/Daemon/User/Installation/PrivilegedPorts.html: > The TCP/IP port numbers below 1024 are special in that normal users are not > allowed to run servers on them. This is a security feaure, in that if you > connect to a service on one of these ports you can be fairly sure that you > have the real thing, and not a fake which some hacker has put up for you. > > When you run a server as a test from a non-priviliged account, you will > normally test it on other ports, such as 2784, 5000, 8001 or 8080. * Fix for `QDir::rmdir(".")` not working in Linux Qt uses `::rmdir` to remove directories on Linux. This comes from [unistd.h](https://pubs.opengroup.org/onlinepubs/007904875/functions/rmdir.html) The documentation for that function states: > If the path argument refers to a path whose final component is either dot > or dot-dot, rmdir() shall fail. So calling `dir.rmdir(".")` will never work on Linux. Instead, get the parent directory, and remove the child directory by name. * Avoid lowercasing source asset paths when resolving dependencies Source asset paths may be case sensitive, so their case must be preserved when doing operations that hit the underlying filesystem. This method was always lowercasing them, which would cause dependencies to not be found. * Correct test to expect product filenames to be lowercase The modtime tests were failing in Linux due to something unrelated to file modtime checking. The Asset Processor Manager does this during AnalyzeJob: ``` if (foundInDatabase && jobs[0].m_fingerprint == jobDetails.m_jobEntry.m_computedFingerprint) { // If the fingerprint hasn't changed, we won't process it.. unless...is it missing a product. ``` In this case, the test was setting up a product whose file case was the same as the source asset, and would write it to the cache dir using mixed case, but use the normal asset processor API to write the product file path to the database, which recorded the path in lowercase. When the manager then went to check if the source asset's products all exist, it checked the lowercase path, which didn't exist. This fixes that test failure, by updating the test to write the product file to the cache using the proper lowercased path. * Update test to define a "not current platform" for Linux This test was failing because it was setting some "not current platform" variable to be set to "pc" on Linux, when `AssetSystem::GetHostAssetPlatform()` is defined to: ```cpp inline const char* GetHostAssetPlatform() { return "mac"; return "pc"; // set this to pc because that's what bootstrap.cfg currently defines the platform to "pc", even on Linux return "pc"; #error Unimplemented Host Asset Platform } ``` The test would go on to assert that "pc" was simultaneously in a list and not in the same list. This fixes the test by updating the code to set the "not the current platform" variable appropriately on Linux. The expectations were also updated to improve the output on test failure. Instead of this: ``` Value of: recogs["rend"].m_platformSpecs.contains(platformWhichIsNotCurrentPlatform) Actual: true Expected: false ``` You now get this: ``` Value of: recogs["rend"].m_platformSpecs.keys() Expected: (has 3 elements and there exists some permutation of elements such that: - element #0 is equal to pc, and - element #1 is equal to es3, and - element #2 is equal to server) and (doesn't contain any element that is equal to pc) Actual: { pc, server, es3 } (of type QList), whose element #0 matches ``` * Prevent windows supported path separators to be included in the test paths for UpdateToCorrectCase_ExistingFile_ReturnsTrue_CorrectsCase * Fix failing linux unit test "PlatformConfigurationUnitTests.TestFailReadConfigFile_RegularScanfolder" caused by static variable not being reset from a different test run when using AssetUtilities::ComputeProjectPath * Fix AZ_RTTI declaration for RequestEscalateAsset Message * Implement FileWatcher for Linux to fix AssetProcessorMessages.All test (RequestAssetStatus) * Split AssetProcessorMessages into 2 tests, one with RequestAssetStatus/ResponseAssetStatus and one without Add The RequestAssetStatus/ResponseAssetStatus as a sandbox test because it relies on FileWatcher thread and seems to be timing related * Remove FileWatcher_win.cpp from the Linux specific folder for FileWatcher * - Fix build error related to non-unity builds - Fixed failed linux test 'Test/LegacyTestAdapter.AllTests/UtilitiesUnitTest' caused by misplaced windows only EXPECT - Remove test trait AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS for linux to expose remaining failed tests * Fixed failed linux test 'Test/LegacyTestAdapter.AllTests/RCcontrollerUnitTests' caused by misplaced windows only EXPECT * - Fix FileWatcher unit test, disable incompatible subtests for Linux - Fix errors in FileWatcher_linux from results of the FileWatcher Unit Test * Remove AZ::AssetProcessor.Tests.Sandbox tests from definition and restore the original AssetProcessorMessages.All tests now that Filewatcher_linux was fixed * Fixes for failed unit tests: AssetProcessorManagerUnitTests and AssetProcessorManagerUnitTests_JobDependencies_Fingerprint - Caused by differences between between case-sensitive files (Linux) and non-case-sensitive Filesystems (Windows) * Update consts in FileWatcher_linux.cpp to constexpr * Fixes related to PR comment suggestions * - Removed std::bind and replaced with lambda in FileWatcher_linux - Replaced String replace functions for path separators to use AZ::IO::Path::LexicallyNormal() instead * Restoring string replace function in PathDependencyManager::ResolveDependencies due to unit test failure Co-authored-by: Chris Burel --- .../Asset/AssetProcessorMessages.h | 2 +- .../Platform/Linux/AzTest_Traits_Linux.h | 2 +- Code/Tools/AssetProcessor/CMakeLists.txt | 3 +- .../native/FileWatcher/FileWatcher_linux.cpp | 197 +++++++++++++++++- .../native/FileWatcher/FileWatcher_win.cpp | 134 ------------ .../AssetManager/PathDependencyManager.cpp | 6 +- .../AssetManager/SourceFileRelocator.cpp | 57 +++-- .../AssetManager/assetProcessorManager.cpp | 5 +- .../tests/AssetProcessorMessagesTests.cpp | 15 +- .../native/tests/SourceFileRelocatorTests.cpp | 58 +++--- .../AssetProcessorManagerTest.cpp | 44 +--- .../platformconfigurationtests.cpp | 94 ++++----- .../native/tests/utilities/assetUtilsTest.cpp | 3 + .../AssetProcessorManagerUnitTests.cpp | 44 +++- .../native/unittests/FileWatcherUnitTests.cpp | 21 +- .../unittests/RCcontrollerUnitTests.cpp | 5 +- .../native/unittests/UtilitiesUnitTests.cpp | 3 +- .../native/utilities/assetUtils.cpp | 7 +- .../native/utilities/assetUtils.h | 3 +- 19 files changed, 375 insertions(+), 328 deletions(-) delete mode 100644 Code/Tools/AssetProcessor/Platform/Linux/native/FileWatcher/FileWatcher_win.cpp 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());