/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ #include "StdAfx.h" #include <Editor/EditorBlastSliceAssetHandler.h> #include <Editor/EditorBlastMeshDataComponent.h> #include <Asset/BlastSliceAsset.h> #include <AzCore/UnitTest/MockComponentApplication.h> #include <AzTest/AzTest.h> #include <AzCore/UnitTest/TestTypes.h> #include <gmock/gmock.h> namespace UnitTest { class MockComponentApplicationBusHandler final //: public MockComponentApplication : public AZ::ComponentApplicationBus::Handler { public: MockComponentApplicationBusHandler() { AZ::ComponentApplicationBus::Handler::BusConnect(); } virtual ~MockComponentApplicationBusHandler() { AZ::ComponentApplicationBus::Handler::BusDisconnect(); } MOCK_METHOD0(Destroy, void()); MOCK_METHOD1(RegisterComponentDescriptor, void(const AZ::ComponentDescriptor*)); MOCK_METHOD1(UnregisterComponentDescriptor, void(const AZ::ComponentDescriptor*)); MOCK_METHOD1(RemoveEntity, bool(AZ::Entity*)); MOCK_METHOD1(DeleteEntity, bool(const AZ::EntityId&)); MOCK_METHOD1(GetEntityName, AZStd::string(const AZ::EntityId&)); MOCK_METHOD1(AddEntity, bool(AZ::Entity*)); MOCK_METHOD1(FindEntity, AZ::Entity*(const AZ::EntityId&)); MOCK_METHOD1(EnumerateEntities, void(const ComponentApplicationRequests::EntityCallback&)); MOCK_METHOD0(GetApplication, AZ::ComponentApplication* ()); MOCK_METHOD0(GetSerializeContext, AZ::SerializeContext* ()); MOCK_METHOD0(GetBehaviorContext, AZ::BehaviorContext* ()); MOCK_METHOD0(GetJsonRegistrationContext, AZ::JsonRegistrationContext* ()); MOCK_METHOD0(GetAppRoot, const char* ()); MOCK_CONST_METHOD0(GetExecutableFolder, const char* ()); MOCK_METHOD0(GetDrillerManager, AZ::Debug::DrillerManager* ()); MOCK_METHOD0(GetTickDeltaTime, float()); MOCK_METHOD1(Tick, void(float)); MOCK_METHOD0(TickSystem, void()); MOCK_CONST_METHOD0(GetRequiredSystemComponents, AZ::ComponentTypeList()); MOCK_METHOD1(ResolveModulePath, void(AZ::OSString&)); MOCK_METHOD0(CreateSerializeContext, void()); MOCK_METHOD0(DestroySerializeContext, void()); MOCK_METHOD0(CreateBehaviorContext, void()); MOCK_METHOD0(DestroyBehaviorContext, void()); MOCK_METHOD0(RegisterCoreComponents, void()); MOCK_METHOD1(AddSystemComponents, void(AZ::Entity*)); MOCK_METHOD0(ReflectSerialize, void()); MOCK_METHOD1(Reflect, void(AZ::ReflectContext*)); MOCK_CONST_METHOD0(GetBinFolder, const char* ()); }; class MockAssetCatalogRequestBusHandler final : public AZ::Data::AssetCatalogRequestBus::Handler { public: MockAssetCatalogRequestBusHandler() { AZ::Data::AssetCatalogRequestBus::Handler::BusConnect(); } virtual ~MockAssetCatalogRequestBusHandler() { AZ::Data::AssetCatalogRequestBus::Handler::BusDisconnect(); } MOCK_METHOD3(GetAssetIdByPath, AZ::Data::AssetId(const char*, const AZ::Data::AssetType&, bool)); MOCK_METHOD1(GetAssetInfoById, AZ::Data::AssetInfo(const AZ::Data::AssetId&)); MOCK_METHOD1(AddAssetType, void(const AZ::Data::AssetType&)); MOCK_METHOD1(AddDeltaCatalog, bool(AZStd::shared_ptr<AzFramework::AssetRegistry>)); MOCK_METHOD1(AddExtension, void(const char*)); MOCK_METHOD0(ClearCatalog, void()); MOCK_METHOD5(CreateBundleManifest, bool(const AZStd::string&, const AZStd::vector<AZStd::string>&, const AZStd::string&, int, const AZStd::vector<AZStd::string>&)); MOCK_METHOD2(CreateDeltaCatalog, bool(const AZStd::vector<AZStd::string>&, const AZStd::string&)); MOCK_METHOD0(DisableCatalog, void()); MOCK_METHOD1(EnableCatalogForAsset, void(const AZ::Data::AssetType&)); MOCK_METHOD3(EnumerateAssets, void(BeginAssetEnumerationCB, AssetEnumerationCB, EndAssetEnumerationCB)); MOCK_METHOD1(GenerateAssetIdTEMP, AZ::Data::AssetId(const char*)); MOCK_METHOD1(GetAllProductDependencies, AZ::Outcome<AZStd::vector<AZ::Data::ProductDependency>, AZStd::string>(const AZ::Data::AssetId&)); MOCK_METHOD3(GetAllProductDependenciesFilter, AZ::Outcome<AZStd::vector<AZ::Data::ProductDependency>, AZStd::string>(const AZ::Data::AssetId&, const AZStd::unordered_set<AZ::Data::AssetId>&, const AZStd::vector<AZStd::string>&)); MOCK_METHOD1(GetAssetPathById, AZStd::string(const AZ::Data::AssetId&)); MOCK_METHOD1(GetDirectProductDependencies, AZ::Outcome<AZStd::vector<AZ::Data::ProductDependency>, AZStd::string>(const AZ::Data::AssetId&)); MOCK_METHOD1(GetHandledAssetTypes, void(AZStd::vector<AZ::Data::AssetType>&)); MOCK_METHOD0(GetRegisteredAssetPaths, AZStd::vector<AZStd::string>()); MOCK_METHOD2(InsertDeltaCatalog, bool(AZStd::shared_ptr<AzFramework::AssetRegistry>, size_t)); MOCK_METHOD2(InsertDeltaCatalogBefore, bool(AZStd::shared_ptr<AzFramework::AssetRegistry>, AZStd::shared_ptr<AzFramework::AssetRegistry>)); MOCK_METHOD1(LoadCatalog, bool(const char*)); MOCK_METHOD2(RegisterAsset, void(const AZ::Data::AssetId&, AZ::Data::AssetInfo&)); MOCK_METHOD1(RemoveDeltaCatalog, bool(AZStd::shared_ptr<AzFramework::AssetRegistry>)); MOCK_METHOD1(SaveCatalog, bool(const char*)); MOCK_METHOD0(StartMonitoringAssets, void()); MOCK_METHOD0(StopMonitoringAssets, void()); MOCK_METHOD1(UnregisterAsset, void(const AZ::Data::AssetId&)); }; class MockAssetManager : public AZ::Data::AssetManager { public: MockAssetManager(const AZ::Data::AssetManager::Descriptor& desc) : AssetManager(desc) { } }; class EditorBlastSliceAssetHandlerTestFixture : public AllocatorsTestFixture { public: AZStd::unique_ptr<MockComponentApplicationBusHandler> m_mockComponentApplicationBusHandler; //AZStd::unique_ptr<UnitTest::MockComponentApplication> m_mockComponentApplicationBusHandler; AZStd::unique_ptr<MockAssetCatalogRequestBusHandler> m_mockAssetCatalogRequestBusHandler; AZStd::unique_ptr<MockAssetManager> m_mockAssetManager; AZStd::unique_ptr<AZ::SerializeContext> m_serializeContext; const AZ::ComponentDescriptor* m_sliceComponentDescriptor = nullptr; void SetUpSliceComponents() { m_serializeContext = AZStd::make_unique<AZ::SerializeContext>(); AZ::Entity::Reflect(m_serializeContext.get()); Blast::BlastSliceAssetStorageComponent::Reflect(m_serializeContext.get()); AzToolsFramework::Components::EditorComponentBase::Reflect(m_serializeContext.get()); m_sliceComponentDescriptor = AZ::SliceComponent::CreateDescriptor(); m_sliceComponentDescriptor->Reflect(m_serializeContext.get()); } void TearDownSliceComponents() { delete m_sliceComponentDescriptor; m_serializeContext.reset(); } void SetUp() override final { AllocatorsTestFixture::SetUp(); AZ::AllocatorInstance<AZ::PoolAllocator>::Create(); AZ::AllocatorInstance<AZ::ThreadPoolAllocator>::Create(); m_mockComponentApplicationBusHandler = AZStd::make_unique<MockComponentApplicationBusHandler>(); m_mockAssetCatalogRequestBusHandler = AZStd::make_unique<MockAssetCatalogRequestBusHandler>(); m_mockAssetManager = AZStd::make_unique<MockAssetManager>(AZ::Data::AssetManager::Descriptor{}); AZ::Data::AssetManager::SetInstance(m_mockAssetManager.get()); } void TearDown() override final { AZ::Data::AssetManager::SetInstance(nullptr); m_mockAssetManager.reset(); m_mockAssetCatalogRequestBusHandler.reset(); m_mockComponentApplicationBusHandler.reset(); AZ::AllocatorInstance<AZ::ThreadPoolAllocator>::Destroy(); AZ::AllocatorInstance<AZ::PoolAllocator>::Destroy(); AllocatorsTestFixture::TearDown(); } void SaveSliceAssetToStream(AZ::Entity* sliceAssetEntity, AZStd::vector<char>& buffer) { buffer.clear(); AZ::IO::ByteContainerStream<AZStd::vector<char>> stream(&buffer); AZ::ObjectStream* objStream = AZ::ObjectStream::Create(&stream, *m_serializeContext.get(), AZ::ObjectStream::ST_XML); objStream->WriteClass(sliceAssetEntity); EXPECT_TRUE(objStream->Finalize()); } }; TEST_F(EditorBlastSliceAssetHandlerTestFixture, EditorBlastSliceAssetHandler_AssetManager_Registered) { Blast::EditorBlastSliceAssetHandler handler; handler.Register(); EXPECT_NE(nullptr, AZ::Data::AssetManager::Instance().GetHandler(azrtti_typeid<Blast::BlastSliceAsset>())); handler.Unregister(); } TEST_F(EditorBlastSliceAssetHandlerTestFixture, BlastSliceAssetStorageComponent_Behavior_Registered) { AZ::BehaviorContext behaviorContext; Blast::BlastSliceAssetStorageComponent::Reflect(&behaviorContext); auto classEntry = behaviorContext.m_classes.find("BlastSliceAssetStorageComponent"); EXPECT_NE(behaviorContext.m_classes.end(), classEntry); AZ::BehaviorClass* behaviorClass = classEntry->second; auto methodEntry = behaviorClass->m_methods.find("GenerateAssetInfo"); EXPECT_NE(behaviorClass->m_methods.end(), methodEntry); AZ::BehaviorMethod* behaviorMethod = methodEntry->second; EXPECT_EQ(4, behaviorMethod->GetNumArguments()); EXPECT_EQ(behaviorMethod->GetArgument(0)->m_typeId, azrtti_typeid<Blast::BlastSliceAssetStorageComponent>()); EXPECT_EQ(behaviorMethod->GetArgument(1)->m_typeId, azrtti_typeid<AZStd::vector<AZStd::string>>()); EXPECT_EQ(behaviorMethod->GetArgument(2)->m_typeId, azrtti_typeid<AZStd::string_view>()); EXPECT_EQ(behaviorMethod->GetArgument(3)->m_typeId, azrtti_typeid<AZStd::string_view>()); } TEST_F(EditorBlastSliceAssetHandlerTestFixture, BlastSliceAsset_Behavior_Registered) { AZ::BehaviorContext behaviorContext; Blast::BlastSliceAsset::Reflect(&behaviorContext); auto classEntry = behaviorContext.m_classes.find("BlastSliceAsset"); EXPECT_NE(behaviorContext.m_classes.end(), classEntry); AZ::BehaviorClass* behaviorClass = classEntry->second; auto setMeshIdListEntry = behaviorClass->m_methods.find("SetMeshIdList"); EXPECT_NE(behaviorClass->m_methods.end(), setMeshIdListEntry); { AZ::BehaviorMethod* behaviorMethod = setMeshIdListEntry->second; EXPECT_EQ(2, behaviorMethod->GetNumArguments()); EXPECT_EQ(behaviorMethod->GetArgument(0)->m_typeId, azrtti_typeid<Blast::BlastSliceAsset>()); EXPECT_EQ(behaviorMethod->GetArgument(1)->m_typeId, azrtti_typeid<AZStd::vector<AZ::Data::AssetId>>()); } auto getMeshIdListEntry = behaviorClass->m_methods.find("GetMeshIdList"); EXPECT_NE(behaviorClass->m_methods.end(), getMeshIdListEntry); { AZ::BehaviorMethod* behaviorMethod = getMeshIdListEntry->second; EXPECT_EQ(1, behaviorMethod->GetNumArguments()); EXPECT_EQ(behaviorMethod->GetArgument(0)->m_typeId, azrtti_typeid<Blast::BlastSliceAsset>()); EXPECT_EQ(behaviorMethod->GetResult()->m_typeId, azrtti_typeid<AZStd::vector<AZ::Data::AssetId>>()); } auto setMaterialIdEntry = behaviorClass->m_methods.find("SetMaterialId"); EXPECT_NE(behaviorClass->m_methods.end(), setMaterialIdEntry); { AZ::BehaviorMethod* behaviorMethod = setMaterialIdEntry->second; EXPECT_EQ(2, behaviorMethod->GetNumArguments()); EXPECT_EQ(behaviorMethod->GetArgument(0)->m_typeId, azrtti_typeid<Blast::BlastSliceAsset>()); EXPECT_EQ(behaviorMethod->GetArgument(1)->m_typeId, azrtti_typeid<AZ::Data::AssetId>()); } auto getMaterialIdEntry = behaviorClass->m_methods.find("GetMaterialId"); EXPECT_NE(behaviorClass->m_methods.end(), getMaterialIdEntry); { AZ::BehaviorMethod* behaviorMethod = getMaterialIdEntry->second; EXPECT_EQ(1, behaviorMethod->GetNumArguments()); EXPECT_EQ(behaviorMethod->GetArgument(0)->m_typeId, azrtti_typeid<Blast::BlastSliceAsset>()); EXPECT_EQ(behaviorMethod->GetResult()->m_typeId, azrtti_typeid<AZ::Data::AssetId>()); } auto getAssetTypeIdEntry = behaviorClass->m_methods.find("GetAssetTypeId"); EXPECT_NE(behaviorClass->m_methods.end(), getAssetTypeIdEntry); { AZ::BehaviorMethod* behaviorMethod = getAssetTypeIdEntry->second; EXPECT_EQ(1, behaviorMethod->GetNumArguments()); EXPECT_EQ(behaviorMethod->GetArgument(0)->m_typeId, azrtti_typeid<Blast::BlastSliceAsset>()); EXPECT_EQ(behaviorMethod->GetResult()->m_typeId, azrtti_typeid<AZ::TypeId>()); } } TEST_F(EditorBlastSliceAssetHandlerTestFixture, EditorBlastSliceAssetHandler_AssetTypeInfoBus_Responds) { auto assetId = azrtti_typeid<Blast::BlastSliceAsset>(); Blast::EditorBlastSliceAssetHandler handler; handler.Register(); AZ::Data::AssetType assetType = AZ::Uuid::CreateNull(); AZ::AssetTypeInfoBus::EventResult(assetType, assetId, &AZ::AssetTypeInfoBus::Events::GetAssetType); EXPECT_NE(AZ::Uuid::CreateNull(), assetType); const char* displayName = nullptr; AZ::AssetTypeInfoBus::EventResult(displayName, assetId, &AZ::AssetTypeInfoBus::Events::GetAssetTypeDisplayName); EXPECT_STREQ("Blast Slice Asset", displayName); const char* group = nullptr; AZ::AssetTypeInfoBus::EventResult(group, assetId, &AZ::AssetTypeInfoBus::Events::GetGroup); EXPECT_STREQ("Blast", group); const char* icon = nullptr; AZ::AssetTypeInfoBus::EventResult(icon, assetId, &AZ::AssetTypeInfoBus::Events::GetBrowserIcon); EXPECT_STREQ("Editor/Icons/Components/Box.png", icon); AZStd::vector<AZStd::string> extensions; AZ::AssetTypeInfoBus::Event(assetId, &AZ::AssetTypeInfoBus::Events::GetAssetTypeExtensions, extensions); ASSERT_EQ(1, extensions.size()); ASSERT_EQ("blast_slice", extensions[0]); handler.Unregister(); } TEST_F(EditorBlastSliceAssetHandlerTestFixture, EditorBlastSliceAssetHandler_AssetHandler_Ready) { auto assetType = azrtti_typeid<Blast::BlastSliceAsset>(); auto&& assetManager = AZ::Data::AssetManager::Instance(); Blast::EditorBlastSliceAssetHandler handler; handler.Register(); EXPECT_EQ(&handler, assetManager.GetHandler(assetType)); // create and release an instance of the BlastSliceAsset asset type { using ::testing::Return; using ::testing::_; EXPECT_CALL(*m_mockAssetCatalogRequestBusHandler, GetAssetInfoById(_)) .Times(1) .WillRepeatedly(Return(AZ::Data::AssetInfo{})); auto assetPtr = assetManager.CreateAsset<Blast::BlastSliceAsset>(AZ::Data::AssetId(AZ::Uuid::CreateRandom(), 0)); EXPECT_NE(nullptr, assetPtr.Get()); EXPECT_EQ(azrtti_typeid<Blast::BlastSliceAsset>(), assetPtr.GetType()); } handler.Unregister(); } TEST_F(EditorBlastSliceAssetHandlerTestFixture, EditorBlastSliceAssetHandler_AssetHandler_LoadsAssetData) { SetUpSliceComponents(); AZStd::vector<AZStd::string> meshAssetPathList = { "/foo/path/thing.cgf", "/foo/path/that.cgf" }; AZ::Entity* storageEntity = aznew AZ::Entity(); auto* blastStorage = storageEntity->CreateComponent<Blast::BlastSliceAssetStorageComponent>(); blastStorage->SetMeshPathList(meshAssetPathList); AZ::Entity sliceEntity; AZ::SliceComponent* slice = sliceEntity.CreateComponent<AZ::SliceComponent>(); slice->AddEntity(storageEntity); AZStd::vector<char> buffer; SaveSliceAssetToStream(&sliceEntity, buffer); // Load a slice with the BlastSliceAssetStorageComponent Blast::EditorBlastSliceAssetHandler handler; handler.Register(); { using ::testing::Return; using ::testing::_; EXPECT_CALL(*m_mockComponentApplicationBusHandler, GetSerializeContext) .Times(1) .WillOnce(Return(m_serializeContext.get())); EXPECT_CALL(*m_mockComponentApplicationBusHandler, FindEntity(_)) .Times(1) .WillOnce(Return(&sliceEntity)); EXPECT_CALL(*m_mockAssetCatalogRequestBusHandler, GetAssetIdByPath(_,_,_)) .Times(2) .WillRepeatedly(Return(AZ::Data::AssetId(AZ::Uuid::CreateRandom(), 0))); EXPECT_CALL(*m_mockAssetCatalogRequestBusHandler, GetAssetInfoById(_)) .Times(2) .WillRepeatedly(Return(AZ::Data::AssetInfo{})); auto&& assetManager = AZ::Data::AssetManager::Instance(); auto assetPtr = assetManager.CreateAsset<Blast::BlastSliceAsset>(AZ::Data::AssetId(AZ::Uuid::CreateRandom(), 0)); AZ::IO::ByteContainerStream<AZStd::vector<char>> stream(&buffer); stream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN); const AZ::Data::AssetFilterCB assetLoadFilterCB{}; bool loaded = handler.LoadAssetData(assetPtr, &stream, assetLoadFilterCB); EXPECT_TRUE(loaded); } handler.Unregister(); TearDownSliceComponents(); } }