b2bafc44ab
* 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<QString>), 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 <burelc@amazon.com>
630 lines
28 KiB
C++
630 lines
28 KiB
C++
/*
|
|
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
|
* its licensors.
|
|
*
|
|
* For complete copyright and license terms please see the LICENSE at the root of this
|
|
* distribution (the "License"). All use of this software is governed by the License,
|
|
* or, if provided, by the license below or the license accompanying this file. Do not
|
|
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
*
|
|
*/
|
|
|
|
#include <QHash>
|
|
|
|
#include "native/tests/AssetProcessorTest.h"
|
|
#include <native/utilities/PlatformConfiguration.h>
|
|
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
|
#include <AzCore/std/parallel/thread.h>
|
|
#include <AzTest/AzTest.h>
|
|
|
|
|
|
using namespace AssetUtilities;
|
|
|
|
class AssetUtilitiesTest
|
|
: public AssetProcessor::AssetProcessorTest
|
|
{
|
|
void SetUp() override
|
|
{
|
|
AssetProcessorTest::SetUp();
|
|
|
|
if (AZ::IO::FileIOBase::GetInstance() == nullptr)
|
|
{
|
|
m_localFileIo = aznew AZ::IO::LocalFileIO();
|
|
AZ::IO::FileIOBase::SetInstance(m_localFileIo);
|
|
}
|
|
}
|
|
|
|
void TearDown() override
|
|
{
|
|
if (m_localFileIo)
|
|
{
|
|
delete m_localFileIo;
|
|
m_localFileIo = nullptr;
|
|
AZ::IO::FileIOBase::SetInstance(nullptr);
|
|
}
|
|
|
|
AssetProcessorTest::TearDown();
|
|
}
|
|
|
|
AZ::IO::FileIOBase* m_localFileIo{};
|
|
};
|
|
|
|
TEST_F(AssetUtilitiesTest, NormlizeFilePath_NormalizedValidPathRelPath_Valid)
|
|
{
|
|
QString result = NormalizeFilePath("a/b\\c\\d/E.txt");
|
|
EXPECT_STREQ(result.toUtf8().constData(), "a/b/c/d/E.txt");
|
|
}
|
|
|
|
TEST_F(AssetUtilitiesTest, NormlizeFilePath_NormalizedValidPathFullPath_Valid)
|
|
{
|
|
QString result = NormalizeFilePath("c:\\a/b\\c\\d/E.txt");
|
|
// on windows, drive letters are normalized to full
|
|
#if defined(AZ_PLATFORM_WINDOWS)
|
|
ASSERT_TRUE(result.compare("C:/a/b/c/d/E.txt", Qt::CaseSensitive) == 0);
|
|
#else
|
|
// on other platforms, C: is a relative path to a file called 'c:')
|
|
EXPECT_STREQ(result.toUtf8().constData(), "c:/a/b/c/d/E.txt");
|
|
#endif
|
|
}
|
|
|
|
TEST_F(AssetUtilitiesTest, NormlizeFilePath_NormalizedValidDirRelPath_Valid)
|
|
{
|
|
QString result = NormalizeDirectoryPath("a/b\\c\\D");
|
|
EXPECT_STREQ(result.toUtf8().constData(), "a/b/c/D");
|
|
}
|
|
|
|
TEST_F(AssetUtilitiesTest, NormlizeFilePath_NormalizedValidDirFullPath_Valid)
|
|
{
|
|
QString result = NormalizeDirectoryPath("c:\\a/b\\C\\d\\");
|
|
|
|
// on windows, drive letters are normalized to full
|
|
#if defined(AZ_PLATFORM_WINDOWS)
|
|
EXPECT_STREQ(result.toUtf8().constData(), "C:/a/b/C/d");
|
|
#else
|
|
EXPECT_STREQ(result.toUtf8().constData(), "c:/a/b/C/d");
|
|
#endif
|
|
}
|
|
|
|
TEST_F(AssetUtilitiesTest, ComputeCRC32Lowercase_IsCaseInsensitive)
|
|
{
|
|
const char* upperCaseString = "HELLOworld";
|
|
const char* lowerCaseString = "helloworld";
|
|
|
|
EXPECT_EQ(AssetUtilities::ComputeCRC32Lowercase(lowerCaseString), AssetUtilities::ComputeCRC32Lowercase(upperCaseString));
|
|
|
|
// also try the length-based one.
|
|
EXPECT_EQ(AssetUtilities::ComputeCRC32Lowercase(lowerCaseString, size_t(5)), AssetUtilities::ComputeCRC32Lowercase(upperCaseString, size_t(5)));
|
|
}
|
|
|
|
TEST_F(AssetUtilitiesTest, ComputeCRC32_IsCaseSensitive)
|
|
{
|
|
const char* upperCaseString = "HELLOworld";
|
|
const char* lowerCaseString = "helloworld";
|
|
|
|
EXPECT_NE(AssetUtilities::ComputeCRC32(lowerCaseString), AssetUtilities::ComputeCRC32(upperCaseString));
|
|
|
|
// also try the length-based one.
|
|
EXPECT_NE(AssetUtilities::ComputeCRC32(lowerCaseString, size_t(5)), AssetUtilities::ComputeCRC32(upperCaseString, size_t(5)));
|
|
}
|
|
|
|
|
|
TEST_F(AssetUtilitiesTest, UpdateToCorrectCase_MissingFile_ReturnsFalse)
|
|
{
|
|
QTemporaryDir dir;
|
|
QDir tempPath(dir.path());
|
|
QString canonicalTempDirPath = AssetUtilities::NormalizeDirectoryPath(tempPath.canonicalPath());
|
|
UnitTestUtils::ScopedDir changeDir(canonicalTempDirPath);
|
|
tempPath = QDir(canonicalTempDirPath);
|
|
|
|
QString fileName = "someFile.txt";
|
|
EXPECT_FALSE(AssetUtilities::UpdateToCorrectCase(canonicalTempDirPath, fileName));
|
|
}
|
|
|
|
#if AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
|
|
TEST_F(AssetUtilitiesTest, DISABLED_UpdateToCorrectCase_ExistingFile_ReturnsTrue_CorrectsCase)
|
|
#else
|
|
TEST_F(AssetUtilitiesTest, UpdateToCorrectCase_ExistingFile_ReturnsTrue_CorrectsCase)
|
|
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_PROCESSOR_TESTS
|
|
{
|
|
QTemporaryDir dir;
|
|
QDir tempPath(dir.path());
|
|
QString canonicalTempDirPath = AssetUtilities::NormalizeDirectoryPath(tempPath.canonicalPath());
|
|
UnitTestUtils::ScopedDir changeDir(canonicalTempDirPath);
|
|
tempPath = QDir(canonicalTempDirPath);
|
|
|
|
QStringList thingsToTry;
|
|
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";
|
|
thingsToTry << "specialFileName#.txt";
|
|
thingsToTry << "specialFileName$.txt";
|
|
thingsToTry << "specialFile%Name%.txt";
|
|
thingsToTry << "specialFileName&.txt";
|
|
thingsToTry << "specialFileName(.txt";
|
|
thingsToTry << "specialFileName+.txt";
|
|
thingsToTry << "specialFileName[9].txt";
|
|
thingsToTry << "specialFileName[A-Za-z].txt"; // these should all be treated as literally the name of the file, not a regex!
|
|
|
|
for (QString triedThing : thingsToTry)
|
|
{
|
|
triedThing = NormalizeFilePath(triedThing);
|
|
EXPECT_TRUE(UnitTestUtils::CreateDummyFile(tempPath.absoluteFilePath(triedThing)));
|
|
|
|
QString lowercaseVersion = triedThing.toLower();
|
|
// each one should be found. If it fails, we'll pipe out the name of the file it fails on for extra context.
|
|
|
|
EXPECT_TRUE(AssetUtilities::UpdateToCorrectCase(canonicalTempDirPath, lowercaseVersion)) << "File being Examined: " << lowercaseVersion.toUtf8().constData();
|
|
// each one should correct, and return a normalized path.
|
|
EXPECT_STREQ(AssetUtilities::NormalizeFilePath(lowercaseVersion).toUtf8().constData(), AssetUtilities::NormalizeFilePath(triedThing).toUtf8().constData());
|
|
}
|
|
}
|
|
|
|
TEST_F(AssetUtilitiesTest, GenerateFingerprint_BasicTest)
|
|
{
|
|
QTemporaryDir dir;
|
|
QDir tempPath(dir.path());
|
|
QString canonicalTempDirPath = AssetUtilities::NormalizeDirectoryPath(tempPath.canonicalPath());
|
|
UnitTestUtils::ScopedDir changeDir(canonicalTempDirPath);
|
|
tempPath = QDir(canonicalTempDirPath);
|
|
QString absoluteTestFilePath1 = tempPath.absoluteFilePath("basicfile.txt");
|
|
QString absoluteTestFilePath2 = tempPath.absoluteFilePath("basicfile2.txt");
|
|
EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFilePath1, "contents"));
|
|
EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFilePath2, "contents"));
|
|
|
|
AZStd::string fileEncoded1 = absoluteTestFilePath1.toUtf8().constData();
|
|
AZStd::string fileEncoded2 = absoluteTestFilePath2.toUtf8().constData();
|
|
|
|
AssetProcessor::JobDetails jobDetail;
|
|
// it is expected that the only parts of jobDetails that matter are:
|
|
// jobDetail.m_extraInformationForFingerprinting
|
|
// jobDetail.m_fingerprintFiles
|
|
// jobDetail.m_jobDependencyList
|
|
|
|
jobDetail.m_extraInformationForFingerprinting = "extra info1";
|
|
// the fingerprint should always be stable over repeated runs, even with minimal info:
|
|
unsigned int result1 = AssetUtilities::GenerateFingerprint(jobDetail);
|
|
unsigned int result2 = AssetUtilities::GenerateFingerprint(jobDetail);
|
|
EXPECT_EQ(result1, result2);
|
|
|
|
// the fingerprint should always be different when anything changes:
|
|
jobDetail.m_extraInformationForFingerprinting = "extra info1";
|
|
result1 = AssetUtilities::GenerateFingerprint(jobDetail);
|
|
jobDetail.m_extraInformationForFingerprinting = "extra info2";
|
|
result2 = AssetUtilities::GenerateFingerprint(jobDetail);
|
|
EXPECT_NE(result1, result2);
|
|
|
|
jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(absoluteTestFilePath1.toUtf8().constData(), "basicfile.txt"));
|
|
result1 = AssetUtilities::GenerateFingerprint(jobDetail);
|
|
result2 = AssetUtilities::GenerateFingerprint(jobDetail);
|
|
EXPECT_EQ(result1, result2);
|
|
|
|
// mutating the dependency list should mutate the fingerprint, even if the extra info doesn't change.
|
|
jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(absoluteTestFilePath2.toUtf8().constData(), "basicfile2.txt"));
|
|
result2 = AssetUtilities::GenerateFingerprint(jobDetail);
|
|
EXPECT_NE(result1, result2);
|
|
|
|
UnitTestUtils::SleepForMinimumFileSystemTime();
|
|
|
|
// mutating the actual files should mutate the fingerprint, even if the file list doesn't change.
|
|
// note that both files are in the file list, so changing just the one should result in a change in hash:
|
|
EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFilePath1, "contents new"));
|
|
result1 = AssetUtilities::GenerateFingerprint(jobDetail);
|
|
EXPECT_NE(result1, result2);
|
|
|
|
// changing the other should also change the hash:
|
|
EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFilePath2, "contents new2"));
|
|
result2 = AssetUtilities::GenerateFingerprint(jobDetail);
|
|
EXPECT_NE(result1, result2);
|
|
}
|
|
|
|
TEST_F(AssetUtilitiesTest, GenerateFingerprint_Empty_Asserts)
|
|
{
|
|
AssetProcessor::JobDetails jobDetail;
|
|
AssetUtilities::GenerateFingerprint(jobDetail);
|
|
|
|
EXPECT_EQ(m_errorAbsorber->m_numAssertsAbsorbed, 1);
|
|
m_errorAbsorber->Clear();
|
|
}
|
|
|
|
TEST_F(AssetUtilitiesTest, GenerateFingerprint_MissingFile_NotSameAsZeroByteFile)
|
|
{
|
|
QTemporaryDir dir;
|
|
QDir tempPath(dir.path());
|
|
QString canonicalTempDirPath = AssetUtilities::NormalizeDirectoryPath(tempPath.canonicalPath());
|
|
UnitTestUtils::ScopedDir changeDir(canonicalTempDirPath);
|
|
tempPath = QDir(canonicalTempDirPath);
|
|
QString absoluteTestFilePath1 = tempPath.absoluteFilePath("basicfile.txt");
|
|
QString absoluteTestFilePath2 = tempPath.absoluteFilePath("basicfile2.txt");
|
|
|
|
EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFilePath1, "")); // empty file
|
|
// note: basicfile1 exists but is empty, whereas basicfile2, 3 are missing entirely.
|
|
|
|
AssetProcessor::JobDetails jobDetail;
|
|
jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile.txt").toUtf8().constData(), "basicfile.txt"));
|
|
AZ::u32 fingerprint1 = AssetUtilities::GenerateFingerprint(jobDetail);
|
|
|
|
jobDetail.m_fingerprintFiles.clear();
|
|
jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile2.txt").toUtf8().constData(), "basicfile2.txt"));
|
|
AZ::u32 fingerprint2 = AssetUtilities::GenerateFingerprint(jobDetail);
|
|
|
|
EXPECT_NE(fingerprint1, fingerprint2);
|
|
}
|
|
|
|
TEST_F(AssetUtilitiesTest, GenerateFingerprint_MissingFile_NotSameAsOtherMissingFile)
|
|
{
|
|
QTemporaryDir dir;
|
|
QDir tempPath(dir.path());
|
|
QString canonicalTempDirPath = AssetUtilities::NormalizeDirectoryPath(tempPath.canonicalPath());
|
|
UnitTestUtils::ScopedDir changeDir(canonicalTempDirPath);
|
|
tempPath = QDir(canonicalTempDirPath);
|
|
QString absoluteTestFilePath1 = tempPath.absoluteFilePath("basicfile.txt");
|
|
QString absoluteTestFilePath2 = tempPath.absoluteFilePath("basicfile2.txt");
|
|
|
|
// we create no files on disk.
|
|
|
|
AssetProcessor::JobDetails jobDetail;
|
|
jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile.txt").toUtf8().constData(), "basicfile.txt"));
|
|
AZ::u32 fingerprint1 = AssetUtilities::GenerateFingerprint(jobDetail);
|
|
|
|
jobDetail.m_fingerprintFiles.clear();
|
|
jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile2.txt").toUtf8().constData(), "basicfile2.txt"));
|
|
AZ::u32 fingerprint2 = AssetUtilities::GenerateFingerprint(jobDetail);
|
|
|
|
EXPECT_NE(fingerprint1, fingerprint2);
|
|
}
|
|
|
|
TEST_F(AssetUtilitiesTest, GenerateFingerprint_OneFile_Differs)
|
|
{
|
|
// this test makes sure that changing each part of jobDetail relevant to fingerprints causes the resulting fingerprint to change.
|
|
QTemporaryDir dir;
|
|
QDir tempPath(dir.path());
|
|
QString canonicalTempDirPath = AssetUtilities::NormalizeDirectoryPath(tempPath.canonicalPath());
|
|
UnitTestUtils::ScopedDir changeDir(canonicalTempDirPath);
|
|
tempPath = QDir(canonicalTempDirPath);
|
|
QString absoluteTestFilePath1 = tempPath.absoluteFilePath("basicfile.txt");
|
|
QString absoluteTestFilePath2 = tempPath.absoluteFilePath("basicfile2.txt");
|
|
QString absoluteTestFilePath3 = tempPath.absoluteFilePath("basicfile3.txt");
|
|
|
|
EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFilePath1, "contents"));
|
|
EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFilePath2, "contents")); // same contents
|
|
EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFilePath3, "contents2")); // different contents
|
|
|
|
AssetProcessor::JobDetails jobDetail;
|
|
jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile.txt").toUtf8().constData(), "basicfile.txt"));
|
|
|
|
AZ::u32 fingerprint1 = AssetUtilities::GenerateFingerprint(jobDetail);
|
|
|
|
jobDetail.m_fingerprintFiles.clear();
|
|
jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile2.txt").toUtf8().constData(), "basicfile2.txt"));
|
|
AZ::u32 fingerprint2 = AssetUtilities::GenerateFingerprint(jobDetail);
|
|
|
|
jobDetail.m_fingerprintFiles.clear();
|
|
jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile3.txt").toUtf8().constData(), "basicfile3.txt"));
|
|
AZ::u32 fingerprint3 = AssetUtilities::GenerateFingerprint(jobDetail);
|
|
|
|
jobDetail.m_fingerprintFiles.clear();
|
|
jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile.txt").toUtf8().constData(), "basicfile.txt"));
|
|
EXPECT_EQ(AssetUtilities::GenerateFingerprint(jobDetail), fingerprint1);
|
|
|
|
jobDetail.m_fingerprintFiles.clear();
|
|
jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile2.txt").toUtf8().constData(), "basicfile2.txt"));
|
|
EXPECT_EQ(AssetUtilities::GenerateFingerprint(jobDetail), fingerprint2);
|
|
|
|
jobDetail.m_fingerprintFiles.clear();
|
|
jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile3.txt").toUtf8().constData(), "basicfile3.txt"));
|
|
EXPECT_EQ(AssetUtilities::GenerateFingerprint(jobDetail), fingerprint3);
|
|
|
|
EXPECT_NE(fingerprint1, fingerprint2);
|
|
EXPECT_NE(fingerprint2, fingerprint3);
|
|
EXPECT_NE(fingerprint3, fingerprint1);
|
|
}
|
|
|
|
TEST_F(AssetUtilitiesTest, GenerateFingerprint_MultipleFile_Differs)
|
|
{
|
|
// given multiple files, make sure that the fingerprint for multiple files differs from the one file (that each file is taken into account)
|
|
QTemporaryDir dir;
|
|
QDir tempPath(dir.path());
|
|
QString canonicalTempDirPath = AssetUtilities::NormalizeDirectoryPath(tempPath.canonicalPath());
|
|
UnitTestUtils::ScopedDir changeDir(canonicalTempDirPath);
|
|
tempPath = QDir(canonicalTempDirPath);
|
|
QString absoluteTestFilePath1 = tempPath.absoluteFilePath("basicfile.txt");
|
|
QString absoluteTestFilePath2 = tempPath.absoluteFilePath("basicfile2.txt");
|
|
QString absoluteTestFilePath3 = tempPath.absoluteFilePath("basicfile3.txt");
|
|
|
|
EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFilePath1, "contents"));
|
|
EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFilePath2, "contents")); // same contents
|
|
EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFilePath3, "contents2")); // different contents
|
|
|
|
AssetProcessor::JobDetails jobDetail;
|
|
jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile.txt").toUtf8().constData(), "basicfile.txt"));
|
|
AZ::u32 fingerprint1 = AssetUtilities::GenerateFingerprint(jobDetail);
|
|
jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile2.txt").toUtf8().constData(), "basicfile2.txt"));
|
|
AZ::u32 fingerprint2 = AssetUtilities::GenerateFingerprint(jobDetail);
|
|
jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile3.txt").toUtf8().constData(), "basicfile3.txt"));
|
|
AZ::u32 fingerprint3 = AssetUtilities::GenerateFingerprint(jobDetail);
|
|
|
|
EXPECT_NE(fingerprint1, fingerprint2);
|
|
EXPECT_NE(fingerprint2, fingerprint3);
|
|
EXPECT_NE(fingerprint3, fingerprint1);
|
|
|
|
jobDetail.m_fingerprintFiles.clear();
|
|
jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile.txt").toUtf8().constData(), "basicfile.txt"));
|
|
jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile2.txt").toUtf8().constData(), "basicfile2.txt"));
|
|
fingerprint1 = AssetUtilities::GenerateFingerprint(jobDetail);
|
|
|
|
jobDetail.m_fingerprintFiles.clear();
|
|
jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile2.txt").toUtf8().constData(), "basicfile2.txt"));
|
|
jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile3.txt").toUtf8().constData(), "basicfile3.txt"));
|
|
fingerprint2 = AssetUtilities::GenerateFingerprint(jobDetail);
|
|
|
|
jobDetail.m_fingerprintFiles.clear();
|
|
jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile1.txt").toUtf8().constData(), "basicfile1.txt"));
|
|
jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile3.txt").toUtf8().constData(), "basicfile3.txt"));
|
|
fingerprint3 = AssetUtilities::GenerateFingerprint(jobDetail);
|
|
|
|
EXPECT_NE(fingerprint1, fingerprint2);
|
|
EXPECT_NE(fingerprint2, fingerprint3);
|
|
EXPECT_NE(fingerprint3, fingerprint1);
|
|
}
|
|
|
|
TEST_F(AssetUtilitiesTest, GenerateFingerprint_OrderOnceJobDependency_NoChange)
|
|
{
|
|
// OrderOnce Job dependency should not alter the fingerprint of the job
|
|
QTemporaryDir dir;
|
|
QDir tempPath(dir.path());
|
|
QString canonicalTempDirPath = AssetUtilities::NormalizeDirectoryPath(tempPath.canonicalPath());
|
|
UnitTestUtils::ScopedDir changeDir(canonicalTempDirPath);
|
|
tempPath = QDir(canonicalTempDirPath);
|
|
const char relFile1Path[] = "file.txt";
|
|
const char relFile2Path[] = "secondFile.txt";
|
|
QString absoluteTestFile1Path = tempPath.absoluteFilePath(relFile1Path);
|
|
QString absoluteTestFile2Path = tempPath.absoluteFilePath(relFile2Path);
|
|
|
|
EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFile1Path, "contents"));
|
|
EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFile2Path, "contents"));
|
|
|
|
AssetProcessor::JobDetails jobDetail;
|
|
|
|
jobDetail.m_jobEntry.m_databaseSourceName = relFile1Path;
|
|
jobDetail.m_jobEntry.m_watchFolderPath = tempPath.absolutePath();
|
|
jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(absoluteTestFile1Path.toUtf8().constData(), relFile1Path));
|
|
|
|
AZ::u32 fingerprintWithoutOrderOnceJobDependency = AssetUtilities::GenerateFingerprint(jobDetail);
|
|
|
|
AssetBuilderSDK::SourceFileDependency dep = { relFile2Path, AZ::Uuid::CreateNull() };
|
|
AssetBuilderSDK::JobDependency jobDep("key", "pc", AssetBuilderSDK::JobDependencyType::OrderOnce, dep);
|
|
jobDetail.m_jobDependencyList.push_back(AssetProcessor::JobDependencyInternal(jobDep));
|
|
|
|
AZ::u32 fingerprintWithOrderOnceJobDependency = AssetUtilities::GenerateFingerprint(jobDetail);
|
|
|
|
EXPECT_EQ(fingerprintWithoutOrderOnceJobDependency, fingerprintWithOrderOnceJobDependency);
|
|
}
|
|
|
|
namespace AssetUtilsTest
|
|
{
|
|
class MockJobDependencyResponder : public AssetProcessor::ProcessingJobInfoBus::Handler
|
|
{
|
|
public:
|
|
MOCK_METHOD1(GetJobFingerprint, AZ::u32(const AssetProcessor::JobIndentifier&));
|
|
};
|
|
}
|
|
|
|
TEST_F(AssetUtilitiesTest, GenerateFingerprint_GivenJobDependencies_AffectsOutcome)
|
|
{
|
|
using namespace testing;
|
|
using ::testing::NiceMock;
|
|
|
|
NiceMock<AssetUtilsTest::MockJobDependencyResponder> responder;
|
|
|
|
responder.BusConnect();
|
|
|
|
QTemporaryDir dir;
|
|
QDir tempPath(dir.path());
|
|
QString canonicalTempDirPath = AssetUtilities::NormalizeDirectoryPath(tempPath.canonicalPath());
|
|
UnitTestUtils::ScopedDir changeDir(canonicalTempDirPath);
|
|
tempPath = QDir(canonicalTempDirPath);
|
|
QString absoluteTestFilePath1 = tempPath.absoluteFilePath("basicfile.txt");
|
|
|
|
AssetProcessor::JobDetails jobDetail;
|
|
jobDetail.m_fingerprintFiles.insert(AZStd::make_pair(tempPath.absoluteFilePath("basicfile.txt").toUtf8().constData(), "basicfile.txt"));
|
|
AZ::u32 fingerprint1 = AssetUtilities::GenerateFingerprint(jobDetail);
|
|
|
|
// add a job dependency - it should alter the fingerprint, even if the file does not exist.
|
|
AssetBuilderSDK::JobDependency jobDep("thing", "pc", AssetBuilderSDK::JobDependencyType::Order, AssetBuilderSDK::SourceFileDependency("basicfile2.txt", AZ::Uuid::CreateNull()));
|
|
AssetProcessor::JobDependencyInternal internalJobDep(jobDep);
|
|
internalJobDep.m_builderUuidList.insert(AZ::Uuid::CreateRandom());
|
|
jobDetail.m_jobDependencyList.push_back(internalJobDep);
|
|
|
|
EXPECT_CALL(responder, GetJobFingerprint(_))
|
|
.WillOnce(
|
|
Return(0x12341234));
|
|
|
|
AZ::u32 fingerprint2 = AssetUtilities::GenerateFingerprint(jobDetail);
|
|
|
|
// different job fingerprint -> different result
|
|
EXPECT_CALL(responder, GetJobFingerprint(_))
|
|
.WillOnce(
|
|
Return(0x11111111));
|
|
|
|
AZ::u32 fingerprint3 = AssetUtilities::GenerateFingerprint(jobDetail);
|
|
|
|
EXPECT_NE(fingerprint1, fingerprint2);
|
|
EXPECT_NE(fingerprint2, fingerprint3);
|
|
EXPECT_NE(fingerprint3, fingerprint1);
|
|
}
|
|
|
|
TEST_F(AssetUtilitiesTest, GetFileFingerprint_BasicTest)
|
|
{
|
|
QTemporaryDir dir;
|
|
QDir tempPath(dir.path());
|
|
QString canonicalTempDirPath = AssetUtilities::NormalizeDirectoryPath(tempPath.canonicalPath());
|
|
UnitTestUtils::ScopedDir changeDir(canonicalTempDirPath);
|
|
tempPath = QDir(canonicalTempDirPath);
|
|
QString absoluteTestFilePath1 = tempPath.absoluteFilePath("basicfile.txt");
|
|
QString absoluteTestFilePath2 = tempPath.absoluteFilePath("basicfile2.txt");
|
|
|
|
EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFilePath1, "contents"));
|
|
UnitTestUtils::SleepForMinimumFileSystemTime();
|
|
EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFilePath2, "contents2"));
|
|
|
|
AZStd::string fileEncoded1 = absoluteTestFilePath1.toUtf8().constData();
|
|
AZStd::string fileEncoded2 = absoluteTestFilePath2.toUtf8().constData();
|
|
|
|
// repeatedly hashing the same file should result in the same hash:
|
|
EXPECT_STREQ(AssetUtilities::GetFileFingerprint(fileEncoded1, "").c_str(), AssetUtilities::GetFileFingerprint(fileEncoded1, "").c_str());
|
|
EXPECT_STREQ(AssetUtilities::GetFileFingerprint(fileEncoded1, "Name").c_str(), AssetUtilities::GetFileFingerprint(fileEncoded1, "Name").c_str());
|
|
EXPECT_STREQ(AssetUtilities::GetFileFingerprint(fileEncoded2, "").c_str(), AssetUtilities::GetFileFingerprint(fileEncoded2, "").c_str());
|
|
EXPECT_STREQ(AssetUtilities::GetFileFingerprint(fileEncoded2, "Name").c_str(), AssetUtilities::GetFileFingerprint(fileEncoded2, "Name").c_str());
|
|
|
|
// mutating the 'name' should mutate the fingerprint:
|
|
EXPECT_STRNE(AssetUtilities::GetFileFingerprint(fileEncoded1, "").c_str(), AssetUtilities::GetFileFingerprint(fileEncoded1, "Name").c_str());
|
|
|
|
// two different files should not hash to the same fingerprint:
|
|
EXPECT_STRNE(AssetUtilities::GetFileFingerprint(fileEncoded1, "").c_str(), AssetUtilities::GetFileFingerprint(fileEncoded2, "").c_str());
|
|
|
|
UnitTestUtils::SleepForMinimumFileSystemTime();
|
|
|
|
AZStd::string oldFingerprint1 = AssetUtilities::GetFileFingerprint(fileEncoded1, "");
|
|
AZStd::string oldFingerprint2 = AssetUtilities::GetFileFingerprint(fileEncoded2, "");
|
|
AZStd::string oldFingerprint1a = AssetUtilities::GetFileFingerprint(fileEncoded1, "Name1");
|
|
AZStd::string oldFingerprint2a = AssetUtilities::GetFileFingerprint(fileEncoded2, "Name2");
|
|
|
|
EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFilePath1, "contents1a"));
|
|
EXPECT_TRUE(UnitTestUtils::CreateDummyFile(absoluteTestFilePath2, "contents2a"));
|
|
|
|
EXPECT_STRNE(oldFingerprint1.c_str(), AssetUtilities::GetFileFingerprint(fileEncoded1, "").c_str());
|
|
EXPECT_STRNE(oldFingerprint2.c_str(), AssetUtilities::GetFileFingerprint(fileEncoded2, "").c_str());
|
|
EXPECT_STRNE(oldFingerprint1a.c_str(), AssetUtilities::GetFileFingerprint(fileEncoded1, "Name1").c_str());
|
|
EXPECT_STRNE(oldFingerprint2a.c_str(), AssetUtilities::GetFileFingerprint(fileEncoded2, "Name2").c_str());
|
|
}
|
|
|
|
|
|
TEST_F(AssetUtilitiesTest, GetFileFingerprint_NonExistentFiles)
|
|
{
|
|
AZStd::string nonExistentFile1 = AZ::Uuid::CreateRandom().ToString<AZStd::string>() + ".txt";
|
|
ASSERT_FALSE(QFileInfo::exists(nonExistentFile1.c_str()));
|
|
|
|
EXPECT_STRNE(AssetUtilities::GetFileFingerprint(nonExistentFile1, "").c_str(), AssetUtilities::GetFileFingerprint(nonExistentFile1, "Name").c_str());
|
|
EXPECT_STREQ(AssetUtilities::GetFileFingerprint(nonExistentFile1, "Name").c_str(), AssetUtilities::GetFileFingerprint(nonExistentFile1, "Name").c_str());
|
|
}
|
|
|
|
TEST_F(AssetUtilitiesTest, GetServerAddress_ReadFromConfig_Valid)
|
|
{
|
|
QTemporaryDir tempDir;
|
|
QDir tempPath(tempDir.path());
|
|
QString assetServerAddress("T:/AssetServerCacheDummyFolder");
|
|
QString assetProcesorPlatformConfigPath = tempPath.absoluteFilePath("AssetProcessorPlatformConfig.ini");
|
|
UnitTestUtils::CreateDummyFile(assetProcesorPlatformConfigPath, QString("[Server]\ncacheServerAddress=%1\n").arg(assetServerAddress));
|
|
|
|
AssetUtilities::ResetAssetRoot();
|
|
QDir newRoot;
|
|
AssetUtilities::ComputeEngineRoot(newRoot, &tempPath);
|
|
|
|
auto settingsRegistry = AZ::SettingsRegistry::Get();
|
|
AZ::SettingsRegistryMergeUtils::ConfigParserSettings configParserSettings;
|
|
configParserSettings.m_registryRootPointerPath = AssetProcessor::AssetProcessorSettingsKey;
|
|
AssetProcessor::PlatformConfiguration::MergeConfigFileToSettingsRegistry(*settingsRegistry,
|
|
assetProcesorPlatformConfigPath.toUtf8().data());
|
|
QString assetServerAddressReturned = AssetUtilities::ServerAddress();
|
|
EXPECT_STREQ(assetServerAddressReturned.toUtf8().data(), assetServerAddress.toUtf8().data());
|
|
}
|
|
|
|
|
|
TEST_F(AssetUtilitiesTest, CreateDirWithTimeout_Valid)
|
|
{
|
|
QTemporaryDir tempDir;
|
|
QDir tempPath(tempDir.path());
|
|
QDir dir(tempPath.filePath("folder"));
|
|
unsigned int timeToWaitInSecs = 3;
|
|
AZStd::vector<AZStd::thread*> threadList;
|
|
AZStd::vector<bool> resultList;
|
|
AZStd::mutex resultMutex;
|
|
auto runFunc = [&]()
|
|
{
|
|
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(100));// sleeping to sync all the threads
|
|
bool result = AssetUtilities::CreateDirectoryWithTimeout(dir, timeToWaitInSecs);
|
|
AZStd::lock_guard<AZStd::mutex> locker(resultMutex);
|
|
resultList.push_back(result);
|
|
};
|
|
|
|
int numberOfThreads = 5;
|
|
|
|
ASSERT_FALSE(dir.exists());
|
|
|
|
for (int idx = 0; idx < numberOfThreads; idx++)
|
|
{
|
|
AZStd::thread* workerThread = new AZStd::thread(runFunc);
|
|
threadList.emplace_back(workerThread);
|
|
}
|
|
|
|
for (auto thread : threadList)
|
|
{
|
|
if (thread->joinable())
|
|
{
|
|
thread->join();
|
|
}
|
|
delete thread;
|
|
}
|
|
|
|
for (int idx = 0; idx < numberOfThreads; idx++)
|
|
{
|
|
ASSERT_TRUE(resultList[idx]);
|
|
}
|
|
|
|
ASSERT_TRUE(dir.exists());
|
|
}
|
|
|
|
TEST_F(AssetUtilitiesTest, CreateDir_InvalidDir_Timeout_Valid)
|
|
{
|
|
QDir dir(":\folder");
|
|
unsigned int timeToWaitInSecs = 1;
|
|
AZStd::vector<AZStd::thread*> threadList;
|
|
AZStd::vector<bool> resultList;
|
|
AZStd::mutex resultMutex;
|
|
auto runFunc = [&]()
|
|
{
|
|
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(100));// sleeping to sync all the threads
|
|
bool result = AssetUtilities::CreateDirectoryWithTimeout(dir, timeToWaitInSecs);
|
|
AZStd::lock_guard<AZStd::mutex> locker(resultMutex);
|
|
resultList.push_back(result);
|
|
};
|
|
|
|
int numberOfThreads = 5;
|
|
|
|
ASSERT_FALSE(dir.exists());
|
|
|
|
for (int idx = 0; idx < numberOfThreads; idx++)
|
|
{
|
|
AZStd::thread* workerThread = new AZStd::thread(runFunc);
|
|
threadList.emplace_back(workerThread);
|
|
}
|
|
|
|
for (auto thread : threadList)
|
|
{
|
|
if (thread->joinable())
|
|
{
|
|
thread->join();
|
|
}
|
|
delete thread;
|
|
}
|
|
|
|
for (int idx = 0; idx < numberOfThreads; idx++)
|
|
{
|
|
ASSERT_FALSE(resultList[idx]);
|
|
}
|
|
|
|
ASSERT_FALSE(dir.exists());
|
|
}
|
|
|