You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/Framework/Tests/NetBindingSystemImplTest.cpp

606 lines
25 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Component/ComponentApplication.h>
#include <AzFramework/Network/NetBindingSystemImpl.h>
#include <AzFramework/Network/NetBindable.h>
#include <AzFramework/Network/NetBindingSystemComponent.h>
#include <AzCore/Asset/AssetManagerComponent.h>
#include <AzCore/Memory/AllocationRecords.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <GridMate/Serialize/CompressionMarshal.h>
#include <GridMate/Replica/ReplicaFunctions.h>
#include <AzCore/Asset/AssetManager.h>
#include "NetBindingMocks.h"
#include <gmock/gmock-matchers.h>
#include <gmock/gmock-more-actions.h>
#include <gmock/gmock-spec-builders.h>
#include <AzCore/Slice/SliceComponent.h>
namespace UnitTest
{
using namespace AZ;
using namespace AzFramework;
using namespace GridMate;
class NetBindingWithSlicesTest
: public ScopedAllocatorSetupFixture
{
public:
const NetBindingContextSequence k_fakeContextSeq = 1;
const AZ::SliceComponent::SliceInstanceId k_fakeSliceInstanceId = Uuid::CreateRandom();
const AZ::SliceComponent::SliceInstanceId k_fakeSliceInstanceId_Another = Uuid::CreateRandom();
SliceInstantiationTicket m_sliceTicket = SliceInstantiationTicket(EntityContextId::CreateName("Test"), 1);
const Data::AssetId k_fakeAssetId = Data::AssetId(Uuid::CreateRandom(), 0);
const EntityId k_fakeEntityId_One = EntityId(9001);
const ReplicaId k_repId_One = 1001;
const EntityId k_fakeEntityId_Two = EntityId(9002);
const ReplicaId k_repId_Two = 1002;
AZStd::unique_ptr<NetBindingSystemImpl> m_netBindingImpl;
AZStd::unique_ptr<MockComponentApplication> m_componentApplication;
AZStd::unique_ptr<SerializeContext> m_applicationContext;
AZStd::unique_ptr<MockGameEntityContext> m_gameEntityMock;
AZStd::unique_ptr<MockReplicaManager> m_replicaManagerMock;
ReplicaPtr m_replicaMock;
ComponentDescriptor* m_netBindingSystemComponentDescriptor = nullptr;
AZStd::intrusive_ptr<MockNetBindingSystemContextData> m_contextChunkMock;
MockAssetHandler* m_myAssetHandlerAndCatalog = nullptr; // owned by AssetManager
AZStd::unique_ptr<MockAsset> m_fakeAsset;
const float k_wayOverSliceTimeout = NetBindingSystemImpl::s_sliceBindingTimeout.count() * 2.f;
const float k_smallStep = 0.1f;
void SetUpFakeAssetManager()
{
using namespace testing;
const Data::AssetManager::Descriptor desc;
Data::AssetManager::Create(desc);
m_myAssetHandlerAndCatalog = aznew NiceMock<MockAssetHandler>;
ON_CALL(*m_myAssetHandlerAndCatalog, CreateAsset(_, _))
.WillByDefault(Invoke([this](const Data::AssetId&, const Data::AssetType&) -> Data::AssetPtr
{
m_fakeAsset = AZStd::make_unique<NiceMock<MockAsset>>(k_fakeAssetId);
return m_fakeAsset.get();
}));
ON_CALL(*m_myAssetHandlerAndCatalog, DestroyAsset(_))
.WillByDefault(Invoke([this](const Data::AssetPtr asset)
{
EXPECT_EQ(asset, m_fakeAsset.get());
m_fakeAsset.reset();
}));
Data::AssetManager::Instance().RegisterHandler(m_myAssetHandlerAndCatalog, AzTypeInfo<DynamicSliceAsset>::Uuid());
Data::AssetManager::Instance().RegisterHandler(m_myAssetHandlerAndCatalog, AzTypeInfo<MockAsset>::Uuid());
}
void SetUp() override
{
using namespace testing;
m_applicationContext.reset(aznew SerializeContext());
AllocatorInstance<GridMateAllocatorMP>::Create();
AllocatorInstance<ThreadPoolAllocator>::Create();
DefaultValue<SliceInstantiationTicket>::Set(m_sliceTicket);
m_gameEntityMock = AZStd::make_unique<NiceMock<MockGameEntityContext>>();
m_componentApplication = AZStd::make_unique<NiceMock<MockComponentApplication>>();
ON_CALL(*m_componentApplication, GetSerializeContext())
.WillByDefault(Invoke([this]()
{
return m_applicationContext.get();
}));
ON_CALL(*m_gameEntityMock, GetGameEntityContextId())
.WillByDefault(Return(EntityContextId::CreateRandom()));
m_netBindingSystemComponentDescriptor = NetBindingSystemComponent::CreateDescriptor();
ReplicaChunkDescriptorTable::Get().RegisterChunkType<MockNetBindingSystemContextData>();
m_contextChunkMock.reset(CreateReplicaChunk<NiceMock<MockNetBindingSystemContextData>>());
ON_CALL(*m_contextChunkMock, ShouldBindToNetwork())
.WillByDefault(Return(true));
m_replicaManagerMock = AZStd::make_unique<NiceMock<MockReplicaManager>>();
ON_CALL(*m_contextChunkMock, GetReplicaManager())
.WillByDefault(Invoke([this]()
{
return m_replicaManagerMock.get();
}));
m_replicaMock = Replica::CreateReplica("unittest");
ON_CALL(*m_replicaManagerMock, FindReplica(_))
.WillByDefault(Invoke([this](ReplicaId id) -> ReplicaPtr
{
AZ_UNUSED(id);
return m_replicaMock;
}));
ON_CALL(*m_contextChunkMock, OnReplicaActivate(_))
.WillByDefault(Invoke(m_contextChunkMock.get(), &MockNetBindingSystemContextData::Base_OnReplicaActivate));
m_netBindingImpl = AZStd::make_unique<AzFramework::NetBindingSystemImpl>();
m_netBindingImpl->Init();
m_contextChunkMock->OnReplicaActivate(ReplicaContext(nullptr, TimeContext()));
SetUpFakeAssetManager();
}
void TearDown() override
{
Data::AssetManager::Destroy();
m_replicaMock.reset();
m_replicaManagerMock.reset();
m_contextChunkMock.reset();
m_fakeAsset.reset();
m_netBindingImpl->Shutdown();
m_netBindingImpl.reset();
ReplicaChunkDescriptorTable::Get().UnregisterReplicaChunkDescriptor(ReplicaChunkClassId(MockNetBindingSystemContextData::GetChunkName()));
m_netBindingSystemComponentDescriptor->ReleaseDescriptor();
m_componentApplication.reset();
m_gameEntityMock.reset();
AllocatorInstance<GridMateAllocatorMP>::Destroy();
AllocatorInstance<ThreadPoolAllocator>::Destroy();
m_applicationContext.reset();
}
};
TEST_F(NetBindingWithSlicesTest, SameSliceInstanceId_InstantiateDynamicSlice_CallOnce)
{
using namespace testing;
EXPECT_CALL(*m_gameEntityMock, InstantiateDynamicSlice(_, _, _))
.Times(1);
EXPECT_CALL(*m_gameEntityMock, CancelDynamicSliceInstantiation(_))
.Times(1);
{
NetBindingSliceContext spawnContext;
spawnContext.m_contextSequence = k_fakeContextSeq;
spawnContext.m_sliceAssetId = k_fakeAssetId;
spawnContext.m_runtimeEntityId = k_fakeEntityId_One;
spawnContext.m_staticEntityId = k_fakeEntityId_One;
spawnContext.m_sliceInstanceId = k_fakeSliceInstanceId; // both mock entities come from the same slice
EBUS_EVENT(NetBindingSystemBus, SpawnEntityFromSlice, k_repId_One, spawnContext);
}
{
NetBindingSliceContext spawnContext;
spawnContext.m_contextSequence = k_fakeContextSeq;
spawnContext.m_sliceAssetId = k_fakeAssetId;
spawnContext.m_runtimeEntityId = k_fakeEntityId_Two;
spawnContext.m_staticEntityId = k_fakeEntityId_Two;
spawnContext.m_sliceInstanceId = k_fakeSliceInstanceId; // both mock entities come from the same slice
EBUS_EVENT(NetBindingSystemBus, SpawnEntityFromSlice, k_repId_Two, spawnContext);
}
// this should kick off NetBindingSystemImpl::ProcessBindRequests
EBUS_EVENT(AZ::TickBus, OnTick, k_smallStep, AZ::ScriptTimePoint());
}
TEST_F(NetBindingWithSlicesTest, DifferentSliceInstanceId_InstantiateDynamicSlice_CalledTwice)
{
using namespace testing;
EXPECT_CALL(*m_gameEntityMock, InstantiateDynamicSlice(_, _, _))
.Times(2);
EXPECT_CALL(*m_gameEntityMock, CancelDynamicSliceInstantiation(_))
.Times(2);
{
NetBindingSliceContext spawnContext;
spawnContext.m_contextSequence = k_fakeContextSeq;
spawnContext.m_sliceAssetId = k_fakeAssetId;
spawnContext.m_runtimeEntityId = k_fakeEntityId_One;
spawnContext.m_staticEntityId = k_fakeEntityId_One;
spawnContext.m_sliceInstanceId = k_fakeSliceInstanceId;
EBUS_EVENT(NetBindingSystemBus, SpawnEntityFromSlice, k_repId_One, spawnContext);
}
{
NetBindingSliceContext spawnContext;
spawnContext.m_contextSequence = k_fakeContextSeq;
spawnContext.m_sliceAssetId = k_fakeAssetId;
spawnContext.m_runtimeEntityId = k_fakeEntityId_Two;
spawnContext.m_staticEntityId = k_fakeEntityId_Two;
spawnContext.m_sliceInstanceId = k_fakeSliceInstanceId_Another; // different slice entity
EBUS_EVENT(NetBindingSystemBus, SpawnEntityFromSlice, k_repId_Two, spawnContext);
}
// this should kick off NetBindingSystemImpl::ProcessBindRequests
EBUS_EVENT(AZ::TickBus, OnTick, k_smallStep, AZ::ScriptTimePoint());
}
TEST_F(NetBindingWithSlicesTest, AssetManagerDestroyed_InstantiateDynamicSlice_NotCalled)
{
{
NetBindingSliceContext spawnContext;
spawnContext.m_contextSequence = k_fakeContextSeq;
spawnContext.m_sliceAssetId = k_fakeAssetId;
spawnContext.m_runtimeEntityId = k_fakeEntityId_One;
spawnContext.m_staticEntityId = k_fakeEntityId_One;
spawnContext.m_sliceInstanceId = k_fakeSliceInstanceId; // both mock entities come from the same slice
EBUS_EVENT(NetBindingSystemBus, SpawnEntityFromSlice, k_repId_One, spawnContext);
}
Data::AssetManager::Destroy();
// this should kick off NetBindingSystemImpl::ProcessBindRequests, but InstantiateDynamicSlice will not be called
EBUS_EVENT(AZ::TickBus, OnTick, k_smallStep, AZ::ScriptTimePoint());
}
class ExtendedBindingWithSlicesTest
: public NetBindingWithSlicesTest
{
public:
void SetUp() override
{
NetBindingWithSlicesTest::SetUp();
}
void TearDown() override
{
using namespace testing;
EXPECT_CALL(*m_gameEntityMock, DestroyGameEntity(k_fakeEntityId_One))
.Times(AtMost(1));
EXPECT_CALL(*m_gameEntityMock, DestroyGameEntity(k_fakeEntityId_Two))
.Times(AtMost(1));
NetBindingWithSlicesTest::TearDown();
}
class InstantiateMockSlice
{
public:
explicit InstantiateMockSlice(ExtendedBindingWithSlicesTest* parent)
{
using namespace testing;
m_mockSliceRef = AZStd::make_unique<MockSliceReference>();
m_mockSliceInstance = AZStd::make_unique<MockSliceInstance>();
// container owns the entities and will delete them
auto mockContainer = AZStd::make_unique<SliceComponent::InstantiatedContainer>();
auto binding1 = AZStd::make_unique<NiceMock<MockBindingComponent>>();
mockContainer->m_entities.push_back(CreateMockEntity(parent->k_fakeEntityId_One, binding1.release()));
auto binding2 = AZStd::make_unique<NiceMock<MockBindingComponent>>();
mockContainer->m_entities.push_back(CreateMockEntity(parent->k_fakeEntityId_Two, binding2.release()));
m_mockSliceInstance->SetMockInstantiatedContainer(mockContainer.release());
SliceComponent::SliceInstanceAddress sliceInstanceAddress(m_mockSliceRef.get(), m_mockSliceInstance.get());
// This will pass our mock slice to NetBindingSystem
EBUS_EVENT_ID(parent->m_sliceTicket, SliceInstantiationResultBus, OnSlicePreInstantiate, parent->k_fakeAssetId, sliceInstanceAddress);
EBUS_EVENT_ID(parent->m_sliceTicket, SliceInstantiationResultBus, OnSliceInstantiated, parent->k_fakeAssetId, sliceInstanceAddress);
}
Entity* CreateMockEntity(const EntityId& id, Component* optional = nullptr)
{
using namespace testing;
auto mock = AZStd::make_unique<NiceMock<MockEntity>>();
mock->SetId(EntityId(id));
if (optional)
{
mock->AddComponent(optional); // entity owns the component
}
ON_CALL(*mock, Init())
.WillByDefault(Invoke(mock.get(), &MockEntity::Base_Init));
mock->Init();
ON_CALL(*mock, Activate())
.WillByDefault(Invoke(mock.get(), &MockEntity::Base_Activate));
ON_CALL(*mock, Deactivate())
.WillByDefault(Invoke(mock.get(), &MockEntity::Base_Deactivate));
return mock.release();
}
AZStd::unique_ptr<MockSliceReference> m_mockSliceRef;
AZStd::unique_ptr<MockSliceInstance> m_mockSliceInstance;
};
AZStd::unique_ptr<InstantiateMockSlice> m_slice;
void CreateMockSlice()
{
m_slice = AZStd::make_unique<InstantiateMockSlice>(this);
}
};
TEST_F(ExtendedBindingWithSlicesTest, ActiveSlice_EntitiesThatWerentBounded_StayDeactivated)
{
{
NetBindingSliceContext spawnContext;
spawnContext.m_contextSequence = k_fakeContextSeq;
spawnContext.m_sliceAssetId = k_fakeAssetId;
spawnContext.m_runtimeEntityId = k_fakeEntityId_One;
spawnContext.m_staticEntityId = k_fakeEntityId_One;
spawnContext.m_sliceInstanceId = k_fakeSliceInstanceId;
EBUS_EVENT(NetBindingSystemBus, SpawnEntityFromSlice, k_repId_One, spawnContext);
}
EBUS_EVENT(AZ::TickBus, OnTick, k_smallStep, AZ::ScriptTimePoint());
CreateMockSlice();
MockEntity* mock1 = static_cast<MockEntity*>(m_componentApplication->FindEntity(k_fakeEntityId_One));
EXPECT_CALL(*mock1, Activate()).
Times(1);
MockEntity* mock2 = static_cast<MockEntity*>(m_componentApplication->FindEntity(k_fakeEntityId_Two));
EXPECT_CALL(*mock2, Activate()).
Times(0);
EXPECT_CALL(*m_gameEntityMock, DestroyGameEntity(k_fakeEntityId_Two))
.Times(0);
// Now it should time out the slice handler and the second entity should remain deactivated since we didn't give binding request for it
EBUS_EVENT(AZ::TickBus, OnTick, k_wayOverSliceTimeout, AZ::ScriptTimePoint());
}
TEST_F(ExtendedBindingWithSlicesTest, ActiveSlice_SpawnSecondEntity_AfterLongDelay_InSameSlicenInstance)
{
EXPECT_CALL(*m_gameEntityMock, DestroyGameEntity(k_fakeEntityId_Two))
.Times(0);
{
NetBindingSliceContext spawnContext;
spawnContext.m_contextSequence = k_fakeContextSeq;
spawnContext.m_sliceAssetId = k_fakeAssetId;
spawnContext.m_runtimeEntityId = k_fakeEntityId_One;
spawnContext.m_staticEntityId = k_fakeEntityId_One;
spawnContext.m_sliceInstanceId = k_fakeSliceInstanceId;
EBUS_EVENT(NetBindingSystemBus, SpawnEntityFromSlice, k_repId_One, spawnContext);
}
EBUS_EVENT(AZ::TickBus, OnTick, k_smallStep, AZ::ScriptTimePoint());
CreateMockSlice();
MockEntity* mock2 = static_cast<MockEntity*>(m_componentApplication->FindEntity(k_fakeEntityId_Two));
EXPECT_CALL(*mock2, Activate()).
Times(0);
// This should not trigger removal of the second entity yet
auto halfTimeoutInSeconds = AZStd::chrono::seconds(NetBindingSystemImpl::s_sliceBindingTimeout).count() * 10.f;
EBUS_EVENT(AZ::TickBus, OnTick, halfTimeoutInSeconds, AZ::ScriptTimePoint());
{
NetBindingSliceContext spawnContext;
spawnContext.m_contextSequence = k_fakeContextSeq;
spawnContext.m_sliceAssetId = k_fakeAssetId;
spawnContext.m_runtimeEntityId = k_fakeEntityId_Two;
spawnContext.m_staticEntityId = k_fakeEntityId_Two;
spawnContext.m_sliceInstanceId = k_fakeSliceInstanceId; // both mock entities come from the same slice
EBUS_EVENT(NetBindingSystemBus, SpawnEntityFromSlice, k_repId_Two, spawnContext);
}
EXPECT_CALL(*mock2, Activate()).
Times(1);
// This should give net binding system time to bind the second entity
EBUS_EVENT(AZ::TickBus, OnTick, k_smallStep, AZ::ScriptTimePoint());
// Let the slice timeout, this should lead to no destruction since both entities ought to have been bound by now
EBUS_EVENT(AZ::TickBus, OnTick, k_wayOverSliceTimeout, AZ::ScriptTimePoint());
}
TEST_F(ExtendedBindingWithSlicesTest, ActiveSlice_DespawnLastEntity_DespawnWholeSliceAfterTimeout)
{
{
NetBindingSliceContext spawnContext;
spawnContext.m_contextSequence = k_fakeContextSeq;
spawnContext.m_sliceAssetId = k_fakeAssetId;
spawnContext.m_runtimeEntityId = k_fakeEntityId_One;
spawnContext.m_staticEntityId = k_fakeEntityId_One;
spawnContext.m_sliceInstanceId = k_fakeSliceInstanceId;
EBUS_EVENT(NetBindingSystemBus, SpawnEntityFromSlice, k_repId_One, spawnContext);
}
EBUS_EVENT(AZ::TickBus, OnTick, k_smallStep, AZ::ScriptTimePoint());
CreateMockSlice();
MockEntity* mock1 = static_cast<MockEntity*>(m_componentApplication->FindEntity(k_fakeEntityId_One));
EXPECT_CALL(*mock1, Activate()).
Times(1);
// This should not trigger removal of the second entity yet
auto halfTimeoutInSeconds = AZStd::chrono::seconds(NetBindingSystemImpl::s_sliceBindingTimeout).count() * 10.f;
EBUS_EVENT(AZ::TickBus, OnTick, halfTimeoutInSeconds, AZ::ScriptTimePoint());
EXPECT_CALL(*mock1, Deactivate()).
Times(1);
EBUS_EVENT(NetBindingSystemBus, UnbindGameEntity, k_fakeEntityId_One, k_fakeSliceInstanceId);
EXPECT_CALL(*m_gameEntityMock, DestroyGameEntity(k_fakeEntityId_One))
.Times(1);
EXPECT_CALL(*m_gameEntityMock, DestroyGameEntity(k_fakeEntityId_Two))
.Times(1);
EBUS_EVENT(AZ::TickBus, OnTick, k_wayOverSliceTimeout, AZ::ScriptTimePoint());
}
TEST_F(ExtendedBindingWithSlicesTest, ActiveSlice_DespawnLastEntityBeforeSliceInstantiation_DespawnWholeSlice)
{
{
NetBindingSliceContext spawnContext;
spawnContext.m_contextSequence = k_fakeContextSeq;
spawnContext.m_sliceAssetId = k_fakeAssetId;
spawnContext.m_runtimeEntityId = k_fakeEntityId_One;
spawnContext.m_staticEntityId = k_fakeEntityId_One;
spawnContext.m_sliceInstanceId = k_fakeSliceInstanceId;
EBUS_EVENT(NetBindingSystemBus, SpawnEntityFromSlice, k_repId_One, spawnContext);
}
EBUS_EVENT(AZ::TickBus, OnTick, k_smallStep, AZ::ScriptTimePoint());
EBUS_EVENT(NetBindingSystemBus, UnbindGameEntity, k_fakeEntityId_One, k_fakeSliceInstanceId);
CreateMockSlice();
MockEntity* mock1 = static_cast<MockEntity*>(m_componentApplication->FindEntity(k_fakeEntityId_One));
EXPECT_CALL(*mock1, Activate()).
Times(0);
auto halfTimeoutInSeconds = AZStd::chrono::seconds(NetBindingSystemImpl::s_sliceBindingTimeout).count() * 10.f;
EBUS_EVENT(AZ::TickBus, OnTick, halfTimeoutInSeconds, AZ::ScriptTimePoint());
EBUS_EVENT(AZ::TickBus, OnTick, k_wayOverSliceTimeout, AZ::ScriptTimePoint());
}
TEST_F(ExtendedBindingWithSlicesTest, ActiveSlice_ReuseEntity)
{
EXPECT_CALL(*m_gameEntityMock, DestroyGameEntity(k_fakeEntityId_Two))
.Times(0);
{
NetBindingSliceContext spawnContext;
spawnContext.m_contextSequence = k_fakeContextSeq;
spawnContext.m_sliceAssetId = k_fakeAssetId;
spawnContext.m_runtimeEntityId = k_fakeEntityId_One;
spawnContext.m_staticEntityId = k_fakeEntityId_One;
spawnContext.m_sliceInstanceId = k_fakeSliceInstanceId;
EBUS_EVENT(NetBindingSystemBus, SpawnEntityFromSlice, k_repId_One, spawnContext);
}
{
NetBindingSliceContext spawnContext;
spawnContext.m_contextSequence = k_fakeContextSeq;
spawnContext.m_sliceAssetId = k_fakeAssetId;
spawnContext.m_runtimeEntityId = k_fakeEntityId_Two;
spawnContext.m_staticEntityId = k_fakeEntityId_Two;
spawnContext.m_sliceInstanceId = k_fakeSliceInstanceId; // both mock entities come from the same slice
EBUS_EVENT(NetBindingSystemBus, SpawnEntityFromSlice, k_repId_Two, spawnContext);
}
EBUS_EVENT(AZ::TickBus, OnTick, k_smallStep, AZ::ScriptTimePoint());
CreateMockSlice();
MockEntity* mock2 = static_cast<MockEntity*>(m_componentApplication->FindEntity(k_fakeEntityId_Two));
EXPECT_CALL(*mock2, Activate()).
Times(1);
EBUS_EVENT(AZ::TickBus, OnTick, k_wayOverSliceTimeout, AZ::ScriptTimePoint());
EXPECT_CALL(*mock2, Deactivate()).
Times(1);
// some time later the second entity goes away and comes back
EBUS_EVENT(NetBindingSystemBus, UnbindGameEntity, k_fakeEntityId_Two, k_fakeSliceInstanceId);
EBUS_EVENT(AZ::TickBus, OnTick, k_smallStep, AZ::ScriptTimePoint());
{
NetBindingSliceContext spawnContext;
spawnContext.m_contextSequence = k_fakeContextSeq;
spawnContext.m_sliceAssetId = k_fakeAssetId;
spawnContext.m_runtimeEntityId = k_fakeEntityId_Two;
spawnContext.m_staticEntityId = k_fakeEntityId_Two;
spawnContext.m_sliceInstanceId = k_fakeSliceInstanceId; // both mock entities come from the same slice
EBUS_EVENT(NetBindingSystemBus, SpawnEntityFromSlice, k_repId_Two, spawnContext);
}
// The same entity should be activated for the second time
EXPECT_CALL(*mock2, Activate()).
Times(1); // Note, Google Mock treats each expect_call separately and satisfies them separately. That's why it's 1 here, despite being a second call.
EBUS_EVENT(AZ::TickBus, OnTick, k_smallStep, AZ::ScriptTimePoint());
}
TEST_F(ExtendedBindingWithSlicesTest, SliceFailedToSpawn)
{
{
NetBindingSliceContext spawnContext;
spawnContext.m_contextSequence = k_fakeContextSeq;
spawnContext.m_sliceAssetId = k_fakeAssetId;
spawnContext.m_runtimeEntityId = k_fakeEntityId_One;
spawnContext.m_staticEntityId = k_fakeEntityId_One;
spawnContext.m_sliceInstanceId = k_fakeSliceInstanceId;
EBUS_EVENT(NetBindingSystemBus, SpawnEntityFromSlice, k_repId_One, spawnContext);
}
EBUS_EVENT(AZ::TickBus, OnTick, k_smallStep, AZ::ScriptTimePoint());
EBUS_EVENT_ID(m_sliceTicket, SliceInstantiationResultBus, OnSliceInstantiationFailed, k_fakeAssetId);
EBUS_EVENT(AZ::TickBus, OnTick, k_smallStep, AZ::ScriptTimePoint());
EXPECT_TRUE(m_componentApplication->FindEntity(k_fakeEntityId_One) == nullptr);
}
TEST_F(ExtendedBindingWithSlicesTest, SliceSpawned_AfterTimeout)
{
{
NetBindingSliceContext spawnContext;
spawnContext.m_contextSequence = k_fakeContextSeq;
spawnContext.m_sliceAssetId = k_fakeAssetId;
spawnContext.m_runtimeEntityId = k_fakeEntityId_One;
spawnContext.m_staticEntityId = k_fakeEntityId_One;
spawnContext.m_sliceInstanceId = k_fakeSliceInstanceId;
EBUS_EVENT(NetBindingSystemBus, SpawnEntityFromSlice, k_repId_One, spawnContext);
}
EBUS_EVENT(AZ::TickBus, OnTick, k_smallStep, AZ::ScriptTimePoint());
EBUS_EVENT(AZ::TickBus, OnTick, k_wayOverSliceTimeout, AZ::ScriptTimePoint());
CreateMockSlice();
MockEntity* mock1 = static_cast<MockEntity*>(m_componentApplication->FindEntity(k_fakeEntityId_One));
EXPECT_CALL(*mock1, Activate()).
Times(1);
EBUS_EVENT(AZ::TickBus, OnTick, k_smallStep, AZ::ScriptTimePoint());
}
}