Merge branch 'upstream/development' into LYN-8514_AutomatedReviewServerLogChecks

This commit is contained in:
Gene Walters
2021-12-01 08:41:11 -08:00
46 changed files with 1209 additions and 403 deletions
@@ -325,13 +325,13 @@ namespace AZ
T& operator*() const
{
AZ_Assert(m_assetData, "Asset is not loaded");
AZ_Assert(m_assetData, "Asset %s (%s) is not loaded", m_assetId.ToString<AZStd::string>().c_str(), m_assetHint.c_str());
return *Get();
}
T* operator->() const
{
AZ_Assert(m_assetData, "Asset is not loaded");
AZ_Assert(m_assetData, "Asset %s (%s) is not loaded", m_assetId.ToString<AZStd::string>().c_str(), m_assetHint.c_str());
return Get();
}
+2 -2
View File
@@ -16,7 +16,7 @@
//
// When AZ_CRC("My string") is used by default it will map to AZ::Crc32("My string").
// We do have a pro-processor program which will precompute the crc for you and
// transform that macro to AZ_CRC("My string",0xabcdef00) this will expand to just 0xabcdef00.
// transform that macro to AZ_CRC("My string", 0x18fbd270) this will expand to just 0x18fbd270.
// This will remove completely the "My string" from your executable, it will add it to a database and so on.
// WHen you want to update the string, just change the string.
// If you don't run the precompile step the code should still run fine, except it will be slower,
@@ -24,7 +24,7 @@
// a constant expression.
// For example
// switch(id) {
// case AZ_CRC("My string",0xabcdef00): {} break; // this will compile fine
// case AZ_CRC("My string",0x18fbd270): {} break; // this will compile fine
// case AZ_CRC("My string"): {} break; // this will cause "error C2051: case expression not constant"
// }
// So it's you choice what you do, depending on your needs.
@@ -26,6 +26,7 @@
#include <AzCore/std/functional.h>
#include <AzCore/std/parallel/condition_variable.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/UnitTest/Mocks/MockFileIOBase.h>
#include <AZTestShared/Utils/Utils.h>
#include <Streamer/IStreamerMock.h>
#include <Tests/Asset/BaseAssetManagerTest.h>
@@ -131,8 +132,8 @@ namespace UnitTest
* This will test the aspect of the system where ObjectStreams and asset jobs loading dependent
* assets will do the work in their own thread.
*/
class AssetJobsFloodTest
: public BaseAssetManagerTest
class AssetJobsFloodTest : public DisklessAssetManagerBase
{
public:
TestAssetManager* m_testAssetManager{ nullptr };
@@ -183,15 +184,14 @@ namespace UnitTest
void SetUp() override
{
BaseAssetManagerTest::SetUp();
DisklessAssetManagerBase::SetUp();
SetupTest();
}
void TearDown() override
{
TearDownTest();
AssetManager::Destroy();
BaseAssetManagerTest::TearDown();
DisklessAssetManagerBase::TearDown();
}
void SetupAssets()
@@ -257,9 +257,9 @@ namespace UnitTest
AssetWithSerializedData ap2;
AssetWithSerializedData ap3;
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset4.txt", AZ::DataStream::ST_XML, &ap1, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset5.txt", AZ::DataStream::ST_XML, &ap2, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset6.txt", AZ::DataStream::ST_XML, &ap3, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset4.txt", &ap1, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset5.txt", &ap2, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset6.txt", &ap3, m_serializeContext));
AssetWithAssetReference assetWithPreload1;
AssetWithAssetReference assetWithPreload2;
@@ -273,11 +273,11 @@ namespace UnitTest
noLoadAsset.m_asset = m_testAssetManager->CreateAsset<AssetWithSerializedData>(MyAsset2Id, AssetLoadBehavior::NoLoad);
EXPECT_EQ(m_assetHandlerAndCatalog->m_numCreations, 4);
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset1.txt", AZ::DataStream::ST_XML, &assetWithPreload1, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset2.txt", AZ::DataStream::ST_XML, &assetWithPreload2, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset3.txt", AZ::DataStream::ST_XML, &assetWithPreload3, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "DelayLoadAsset.txt", AZ::DataStream::ST_XML, &delayedAsset, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "NoLoadAsset.txt", AZ::DataStream::ST_XML, &noLoadAsset, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset1.txt", &assetWithPreload1, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset2.txt", &assetWithPreload2, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset3.txt", &assetWithPreload3, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("DelayLoadAsset.txt", &delayedAsset, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("NoLoadAsset.txt", &noLoadAsset, m_serializeContext));
AssetWithQueueAndPreLoadReferences preLoadRoot;
AssetWithQueueAndPreLoadReferences preLoadA;
@@ -297,16 +297,16 @@ namespace UnitTest
preLoadBrokenA.m_preLoad = m_testAssetManager->CreateAsset<AssetWithAssetReference>(PreloadBrokenDepBId, AssetLoadBehavior::PreLoad);
preLoadBrokenB.m_preLoad = m_testAssetManager->CreateAsset<AssetWithAssetReference>(PreloadAssetNoDataId, AssetLoadBehavior::PreLoad);
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "PreLoadRoot.txt", AZ::DataStream::ST_XML, &preLoadRoot, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "PreLoadA.txt", AZ::DataStream::ST_XML, &preLoadA, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "PreLoadB.txt", AZ::DataStream::ST_XML, &noRefs, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "PreLoadC.txt", AZ::DataStream::ST_XML, &noRefs, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "QueueLoadA.txt", AZ::DataStream::ST_XML, &queueLoadA, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "QueueLoadB.txt", AZ::DataStream::ST_XML, &noRefs, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "QueueLoadC.txt", AZ::DataStream::ST_XML, &noRefs, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "PreLoadBrokenA.txt", AZ::DataStream::ST_XML, &preLoadBrokenA, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "PreLoadBrokenB.txt", AZ::DataStream::ST_XML, &preLoadBrokenB, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "PreLoadNoData.txt", AZ::DataStream::ST_XML, &noRefs, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("PreLoadRoot.txt", &preLoadRoot, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("PreLoadA.txt", &preLoadA, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("PreLoadB.txt", &noRefs, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("PreLoadC.txt", &noRefs, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("QueueLoadA.txt", &queueLoadA, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("QueueLoadB.txt", &noRefs, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("QueueLoadC.txt", &noRefs, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("PreLoadBrokenA.txt", &preLoadBrokenA, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("PreLoadBrokenB.txt", &preLoadBrokenB, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("PreLoadNoData.txt", &noRefs, m_serializeContext));
AssetWithQueueAndPreLoadReferences circularA;
AssetWithQueueAndPreLoadReferences circularB;
@@ -318,43 +318,15 @@ namespace UnitTest
circularC.m_preLoad = m_testAssetManager->CreateAsset<AssetWithAssetReference>(CircularBId, AssetLoadBehavior::PreLoad);
circularD.m_preLoad = circularC.m_preLoad;
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "CircularA.txt", AZ::DataStream::ST_XML, &circularA, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "CircularB.txt", AZ::DataStream::ST_XML, &circularB, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "CircularC.txt", AZ::DataStream::ST_XML, &circularC, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "CircularD.txt", AZ::DataStream::ST_XML, &circularD, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("CircularA.txt", &circularA, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("CircularB.txt", &circularB, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("CircularC.txt", &circularC, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("CircularD.txt", &circularD, m_serializeContext));
m_assetHandlerAndCatalog->m_numCreations = 0;
}
}
void TearDownTest()
{
DeleteAssetFromDisk(GetTestFolderPath() + "TestAsset4.txt");
DeleteAssetFromDisk(GetTestFolderPath() + "TestAsset5.txt");
DeleteAssetFromDisk(GetTestFolderPath() + "TestAsset6.txt");
DeleteAssetFromDisk(GetTestFolderPath() + "TestAsset1.txt");
DeleteAssetFromDisk(GetTestFolderPath() + "TestAsset2.txt");
DeleteAssetFromDisk(GetTestFolderPath() + "TestAsset3.txt");
DeleteAssetFromDisk(GetTestFolderPath() + "DelayLoadAsset.txt");
DeleteAssetFromDisk(GetTestFolderPath() + "NoLoadAsset.txt");
DeleteAssetFromDisk(GetTestFolderPath() + "PreLoadRoot.txt");
DeleteAssetFromDisk(GetTestFolderPath() + "PreLoadA.txt");
DeleteAssetFromDisk(GetTestFolderPath() + "PreLoadB.txt");
DeleteAssetFromDisk(GetTestFolderPath() + "PreLoadC.txt");
DeleteAssetFromDisk(GetTestFolderPath() + "QueueLoadA.txt");
DeleteAssetFromDisk(GetTestFolderPath() + "QueueLoadB.txt");
DeleteAssetFromDisk(GetTestFolderPath() + "QueueLoadC.txt");
DeleteAssetFromDisk(GetTestFolderPath() + "PreLoadBrokenA.txt");
DeleteAssetFromDisk(GetTestFolderPath() + "PreLoadBrokenB.txt");
DeleteAssetFromDisk(GetTestFolderPath() + "PreLoadNoData.txt");
DeleteAssetFromDisk(GetTestFolderPath() + "CircularA.txt");
DeleteAssetFromDisk(GetTestFolderPath() + "CircularB.txt");
DeleteAssetFromDisk(GetTestFolderPath() + "CircularC.txt");
DeleteAssetFromDisk(GetTestFolderPath() + "CircularD.txt");
}
void CheckFinishedCreationsAndDestructions()
{
// Make sure asset jobs have finished before validating the number of destroyed assets, because it's possible that the asset job
@@ -367,7 +339,7 @@ namespace UnitTest
};
static constexpr AZStd::chrono::seconds MaxDispatchTimeoutSeconds = BaseAssetManagerTest::DefaultTimeoutSeconds * 12;
template <typename Pred>
bool DispatchEventsUntilCondition(AZ::Data::AssetManager& assetManager, Pred&& conditionPredicate,
AZStd::chrono::seconds logIntervalSeconds = BaseAssetManagerTest::DefaultTimeoutSeconds,
@@ -608,7 +580,7 @@ namespace UnitTest
AZ::Data::AssetData::AssetStatus expected_base_status = AZ::Data::AssetData::AssetStatus::Ready;
EXPECT_EQ(baseStatus, expected_base_status);
}
TEST_F(AssetJobsFloodTest, RapidAcquireAndRelease)
{
auto assetUuids = {
@@ -641,7 +613,7 @@ namespace UnitTest
{
Asset<AssetWithAssetReference> asset1 =
m_testAssetManager->GetAsset(assetUuid, azrtti_typeid<AssetWithAssetReference>(), AZ::Data::AssetLoadBehavior::PreLoad);
if (checkLoaded)
{
asset1.BlockUntilLoadComplete();
@@ -714,8 +686,8 @@ namespace UnitTest
AssetWithSerializedData ap;
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "a.txt", AZ::DataStream::ST_XML, &ap, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "b.txt", AZ::DataStream::ST_XML, &ap, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("a.txt", &ap, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("b.txt", &ap, m_serializeContext));
}
auto& assetManager = AssetManager::Instance();
@@ -778,7 +750,7 @@ namespace UnitTest
* Verify that loads without using the Asset Container still work correctly
*/
class AssetContainerDisableTest
: public BaseAssetManagerTest
: public DisklessAssetManagerBase
{
public:
static inline const AZ::Uuid MyAsset1Id{ "{5B29FE2B-6B41-48C9-826A-C723951B0560}" };
@@ -797,7 +769,7 @@ namespace UnitTest
void SetUp() override
{
BaseAssetManagerTest::SetUp();
DisklessAssetManagerBase::SetUp();
SetupTest();
}
@@ -807,7 +779,7 @@ namespace UnitTest
AssetManager::Instance().UnregisterHandler(m_assetHandlerAndCatalog);
delete m_assetHandlerAndCatalog;
AssetManager::Destroy();
BaseAssetManagerTest::TearDown();
DisklessAssetManagerBase::TearDown();
}
void SetupAssets()
@@ -849,9 +821,9 @@ namespace UnitTest
AssetWithSerializedData ap2;
AssetWithSerializedData ap3;
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset4.txt", AZ::DataStream::ST_XML, &ap1, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset5.txt", AZ::DataStream::ST_XML, &ap2, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset6.txt", AZ::DataStream::ST_XML, &ap3, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset4.txt", &ap1, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset5.txt", &ap2, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset6.txt", &ap3, m_serializeContext));
AssetWithAssetReference assetWithPreload1;
AssetWithAssetReference assetWithPreload2;
@@ -862,9 +834,9 @@ namespace UnitTest
assetWithPreload3.m_asset = m_testAssetManager->CreateAsset<AssetWithSerializedData>(MyAsset6Id, AssetLoadBehavior::PreLoad);
EXPECT_EQ(m_assetHandlerAndCatalog->m_numCreations, 3);
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset1.txt", AZ::DataStream::ST_XML, &assetWithPreload1, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset2.txt", AZ::DataStream::ST_XML, &assetWithPreload2, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset3.txt", AZ::DataStream::ST_XML, &assetWithPreload3, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset1.txt", &assetWithPreload1, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset2.txt", &assetWithPreload2, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset3.txt", &assetWithPreload3, m_serializeContext));
m_assetHandlerAndCatalog->m_numCreations = 0;
}
@@ -2014,11 +1986,12 @@ namespace UnitTest
CheckFinishedCreationsAndDestructions();
m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect();
}
/**
* Run multiple threads that get and release assets simultaneously to test AssetManager's thread safety
*/
class AssetJobsMultithreadedTest
: public BaseAssetManagerTest
: public DisklessAssetManagerBase
{
public:
static inline const AZ::Uuid MyAsset1Id{ "{5B29FE2B-6B41-48C9-826A-C723951B0560}" };
@@ -2028,6 +2001,7 @@ namespace UnitTest
static inline const AZ::Uuid MyAsset5Id{ "{D9CDAB04-D206-431E-BDC0-1DD615D56197}" };
static inline const AZ::Uuid MyAsset6Id{ "{B2F139C3-5032-4B52-ADCA-D52A8F88E043}" };
// Initialize the Job Manager with 2 threads for the Asset Manager to use.
size_t GetNumJobManagerThreads() const override { return 2; }
@@ -2078,9 +2052,9 @@ namespace UnitTest
AssetWithSerializedData ap2;
AssetWithSerializedData ap3;
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset4.txt", AZ::DataStream::ST_XML, &ap1, &context));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset5.txt", AZ::DataStream::ST_XML, &ap2, &context));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset6.txt", AZ::DataStream::ST_XML, &ap3, &context));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset4.txt", &ap1, &context));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset5.txt", &ap2, &context));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset6.txt", &ap3, &context));
AssetWithAssetReference assetWithPreload1;
AssetWithAssetReference assetWithPreload2;
@@ -2089,9 +2063,9 @@ namespace UnitTest
assetWithPreload2.m_asset = AssetManager::Instance().CreateAsset<AssetWithSerializedData>(MyAsset5Id, AssetLoadBehavior::PreLoad);
assetWithPreload3.m_asset = AssetManager::Instance().CreateAsset<AssetWithSerializedData>(MyAsset6Id, AssetLoadBehavior::PreLoad);
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset1.txt", AZ::DataStream::ST_XML, &assetWithPreload1, &context));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset2.txt", AZ::DataStream::ST_XML, &assetWithPreload2, &context));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset3.txt", AZ::DataStream::ST_XML, &assetWithPreload3, &context));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset1.txt", &assetWithPreload1, &context));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset2.txt", &assetWithPreload2, &context));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset3.txt", &assetWithPreload3, &context));
EXPECT_TRUE(assetHandlerAndCatalog->m_numCreations == 3);
assetHandlerAndCatalog->m_numCreations = 0;
@@ -2191,22 +2165,22 @@ namespace UnitTest
// A will be saved to disk with MyAsset1Id
AssetWithAssetReference a;
a.m_asset = AssetManager::Instance().CreateAsset<AssetWithSerializedData>(MyAsset2Id);
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset1.txt", AZ::DataStream::ST_XML, &a, &context));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset1.txt", &a, &context));
AssetWithAssetReference b;
b.m_asset = AssetManager::Instance().CreateAsset<AssetWithSerializedData>(MyAsset3Id);
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset2.txt", AZ::DataStream::ST_XML, &b, &context));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset2.txt", &b, &context));
AssetWithAssetReference c;
c.m_asset = AssetManager::Instance().CreateAsset<AssetWithSerializedData>(MyAsset4Id);
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset3.txt", AZ::DataStream::ST_XML, &c, &context));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset3.txt", &c, &context));
AssetWithAssetReference d;
d.m_asset = AssetManager::Instance().CreateAsset<AssetWithSerializedData>(MyAsset5Id);
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset4.txt", AZ::DataStream::ST_XML, &d, &context));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset4.txt", &d, &context));
AssetWithAssetReference e;
e.m_asset = AssetManager::Instance().CreateAsset<AssetWithSerializedData>(MyAsset6Id);
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset5.txt", AZ::DataStream::ST_XML, &e, &context));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset5.txt", &e, &context));
AssetWithAssetReference f;
f.m_asset = AssetManager::Instance().CreateAsset<AssetWithSerializedData>(MyAsset1Id); // refer back to asset1
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset6.txt", AZ::DataStream::ST_XML, &f, &context));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset6.txt", &f, &context));
EXPECT_TRUE(assetHandlerAndCatalog->m_numCreations == 6);
assetHandlerAndCatalog->m_numCreations = 0;
@@ -2347,26 +2321,26 @@ namespace UnitTest
// AssetD is MYASSETD
AssetWithSerializedData d;
d.m_data = 42;
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset4.txt", AZ::DataStream::ST_XML, &d, &context));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset4.txt", &d, &context));
// AssetC is MYASSETC
AssetWithAssetReference c;
c.m_asset = db.CreateAsset<AssetWithSerializedData>(AssetId(MyAssetDId)); // point at D
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset3.txt", AZ::DataStream::ST_XML, &c, &context));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset3.txt", &c, &context));
// AssetB is MYASSETB
AssetWithAssetReference b;
b.m_asset = db.CreateAsset<AssetWithAssetReference>(AssetId(MyAssetCId)); // point at C
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset2.txt", AZ::DataStream::ST_XML, &b, &context));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset2.txt", &b, &context));
// AssetA will be written to disk as MYASSETA
AssetWithAssetReference a;
a.m_asset = db.CreateAsset<AssetWithAssetReference>(AssetId(MyAssetBId)); // point at B
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset1.txt", AZ::DataStream::ST_XML, &a, &context));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset1.txt", &a, &context));
}
const size_t numThreads = 4;
AZStd::atomic_int threadCount(numThreads);
constexpr size_t NumThreads = 4;
AZStd::atomic_int threadCount(NumThreads);
AZStd::condition_variable cv;
AZStd::vector<AZStd::thread> threads;
AZStd::atomic_bool keepDispatching(true);
@@ -2381,7 +2355,7 @@ namespace UnitTest
AZStd::thread dispatchThread(dispatch);
for (size_t threadIdx = 0; threadIdx < numThreads; ++threadIdx)
for (size_t threadIdx = 0; threadIdx < NumThreads; ++threadIdx)
{
threads.emplace_back([&threadCount, &db, &cv]()
{
@@ -2569,7 +2543,6 @@ namespace UnitTest
#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
TEST_F(AssetJobsMultithreadedTest, DISABLED_ParallelDeepAssetReferences)
#else
// temporarily disabled until sporadic failures can be root caused
TEST_F(AssetJobsMultithreadedTest, ParallelDeepAssetReferences)
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
{
@@ -2577,7 +2550,7 @@ namespace UnitTest
}
class AssetManagerTests
: public BaseAssetManagerTest
: public DisklessAssetManagerBase
{
protected:
static inline const AZ::Uuid MyAsset1Id{ "{5B29FE2B-6B41-48C9-826A-C723951B0560}" };
@@ -2592,7 +2565,7 @@ namespace UnitTest
void SetUp() override
{
BaseAssetManagerTest::SetUp();
DisklessAssetManagerBase::SetUp();
m_console = AZStd::make_unique<AZ::Console>();
AZ::Interface<AZ::IConsole>::Register(m_console.get());
@@ -2631,7 +2604,7 @@ namespace UnitTest
AssetManager::Destroy();
AZ::Interface<AZ::IConsole>::Unregister(m_console.get());
m_console = nullptr;
BaseAssetManagerTest::TearDown();
DisklessAssetManagerBase::TearDown();
}
};
@@ -2982,7 +2955,7 @@ namespace UnitTest
* the middle of loading. The tests help ensure that assets can't get stuck in perpetual loading states.
**/
class AssetManagerClearAssetReferenceTests
: public BaseAssetManagerTest
: public DisklessAssetManagerBase
{
protected:
static inline const AZ::Uuid RootAssetId{ "{AB13F568-C676-41FE-A7E9-341F71A78104}" };
@@ -3001,7 +2974,7 @@ namespace UnitTest
void SetUp() override
{
BaseAssetManagerTest::SetUp();
DisklessAssetManagerBase::SetUp();
// create the database
AssetManager::Descriptor desc;
@@ -3039,21 +3012,18 @@ namespace UnitTest
// Create and save the dependent asset first, so that we can get a reference to it.
AssetWithSerializedData dependentBlockingAsset;
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "DependentPreloadBlockingAsset.txt",
AZ::DataStream::ST_XML, &dependentBlockingAsset, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("DependentPreloadBlockingAsset.txt", &dependentBlockingAsset, m_serializeContext));
AssetWithAssetReference dependentAsset;
dependentAsset.m_asset = AssetManager::Instance().CreateAsset<AssetWithAssetReference>(
NestedDependentPreloadBlockingAssetId, AssetLoadBehavior::PreLoad);
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "DependentPreloadAsset.txt",
AZ::DataStream::ST_XML, &dependentAsset, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("DependentPreloadAsset.txt", &dependentAsset, m_serializeContext));
// Create and save the top-level asset.
AssetWithAssetReference rootAsset;
rootAsset.m_asset = AssetManager::Instance().CreateAsset<AssetWithAssetReference>(
DependentPreloadAssetId, AssetLoadBehavior::PreLoad);
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "RootAsset.txt",
AZ::DataStream::ST_XML, &rootAsset, m_serializeContext));
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("RootAsset.txt", &rootAsset, m_serializeContext));
}
void TearDown() override
@@ -3065,7 +3035,7 @@ namespace UnitTest
delete m_assetHandlerAndCatalog;
AssetManager::Destroy();
BaseAssetManagerTest::TearDown();
DisklessAssetManagerBase::TearDown();
}
};
@@ -165,4 +165,254 @@ namespace UnitTest
EXPECT_FALSE(AssetManager::Instance().HasActiveJobsOrStreamerRequests());
}
MemoryStreamerWrapper::MemoryStreamerWrapper()
{
using ::testing::_;
using ::testing::NiceMock;
using ::testing::Return;
ON_CALL(m_mockStreamer, SuspendProcessing()).WillByDefault([this]()
{
m_suspended = true;
});
ON_CALL(m_mockStreamer, ResumeProcessing()).WillByDefault([this]()
{
AZStd::unique_lock lock(m_mutex);
m_suspended = false;
while (!m_processingQueue.empty())
{
FileRequestHandle requestHandle = m_processingQueue.front();
m_processingQueue.pop();
const auto& onCompleteCallback = GetReadRequest(requestHandle)->m_callback;
if (onCompleteCallback)
{
onCompleteCallback(requestHandle);
}
}
});
ON_CALL(m_mockStreamer, Read(_, ::testing::An<IStreamerTypes::RequestMemoryAllocator&>(), _, _, _, _))
.WillByDefault(
[this](
[[maybe_unused]] AZStd::string_view relativePath, IStreamerTypes::RequestMemoryAllocator& allocator, size_t size,
AZStd::chrono::microseconds deadline, IStreamerTypes::Priority priority, [[maybe_unused]] size_t offset)
{
AZStd::unique_lock lock(m_mutex);
ReadRequest request;
// Save off the requested deadline and priority
request.m_deadline = deadline;
request.m_priority = priority;
request.m_data = allocator.Allocate(size, size, 8);
const auto* virtualFile = FindFile(relativePath);
AZ_Assert(
virtualFile->size() == size, "Streamer read request size did not match size of saved file: %d vs %d (%.*s)",
virtualFile->size(), size,
relativePath.size(), relativePath.data());
AZ_Assert(size > 0, "Size is zero %.*s", relativePath.size(), relativePath.data());
memcpy(request.m_data.m_address, virtualFile->data(), size);
// Create a real file request result and return it
request.m_request = m_context.GetNewExternalRequest();
m_readRequests.push_back(request);
return request.m_request;
});
ON_CALL(m_mockStreamer, SetRequestCompleteCallback(_, _))
.WillByDefault([this](FileRequestPtr& request, AZ::IO::IStreamer::OnCompleteCallback callback) -> FileRequestPtr&
{
// Save off the callback just so that we can call it when the request is "done"
AZStd::unique_lock lock(m_mutex);
ReadRequest* readRequest = GetReadRequest(request);
readRequest->m_callback = callback;
return request;
});
ON_CALL(m_mockStreamer, QueueRequest(_))
.WillByDefault([this](const auto& fileRequest)
{
if (!m_suspended)
{
decltype(ReadRequest::m_callback) onCompleteCallback;
AZStd::unique_lock lock(m_mutex);
ReadRequest* readRequest = GetReadRequest(fileRequest);
onCompleteCallback = readRequest->m_callback;
if (onCompleteCallback)
{
onCompleteCallback(fileRequest);
m_readRequests.erase(readRequest);
}
}
else
{
AZStd::unique_lock lock(m_mutex);
m_processingQueue.push(fileRequest);
}
});
ON_CALL(m_mockStreamer, GetRequestStatus(_))
.WillByDefault([]([[maybe_unused]] FileRequestHandle request)
{
// Return whatever request status has been set in this class
return IO::IStreamerTypes::RequestStatus::Completed;
});
ON_CALL(m_mockStreamer, GetReadRequestResult(_, _, _, _))
.WillByDefault([this](
[[maybe_unused]] FileRequestHandle request, void*& buffer, AZ::u64& numBytesRead,
IStreamerTypes::ClaimMemory claimMemory)
{
// Make sure the requestor plans to free the data buffer we allocated.
EXPECT_EQ(claimMemory, IStreamerTypes::ClaimMemory::Yes);
AZStd::unique_lock lock(m_mutex);
ReadRequest* readRequest = GetReadRequest(request);
// Provide valid data buffer results.
numBytesRead = readRequest->m_data.m_size;
buffer = readRequest->m_data.m_address;
return true;
});
ON_CALL(m_mockStreamer, RescheduleRequest(_, _, _))
.WillByDefault([this](IO::FileRequestPtr target, AZStd::chrono::microseconds newDeadline, IO::IStreamerTypes::Priority newPriority)
{
AZStd::unique_lock lock(m_mutex);
ReadRequest* readRequest = GetReadRequest(target);
readRequest->m_deadline = newDeadline;
readRequest->m_priority = newPriority;
return target;
});
}
ReadRequest* MemoryStreamerWrapper::GetReadRequest(FileRequestHandle request)
{
auto itr = AZStd::find_if(
m_readRequests.begin(), m_readRequests.end(),
[request](const ReadRequest& searchItem) -> bool
{
return (searchItem.m_request == request);
});
return itr;
}
AZStd::vector<char>* MemoryStreamerWrapper::FindFile(AZStd::string_view path)
{
auto itr = m_virtualFiles.find(path);
if (itr == m_virtualFiles.end())
{
// Path didn't work as-is, does it have the test folder prefixed? If so try removing it
if (AZ::StringFunc::StartsWith(path, GetTestFolderPath()))
{
AZStd::string_view pathWithoutFolder = path;
pathWithoutFolder = AZ::StringFunc::LStrip(pathWithoutFolder, GetTestFolderPath().c_str());
itr = m_virtualFiles.find(pathWithoutFolder);
}
else // Path isn't prefixed, so try adding it
{
itr = m_virtualFiles.find(GetTestFolderPath().append(path));
}
}
if (itr != m_virtualFiles.end())
{
return &itr->second;
}
// Currently no test expects a file not to exist so we assert to make it easy to quickly find where something went wrong
// If we ever need to test for a non-existent file this assert should just be conditionally disabled for that specific test
AZ_Assert(false, "Failed to find virtual file %*.s", path.size(), path.data())
return nullptr;
}
void DisklessAssetManagerBase::SetUp()
{
using ::testing::_;
using ::testing::NiceMock;
using ::testing::Return;
BaseAssetManagerTest::SetUp();
ON_CALL(m_fileIO, Size(::testing::Matcher<const char*>(::testing::_), _))
.WillByDefault(
[this](const char* path, u64& size)
{
AZStd::scoped_lock lock(m_streamerWrapper->m_mutex);
const auto* file = m_streamerWrapper->FindFile(path);
if (file)
{
size = file->size();
return ResultCode::Success;
}
AZ_Error("DisklessAssetManagerBase", false, "Failed to find virtual file %.*s", path);
return ResultCode::Error;
});
m_prevFileIO = IO::FileIOBase::GetInstance();
IO::FileIOBase::SetInstance(nullptr);
IO::FileIOBase::SetInstance(&m_fileIO);
}
void DisklessAssetManagerBase::TearDown()
{
IO::FileIOBase::SetInstance(nullptr);
IO::FileIOBase::SetInstance(m_prevFileIO);
BaseAssetManagerTest::TearDown();
}
IO::IStreamer* DisklessAssetManagerBase::CreateStreamer()
{
m_streamerWrapper = AZStd::make_unique<MemoryStreamerWrapper>();
return &(m_streamerWrapper->m_mockStreamer);
}
void DisklessAssetManagerBase::DestroyStreamer(IO::IStreamer*)
{
m_streamerWrapper = nullptr;
}
void DisklessAssetManagerBase::WriteAssetToDisk(const AZStd::string& assetName, const AZStd::string&)
{
AZStd::string assetFileName = GetTestFolderPath() + assetName;
AssetWithCustomData asset;
EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile(assetFileName, &asset, m_serializeContext));
}
void DisklessAssetManagerBase::DeleteAssetFromDisk(const AZStd::string&)
{
}
}
@@ -20,7 +20,8 @@
#include <Tests/Asset/TestAssetTypes.h>
#include <Tests/SerializeContextFixture.h>
#include <Tests/TestCatalog.h>
#include <AzCore/UnitTest/Mocks/MockFileIOBase.h>
#include <Streamer/IStreamerMock.h>
namespace UnitTest
{
@@ -58,7 +59,11 @@ namespace UnitTest
// Subclasses can optionally override the streamer creation and destruction
virtual IO::IStreamer* CreateStreamer() { return aznew IO::Streamer(AZStd::thread_desc{}, StreamerComponent::CreateStreamerStack()); }
virtual void DestroyStreamer(IO::IStreamer* streamer) { delete streamer; }
virtual void DestroyStreamer(IO::IStreamer* streamer)
{
delete streamer;
streamer = nullptr;
}
void SetUp() override;
void TearDown() override;
@@ -66,8 +71,8 @@ namespace UnitTest
static void SuppressTraceOutput(bool suppress);
// Helper methods to create and destroy actual assets on the disk for true end-to-end asset loading.
void WriteAssetToDisk(const AZStd::string& assetName, const AZStd::string& assetIdGuid);
void DeleteAssetFromDisk(const AZStd::string& assetName);
virtual void WriteAssetToDisk(const AZStd::string& assetName, const AZStd::string& assetIdGuid);
virtual void DeleteAssetFromDisk(const AZStd::string& assetName);
void BlockUntilAssetJobsAreComplete();
@@ -82,4 +87,57 @@ namespace UnitTest
AZStd::vector<AZStd::string> m_assetsWritten;
};
struct ReadRequest
{
AZStd::chrono::milliseconds m_deadline{};
AZ::IO::IStreamerTypes::Priority m_priority{};
IO::IStreamerTypes::RequestMemoryAllocatorResult m_data{ nullptr, 0, IO::IStreamerTypes::MemoryType::ReadWrite };
AZ::IO::IStreamer::OnCompleteCallback m_callback;
IO::FileRequestPtr m_request;
};
struct MemoryStreamerWrapper
{
MemoryStreamerWrapper();
~MemoryStreamerWrapper() = default;
ReadRequest* GetReadRequest(IO::FileRequestHandle request);
template<typename TObject>
bool WriteMemoryFile(const AZStd::string& filePath, TObject* object, AZ::SerializeContext* context)
{
auto& buffer = m_virtualFiles[filePath];
ByteContainerStream stream(&buffer);
return AZ::Utils::SaveObjectToStream(stream, DataStream::StreamType::ST_XML, object, context);
}
AZStd::vector<char>* FindFile(AZStd::string_view path);
::testing::NiceMock<StreamerMock> m_mockStreamer;
IO::StreamerContext m_context;
AZStd::atomic_bool m_suspended{ false };
AZStd::recursive_mutex m_mutex;
AZStd::queue<FileRequestHandle> m_processingQueue; // Keeps tracks of requests that have been queued while processing is suspended
AZStd::vector<ReadRequest> m_readRequests;
AZStd::unordered_map<AZStd::string, AZStd::vector<char>> m_virtualFiles;
};
struct DisklessAssetManagerBase : BaseAssetManagerTest
{
void SetUp() override;
void TearDown() override;
IO::IStreamer* CreateStreamer() override;
void DestroyStreamer(IO::IStreamer*) override;
void WriteAssetToDisk(const AZStd::string& assetName, const AZStd::string& assetIdGuid) override;
void DeleteAssetFromDisk(const AZStd::string& assetName) override;
AZStd::unique_ptr<MemoryStreamerWrapper> m_streamerWrapper;
::testing::NiceMock<MockFileIOBase> m_fileIO;
IO::FileIOBase* m_prevFileIO{};
};
}
+6 -2
View File
@@ -167,7 +167,8 @@ namespace UnitTest
if (!info.m_streamName.empty())
{
AZStd::string fullName = GetTestFolderPath() + info.m_streamName;
info.m_dataLen = static_cast<size_t>(IO::SystemFile::Length(fullName.c_str()));
IO::FileIOBase* io = IO::FileIOBase::GetInstance();
io->Size(fullName.c_str(), info.m_dataLen);
}
else
{
@@ -187,8 +188,11 @@ namespace UnitTest
if (!info.m_streamName.empty())
{
IO::FileIOBase* io = AZ::IO::FileIOBase::GetInstance();
AZStd::string fullName = GetTestFolderPath() + info.m_streamName;
info.m_dataLen = static_cast<size_t>(IO::SystemFile::Length(fullName.c_str()));
io->Size(fullName.c_str(), info.m_dataLen);
}
else
{
@@ -23,11 +23,11 @@ namespace AzToolsFramework
/// @name Reverse URLs.
/// Used to identify common actions and override them when necessary.
//@{
static const AZ::Crc32 s_backAction = AZ_CRC("com.o3de.action.common.back", 0xd772a2af);
static const AZ::Crc32 s_deleteAction = AZ_CRC("com.o3de.action.common.delete", 0x5731f6cb);
static const AZ::Crc32 s_duplicateAction = AZ_CRC("com.o3de.action.common.duplicate", 0x08ccf461);
static const AZ::Crc32 s_nextComponentMode = AZ_CRC("com.o3de.action.common.nextComponentMode", 0xcc26094f);
static const AZ::Crc32 s_previousComponentMode = AZ_CRC("com.o3de.action.common.previousComponentMode", 0x0d18ff39);
static const AZ::Crc32 s_backAction = AZ_CRC("com.o3de.action.common.back", 0x80c3030f);
static const AZ::Crc32 s_deleteAction = AZ_CRC("com.o3de.action.common.delete", 0x58e78eed);
static const AZ::Crc32 s_duplicateAction = AZ_CRC("com.o3de.action.common.duplicate", 0xbc5a4a23);
static const AZ::Crc32 s_nextComponentMode = AZ_CRC("com.o3de.action.common.nextComponentMode", 0xf9aca3a8);
static const AZ::Crc32 s_previousComponentMode = AZ_CRC("com.o3de.action.common.previousComponentMode", 0x0580eaec);
//@}
/// Specific Action properties to be sent to a type implementing