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
@@ -148,6 +148,7 @@ class TestAutomation(EditorTestSuite):
class test_SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlopes(EditorParallelTest): class test_SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlopes(EditorParallelTest):
from .EditorScripts import SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlope as test_module from .EditorScripts import SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlope as test_module
@pytest.mark.xfail(reason="Intermittently fails to create level")
class test_DynamicSliceInstanceSpawner_Embedded_E2E_Editor(EditorSingleTest): class test_DynamicSliceInstanceSpawner_Embedded_E2E_Editor(EditorSingleTest):
from .EditorScripts import DynamicSliceInstanceSpawner_Embedded_E2E as test_module from .EditorScripts import DynamicSliceInstanceSpawner_Embedded_E2E as test_module
@@ -156,6 +157,7 @@ class TestAutomation(EditorTestSuite):
file_system.delete([os.path.join(workspace.paths.engine_root(), "AutomatedTesting", "Levels", "tmp_level")], file_system.delete([os.path.join(workspace.paths.engine_root(), "AutomatedTesting", "Levels", "tmp_level")],
True, True) True, True)
@pytest.mark.xfail(reason="Intermittently fails to create level")
class test_DynamicSliceInstanceSpawner_External_E2E_Editor(EditorSingleTest): class test_DynamicSliceInstanceSpawner_External_E2E_Editor(EditorSingleTest):
from .EditorScripts import DynamicSliceInstanceSpawner_External_E2E as test_module from .EditorScripts import DynamicSliceInstanceSpawner_External_E2E as test_module
@@ -163,7 +165,8 @@ class TestAutomation(EditorTestSuite):
def teardown(self, request, workspace, editor, editor_test_results, launcher_platform): def teardown(self, request, workspace, editor, editor_test_results, launcher_platform):
file_system.delete([os.path.join(workspace.paths.engine_root(), "AutomatedTesting", "Levels", "tmp_level")], file_system.delete([os.path.join(workspace.paths.engine_root(), "AutomatedTesting", "Levels", "tmp_level")],
True, True) True, True)
@pytest.mark.xfail(reason="Intermittently fails to create level")
class test_LayerBlender_E2E_Editor(EditorSingleTest): class test_LayerBlender_E2E_Editor(EditorSingleTest):
from .EditorScripts import LayerBlender_E2E_Editor as test_module from .EditorScripts import LayerBlender_E2E_Editor as test_module
@@ -325,13 +325,13 @@ namespace AZ
T& operator*() const 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(); return *Get();
} }
T* operator->() const 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(); 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"). // 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 // 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. // 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. // 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, // 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. // a constant expression.
// For example // For example
// switch(id) { // 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" // 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. // So it's you choice what you do, depending on your needs.
@@ -26,6 +26,7 @@
#include <AzCore/std/functional.h> #include <AzCore/std/functional.h>
#include <AzCore/std/parallel/condition_variable.h> #include <AzCore/std/parallel/condition_variable.h>
#include <AzCore/UnitTest/TestTypes.h> #include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/UnitTest/Mocks/MockFileIOBase.h>
#include <AZTestShared/Utils/Utils.h> #include <AZTestShared/Utils/Utils.h>
#include <Streamer/IStreamerMock.h> #include <Streamer/IStreamerMock.h>
#include <Tests/Asset/BaseAssetManagerTest.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 * This will test the aspect of the system where ObjectStreams and asset jobs loading dependent
* assets will do the work in their own thread. * assets will do the work in their own thread.
*/ */
class AssetJobsFloodTest
: public BaseAssetManagerTest class AssetJobsFloodTest : public DisklessAssetManagerBase
{ {
public: public:
TestAssetManager* m_testAssetManager{ nullptr }; TestAssetManager* m_testAssetManager{ nullptr };
@@ -183,15 +184,14 @@ namespace UnitTest
void SetUp() override void SetUp() override
{ {
BaseAssetManagerTest::SetUp(); DisklessAssetManagerBase::SetUp();
SetupTest(); SetupTest();
} }
void TearDown() override void TearDown() override
{ {
TearDownTest();
AssetManager::Destroy(); AssetManager::Destroy();
BaseAssetManagerTest::TearDown(); DisklessAssetManagerBase::TearDown();
} }
void SetupAssets() void SetupAssets()
@@ -257,9 +257,9 @@ namespace UnitTest
AssetWithSerializedData ap2; AssetWithSerializedData ap2;
AssetWithSerializedData ap3; AssetWithSerializedData ap3;
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset4.txt", AZ::DataStream::ST_XML, &ap1, m_serializeContext)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset4.txt", &ap1, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset5.txt", AZ::DataStream::ST_XML, &ap2, m_serializeContext)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset5.txt", &ap2, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset6.txt", AZ::DataStream::ST_XML, &ap3, m_serializeContext)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset6.txt", &ap3, m_serializeContext));
AssetWithAssetReference assetWithPreload1; AssetWithAssetReference assetWithPreload1;
AssetWithAssetReference assetWithPreload2; AssetWithAssetReference assetWithPreload2;
@@ -273,11 +273,11 @@ namespace UnitTest
noLoadAsset.m_asset = m_testAssetManager->CreateAsset<AssetWithSerializedData>(MyAsset2Id, AssetLoadBehavior::NoLoad); noLoadAsset.m_asset = m_testAssetManager->CreateAsset<AssetWithSerializedData>(MyAsset2Id, AssetLoadBehavior::NoLoad);
EXPECT_EQ(m_assetHandlerAndCatalog->m_numCreations, 4); EXPECT_EQ(m_assetHandlerAndCatalog->m_numCreations, 4);
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset1.txt", AZ::DataStream::ST_XML, &assetWithPreload1, m_serializeContext)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset1.txt", &assetWithPreload1, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset2.txt", AZ::DataStream::ST_XML, &assetWithPreload2, m_serializeContext)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset2.txt", &assetWithPreload2, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset3.txt", AZ::DataStream::ST_XML, &assetWithPreload3, m_serializeContext)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset3.txt", &assetWithPreload3, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "DelayLoadAsset.txt", AZ::DataStream::ST_XML, &delayedAsset, m_serializeContext)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("DelayLoadAsset.txt", &delayedAsset, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "NoLoadAsset.txt", AZ::DataStream::ST_XML, &noLoadAsset, m_serializeContext)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("NoLoadAsset.txt", &noLoadAsset, m_serializeContext));
AssetWithQueueAndPreLoadReferences preLoadRoot; AssetWithQueueAndPreLoadReferences preLoadRoot;
AssetWithQueueAndPreLoadReferences preLoadA; AssetWithQueueAndPreLoadReferences preLoadA;
@@ -297,16 +297,16 @@ namespace UnitTest
preLoadBrokenA.m_preLoad = m_testAssetManager->CreateAsset<AssetWithAssetReference>(PreloadBrokenDepBId, AssetLoadBehavior::PreLoad); preLoadBrokenA.m_preLoad = m_testAssetManager->CreateAsset<AssetWithAssetReference>(PreloadBrokenDepBId, AssetLoadBehavior::PreLoad);
preLoadBrokenB.m_preLoad = m_testAssetManager->CreateAsset<AssetWithAssetReference>(PreloadAssetNoDataId, 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(m_streamerWrapper->WriteMemoryFile("PreLoadRoot.txt", &preLoadRoot, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "PreLoadA.txt", AZ::DataStream::ST_XML, &preLoadA, m_serializeContext)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("PreLoadA.txt", &preLoadA, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "PreLoadB.txt", AZ::DataStream::ST_XML, &noRefs, m_serializeContext)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("PreLoadB.txt", &noRefs, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "PreLoadC.txt", AZ::DataStream::ST_XML, &noRefs, m_serializeContext)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("PreLoadC.txt", &noRefs, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "QueueLoadA.txt", AZ::DataStream::ST_XML, &queueLoadA, m_serializeContext)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("QueueLoadA.txt", &queueLoadA, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "QueueLoadB.txt", AZ::DataStream::ST_XML, &noRefs, m_serializeContext)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("QueueLoadB.txt", &noRefs, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "QueueLoadC.txt", AZ::DataStream::ST_XML, &noRefs, m_serializeContext)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("QueueLoadC.txt", &noRefs, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "PreLoadBrokenA.txt", AZ::DataStream::ST_XML, &preLoadBrokenA, m_serializeContext)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("PreLoadBrokenA.txt", &preLoadBrokenA, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "PreLoadBrokenB.txt", AZ::DataStream::ST_XML, &preLoadBrokenB, m_serializeContext)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("PreLoadBrokenB.txt", &preLoadBrokenB, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "PreLoadNoData.txt", AZ::DataStream::ST_XML, &noRefs, m_serializeContext)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("PreLoadNoData.txt", &noRefs, m_serializeContext));
AssetWithQueueAndPreLoadReferences circularA; AssetWithQueueAndPreLoadReferences circularA;
AssetWithQueueAndPreLoadReferences circularB; AssetWithQueueAndPreLoadReferences circularB;
@@ -318,43 +318,15 @@ namespace UnitTest
circularC.m_preLoad = m_testAssetManager->CreateAsset<AssetWithAssetReference>(CircularBId, AssetLoadBehavior::PreLoad); circularC.m_preLoad = m_testAssetManager->CreateAsset<AssetWithAssetReference>(CircularBId, AssetLoadBehavior::PreLoad);
circularD.m_preLoad = circularC.m_preLoad; circularD.m_preLoad = circularC.m_preLoad;
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "CircularA.txt", AZ::DataStream::ST_XML, &circularA, m_serializeContext)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("CircularA.txt", &circularA, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "CircularB.txt", AZ::DataStream::ST_XML, &circularB, m_serializeContext)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("CircularB.txt", &circularB, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "CircularC.txt", AZ::DataStream::ST_XML, &circularC, m_serializeContext)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("CircularC.txt", &circularC, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "CircularD.txt", AZ::DataStream::ST_XML, &circularD, m_serializeContext)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("CircularD.txt", &circularD, m_serializeContext));
m_assetHandlerAndCatalog->m_numCreations = 0; 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() void CheckFinishedCreationsAndDestructions()
{ {
// Make sure asset jobs have finished before validating the number of destroyed assets, because it's possible that the asset job // 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; static constexpr AZStd::chrono::seconds MaxDispatchTimeoutSeconds = BaseAssetManagerTest::DefaultTimeoutSeconds * 12;
template <typename Pred> template <typename Pred>
bool DispatchEventsUntilCondition(AZ::Data::AssetManager& assetManager, Pred&& conditionPredicate, bool DispatchEventsUntilCondition(AZ::Data::AssetManager& assetManager, Pred&& conditionPredicate,
AZStd::chrono::seconds logIntervalSeconds = BaseAssetManagerTest::DefaultTimeoutSeconds, AZStd::chrono::seconds logIntervalSeconds = BaseAssetManagerTest::DefaultTimeoutSeconds,
@@ -608,7 +580,7 @@ namespace UnitTest
AZ::Data::AssetData::AssetStatus expected_base_status = AZ::Data::AssetData::AssetStatus::Ready; AZ::Data::AssetData::AssetStatus expected_base_status = AZ::Data::AssetData::AssetStatus::Ready;
EXPECT_EQ(baseStatus, expected_base_status); EXPECT_EQ(baseStatus, expected_base_status);
} }
TEST_F(AssetJobsFloodTest, RapidAcquireAndRelease) TEST_F(AssetJobsFloodTest, RapidAcquireAndRelease)
{ {
auto assetUuids = { auto assetUuids = {
@@ -641,7 +613,7 @@ namespace UnitTest
{ {
Asset<AssetWithAssetReference> asset1 = Asset<AssetWithAssetReference> asset1 =
m_testAssetManager->GetAsset(assetUuid, azrtti_typeid<AssetWithAssetReference>(), AZ::Data::AssetLoadBehavior::PreLoad); m_testAssetManager->GetAsset(assetUuid, azrtti_typeid<AssetWithAssetReference>(), AZ::Data::AssetLoadBehavior::PreLoad);
if (checkLoaded) if (checkLoaded)
{ {
asset1.BlockUntilLoadComplete(); asset1.BlockUntilLoadComplete();
@@ -714,8 +686,8 @@ namespace UnitTest
AssetWithSerializedData ap; AssetWithSerializedData ap;
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "a.txt", AZ::DataStream::ST_XML, &ap, m_serializeContext)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("a.txt", &ap, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "b.txt", AZ::DataStream::ST_XML, &ap, m_serializeContext)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("b.txt", &ap, m_serializeContext));
} }
auto& assetManager = AssetManager::Instance(); auto& assetManager = AssetManager::Instance();
@@ -778,7 +750,7 @@ namespace UnitTest
* Verify that loads without using the Asset Container still work correctly * Verify that loads without using the Asset Container still work correctly
*/ */
class AssetContainerDisableTest class AssetContainerDisableTest
: public BaseAssetManagerTest : public DisklessAssetManagerBase
{ {
public: public:
static inline const AZ::Uuid MyAsset1Id{ "{5B29FE2B-6B41-48C9-826A-C723951B0560}" }; static inline const AZ::Uuid MyAsset1Id{ "{5B29FE2B-6B41-48C9-826A-C723951B0560}" };
@@ -797,7 +769,7 @@ namespace UnitTest
void SetUp() override void SetUp() override
{ {
BaseAssetManagerTest::SetUp(); DisklessAssetManagerBase::SetUp();
SetupTest(); SetupTest();
} }
@@ -807,7 +779,7 @@ namespace UnitTest
AssetManager::Instance().UnregisterHandler(m_assetHandlerAndCatalog); AssetManager::Instance().UnregisterHandler(m_assetHandlerAndCatalog);
delete m_assetHandlerAndCatalog; delete m_assetHandlerAndCatalog;
AssetManager::Destroy(); AssetManager::Destroy();
BaseAssetManagerTest::TearDown(); DisklessAssetManagerBase::TearDown();
} }
void SetupAssets() void SetupAssets()
@@ -849,9 +821,9 @@ namespace UnitTest
AssetWithSerializedData ap2; AssetWithSerializedData ap2;
AssetWithSerializedData ap3; AssetWithSerializedData ap3;
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset4.txt", AZ::DataStream::ST_XML, &ap1, m_serializeContext)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset4.txt", &ap1, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset5.txt", AZ::DataStream::ST_XML, &ap2, m_serializeContext)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset5.txt", &ap2, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset6.txt", AZ::DataStream::ST_XML, &ap3, m_serializeContext)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset6.txt", &ap3, m_serializeContext));
AssetWithAssetReference assetWithPreload1; AssetWithAssetReference assetWithPreload1;
AssetWithAssetReference assetWithPreload2; AssetWithAssetReference assetWithPreload2;
@@ -862,9 +834,9 @@ namespace UnitTest
assetWithPreload3.m_asset = m_testAssetManager->CreateAsset<AssetWithSerializedData>(MyAsset6Id, AssetLoadBehavior::PreLoad); assetWithPreload3.m_asset = m_testAssetManager->CreateAsset<AssetWithSerializedData>(MyAsset6Id, AssetLoadBehavior::PreLoad);
EXPECT_EQ(m_assetHandlerAndCatalog->m_numCreations, 3); EXPECT_EQ(m_assetHandlerAndCatalog->m_numCreations, 3);
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset1.txt", AZ::DataStream::ST_XML, &assetWithPreload1, m_serializeContext)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset1.txt", &assetWithPreload1, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset2.txt", AZ::DataStream::ST_XML, &assetWithPreload2, m_serializeContext)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset2.txt", &assetWithPreload2, m_serializeContext));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset3.txt", AZ::DataStream::ST_XML, &assetWithPreload3, m_serializeContext)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset3.txt", &assetWithPreload3, m_serializeContext));
m_assetHandlerAndCatalog->m_numCreations = 0; m_assetHandlerAndCatalog->m_numCreations = 0;
} }
@@ -2014,11 +1986,12 @@ namespace UnitTest
CheckFinishedCreationsAndDestructions(); CheckFinishedCreationsAndDestructions();
m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect(); m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect();
} }
/** /**
* Run multiple threads that get and release assets simultaneously to test AssetManager's thread safety * Run multiple threads that get and release assets simultaneously to test AssetManager's thread safety
*/ */
class AssetJobsMultithreadedTest class AssetJobsMultithreadedTest
: public BaseAssetManagerTest : public DisklessAssetManagerBase
{ {
public: public:
static inline const AZ::Uuid MyAsset1Id{ "{5B29FE2B-6B41-48C9-826A-C723951B0560}" }; 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 MyAsset5Id{ "{D9CDAB04-D206-431E-BDC0-1DD615D56197}" };
static inline const AZ::Uuid MyAsset6Id{ "{B2F139C3-5032-4B52-ADCA-D52A8F88E043}" }; static inline const AZ::Uuid MyAsset6Id{ "{B2F139C3-5032-4B52-ADCA-D52A8F88E043}" };
// Initialize the Job Manager with 2 threads for the Asset Manager to use. // Initialize the Job Manager with 2 threads for the Asset Manager to use.
size_t GetNumJobManagerThreads() const override { return 2; } size_t GetNumJobManagerThreads() const override { return 2; }
@@ -2078,9 +2052,9 @@ namespace UnitTest
AssetWithSerializedData ap2; AssetWithSerializedData ap2;
AssetWithSerializedData ap3; AssetWithSerializedData ap3;
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset4.txt", AZ::DataStream::ST_XML, &ap1, &context)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset4.txt", &ap1, &context));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset5.txt", AZ::DataStream::ST_XML, &ap2, &context)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset5.txt", &ap2, &context));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset6.txt", AZ::DataStream::ST_XML, &ap3, &context)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset6.txt", &ap3, &context));
AssetWithAssetReference assetWithPreload1; AssetWithAssetReference assetWithPreload1;
AssetWithAssetReference assetWithPreload2; AssetWithAssetReference assetWithPreload2;
@@ -2089,9 +2063,9 @@ namespace UnitTest
assetWithPreload2.m_asset = AssetManager::Instance().CreateAsset<AssetWithSerializedData>(MyAsset5Id, AssetLoadBehavior::PreLoad); assetWithPreload2.m_asset = AssetManager::Instance().CreateAsset<AssetWithSerializedData>(MyAsset5Id, AssetLoadBehavior::PreLoad);
assetWithPreload3.m_asset = AssetManager::Instance().CreateAsset<AssetWithSerializedData>(MyAsset6Id, 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(m_streamerWrapper->WriteMemoryFile("TestAsset1.txt", &assetWithPreload1, &context));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset2.txt", AZ::DataStream::ST_XML, &assetWithPreload2, &context)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset2.txt", &assetWithPreload2, &context));
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "TestAsset3.txt", AZ::DataStream::ST_XML, &assetWithPreload3, &context)); EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("TestAsset3.txt", &assetWithPreload3, &context));
EXPECT_TRUE(assetHandlerAndCatalog->m_numCreations == 3); EXPECT_TRUE(assetHandlerAndCatalog->m_numCreations == 3);
assetHandlerAndCatalog->m_numCreations = 0; assetHandlerAndCatalog->m_numCreations = 0;
@@ -2191,22 +2165,22 @@ namespace UnitTest
// A will be saved to disk with MyAsset1Id // A will be saved to disk with MyAsset1Id
AssetWithAssetReference a; AssetWithAssetReference a;
a.m_asset = AssetManager::Instance().CreateAsset<AssetWithSerializedData>(MyAsset2Id); 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; AssetWithAssetReference b;
b.m_asset = AssetManager::Instance().CreateAsset<AssetWithSerializedData>(MyAsset3Id); 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; AssetWithAssetReference c;
c.m_asset = AssetManager::Instance().CreateAsset<AssetWithSerializedData>(MyAsset4Id); 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; AssetWithAssetReference d;
d.m_asset = AssetManager::Instance().CreateAsset<AssetWithSerializedData>(MyAsset5Id); 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; AssetWithAssetReference e;
e.m_asset = AssetManager::Instance().CreateAsset<AssetWithSerializedData>(MyAsset6Id); 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; AssetWithAssetReference f;
f.m_asset = AssetManager::Instance().CreateAsset<AssetWithSerializedData>(MyAsset1Id); // refer back to asset1 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); EXPECT_TRUE(assetHandlerAndCatalog->m_numCreations == 6);
assetHandlerAndCatalog->m_numCreations = 0; assetHandlerAndCatalog->m_numCreations = 0;
@@ -2347,26 +2321,26 @@ namespace UnitTest
// AssetD is MYASSETD // AssetD is MYASSETD
AssetWithSerializedData d; AssetWithSerializedData d;
d.m_data = 42; 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 // AssetC is MYASSETC
AssetWithAssetReference c; AssetWithAssetReference c;
c.m_asset = db.CreateAsset<AssetWithSerializedData>(AssetId(MyAssetDId)); // point at D 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 // AssetB is MYASSETB
AssetWithAssetReference b; AssetWithAssetReference b;
b.m_asset = db.CreateAsset<AssetWithAssetReference>(AssetId(MyAssetCId)); // point at C 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 // AssetA will be written to disk as MYASSETA
AssetWithAssetReference a; AssetWithAssetReference a;
a.m_asset = db.CreateAsset<AssetWithAssetReference>(AssetId(MyAssetBId)); // point at B 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; constexpr size_t NumThreads = 4;
AZStd::atomic_int threadCount(numThreads); AZStd::atomic_int threadCount(NumThreads);
AZStd::condition_variable cv; AZStd::condition_variable cv;
AZStd::vector<AZStd::thread> threads; AZStd::vector<AZStd::thread> threads;
AZStd::atomic_bool keepDispatching(true); AZStd::atomic_bool keepDispatching(true);
@@ -2381,7 +2355,7 @@ namespace UnitTest
AZStd::thread dispatchThread(dispatch); 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]() threads.emplace_back([&threadCount, &db, &cv]()
{ {
@@ -2569,7 +2543,6 @@ namespace UnitTest
#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS #if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
TEST_F(AssetJobsMultithreadedTest, DISABLED_ParallelDeepAssetReferences) TEST_F(AssetJobsMultithreadedTest, DISABLED_ParallelDeepAssetReferences)
#else #else
// temporarily disabled until sporadic failures can be root caused
TEST_F(AssetJobsMultithreadedTest, ParallelDeepAssetReferences) TEST_F(AssetJobsMultithreadedTest, ParallelDeepAssetReferences)
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS #endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
{ {
@@ -2577,7 +2550,7 @@ namespace UnitTest
} }
class AssetManagerTests class AssetManagerTests
: public BaseAssetManagerTest : public DisklessAssetManagerBase
{ {
protected: protected:
static inline const AZ::Uuid MyAsset1Id{ "{5B29FE2B-6B41-48C9-826A-C723951B0560}" }; static inline const AZ::Uuid MyAsset1Id{ "{5B29FE2B-6B41-48C9-826A-C723951B0560}" };
@@ -2592,7 +2565,7 @@ namespace UnitTest
void SetUp() override void SetUp() override
{ {
BaseAssetManagerTest::SetUp(); DisklessAssetManagerBase::SetUp();
m_console = AZStd::make_unique<AZ::Console>(); m_console = AZStd::make_unique<AZ::Console>();
AZ::Interface<AZ::IConsole>::Register(m_console.get()); AZ::Interface<AZ::IConsole>::Register(m_console.get());
@@ -2631,7 +2604,7 @@ namespace UnitTest
AssetManager::Destroy(); AssetManager::Destroy();
AZ::Interface<AZ::IConsole>::Unregister(m_console.get()); AZ::Interface<AZ::IConsole>::Unregister(m_console.get());
m_console = nullptr; 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. * the middle of loading. The tests help ensure that assets can't get stuck in perpetual loading states.
**/ **/
class AssetManagerClearAssetReferenceTests class AssetManagerClearAssetReferenceTests
: public BaseAssetManagerTest : public DisklessAssetManagerBase
{ {
protected: protected:
static inline const AZ::Uuid RootAssetId{ "{AB13F568-C676-41FE-A7E9-341F71A78104}" }; static inline const AZ::Uuid RootAssetId{ "{AB13F568-C676-41FE-A7E9-341F71A78104}" };
@@ -3001,7 +2974,7 @@ namespace UnitTest
void SetUp() override void SetUp() override
{ {
BaseAssetManagerTest::SetUp(); DisklessAssetManagerBase::SetUp();
// create the database // create the database
AssetManager::Descriptor desc; 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. // Create and save the dependent asset first, so that we can get a reference to it.
AssetWithSerializedData dependentBlockingAsset; AssetWithSerializedData dependentBlockingAsset;
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "DependentPreloadBlockingAsset.txt", EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("DependentPreloadBlockingAsset.txt", &dependentBlockingAsset, m_serializeContext));
AZ::DataStream::ST_XML, &dependentBlockingAsset, m_serializeContext));
AssetWithAssetReference dependentAsset; AssetWithAssetReference dependentAsset;
dependentAsset.m_asset = AssetManager::Instance().CreateAsset<AssetWithAssetReference>( dependentAsset.m_asset = AssetManager::Instance().CreateAsset<AssetWithAssetReference>(
NestedDependentPreloadBlockingAssetId, AssetLoadBehavior::PreLoad); NestedDependentPreloadBlockingAssetId, AssetLoadBehavior::PreLoad);
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "DependentPreloadAsset.txt", EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("DependentPreloadAsset.txt", &dependentAsset, m_serializeContext));
AZ::DataStream::ST_XML, &dependentAsset, m_serializeContext));
// Create and save the top-level asset. // Create and save the top-level asset.
AssetWithAssetReference rootAsset; AssetWithAssetReference rootAsset;
rootAsset.m_asset = AssetManager::Instance().CreateAsset<AssetWithAssetReference>( rootAsset.m_asset = AssetManager::Instance().CreateAsset<AssetWithAssetReference>(
DependentPreloadAssetId, AssetLoadBehavior::PreLoad); DependentPreloadAssetId, AssetLoadBehavior::PreLoad);
EXPECT_TRUE(AZ::Utils::SaveObjectToFile(GetTestFolderPath() + "RootAsset.txt", EXPECT_TRUE(m_streamerWrapper->WriteMemoryFile("RootAsset.txt", &rootAsset, m_serializeContext));
AZ::DataStream::ST_XML, &rootAsset, m_serializeContext));
} }
void TearDown() override void TearDown() override
@@ -3065,7 +3035,7 @@ namespace UnitTest
delete m_assetHandlerAndCatalog; delete m_assetHandlerAndCatalog;
AssetManager::Destroy(); AssetManager::Destroy();
BaseAssetManagerTest::TearDown(); DisklessAssetManagerBase::TearDown();
} }
}; };
@@ -165,4 +165,254 @@ namespace UnitTest
EXPECT_FALSE(AssetManager::Instance().HasActiveJobsOrStreamerRequests()); 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/Asset/TestAssetTypes.h>
#include <Tests/SerializeContextFixture.h> #include <Tests/SerializeContextFixture.h>
#include <Tests/TestCatalog.h> #include <Tests/TestCatalog.h>
#include <AzCore/UnitTest/Mocks/MockFileIOBase.h>
#include <Streamer/IStreamerMock.h>
namespace UnitTest namespace UnitTest
{ {
@@ -58,7 +59,11 @@ namespace UnitTest
// Subclasses can optionally override the streamer creation and destruction // Subclasses can optionally override the streamer creation and destruction
virtual IO::IStreamer* CreateStreamer() { return aznew IO::Streamer(AZStd::thread_desc{}, StreamerComponent::CreateStreamerStack()); } 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 SetUp() override;
void TearDown() override; void TearDown() override;
@@ -66,8 +71,8 @@ namespace UnitTest
static void SuppressTraceOutput(bool suppress); static void SuppressTraceOutput(bool suppress);
// Helper methods to create and destroy actual assets on the disk for true end-to-end asset loading. // 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); virtual void WriteAssetToDisk(const AZStd::string& assetName, const AZStd::string& assetIdGuid);
void DeleteAssetFromDisk(const AZStd::string& assetName); virtual void DeleteAssetFromDisk(const AZStd::string& assetName);
void BlockUntilAssetJobsAreComplete(); void BlockUntilAssetJobsAreComplete();
@@ -82,4 +87,57 @@ namespace UnitTest
AZStd::vector<AZStd::string> m_assetsWritten; 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()) if (!info.m_streamName.empty())
{ {
AZStd::string fullName = GetTestFolderPath() + info.m_streamName; 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 else
{ {
@@ -187,8 +188,11 @@ namespace UnitTest
if (!info.m_streamName.empty()) if (!info.m_streamName.empty())
{ {
IO::FileIOBase* io = AZ::IO::FileIOBase::GetInstance();
AZStd::string fullName = GetTestFolderPath() + info.m_streamName; 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 else
{ {
@@ -23,11 +23,11 @@ namespace AzToolsFramework
/// @name Reverse URLs. /// @name Reverse URLs.
/// Used to identify common actions and override them when necessary. /// 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_backAction = AZ_CRC("com.o3de.action.common.back", 0x80c3030f);
static const AZ::Crc32 s_deleteAction = AZ_CRC("com.o3de.action.common.delete", 0x5731f6cb); 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", 0x08ccf461); 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", 0xcc26094f); 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", 0x0d18ff39); static const AZ::Crc32 s_previousComponentMode = AZ_CRC("com.o3de.action.common.previousComponentMode", 0x0580eaec);
//@} //@}
/// Specific Action properties to be sent to a type implementing /// Specific Action properties to be sent to a type implementing
@@ -132,6 +132,12 @@ namespace AssetProcessor
// calls PrintStat on each element in the vector. // calls PrintStat on each element in the vector.
void PrintStatsArray(AZStd::vector<AZStd::string>& keys, int maxToPrint, const char* header) void PrintStatsArray(AZStd::vector<AZStd::string>& keys, int maxToPrint, const char* header)
{ {
// don't print anything out at all, not even a header, if the keys are empty.
if (keys.empty())
{
return;
}
if ((m_dumpHumanReadableStats)&&(header)) if ((m_dumpHumanReadableStats)&&(header))
{ {
AZ_TracePrintf(AssetProcessor::ConsoleChannel,"Top %i %s\n", maxToPrint, header); AZ_TracePrintf(AssetProcessor::ConsoleChannel,"Top %i %s\n", maxToPrint, header);
@@ -1182,7 +1182,11 @@ namespace AssetUtilities
} }
} }
// keep track of how much time we spend actually hashing files.
AZStd::string statName = AZStd::string::format("HashFile,%s", filePath);
AssetProcessor::StatsCapture::BeginCaptureStat(statName.c_str());
hash = AssetBuilderSDK::GetFileHash(filePath, bytesReadOut, hashMsDelay); hash = AssetBuilderSDK::GetFileHash(filePath, bytesReadOut, hashMsDelay);
AssetProcessor::StatsCapture::EndCaptureStat(statName.c_str());
return hash; return hash;
} }
+1
View File
@@ -92,6 +92,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
AZ::AzTest AZ::AzTest
AZ::AzFramework AZ::AzFramework
AZ::AzFrameworkTestShared AZ::AzFrameworkTestShared
AZ::AzQtComponents
AZ::ProjectManager.Static AZ::ProjectManager.Static
) )
@@ -73,10 +73,10 @@ namespace O3DE::ProjectManager
QString m_path; QString m_path;
QString m_name = "Unknown Gem Name"; QString m_name = "Unknown Gem Name";
QString m_displayName = "Unknown Gem Name"; QString m_displayName;
QString m_creator = "Unknown Creator"; QString m_creator = "Unknown Creator";
GemOrigin m_gemOrigin = Local; GemOrigin m_gemOrigin = Local;
bool m_isAdded = false; //! Is the gem currently added and enabled in the project? bool m_isAdded = false; //! Is the gem explicitly added (not a dependency) and enabled in the project?
QString m_summary = "No summary provided."; QString m_summary = "No summary provided.";
Platforms m_platforms; Platforms m_platforms;
Types m_types; //! Asset and/or Code and/or Tool Types m_types; //! Asset and/or Code and/or Tool
@@ -8,8 +8,8 @@
#include <AzCore/UnitTest/TestTypes.h> #include <AzCore/UnitTest/TestTypes.h>
#include <AzTest/Utils.h> #include <AzTest/Utils.h>
#include <GemCatalog/GemModel.h>
#include <GemCatalog/GemSortFilterProxyModel.h>
namespace O3DE::ProjectManager namespace O3DE::ProjectManager
{ {
@@ -17,14 +17,22 @@ namespace O3DE::ProjectManager
: public ::UnitTest::ScopedAllocatorSetupFixture : public ::UnitTest::ScopedAllocatorSetupFixture
{ {
public: public:
void SetUp() override
{
m_gemModel.reset(new GemModel());
}
GemCatalogTests() = default; void TearDown() override
{
m_gemModel.release();
}
protected:
AZStd::unique_ptr<GemModel> m_gemModel;
}; };
TEST_F(GemCatalogTests, GemCatalog_Displays_But_Does_Not_Add_Dependencies) TEST_F(GemCatalogTests, GemCatalog_GemWithDependencies_DisplaysButDoesNotAddDependencies)
{ {
GemModel* gemModel = new GemModel();
// given 3 gems a,b,c where a depends on b which depends on c // given 3 gems a,b,c where a depends on b which depends on c
GemInfo gemA, gemB, gemC; GemInfo gemA, gemB, gemC;
QModelIndex indexA, indexB, indexC; QModelIndex indexA, indexB, indexC;
@@ -35,30 +43,531 @@ namespace O3DE::ProjectManager
gemA.m_dependencies = QStringList({ "b" }); gemA.m_dependencies = QStringList({ "b" });
gemB.m_dependencies = QStringList({ "c" }); gemB.m_dependencies = QStringList({ "c" });
gemModel->AddGem(gemA); indexA = m_gemModel->AddGem(gemA);
indexA = gemModel->FindIndexByNameString(gemA.m_name); indexB = m_gemModel->AddGem(gemB);
indexC = m_gemModel->AddGem(gemC);
gemModel->AddGem(gemB); m_gemModel->UpdateGemDependencies();
indexB = gemModel->FindIndexByNameString(gemB.m_name);
gemModel->AddGem(gemC);
indexC = gemModel->FindIndexByNameString(gemC.m_name);
gemModel->UpdateGemDependencies();
EXPECT_FALSE(GemModel::IsAdded(indexA)); EXPECT_FALSE(GemModel::IsAdded(indexA));
EXPECT_FALSE(GemModel::IsAddedDependency(indexB) || GemModel::IsAddedDependency(indexC)); EXPECT_FALSE(GemModel::IsAddedDependency(indexB) || GemModel::IsAddedDependency(indexC));
// when a is added // when a is added
GemModel::SetIsAdded(*gemModel, indexA, true); GemModel::SetIsAdded(*m_gemModel, indexA, true);
// expect b and c are now dependencies of an added gem but not themselves added // expect b and c are now dependencies of an added gem but not themselves added
// cmake will handle dependencies // cmake will handle dependencies
EXPECT_TRUE(GemModel::IsAddedDependency(indexB) && GemModel::IsAddedDependency(indexC)); EXPECT_TRUE(GemModel::IsAddedDependency(indexB) && GemModel::IsAddedDependency(indexC));
EXPECT_TRUE(!GemModel::IsAdded(indexB) && !GemModel::IsAdded(indexC)); EXPECT_FALSE(GemModel::IsAdded(indexB) || GemModel::IsAdded(indexC));
QVector<QModelIndex> gemsToAdd = gemModel->GatherGemsToBeAdded(); const QVector<QModelIndex>& gemsToAdd = m_gemModel->GatherGemsToBeAdded();
EXPECT_TRUE(gemsToAdd.size() == 1); EXPECT_TRUE(gemsToAdd.size() == 1);
EXPECT_EQ(GemModel::GetName(gemsToAdd.at(0)), gemA.m_name); EXPECT_EQ(GemModel::GetName(gemsToAdd.at(0)), gemA.m_name);
} }
class GemCatalogFilterTests
: public GemCatalogTests
{
public:
void SetUp() override
{
GemCatalogTests::SetUp();
m_proxyModel.reset(new GemSortFilterProxyModel(m_gemModel.get()));
}
void TearDown() override
{
m_proxyModel.release();
GemCatalogTests::TearDown();
}
protected:
AZStd::unique_ptr<GemSortFilterProxyModel> m_proxyModel;
};
class GemCatalogSearchFilterTests
: public GemCatalogFilterTests
{
public:
void SetUp() override
{
GemCatalogFilterTests::SetUp();
GemInfo gemfilterName, gemfilterDisplayName, gemfilterCreator, gemfilterSummary, gemfilterFeature;
gemfilterName.m_name = "Name";
gemfilterDisplayName.m_name = "D";
gemfilterCreator.m_name = "C";
gemfilterSummary.m_name = "S";
gemfilterFeature.m_name = "F";
gemfilterDisplayName.m_displayName = "Display Name";
gemfilterCreator.m_creator = "Johnathon Doe";
gemfilterSummary.m_summary = "Unique Summary";
gemfilterFeature.m_features.append("Creative Feature");
m_gemRows.append(m_gemModel->AddGem(gemfilterName).row());
m_gemRows.append(m_gemModel->AddGem(gemfilterDisplayName).row());
m_gemRows.append(m_gemModel->AddGem(gemfilterCreator).row());
m_gemRows.append(m_gemModel->AddGem(gemfilterSummary).row());
m_gemRows.append(m_gemModel->AddGem(gemfilterFeature).row());
}
protected:
enum RowOrder
{
Name,
DisplayName,
Creator,
Summary,
Features
};
QVector<int> m_gemRows;
};
TEST_F(GemCatalogSearchFilterTests, GemCatalogFilters_SearchStringName_ShowsNameGems)
{
m_proxyModel->SetSearchString("Name");
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[Name], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[DisplayName], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[Creator], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[Summary], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[Features], QModelIndex()));
}
TEST_F(GemCatalogSearchFilterTests, GemCatalogFilters_SearchStringDisplayName_ShowsDisplayNameGem)
{
m_proxyModel->SetSearchString("Display Name");
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[Name], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[DisplayName], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[Creator], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[Summary], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[Features], QModelIndex()));
}
TEST_F(GemCatalogSearchFilterTests, GemCatalogFilters_SearchStringCreator_ShowsCreatorGem)
{
m_proxyModel->SetSearchString("Johnathon Doe");
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[Name], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[DisplayName], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[Creator], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[Summary], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[Features], QModelIndex()));
}
TEST_F(GemCatalogSearchFilterTests, GemCatalogFilters_SearchStringSummary_ShowsSummaryGem)
{
m_proxyModel->SetSearchString("Unique Summary");
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[Name], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[DisplayName], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[Creator], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[Summary], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[Features], QModelIndex()));
}
TEST_F(GemCatalogSearchFilterTests, GemCatalogFilters_SearchStringFeatures_ShowsFeatureGem)
{
m_proxyModel->SetSearchString("Creative");
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[Name], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[DisplayName], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[Creator], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[Summary], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[Features], QModelIndex()));
}
TEST_F(GemCatalogSearchFilterTests, GemCatalogFilters_SearchStringEmpty_ShowsAll)
{
m_proxyModel->SetSearchString("");
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[Name], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[DisplayName], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[Creator], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[Summary], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[Features], QModelIndex()));
}
TEST_F(GemCatalogSearchFilterTests, GemCatalogFilters_SearchStringCommonCharacter_ShowsAll)
{
// All gems contain "a" in a searchable field so all should be shown
m_proxyModel->SetSearchString("a");
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[Name], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[DisplayName], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[Creator], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[Summary], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[Features], QModelIndex()));
}
TEST_F(GemCatalogSearchFilterTests, GemCatalogFilters_SearchStringDifferentCaseCommonCharacter_ShowsAll)
{
// No gems contain the character "A" but search should be case insensitive
m_proxyModel->SetSearchString("A");
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[Name], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[DisplayName], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[Creator], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[Summary], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[Features], QModelIndex()));
}
TEST_F(GemCatalogSearchFilterTests, GemCatalogFilters_SearchStringNoneContainCharacter_ShowsNone)
{
// No gems contain the character "z" or "Z" so none should be shown
m_proxyModel->SetSearchString("z");
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[Name], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[DisplayName], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[Creator], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[Summary], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[Features], QModelIndex()));
}
TEST_F(GemCatalogSearchFilterTests, GemCatalogFilters_SearchStringPartialMatchString_ShowsNone)
{
// Token matching is currently not supported
// The whole string must match a substring
m_proxyModel->SetSearchString("Name Token");
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[Name], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[DisplayName], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[Creator], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[Summary], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[Features], QModelIndex()));
}
class GemCatalogSelectedActiveFilterTests
: public GemCatalogFilterTests
{
public:
void SetUp() override
{
GemCatalogFilterTests::SetUp();
GemInfo gemSelected, gemSelectedDep, gemUnselected, gemUnselectedDep, gemActive, gemInactive;
gemSelected.m_name = "selected";
gemSelectedDep.m_name = "selectedDep";
gemUnselected.m_name = "unselected";
gemUnselectedDep.m_name = "unselectedDep";
gemActive.m_name = "active";
gemInactive.m_name = "inactive";
gemSelected.m_dependencies = QStringList({ "selectedDep" });
gemUnselected.m_dependencies = QStringList({ "unselectedDep" });
m_gemIndices.append(m_gemModel->AddGem(gemSelected));
m_gemIndices.append(m_gemModel->AddGem(gemSelectedDep));
m_gemIndices.append(m_gemModel->AddGem(gemUnselected));
m_gemIndices.append(m_gemModel->AddGem(gemUnselectedDep));
m_gemIndices.append(m_gemModel->AddGem(gemActive));
m_gemIndices.append(m_gemModel->AddGem(gemInactive));
m_gemModel->UpdateGemDependencies();
// Set intial state of catalog with the to be unselected gem currently added along with active gem
GemModel::SetIsAdded(*m_gemModel, m_gemIndices[Unselected], true);
GemModel::SetWasPreviouslyAdded(*m_gemModel, m_gemIndices[Unselected], true);
GemModel::SetIsAdded(*m_gemModel, m_gemIndices[Active], true);
GemModel::SetWasPreviouslyAdded(*m_gemModel, m_gemIndices[Active], true);
// Add selected gem and remove unselected gem
GemModel::SetIsAdded(*m_gemModel, m_gemIndices[Selected], true);
GemModel::SetIsAdded(*m_gemModel, m_gemIndices[Unselected], false);
}
protected:
enum IndexOrder
{
Selected,
SelectedDep,
Unselected,
UnselectedDep,
Active,
Inactive
};
QVector<QModelIndex> m_gemIndices;
};
TEST_F(GemCatalogSelectedActiveFilterTests, GemCatalogFilters_SelectedActiveIntialState_AddedGemsAndDependenciesAreAdded)
{
// Check if gems are all in expected state
// if this test fails all other Selected/Active tests are invalid
EXPECT_TRUE(GemModel::IsAdded(m_gemIndices[Selected]));
EXPECT_TRUE(GemModel::IsAddedDependency(m_gemIndices[SelectedDep]));
EXPECT_FALSE(GemModel::IsAdded(m_gemIndices[Unselected]));
EXPECT_FALSE(GemModel::IsAddedDependency(m_gemIndices[UnselectedDep]));
EXPECT_TRUE(GemModel::IsAdded(m_gemIndices[Active]));
EXPECT_FALSE(GemModel::IsAdded(m_gemIndices[Inactive]));
}
TEST_F(GemCatalogSelectedActiveFilterTests, GemCatalogFilters_SelectedActiveNoFilter_ShowsAll)
{
// Filter is clear
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemIndices[Selected].row(), QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemIndices[SelectedDep].row(), QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemIndices[Unselected].row(), QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemIndices[UnselectedDep].row(), QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemIndices[Active].row(), QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemIndices[Inactive].row(), QModelIndex()));
}
TEST_F(GemCatalogSelectedActiveFilterTests, GemCatalogFilters_FilterSelected_ShowsSelectedAndDependencies)
{
// Check selected filter
// Selected dependencies should also be shown
m_proxyModel->SetGemSelected(GemSortFilterProxyModel::GemSelected::Selected);
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemIndices[Selected].row(), QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemIndices[SelectedDep].row(), QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemIndices[Unselected].row(), QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemIndices[UnselectedDep].row(), QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemIndices[Active].row(), QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemIndices[Inactive].row(), QModelIndex()));
}
TEST_F(GemCatalogSelectedActiveFilterTests, GemCatalogFilters_FilterUnselected_ShowsUnselectedAndDependencies)
{
// Check unselected filter
// Unselected dependencies should also be shown
m_proxyModel->SetGemSelected(GemSortFilterProxyModel::GemSelected::Unselected);
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemIndices[Selected].row(), QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemIndices[SelectedDep].row(), QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemIndices[Unselected].row(), QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemIndices[UnselectedDep].row(), QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemIndices[Active].row(), QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemIndices[Inactive].row(), QModelIndex()));
}
TEST_F(GemCatalogSelectedActiveFilterTests, GemCatalogFilters_FilterSelectedAndUnselected_ShowsAllChangesAndDependencies)
{
// Check both un/selected filter
m_proxyModel->SetGemSelected(GemSortFilterProxyModel::GemSelected::Both);
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemIndices[Selected].row(), QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemIndices[SelectedDep].row(), QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemIndices[Unselected].row(), QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemIndices[UnselectedDep].row(), QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemIndices[Active].row(), QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemIndices[Inactive].row(), QModelIndex()));
}
TEST_F(GemCatalogSelectedActiveFilterTests, GemCatalogFilters_FilterActive_ShowsActive)
{
// Check active filter
// Active dependencies should also be shown
m_proxyModel->SetGemActive(GemSortFilterProxyModel::GemActive::Active);
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemIndices[Selected].row(), QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemIndices[SelectedDep].row(), QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemIndices[Unselected].row(), QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemIndices[UnselectedDep].row(), QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemIndices[Active].row(), QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemIndices[Inactive].row(), QModelIndex()));
}
TEST_F(GemCatalogSelectedActiveFilterTests, GemCatalogFilters_FilterActive_ShowsInactive)
{
// Check inactive filter
m_proxyModel->SetGemActive(GemSortFilterProxyModel::GemActive::Inactive);
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemIndices[Selected].row(), QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemIndices[SelectedDep].row(), QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemIndices[Unselected].row(), QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemIndices[UnselectedDep].row(), QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemIndices[Active].row(), QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemIndices[Inactive].row(), QModelIndex()));
}
class GemCatalogMiscFilterTests
: public GemCatalogFilterTests
{
public:
void SetUp() override
{
GemCatalogFilterTests::SetUp();
GemInfo gemA, gemB, gemC;
gemA.m_name = "Default Audio";
gemB.m_name = "Mobile UX";
gemC.m_name = "City Props";
gemA.m_gemOrigin = GemInfo::GemOrigin::Open3DEngine;
gemB.m_gemOrigin = GemInfo::GemOrigin::Local;
gemC.m_gemOrigin = GemInfo::GemOrigin::Remote;
gemA.m_types = GemInfo::Type::Code;
gemB.m_types = GemInfo::Type::Code | GemInfo::Type::Tool;
gemC.m_types = GemInfo::Type::Asset;
using Plat = GemInfo::Platform;
gemA.m_platforms = Plat::Windows;
gemB.m_platforms = Plat::Android | Plat::iOS;
gemC.m_platforms = Plat::Android | Plat::iOS | Plat::Linux | Plat::macOS | Plat::Windows;
gemA.m_features = QStringList({ "Audio", "Framework", "SDK" });
gemB.m_features = QStringList({ "Framework", "Tools", "UI" });
gemC.m_features = QStringList({ "Assets", "Content", "Environment" });
m_gemRows.append(m_gemModel->AddGem(gemA).row());
m_gemRows.append(m_gemModel->AddGem(gemB).row());
m_gemRows.append(m_gemModel->AddGem(gemC).row());
}
protected:
enum RowOrder
{
DefaultAudio,
MobileUX,
CityProps
};
QVector<int> m_gemRows;
};
TEST_F(GemCatalogMiscFilterTests, GemCatalogFilters_MiscNoFilter_ShowsAll)
{
// No filter
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[DefaultAudio], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[MobileUX], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[CityProps], QModelIndex()));
}
TEST_F(GemCatalogMiscFilterTests, GemCatalogFilters_FilterSingleOrigin_ShowsOriginMatch)
{
m_proxyModel->SetGemOrigins(GemInfo::GemOrigin::Open3DEngine);
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[DefaultAudio], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[MobileUX], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[CityProps], QModelIndex()));
m_proxyModel->SetGemOrigins(GemInfo::GemOrigin::Local);
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[DefaultAudio], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[MobileUX], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[CityProps], QModelIndex()));
m_proxyModel->SetGemOrigins(GemInfo::GemOrigin::Remote);
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[DefaultAudio], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[MobileUX], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[CityProps], QModelIndex()));
}
TEST_F(GemCatalogMiscFilterTests, GemCatalogFilters_FilterMultipleOrigins_ShowsMultipleOriginMatches)
{
m_proxyModel->SetGemOrigins(GemInfo::GemOrigin::Open3DEngine | GemInfo::GemOrigin::Local);
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[DefaultAudio], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[MobileUX], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[CityProps], QModelIndex()));
}
TEST_F(GemCatalogMiscFilterTests, GemCatalogFilters_FilterSingleType_ShowsTypeMatch)
{
m_proxyModel->SetTypes(GemInfo::Type::Code);
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[DefaultAudio], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[MobileUX], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[CityProps], QModelIndex()));
m_proxyModel->SetTypes(GemInfo::Type::Tool);
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[DefaultAudio], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[MobileUX], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[CityProps], QModelIndex()));
m_proxyModel->SetTypes(GemInfo::Type::Asset);
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[DefaultAudio], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[MobileUX], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[CityProps], QModelIndex()));
}
TEST_F(GemCatalogMiscFilterTests, GemCatalogFilters_FilterMultipleTypes_ShowsMultipleTypeMatches)
{
m_proxyModel->SetTypes(GemInfo::Type::Tool | GemInfo::Type::Asset);
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[DefaultAudio], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[MobileUX], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[CityProps], QModelIndex()));
}
TEST_F(GemCatalogMiscFilterTests, GemCatalogFilters_FilterSinglePlatform_ShowsPlatformMatch)
{
m_proxyModel->SetPlatforms(GemInfo::Platform::Windows);
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[DefaultAudio], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[MobileUX], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[CityProps], QModelIndex()));
m_proxyModel->SetPlatforms(GemInfo::Platform::Android);
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[DefaultAudio], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[MobileUX], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[CityProps], QModelIndex()));
m_proxyModel->SetPlatforms(GemInfo::Platform::macOS);
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[DefaultAudio], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[MobileUX], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[CityProps], QModelIndex()));
}
TEST_F(GemCatalogMiscFilterTests, GemCatalogFilters_FilterMultiplePlatforms_ShowsMultiplePlatformMatches)
{
m_proxyModel->SetPlatforms(GemInfo::Platform::Android | GemInfo::Platform::iOS);
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[DefaultAudio], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[MobileUX], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[CityProps], QModelIndex()));
}
TEST_F(GemCatalogMiscFilterTests, GemCatalogFilters_FilterSingleFeature_ShowsFeatureMatch)
{
m_proxyModel->SetFeatures({ "Audio" });
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[DefaultAudio], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[MobileUX], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[CityProps], QModelIndex()));
m_proxyModel->SetFeatures({ "Tools", });
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[DefaultAudio], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[MobileUX], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[CityProps], QModelIndex()));
m_proxyModel->SetFeatures({ "Environment" });
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[DefaultAudio], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[MobileUX], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[CityProps], QModelIndex()));
}
TEST_F(GemCatalogMiscFilterTests, GemCatalogFilters_FilterMultipleFeatures_ShowsMultipleFeatureMatches)
{
m_proxyModel->SetFeatures({ "Assets", "Framework" });
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[DefaultAudio], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[MobileUX], QModelIndex()));
EXPECT_TRUE(m_proxyModel->filterAcceptsRow(m_gemRows[CityProps], QModelIndex()));
}
TEST_F(GemCatalogMiscFilterTests, GemCatalogFilters_FilterPartialMatchFeature_ShowsNone)
{
// Features must be an exact match to filter by them directly
m_proxyModel->SetFeatures({ "Frame" });
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[DefaultAudio], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[MobileUX], QModelIndex()));
EXPECT_FALSE(m_proxyModel->filterAcceptsRow(m_gemRows[CityProps], QModelIndex()));
}
} }
@@ -185,7 +185,7 @@ namespace AZ
if (lodCount > 0) if (lodCount > 0)
{ {
rule->AddLod(); rule->AddLod();
selection.CopyTo(rule->GetNodeSelectionList(index)); selection.CopyTo(rule->GetNodeSelectionList(lodLevel));
} }
else else
{ {
@@ -13,6 +13,7 @@
#include <SceneAPI/SceneCore/Events/AssetImportRequest.h> #include <SceneAPI/SceneCore/Events/AssetImportRequest.h>
#include <SceneAPI/SceneCore/Events/GraphMetaInfoBus.h> #include <SceneAPI/SceneCore/Events/GraphMetaInfoBus.h>
#include <SceneAPI/SceneCore/Events/ManifestMetaInfoBus.h> #include <SceneAPI/SceneCore/Events/ManifestMetaInfoBus.h>
#include <SceneAPI/SceneData/SceneDataConfiguration.h>
namespace AZ namespace AZ
{ {
@@ -27,7 +28,7 @@ namespace AZ
{ {
class LodRule; class LodRule;
class LodRuleBehavior class SCENE_DATA_CLASS LodRuleBehavior
: public SceneCore::BehaviorComponent : public SceneCore::BehaviorComponent
, public Events::ManifestMetaInfoBus::Handler , public Events::ManifestMetaInfoBus::Handler
, public Events::AssetImportRequestBus::Handler , public Events::AssetImportRequestBus::Handler
@@ -36,18 +37,19 @@ namespace AZ
public: public:
AZ_COMPONENT(LodRuleBehavior, "{D2E19864-9A4B-41FD-8ACC-DA6756728CB3}", SceneCore::BehaviorComponent); AZ_COMPONENT(LodRuleBehavior, "{D2E19864-9A4B-41FD-8ACC-DA6756728CB3}", SceneCore::BehaviorComponent);
~LodRuleBehavior() override = default; SCENE_DATA_API ~LodRuleBehavior() override = default;
void Activate() override; SCENE_DATA_API void Activate() override;
void Deactivate() override; SCENE_DATA_API void Deactivate() override;
static void Reflect(ReflectContext* context); static void Reflect(ReflectContext* context);
void InitializeObject(const Containers::Scene& scene, DataTypes::IManifestObject& target) override; SCENE_DATA_API void InitializeObject(const Containers::Scene& scene, DataTypes::IManifestObject& target) override;
Events::ProcessingResult UpdateManifest(Containers::Scene& scene, ManifestAction action, SCENE_DATA_API Events::ProcessingResult UpdateManifest(
Containers::Scene& scene, ManifestAction action,
RequestingApplication requester) override; RequestingApplication requester) override;
void GetVirtualTypeName(AZStd::string& name, Crc32 type) override; SCENE_DATA_API void GetVirtualTypeName(AZStd::string& name, Crc32 type) override;
void GetAllVirtualTypes(AZStd::set<Crc32>& types) override; SCENE_DATA_API void GetAllVirtualTypes(AZStd::set<Crc32>& types) override;
private: private:
size_t SelectLodMeshes(const Containers::Scene& scene, DataTypes::ISceneNodeSelectionList& selection, size_t lodLevel) const; size_t SelectLodMeshes(const Containers::Scene& scene, DataTypes::ISceneNodeSelectionList& selection, size_t lodLevel) const;
@@ -21,7 +21,6 @@ namespace AZ
{ {
const size_t LodRule::m_maxLods; const size_t LodRule::m_maxLods;
AZ_CLASS_ALLOCATOR_IMPL(LodRule, SystemAllocator, 0)
SceneNodeSelectionList& LodRule::GetNodeSelectionList(size_t index) SceneNodeSelectionList& LodRule::GetNodeSelectionList(size_t index)
{ {
+10 -10
View File
@@ -25,26 +25,26 @@ namespace AZ
} }
namespace SceneData namespace SceneData
{ {
class LodRule class SCENE_DATA_CLASS LodRule
: public DataTypes::ILodRule : public DataTypes::ILodRule
{ {
public: public:
AZ_RTTI(LodRule, "{6E796AC8-1484-4909-860A-6D3F22A7346F}", DataTypes::ILodRule); AZ_RTTI(LodRule, "{6E796AC8-1484-4909-860A-6D3F22A7346F}", DataTypes::ILodRule);
AZ_CLASS_ALLOCATOR_DECL AZ_CLASS_ALLOCATOR(LodRule, AZ::SystemAllocator, 0)
~LodRule() override = default; SCENE_DATA_API ~LodRule() override = default;
SceneNodeSelectionList& GetNodeSelectionList(size_t index); SCENE_DATA_API SceneNodeSelectionList& GetNodeSelectionList(size_t index);
DataTypes::ISceneNodeSelectionList& GetSceneNodeSelectionList(size_t index) override; SCENE_DATA_API DataTypes::ISceneNodeSelectionList& GetSceneNodeSelectionList(size_t index) override;
const DataTypes::ISceneNodeSelectionList& GetSceneNodeSelectionList(size_t index) const override; SCENE_DATA_API const DataTypes::ISceneNodeSelectionList& GetSceneNodeSelectionList(size_t index) const override;
size_t GetLodCount() const override; SCENE_DATA_API size_t GetLodCount() const override;
void AddLod(); SCENE_DATA_API void AddLod();
static void Reflect(ReflectContext* context); static void Reflect(ReflectContext* context);
//The engine supports 6 total lods. 1 for the base model then 5 more lods. //The engine supports 6 total lods. 1 for the base model then 5 more lods.
//The rule only captures lods past level 0 so this is set to 5. //The rule only captures lods past level 0 so this is set to 5.
static const size_t m_maxLods = 5; static const size_t m_maxLods = 5;
protected: protected:
@@ -11,5 +11,6 @@ set(FILES
Tests/GraphData/MeshDataTests.cpp Tests/GraphData/MeshDataTests.cpp
Tests/GraphData/MeshDataPrimitiveUtilsTests.cpp Tests/GraphData/MeshDataPrimitiveUtilsTests.cpp
Tests/GraphData/GraphDataBehaviorTests.cpp Tests/GraphData/GraphDataBehaviorTests.cpp
Tests/GraphData/RulesTests.cpp
Tests/SceneManifest/SceneManifestRuleTests.cpp Tests/SceneManifest/SceneManifestRuleTests.cpp
) )
@@ -0,0 +1,78 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzTest/AzTest.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneData/GraphData/MeshData.h>
#include <SceneAPI/SceneData/Behaviors/LodRuleBehavior.h>
#include <SceneAPI/SceneCore/Events/AssetImportRequest.h>
#include <SceneAPI/SceneData/Behaviors/Registry.h>
#include <SceneAPI/SceneData/Groups/MeshGroup.h>
#include <SceneAPI/SceneData/Rules/LodRule.h>
#include <SceneAPI/SceneData/Rules/TangentsRule.h>
namespace AZ
{
namespace SceneData
{
struct SoftNameMock
: SceneAPI::Events::GraphMetaInfoBus::Handler
{
SoftNameMock()
{
BusConnect();
}
~SoftNameMock() override
{
BusDisconnect();
}
void GetVirtualTypes(AZStd::set<Crc32>& types, const SceneAPI::Containers::Scene&, SceneAPI::Containers::SceneGraph::NodeIndex) override
{
// Indicate this node is a LOD1 type
types.emplace(AZ_CRC_CE("LODMesh1"));
}
};
TEST(LOD, LODRuleTest)
{
// Test that UpdateManifest doesn't crash when trying to auto-add new LOD levels
SoftNameMock softNameMock;
SceneAPI::SceneData::LodRuleBehavior lod;
SceneAPI::Containers::Scene scene("test");
auto lodRule = AZStd::shared_ptr<SceneAPI::SceneData::LodRule>(aznew SceneAPI::SceneData::LodRule());
scene.GetManifest().AddEntry(lodRule);
auto group = AZStd::shared_ptr<SceneAPI::SceneData::MeshGroup>(aznew SceneAPI::SceneData::MeshGroup());
// Add a bunch of other rules first
// This is necessary to replicate the bug condition where the index of the rule is used instead of the index of the LOD
for (int i = 0; i < 5; ++i)
{
auto tangentsRule = AZStd::shared_ptr<SceneAPI::SceneData::TangentsRule>(aznew SceneAPI::SceneData::TangentsRule());
group->GetRuleContainer().AddRule(tangentsRule);
}
group->GetRuleContainer().AddRule(lodRule);
scene.GetManifest().AddEntry(group);
auto meshData = AZStd::shared_ptr<GraphData::MeshData>(new GraphData::MeshData());
scene.GetGraph().AddChild(scene.GetGraph().GetRoot(), "test", meshData);
EXPECT_EQ(lodRule->GetLodCount(), 0);
// This should auto-add 1 LOD because of the "test" node we added above along with the SoftNameMock which will report it as an LOD1
lod.UpdateManifest(scene, SceneAPI::Events::AssetImportRequest::Update, SceneAPI::Events::AssetImportRequest::Generic);
EXPECT_EQ(lodRule->GetLodCount(), 1);
}
}
}
@@ -140,9 +140,18 @@ namespace AZ
m_drawData.push_back(drawData); m_drawData.push_back(drawData);
} }
int ImGuiPass::GetTickOrder()
{
// We have to call ImGui::NewFrame (which happens in ImGuiPass::OnTick) after setting
// ImGui::GetIO().NavInputs (which happens in ImGuiPass::OnInputChannelEventFiltered),
// but before ImGui::Render (which happens in ImGuiPass::SetupFrameGraphDependencies).
return AZ::ComponentTickBus::TICK_PRE_RENDER;
}
void ImGuiPass::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint timePoint) void ImGuiPass::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint timePoint)
{ {
auto imguiContextScope = ImguiContextScope(m_imguiContext); auto imguiContextScope = ImguiContextScope(m_imguiContext);
ImGui::NewFrame();
auto& io = ImGui::GetIO(); auto& io = ImGui::GetIO();
io.DeltaTime = deltaTime; io.DeltaTime = deltaTime;
@@ -413,6 +422,7 @@ namespace AZ
void ImGuiPass::Init() void ImGuiPass::Init()
{ {
auto imguiContextScope = ImguiContextScope(m_imguiContext);
auto& io = ImGui::GetIO(); auto& io = ImGui::GetIO();
// ImGui IO Setup // ImGui IO Setup
@@ -421,7 +431,6 @@ namespace AZ
{ {
io.KeyMap[static_cast<ImGuiKey_>(i)] = static_cast<int>(i); io.KeyMap[static_cast<ImGuiKey_>(i)] = static_cast<int>(i);
} }
io.NavActive = true;
// Touch input // Touch input
const AzFramework::InputDevice* inputDevice = nullptr; const AzFramework::InputDevice* inputDevice = nullptr;
@@ -434,6 +443,17 @@ namespace AZ
io.ConfigFlags |= ImGuiConfigFlags_IsTouchScreen; io.ConfigFlags |= ImGuiConfigFlags_IsTouchScreen;
} }
// Gamepad input
inputDevice = nullptr;
AzFramework::InputDeviceRequestBus::EventResult(inputDevice,
AzFramework::InputDeviceGamepad::IdForIndex0,
&AzFramework::InputDeviceRequests::GetInputDevice);
if (inputDevice && inputDevice->IsSupported())
{
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
}
// Set initial display size to something reasonable (this will be updated in FramePrepare) // Set initial display size to something reasonable (this will be updated in FramePrepare)
io.DisplaySize.x = 1920; io.DisplaySize.x = 1920;
io.DisplaySize.y = 1080; io.DisplaySize.y = 1080;
@@ -571,7 +591,6 @@ namespace AZ
auto imguiContextScope = ImguiContextScope(m_imguiContext); auto imguiContextScope = ImguiContextScope(m_imguiContext);
ImGui::GetIO().MouseWheel = m_lastFrameMouseWheel; ImGui::GetIO().MouseWheel = m_lastFrameMouseWheel;
m_lastFrameMouseWheel = 0.0; m_lastFrameMouseWheel = 0.0;
ImGui::NewFrame();
} }
void ImGuiPass::BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context) void ImGuiPass::BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context)
@@ -77,6 +77,7 @@ namespace AZ
void RenderImguiDrawData(const ImDrawData& drawData); void RenderImguiDrawData(const ImDrawData& drawData);
// TickBus::Handler overrides... // TickBus::Handler overrides...
int GetTickOrder() override;
void OnTick(float deltaTime, AZ::ScriptTimePoint timePoint) override; void OnTick(float deltaTime, AZ::ScriptTimePoint timePoint) override;
// AzFramework::InputTextEventListener overrides... // AzFramework::InputTextEventListener overrides...
@@ -67,12 +67,12 @@ namespace AZ
void AtomBridgeSystemComponent::GetProvidedServices(ComponentDescriptor::DependencyArrayType& provided) void AtomBridgeSystemComponent::GetProvidedServices(ComponentDescriptor::DependencyArrayType& provided)
{ {
provided.push_back(AZ_CRC("AtomBridgeService", 0xdb816a99)); provided.push_back(AZ_CRC("AtomBridgeService", 0x92d990b5));
} }
void AtomBridgeSystemComponent::GetIncompatibleServices(ComponentDescriptor::DependencyArrayType& incompatible) void AtomBridgeSystemComponent::GetIncompatibleServices(ComponentDescriptor::DependencyArrayType& incompatible)
{ {
incompatible.push_back(AZ_CRC("AtomBridgeService", 0xdb816a99)); incompatible.push_back(AZ_CRC("AtomBridgeService", 0x92d990b5));
} }
void AtomBridgeSystemComponent::GetRequiredServices(ComponentDescriptor::DependencyArrayType& required) void AtomBridgeSystemComponent::GetRequiredServices(ComponentDescriptor::DependencyArrayType& required)
@@ -109,12 +109,12 @@ namespace AZ
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services) static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{ {
services.push_back(AZ_CRC("AssetCollectionAsyncLoaderTest", 0xdd5ab934)); services.push_back(AZ_CRC("AssetCollectionAsyncLoaderTest", 0x66d04369));
} }
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services) static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{ {
services.push_back(AZ_CRC("AssetCollectionAsyncLoaderTest", 0xdd5ab934)); services.push_back(AZ_CRC("AssetCollectionAsyncLoaderTest", 0x66d04369));
} }
static void Reflect(AZ::ReflectContext* context); static void Reflect(AZ::ReflectContext* context);
@@ -73,7 +73,7 @@ namespace AZ::Render
void AtomViewportDisplayIconsSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) void AtomViewportDisplayIconsSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{ {
required.push_back(AZ_CRC("RPISystem", 0xf2add773)); required.push_back(AZ_CRC("RPISystem", 0xf2add773));
required.push_back(AZ_CRC("AtomBridgeService", 0xdb816a99)); required.push_back(AZ_CRC("AtomBridgeService", 0x92d990b5));
} }
void AtomViewportDisplayIconsSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent) void AtomViewportDisplayIconsSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent)
@@ -59,12 +59,12 @@ namespace AZ
void OcclusionCullingPlaneComponentController::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) void OcclusionCullingPlaneComponentController::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{ {
provided.push_back(AZ_CRC("OcclusionCullingPlaneService", 0x9123f33d)); provided.push_back(AZ_CRC("OcclusionCullingPlaneService", 0x7d036c2e));
} }
void OcclusionCullingPlaneComponentController::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) void OcclusionCullingPlaneComponentController::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{ {
incompatible.push_back(AZ_CRC("OcclusionCullingPlaneService", 0x9123f33d)); incompatible.push_back(AZ_CRC("OcclusionCullingPlaneService", 0x7d036c2e));
} }
void OcclusionCullingPlaneComponentController::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) void OcclusionCullingPlaneComponentController::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
@@ -31,12 +31,12 @@ namespace Blast
private: private:
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{ {
provided.push_back(AZ_CRC("BlastEditorService", 0x0a61cda5)); provided.push_back(AZ_CRC("BlastEditorService", 0xeddfed0d));
} }
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{ {
required.push_back(AZ_CRC("BlastService", 0x75beae2d)); required.push_back(AZ_CRC("BlastService", 0x46927a9f));
} }
AZStd::unique_ptr<EditorBlastChunksAssetHandler> m_editorBlastChunksAssetHandler; AZStd::unique_ptr<EditorBlastChunksAssetHandler> m_editorBlastChunksAssetHandler;
@@ -33,7 +33,7 @@ namespace GraphModelIntegration
//! Constructor //! Constructor
//! \param nodeName Name of the node that will show up in the Palette //! \param nodeName Name of the node that will show up in the Palette
//! \param editorId Unique name of the client system editor (ex: AZ_CRC("ShaderCanvas", 0xa6d1a85a)) //! \param editorId Unique name of the client system editor (ex: AZ_CRC("ShaderCanvas", 0x0a1dff96))
//! \param dataType The type of data that the InputGraphNode or OutputGraphNode will represent //! \param dataType The type of data that the InputGraphNode or OutputGraphNode will represent
InputOutputNodePaletteItem(AZStd::string_view nodeName, GraphCanvas::EditorId editorId, GraphModel::DataTypePtr dataType) InputOutputNodePaletteItem(AZStd::string_view nodeName, GraphCanvas::EditorId editorId, GraphModel::DataTypePtr dataType)
: DraggableNodePaletteTreeItem(nodeName, editorId) : DraggableNodePaletteTreeItem(nodeName, editorId)
@@ -95,7 +95,7 @@ namespace GraphModelIntegration
AZ_CLASS_ALLOCATOR(ModuleNodePaletteItem, AZ::SystemAllocator, 0); AZ_CLASS_ALLOCATOR(ModuleNodePaletteItem, AZ::SystemAllocator, 0);
//! Constructor //! Constructor
//! \param editorId Unique name of the client system editor (ex: AZ_CRC("ShaderCanvas", 0xa6d1a85a)) //! \param editorId Unique name of the client system editor (ex: AZ_CRC("ShaderCanvas", 0x0a1dff96))
//! \param sourceFileId The unique id for the module node graph source file. //! \param sourceFileId The unique id for the module node graph source file.
//! \param sourceFilePath The path to the module node graph source file. This will be used for node naming and debug output. //! \param sourceFilePath The path to the module node graph source file. This will be used for node naming and debug output.
ModuleNodePaletteItem(GraphCanvas::EditorId editorId, AZ::Uuid sourceFileId, AZStd::string_view sourceFilePath) ModuleNodePaletteItem(GraphCanvas::EditorId editorId, AZ::Uuid sourceFileId, AZStd::string_view sourceFilePath)
@@ -34,7 +34,7 @@ namespace GraphModelIntegration
//! Constructor //! Constructor
//! \param nodeName Name of the node that will show up in the Palette //! \param nodeName Name of the node that will show up in the Palette
//! \param editorId Unique name of the client system editor (ex: AZ_CRC("ShaderCanvas", 0xa6d1a85a)) //! \param editorId Unique name of the client system editor (ex: AZ_CRC("ShaderCanvas", 0x0a1dff96))
StandardNodePaletteItem(AZStd::string_view nodeName, GraphCanvas::EditorId editorId) StandardNodePaletteItem(AZStd::string_view nodeName, GraphCanvas::EditorId editorId)
: DraggableNodePaletteTreeItem(nodeName, editorId) : DraggableNodePaletteTreeItem(nodeName, editorId)
{ {
+9 -25
View File
@@ -172,7 +172,6 @@ void ImGuiManager::Initialize()
// Broadcast ImGui Ready to Listeners // Broadcast ImGui Ready to Listeners
ImGuiUpdateListenerBus::Broadcast(&IImGuiUpdateListener::OnImGuiInitialize); ImGuiUpdateListenerBus::Broadcast(&IImGuiUpdateListener::OnImGuiInitialize);
m_currentControllerIndex = -1;
m_button1Pressed = m_button2Pressed = false; m_button1Pressed = m_button2Pressed = false;
m_menuBarStatusChanged = false; m_menuBarStatusChanged = false;
@@ -227,6 +226,7 @@ void ImGui::ImGuiManager::RestoreRenderWindowSizeToDefault()
void ImGui::ImGuiManager::SetDpiScalingFactor(float dpiScalingFactor) void ImGui::ImGuiManager::SetDpiScalingFactor(float dpiScalingFactor)
{ {
ImGui::ImGuiContextScope contextScope(m_imguiContext);
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
// Set the global font scale to size our UI to the scaling factor // Set the global font scale to size our UI to the scaling factor
// Note: Currently we use the default, 13px fixed-size IMGUI font, so this can get somewhat blurry // Note: Currently we use the default, 13px fixed-size IMGUI font, so this can get somewhat blurry
@@ -235,6 +235,7 @@ void ImGui::ImGuiManager::SetDpiScalingFactor(float dpiScalingFactor)
float ImGui::ImGuiManager::GetDpiScalingFactor() const float ImGui::ImGuiManager::GetDpiScalingFactor() const
{ {
ImGui::ImGuiContextScope contextScope(m_imguiContext);
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
return io.FontGlobalScale; return io.FontGlobalScale;
} }
@@ -406,7 +407,7 @@ bool ImGuiManager::OnInputChannelEventFiltered(const InputChannel& inputChannel)
// Cycle through ImGui Menu Bar States on Home button press // Cycle through ImGui Menu Bar States on Home button press
if (inputChannelId == InputDeviceKeyboard::Key::NavigationHome) if (inputChannelId == InputDeviceKeyboard::Key::NavigationHome)
{ {
ToggleThroughImGuiVisibleState(-1); ToggleThroughImGuiVisibleState();
} }
// Cycle through Standalone Editor Window States // Cycle through Standalone Editor Window States
@@ -453,19 +454,10 @@ bool ImGuiManager::OnInputChannelEventFiltered(const InputChannel& inputChannel)
} }
// Handle Controller Inputs // Handle Controller Inputs
int inputControllerIndex = -1;
bool controllerInput = false;
if (InputDeviceGamepad::IsGamepadDevice(inputDeviceId)) if (InputDeviceGamepad::IsGamepadDevice(inputDeviceId))
{ {
inputControllerIndex = inputDeviceId.GetIndex(); // Only pipe in Controller Nav Inputs when at least 1 of the two controller modes are enabled.
controllerInput = true; if (m_controllerModeFlags)
}
if (controllerInput)
{
// Only pipe in Controller Nav Inputs if we are the current Controller Index and at least 1 of the two controller modes are enabled.
if (m_currentControllerIndex == inputControllerIndex && m_controllerModeFlags)
{ {
const auto lyButtonToImGuiNav = s_lyInputToImGuiNavIndexMap.find(inputChannelId); const auto lyButtonToImGuiNav = s_lyInputToImGuiNavIndexMap.find(inputChannelId);
if (lyButtonToImGuiNav != s_lyInputToImGuiNavIndexMap.end()) if (lyButtonToImGuiNav != s_lyInputToImGuiNavIndexMap.end())
@@ -476,7 +468,7 @@ bool ImGuiManager::OnInputChannelEventFiltered(const InputChannel& inputChannel)
} }
//Switch menu bar display only if two buttons are pressed at the same time //Switch menu bar display only if two buttons are pressed at the same time
if (inputChannelId == InputDeviceGamepad::Button::L3) if (inputChannelId == InputDeviceGamepad::Button::L1)
{ {
if (inputChannel.IsStateBegan()) if (inputChannel.IsStateBegan())
{ {
@@ -488,7 +480,7 @@ bool ImGuiManager::OnInputChannelEventFiltered(const InputChannel& inputChannel)
m_menuBarStatusChanged = false; m_menuBarStatusChanged = false;
} }
} }
if (inputChannelId == InputDeviceGamepad::Button::R3) if (inputChannelId == InputDeviceGamepad::Button::R1)
{ {
if (inputChannel.IsStateBegan()) if (inputChannel.IsStateBegan())
{ {
@@ -502,7 +494,7 @@ bool ImGuiManager::OnInputChannelEventFiltered(const InputChannel& inputChannel)
} }
if (!m_menuBarStatusChanged && m_button1Pressed && m_button2Pressed) if (!m_menuBarStatusChanged && m_button1Pressed && m_button2Pressed)
{ {
ToggleThroughImGuiVisibleState(inputControllerIndex); ToggleThroughImGuiVisibleState();
} }
// If we have the Discrete Input Mode Enabled.. and we are in the Visible State, then consume input here // If we have the Discrete Input Mode Enabled.. and we are in the Visible State, then consume input here
@@ -627,14 +619,13 @@ bool ImGuiManager::OnInputTextEventFiltered(const AZStd::string& textUTF8)
return io.WantTextInput && m_clientMenuBarState == DisplayState::Visible;; return io.WantTextInput && m_clientMenuBarState == DisplayState::Visible;;
} }
void ImGuiManager::ToggleThroughImGuiVisibleState(int controllerIndex) void ImGuiManager::ToggleThroughImGuiVisibleState()
{ {
ImGui::ImGuiContextScope contextScope(m_imguiContext); ImGui::ImGuiContextScope contextScope(m_imguiContext);
switch (m_clientMenuBarState) switch (m_clientMenuBarState)
{ {
case DisplayState::Hidden: case DisplayState::Hidden:
m_currentControllerIndex = controllerIndex;
m_clientMenuBarState = DisplayState::Visible; m_clientMenuBarState = DisplayState::Visible;
// Draw the ImGui Mouse cursor if either the hardware mouse is connected, or the controller mouse is enabled. // Draw the ImGui Mouse cursor if either the hardware mouse is connected, or the controller mouse is enabled.
@@ -669,7 +660,6 @@ void ImGuiManager::ToggleThroughImGuiVisibleState(int controllerIndex)
default: default:
m_clientMenuBarState = DisplayState::Hidden; m_clientMenuBarState = DisplayState::Hidden;
m_currentControllerIndex = -1;
// Enable system cursor if it's in editor and it's not editor game mode // Enable system cursor if it's in editor and it's not editor game mode
if (gEnv->IsEditor() && !gEnv->IsEditorGameMode()) if (gEnv->IsEditor() && !gEnv->IsEditorGameMode())
@@ -686,12 +676,6 @@ void ImGuiManager::ToggleThroughImGuiVisibleState(int controllerIndex)
m_setEnabledEvent.Signal(m_clientMenuBarState == DisplayState::Hidden); m_setEnabledEvent.Signal(m_clientMenuBarState == DisplayState::Hidden);
} }
void ImGuiManager::ToggleThroughImGuiVisibleState()
{
ToggleThroughImGuiVisibleState(-1);
}
void ImGuiManager::RenderImGuiBuffers(const ImVec2& scaleRects) void ImGuiManager::RenderImGuiBuffers(const ImVec2& scaleRects)
{ {
ImGui::ImGuiContextScope contextScope(m_imguiContext); ImGui::ImGuiContextScope contextScope(m_imguiContext);
-5
View File
@@ -76,9 +76,6 @@ namespace ImGui
// Sets up initial window size and listens for changes // Sets up initial window size and listens for changes
void InitWindowSize(); void InitWindowSize();
// A function to toggle through the available ImGui Visibility States
void ToggleThroughImGuiVisibleState(int controllerIndex);
private: private:
ImGuiContext* m_imguiContext = nullptr; ImGuiContext* m_imguiContext = nullptr;
DisplayState m_clientMenuBarState = DisplayState::Hidden; DisplayState m_clientMenuBarState = DisplayState::Hidden;
@@ -96,8 +93,6 @@ namespace ImGui
std::vector<uint16> m_idxBuffer; std::vector<uint16> m_idxBuffer;
//Controller navigation //Controller navigation
static const int MaxControllerNumber = 4;
int m_currentControllerIndex;
bool m_button1Pressed, m_button2Pressed, m_menuBarStatusChanged; bool m_button1Pressed, m_button2Pressed, m_menuBarStatusChanged;
bool m_hardwardeMouseConnected = false; bool m_hardwardeMouseConnected = false;
@@ -22,7 +22,7 @@ namespace LmbrCentral
{ {
AZ_CLASS_ALLOCATOR_IMPL(EditorTubeShapeComponentMode, AZ::SystemAllocator, 0) AZ_CLASS_ALLOCATOR_IMPL(EditorTubeShapeComponentMode, AZ::SystemAllocator, 0)
static const AZ::Crc32 s_resetVariableRadii = AZ_CRC("com.o3de.action.tubeshape.reset_radii", 0x0f2ef8e2); static const AZ::Crc32 s_resetVariableRadii = AZ_CRC("com.o3de.action.tubeshape.reset_radii", 0xa987659c);
static const char* const s_resetRadiiTitle = "Reset Radii"; static const char* const s_resetRadiiTitle = "Reset Radii";
static const char* const s_resetRadiiDesc = "Reset all variable radius values to the default"; static const char* const s_resetRadiiDesc = "Reset all variable radius values to the default";
@@ -50,7 +50,7 @@ class PropertyHandlerUiParticleColorKeyframe
public: public:
AZ_CLASS_ALLOCATOR(PropertyHandlerUiParticleColorKeyframe, AZ::SystemAllocator, 0); AZ_CLASS_ALLOCATOR(PropertyHandlerUiParticleColorKeyframe, AZ::SystemAllocator, 0);
AZ::u32 GetHandlerName(void) const override { return AZ_CRC("UiParticleColorKeyframeCtrl", 0x8cb3a9f1); } AZ::u32 GetHandlerName(void) const override { return AZ_CRC("UiParticleColorKeyframeCtrl", 0xe3ef28b6); }
bool IsDefaultHandler() const override { return true; } bool IsDefaultHandler() const override { return true; }
QWidget* CreateGUI(QWidget* pParent) override; QWidget* CreateGUI(QWidget* pParent) override;
void ConsumeAttribute(PropertyUiParticleColorKeyframeCtrl* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override; void ConsumeAttribute(PropertyUiParticleColorKeyframeCtrl* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override;
@@ -50,7 +50,7 @@ class PropertyHandlerUiParticleFloatKeyframe
public: public:
AZ_CLASS_ALLOCATOR(PropertyHandlerUiParticleFloatKeyframe, AZ::SystemAllocator, 0); AZ_CLASS_ALLOCATOR(PropertyHandlerUiParticleFloatKeyframe, AZ::SystemAllocator, 0);
AZ::u32 GetHandlerName(void) const override { return AZ_CRC("UiParticleFloatKeyframeCtrl", 0xba9359a2); } AZ::u32 GetHandlerName(void) const override { return AZ_CRC("UiParticleFloatKeyframeCtrl", 0x448a90ec); }
bool IsDefaultHandler() const override { return true; } bool IsDefaultHandler() const override { return true; }
QWidget* CreateGUI(QWidget* pParent) override; QWidget* CreateGUI(QWidget* pParent) override;
void ConsumeAttribute(PropertyUiParticleFloatKeyframeCtrl* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override; void ConsumeAttribute(PropertyUiParticleFloatKeyframeCtrl* GUI, AZ::u32 attrib, AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override;
@@ -31,12 +31,12 @@ namespace LyShine
void LyShineLoadScreenComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) void LyShineLoadScreenComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{ {
provided.emplace_back(AZ_CRC("LyShineLoadScreenService", 0xBB5EAB17)); provided.emplace_back(AZ_CRC("LyShineLoadScreenService", 0xbb5eab17));
} }
void LyShineLoadScreenComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) void LyShineLoadScreenComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{ {
incompatible.emplace_back(AZ_CRC("LyShineLoadScreenService", 0xBB5EAB17)); incompatible.emplace_back(AZ_CRC("LyShineLoadScreenService", 0xbb5eab17));
} }
void LyShineLoadScreenComponent::Init() void LyShineLoadScreenComponent::Init()
@@ -27,10 +27,10 @@ namespace PhysX
namespace namespace
{ {
//! Uri's for shortcut actions. //! Uri's for shortcut actions.
const AZ::Crc32 SetDimensionsSubModeActionUri = AZ_CRC("com.o3de.action.physx.setdimensionssubmode", 0x77b70dd6); const AZ::Crc32 SetDimensionsSubModeActionUri = AZ_CRC("com.o3de.action.physx.setdimensionssubmode", 0x508b1781);
const AZ::Crc32 SetOffsetSubModeActionUri = AZ_CRC("com.o3de.action.physx.setoffsetsubmode", 0xc06132e5); const AZ::Crc32 SetOffsetSubModeActionUri = AZ_CRC("com.o3de.action.physx.setoffsetsubmode", 0x777ac743);
const AZ::Crc32 SetRotationSubModeActionUri = AZ_CRC("com.o3de.action.physx.setrotationsubmode", 0xc4225918); const AZ::Crc32 SetRotationSubModeActionUri = AZ_CRC("com.o3de.action.physx.setrotationsubmode", 0xf1a8f3ff);
const AZ::Crc32 ResetSubModeActionUri = AZ_CRC("com.o3de.action.physx.resetsubmode", 0xb70b120e); const AZ::Crc32 ResetSubModeActionUri = AZ_CRC("com.o3de.action.physx.resetsubmode", 0x599d1594);
} // namespace } // namespace
AZ_CLASS_ALLOCATOR_IMPL(ColliderComponentMode, AZ::SystemAllocator, 0); AZ_CLASS_ALLOCATOR_IMPL(ColliderComponentMode, AZ::SystemAllocator, 0);
@@ -32,7 +32,7 @@ namespace PhysXDebug
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{ {
provided.push_back(AZ_CRC("PhysXDebugEditorService", 0xe3dde7d8)); provided.push_back(AZ_CRC("PhysXDebugEditorService", 0xf8611967));
} }
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
@@ -33,8 +33,8 @@ namespace WhiteBox
AZ::Color, cl_whiteBoxVertexIndicatorColor, AZ::Color::CreateFromRgba(0, 0, 0, 102), nullptr, AZ::Color, cl_whiteBoxVertexIndicatorColor, AZ::Color::CreateFromRgba(0, 0, 0, 102), nullptr,
AZ::ConsoleFunctorFlags::Null, "The color of the vertex indicator"); AZ::ConsoleFunctorFlags::Null, "The color of the vertex indicator");
static const AZ::Crc32 HideEdge = AZ_CRC("com.o3de.action.whitebox.hide_edge", 0x6a60ae23); static const AZ::Crc32 HideEdge = AZ_CRC("com.o3de.action.whitebox.hide_edge", 0x84f6a9b9);
static const AZ::Crc32 HideVertex = AZ_CRC("com.o3de.action.whitebox.hide_vertex", 0x4a4bd092); static const AZ::Crc32 HideVertex = AZ_CRC("com.o3de.action.whitebox.hide_vertex", 0x5f81c937);
static const char* const HideEdgeTitle = "Hide Edge"; static const char* const HideEdgeTitle = "Hide Edge";
static const char* const HideEdgeDesc = "Hide the selected edge to merge the two connected polygons"; static const char* const HideEdgeDesc = "Hide the selected edge to merge the two connected polygons";
-1
View File
@@ -16,7 +16,6 @@ if(LY_MONOLITHIC_GAME)
ly_set(PAL_TRAIT_BUILD_HOST_TOOLS FALSE) ly_set(PAL_TRAIT_BUILD_HOST_TOOLS FALSE)
ly_set(PAL_TRAIT_BUILD_HOST_GUI_TOOLS FALSE) ly_set(PAL_TRAIT_BUILD_HOST_GUI_TOOLS FALSE)
ly_set(PAL_TRAIT_BUILD_TESTS_SUPPORTED FALSE) ly_set(PAL_TRAIT_BUILD_TESTS_SUPPORTED FALSE)
ly_set(PAL_TRAIT_BUILD_SERVER_SUPPORTED FALSE)
else() else()
ly_set(PAL_TRAIT_MONOLITHIC_DRIVEN_LIBRARY_TYPE SHARED) ly_set(PAL_TRAIT_MONOLITHIC_DRIVEN_LIBRARY_TYPE SHARED)
ly_set(PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE GEM_MODULE) ly_set(PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE GEM_MODULE)
-130
View File
@@ -1,130 +0,0 @@
#
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#
from __future__ import absolute_import
import os
import re
import json
import sys
try:
import six
except ImportError:
import pip
pip.main(['install', 'six', '--ignore-installed', '-q'])
import six
from pathlib import Path
this_file_path = os.path.dirname(os.path.realpath(__file__))
# resolve symlinks and eliminate ".." components
engine_root_path = Path(__file__).resolve().parents[3]
def convert_glob_pattern_to_regex_pattern(glob_pattern):
# switch to forward slashes because way easier to pattern match against
pattern = re.sub(r'\\', r'/', glob_pattern)
# Replace the dots and question marks
pattern = re.sub(r'\.', r'\\.', pattern)
pattern = re.sub(r'\?', r'.', pattern)
# Handle the * vs ** expansions
pattern = re.sub(r'([^*])\*($|[^*])', r'\1[^/\\\\]*\2', pattern)
pattern = re.sub(r'\*\*/', r'(.*/)?', pattern)
pattern = re.sub(r'\*\*', r'.*', pattern)
# replace the forward slashes with [/\\] so it works on PC/unix
pattern = re.sub(r'([^^])/', r'\1[/\\\\]', pattern)
return pattern
# Convert the package json into a pair of regexes we can use to look for includes and excludes
def convert_glob_list_to_regex_list(filelist, prefix):
includes = []
excludes = []
for key, value in six.iteritems(filelist):
glob_pattern = os.path.join(prefix, key)
if isinstance(value, dict):
(sub_includes, sub_excludes) = convert_glob_list_to_regex_list(value, glob_pattern)
includes.extend(sub_includes)
excludes.extend(sub_excludes)
else:
# Simulate what glob would do with file walking to scope the * within a directory
# and ** across directories
regex_pattern = convert_glob_pattern_to_regex_pattern(os.path.normpath(glob_pattern))
# Deal with the commands. include/exclude are straight forward. Moves/renames are to be considered
# includes, and we will stick with validating the original contents for now
if value == "#include":
includes.append(regex_pattern)
elif value == "#exclude":
excludes.append(regex_pattern)
elif value.startswith('#move:'):
includes.append(regex_pattern)
elif value.startswith('#rename:'):
includes.append(regex_pattern)
else:
pass
return (includes, excludes)
def generate_excludes_for_platform(root, platform):
if platform == 'all':
platform_exclusions_filename = os.path.join(this_file_path, 'platform_exclusions.json')
with open(platform_exclusions_filename, 'r') as platform_exclusions_file:
platform_exclusions = json.load(platform_exclusions_file)
else:
# Use real path in case root is a symlink path
if os.name == 'posix' and os.path.islink(root):
root = os.readlink(root)
# "root" is the root of the folder structure we're validating
# "engine_root_path" is the engine root where the restricted platform folder is linked
relative_folder = os.path.relpath(this_file_path, engine_root_path)
platform_exclusions_filename = os.path.join(engine_root_path, 'restricted', platform, relative_folder, platform.lower() + '_exclusions.json')
with open(platform_exclusions_filename, 'r') as platform_exclusions_file:
platform_exclusions = json.load(platform_exclusions_file)
if platform not in platform_exclusions:
raise KeyError('No {} found in {}'.format(platform, platform_exclusions_filename))
if '@lyengine' not in platform_exclusions[platform]:
raise KeyError('No {}/@lyengine found in {}'.format(platform, package_file_list))
(_, excludes) = convert_glob_list_to_regex_list(platform_exclusions[platform]['@lyengine'], root)
del _
return excludes
def generate_include_exclude_regexes(package_platform, package_type, root, prohibited_platforms):
# The general contents will be indicated by the package file
if package_type == 'all':
package_file_list = os.path.join(this_file_path, 'package_filelists', 'all.json')
else:
# Search non-restricted platform first
package_file_list = os.path.join(this_file_path, 'Platform', package_platform, 'package_filelists', f'{package_type}.json')
if not os.path.exists(filelist):
# Use real path in case root is a symlink path
if os.name == 'posix' and os.path.islink(root):
root = os.readlink(root)
# "root" is the root of the folder structure we're validating
# "engine_root_path" is the engine root where the restricted platform folder is linked
rel_path = os.path.relpath(this_file_path, engine_root_path)
package_file_list = os.path.join(engine_root_path, 'restricted', package_platform, rel_path, 'package_filelists',
f'{package_type}.json')
with open(package_file_list, 'r') as package_file:
package = json.load(package_file)
if '@lyengine' not in package:
raise KeyError('No @lyengine found in {}'.format(package_file_list))
(includes_list, excludes_list) = convert_glob_list_to_regex_list(package['@lyengine'], root)
prohibited_platforms.append('all')
# Add the exclusions of each prohibited platform
for p in prohibited_platforms:
excludes_list.extend(generate_excludes_for_platform(root, p))
includes = re.compile('|'.join(includes_list), re.IGNORECASE)
excludes = re.compile('|'.join(excludes_list), re.IGNORECASE)
return (includes, excludes)
def generate_exclude_regexes_for_platform(root, platform):
return re.compile('|'.join(generate_excludes_for_platform(root, platform)), re.IGNORECASE)
+2 -4
View File
@@ -6,10 +6,8 @@
# #
# #
# this ctest makes sure that the commit validation function
# also runs its tests during commit validation!
ly_add_pytest( ly_add_pytest(
NAME test_commit_validation NAME test_commit_validation
PATH ${CMAKE_CURRENT_LIST_DIR} PATH ${CMAKE_CURRENT_LIST_DIR}/commit_validation/tests
TEST_SUITE smoke
) )
@@ -181,4 +181,6 @@ EXCLUDED_VALIDATION_PATTERNS = [
'restricted/*/Tools/*RemoteControl', 'restricted/*/Tools/*RemoteControl',
'*/user/Cache/*', '*/user/Cache/*',
'*/user/log/*', '*/user/log/*',
'*/user/log_test_1/*',
'*/user/log_test_2/*',
] ]
@@ -0,0 +1,47 @@
#
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#
import unittest
from unittest.mock import patch, mock_open
from commit_validation.tests.mocks.mock_commit import MockCommit
from commit_validation.validators.crc_validator import CrcValidator
class CrcValidatorTests(unittest.TestCase):
@patch('builtins.open', mock_open(read_data='This file does not contain an AZ_CRC macro'))
def test_fileWithNoCrc_passes(self):
commit = MockCommit(files=['/someCppFile.cpp'])
error_list = []
self.assertTrue(CrcValidator().run(commit, error_list))
self.assertEqual(len(error_list), 0, f"Unexpected errors: {error_list}")
@patch('builtins.open', mock_open(read_data='This file contains an invalid CRC macro AZ_CRC("My string", 0xabcdef00)'))
def test_fileWithInvalidCrc_fails(self):
commit = MockCommit(files=['/someCppFile.cpp'])
error_list = []
self.assertFalse(CrcValidator().run(commit, error_list))
self.assertNotEqual(len(error_list), 0, f"Errors were expected but none were returned.")
@patch('builtins.open', mock_open(read_data='This file contains a valid CRC macro AZ_CRC("My string", 0x18fbd270)'))
def test_fileWithValidCrc_fails(self):
commit = MockCommit(files=['/someCppFile.cpp'])
error_list = []
self.assertTrue(CrcValidator().run(commit, error_list))
self.assertEqual(len(error_list), 0, f"Unexpected errors: {error_list}")
@patch('builtins.open', mock_open(read_data='This file contains an invalid CRC macro AZ_CRC("My string", 0xabcdef00)'))
def test_fileExtensionIgnored_passes(self):
commit = MockCommit(files=['/someCppFile.somerandomextension'])
error_list = []
self.assertTrue(CrcValidator().run(commit, error_list))
self.assertEqual(len(error_list), 0, f"Unexpected errors: {error_list}")
if __name__ == '__main__':
unittest.main()
@@ -0,0 +1,48 @@
#
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#
import binascii
import fnmatch
import pathlib
import re
from typing import Type, List
from commit_validation.commit_validation import Commit, CommitValidator, SOURCE_FILE_EXTENSIONS, EXCLUDED_VALIDATION_PATTERNS, VERBOSE
class CrcValidator(CommitValidator):
"""A file-level validator that makes sure a file does not contain an invalid CRC"""
def run(self, commit: Commit, errors: List[str]) -> bool:
for file_name in commit.get_files():
for pattern in EXCLUDED_VALIDATION_PATTERNS:
if fnmatch.fnmatch(file_name, pattern):
if VERBOSE: print(f'{file_name}::{self.__class__.__name__} SKIPPED - Validation pattern excluded on path.')
break
else:
if pathlib.Path(file_name).suffix.lower() not in SOURCE_FILE_EXTENSIONS:
if VERBOSE: print(f'{file_name}::{self.__class__.__name__} SKIPPED - File excluded based on extension.')
continue
with open(file_name, mode='r', encoding='utf8') as fh:
fileContents = fh.read()
matchesFound = re.findall(r'AZ_CRC\("([^"]+)",([^)]*)\)', fileContents)
for element in matchesFound:
stringInCode = element[0]
valueInCode = element[1].strip()
expectedValue = "{0:#0{1}x}".format(binascii.crc32(stringInCode.lower().encode('utf8')), 10)
if expectedValue != valueInCode:
error_message = str(f'{file_name}::{self.__class__.__name__} FAILED - Source file contains a CRC mismatch!\n'
f' AZ_CRC("{stringInCode}", {valueInCode}), expected value {expectedValue}')
if VERBOSE: print(error_message)
errors.append(error_message)
return (not errors)
def get_validator() -> Type[CrcValidator]:
"""Returns the validator class for this module"""
return CrcValidator
-29
View File
@@ -1,29 +0,0 @@
#
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#
import os
import sys
cur_dir = cur_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, os.path.abspath(f'{cur_dir}/../build/package'))
import util
# Run validator
success = True
validator_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'validator.py')
engine_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
if sys.platform == 'win32':
python = os.path.join(engine_root, 'python', 'python.cmd')
else:
python = os.path.join(engine_root, 'python', 'python.sh')
args = [python, validator_path, '--package_platform', 'Windows', '--package_type', 'all', engine_root]
return_code = util.safe_execute_system_call(args)
if return_code != 0:
success = False
if not success:
util.error('Restricted file validator failed.')
print('Restricted file validator completed successfully.')
+12 -25
View File
@@ -29,8 +29,6 @@ else:
from io import StringIO from io import StringIO
import validator_data_LEGAL_REVIEW_REQUIRED # pull in the data we need to configure this tool import validator_data_LEGAL_REVIEW_REQUIRED # pull in the data we need to configure this tool
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'build', 'package'))
from glob_to_regex import generate_include_exclude_regexes
class Validator(object): class Validator(object):
"""Class to contain the validator program""" """Class to contain the validator program"""
@@ -212,9 +210,6 @@ class Validator(object):
# TODO: Perhaps the directories to skip should become a parameter so we can use the validator # TODO: Perhaps the directories to skip should become a parameter so we can use the validator
# on non-Lumberyard trees. # on non-Lumberyard trees.
def validate_directory_tree(self, root, platform): def validate_directory_tree(self, root, platform):
prohibited_platforms = validator_data_LEGAL_REVIEW_REQUIRED.get_prohibited_platforms_for_package(self.options.package_platform)
(includes, excludes) = generate_include_exclude_regexes(self.options.package_platform, self.options.package_type, root, prohibited_platforms)
"""Walk from root to find all files to validate and call the validator on each file. """Walk from root to find all files to validate and call the validator on each file.
Return 0 if no problems where found, and 1 if any validation failures occured.""" Return 0 if no problems where found, and 1 if any validation failures occured."""
counter = 0 counter = 0
@@ -227,28 +222,22 @@ class Validator(object):
# First deal with the files in the current directory # First deal with the files in the current directory
for filename in filenames: for filename in filenames:
filepath = os.path.join(dirname, filename) filepath = os.path.join(dirname, filename)
include_match = includes.match(filepath) scanned += 1
exclude_match = excludes.match(filepath) file_failed = self.validate_file(os.path.normpath(filepath))
allowed = include_match and not exclude_match if file_failed:
platform_failed = file_failed
if self.options.all or allowed: else:
scanned += 1 validations += 1
file_failed = self.validate_file(os.path.normpath(filepath))
if file_failed:
platform_failed = file_failed
else:
validations += 1
counter += 1
# Trim out allowlisted subdirectories in the current directory if allowed # Trim out allowlisted subdirectories in the current directory if allowed
for name in bypassed_directories: for name in bypassed_directories:
if name in dirnames: if name in dirnames:
dirnames.remove(name) dirnames.remove(name)
if counter == 0 or scanned == 0: if scanned == 0:
logging.error('No files scanned at target search directory: %s', root) logging.error('No files scanned at target search directory: %s', root)
platform_failed = 1 platform_failed = 1
else: else:
print('validated {} of {} package files ({} non-package files skipped)'.format(validations, scanned, counter - scanned)) print('validated {} of {} files'.format(validations, scanned))
return platform_failed return platform_failed
@@ -387,8 +376,6 @@ def parse_options():
choices=platform_choices, choices=platform_choices,
dest='package_platform', dest='package_platform',
help='Package platform to validate. Must be one of {}.'.format(platform_choices)) help='Package platform to validate. Must be one of {}.'.format(platform_choices))
parser.add_option('--package_type', action='store', type='string', default='all', dest='package_type',
help='Package type to validate.')
parser.add_option('-s', '--store-exceptions', action='store', type='string', default='', parser.add_option('-s', '--store-exceptions', action='store', type='string', default='',
dest='exception_file', dest='exception_file',
help='Store list of lines that the validator gave exceptions to by matching accepted use patterns. These can be diffed with prior runs to see what is changing.') help='Store list of lines that the validator gave exceptions to by matching accepted use patterns. These can be diffed with prior runs to see what is changing.')
@@ -430,7 +417,6 @@ def main():
package_failed = 0 package_failed = 0
package_platform = validator.options.package_platform package_platform = validator.options.package_platform
package_type = validator.options.package_type
prohibited_platforms = validator_data_LEGAL_REVIEW_REQUIRED.get_prohibited_platforms_for_package(package_platform) prohibited_platforms = validator_data_LEGAL_REVIEW_REQUIRED.get_prohibited_platforms_for_package(package_platform)
if validator.options.exception_file != '': if validator.options.exception_file != '':
@@ -441,19 +427,20 @@ def main():
sys.exit(1) sys.exit(1)
for platform in prohibited_platforms: for platform in prohibited_platforms:
print('validating {} against {} for package platform {} package type {}'.format(args[0], platform, package_platform, package_type)) print('validating {} against {} for package platform {}'.format(args[0], platform, package_platform))
platform_failed = validator.validate(platform) platform_failed = validator.validate(platform)
if platform_failed: if platform_failed:
print('{} FAILED validation against {} for package platform {} package type {}'.format(args[0], platform, package_platform, package_type)) print('{} FAILED validation against {} for package platform {}'.format(args[0], platform, package_platform))
package_failed = platform_failed package_failed = platform_failed
else: else:
print('{} is VALIDATED against {} for package platform {} package type {}'.format(args[0], platform, package_platform, package_type)) print('{} is VALIDATED against {} for package platform {}'.format(args[0], platform, package_platform))
if validator.options.exception_file != '': if validator.options.exception_file != '':
validator.exceptions_output.close() validator.exceptions_output.close()
return package_failed return package_failed
if __name__ == '__main__': if __name__ == '__main__':
# pylint: disable-msg=C0103 # pylint: disable-msg=C0103
main_results = main() main_results = main()