// Copyright Epic Games, Inc. All Rights Reserved. #include "MassArchetypeData.h" #include "MassEntityManager.h" #include "MassEntityTestTypes.h" #include "MassExecutionContext.h" #include "Algo/RandomShuffle.h" #include "MassExecutor.h" #include "MassProcessingContext.h" #include "Algo/Compare.h" #include "Algo/RemoveIf.h" #define LOCTEXT_NAMESPACE "MassTest" UE_DISABLE_OPTIMIZATION_SHIP //----------------------------------------------------------------------// // tests //----------------------------------------------------------------------// namespace FMassArchetypeEntityCollectionTest { struct FArchetypeEntityCollectionTestBase : FEntityTestBase { TArray Entities; virtual bool SetUp() override { FEntityTestBase::SetUp(); const int32 Count = 100; EntityManager->BatchCreateEntities(FloatsArchetype, Count, Entities); return true; } virtual void TearDown() override { Entities.Reset(); FEntityTestBase::TearDown(); } }; struct FArchetypeEntityCollection_CreateBasic : FArchetypeEntityCollectionTestBase { virtual bool InstantTest() override { TArray EntitiesSubSet; // Should end up as last chunk EntitiesSubSet.Add(Entities[99]); EntitiesSubSet.Add(Entities[97]); EntitiesSubSet.Add(Entities[98]); // Should end up as third chunk EntitiesSubSet.Add(Entities[20]); EntitiesSubSet.Add(Entities[22]); EntitiesSubSet.Add(Entities[21]); // Should end up as second chunk EntitiesSubSet.Add(Entities[18]); // Should end up as first chunk EntitiesSubSet.Add(Entities[10]); EntitiesSubSet.Add(Entities[13]); EntitiesSubSet.Add(Entities[11]); EntitiesSubSet.Add(Entities[12]); FMassArchetypeEntityCollection EntityCollection(FloatsArchetype, EntitiesSubSet, FMassArchetypeEntityCollection::NoDuplicates); FMassArchetypeEntityCollection::FConstEntityRangeArrayView Ranges = EntityCollection.GetRanges(); AITEST_EQUAL("The predicted sub-chunk count should match", Ranges.Num(), 4); AITEST_EQUAL("The [10-13] chunk should be first and start at 10", Ranges[0].SubchunkStart, 10); AITEST_EQUAL("The [10-13] chunk should be first and have a length of 4", Ranges[0].Length, 4); AITEST_EQUAL("The [18] chunk should be second and start at 18", Ranges[1].SubchunkStart, 18); AITEST_EQUAL("The [18] chunk should be second and have a length of 1", Ranges[1].Length, 1); AITEST_EQUAL("The [20-22] chunk should be third and start at 20", Ranges[2].SubchunkStart, 20); AITEST_EQUAL("The [20-22] chunk should be third and have a length of 3", Ranges[2].Length, 3); AITEST_EQUAL("The [97-99] chunk should be third and start at 97", Ranges[3].SubchunkStart, 97); AITEST_EQUAL("The [97-99] chunk should be third and have a length of 3", Ranges[3].Length, 3); return true; } }; IMPLEMENT_AI_INSTANT_TEST(FArchetypeEntityCollection_CreateBasic, "System.Mass.ArchetypeEntityCollection.Create.Basic"); struct FArchetypeEntityCollection_CreateOrderInvariant : FArchetypeEntityCollectionTestBase { virtual bool InstantTest() override { TArray EntitiesSubSet(&Entities[10], 30); EntitiesSubSet.RemoveAt(10, EAllowShrinking::No); FMassArchetypeEntityCollection CollectionFromOrdered(FloatsArchetype, EntitiesSubSet, FMassArchetypeEntityCollection::NoDuplicates); FRandomStream Rand(0); ShuffleDataWithRandomStream(Rand, EntitiesSubSet); FMassArchetypeEntityCollection CollectionFromRandom(FloatsArchetype, EntitiesSubSet, FMassArchetypeEntityCollection::NoDuplicates); AITEST_TRUE("The resulting chunk collection should be the same regardless of the order of input entities", CollectionFromOrdered.IsSame(CollectionFromRandom)); // just to roughly make sure the result is what we expect FMassArchetypeEntityCollection::FConstEntityRangeArrayView Ranges = CollectionFromOrdered.GetRanges(); AITEST_EQUAL("The result should contain two chunks", Ranges.Num(), 2); return true; } }; IMPLEMENT_AI_INSTANT_TEST(FArchetypeEntityCollection_CreateOrderInvariant, "System.Mass.ArchetypeEntityCollection.Create.OrderInvariant"); struct FArchetypeEntityCollection_CreateCrossChunk : FEntityTestBase { virtual bool InstantTest() override { #if WITH_MASSENTITY_DEBUG TArray Entities; const int32 EntitiesPerChunk = EntityManager->DebugGetArchetypeEntitiesCountPerChunk(FloatsArchetype); const int32 SpillOver = 10; const int32 Count = EntitiesPerChunk + SpillOver; EntityManager->BatchCreateEntities(FloatsArchetype, Count, Entities); TArray EntitiesSubCollection; EntitiesSubCollection.Add(Entities[EntitiesPerChunk]); for (int i = 1; i < SpillOver; ++i) { EntitiesSubCollection.Add(Entities[EntitiesPerChunk + i]); EntitiesSubCollection.Add(Entities[EntitiesPerChunk - i]); } FMassArchetypeEntityCollection EntityCollection(FloatsArchetype, EntitiesSubCollection, FMassArchetypeEntityCollection::NoDuplicates); FMassArchetypeEntityCollection::FConstEntityRangeArrayView Ranges = EntityCollection.GetRanges(); AITEST_EQUAL("The given continuous range should get split in two", Ranges.Num(), 2); AITEST_EQUAL("The part in first archetype\'s chunk should contain 9 elements", Ranges[0].Length, 9); AITEST_EQUAL("The part in second archetype\'s chunk should contain 10 elements", Ranges[1].Length, 10); #endif // WITH_MASSENTITY_DEBUG return true; } }; IMPLEMENT_AI_INSTANT_TEST(FArchetypeEntityCollection_CreateCrossChunk, "System.Mass.ArchetypeEntityCollection.Create.CrossChunk"); struct FArchetypeEntityCollection_CreateWithDuplicatesTrivial : FArchetypeEntityCollectionTestBase { virtual bool InstantTest() override { TArray EntitiesWithDuplicates; EntitiesWithDuplicates.Add(Entities[2]); EntitiesWithDuplicates.Add(Entities[2]); FMassArchetypeEntityCollection EntityCollection(FloatsArchetype, EntitiesWithDuplicates, FMassArchetypeEntityCollection::FoldDuplicates); FMassArchetypeEntityCollection::FConstEntityRangeArrayView Ranges = EntityCollection.GetRanges(); AITEST_EQUAL("The result should have a single subchunk", Ranges.Num(), 1); AITEST_EQUAL("The resulting subchunk should be of length 1", Ranges[0].Length, 1); return true; } }; IMPLEMENT_AI_INSTANT_TEST(FArchetypeEntityCollection_CreateWithDuplicatesTrivial, "System.Mass.ArchetypeEntityCollection.Create.TrivialDuplicates"); struct FArchetypeEntityCollection_CreateWithDuplicates : FArchetypeEntityCollectionTestBase { virtual bool InstantTest() override { TArray EntitiesWithDuplicates; EntitiesWithDuplicates.Add(Entities[0]); EntitiesWithDuplicates.Add(Entities[0]); EntitiesWithDuplicates.Add(Entities[0]); EntitiesWithDuplicates.Add(Entities[1]); EntitiesWithDuplicates.Add(Entities[2]); EntitiesWithDuplicates.Add(Entities[2]); FMassArchetypeEntityCollection EntityCollection(FloatsArchetype, EntitiesWithDuplicates, FMassArchetypeEntityCollection::FoldDuplicates); FMassArchetypeEntityCollection::FConstEntityRangeArrayView Ranges = EntityCollection.GetRanges(); AITEST_EQUAL("The result should have a single subchunk", Ranges.Num(), 1); AITEST_EQUAL("The resulting subchunk should be of length 3", Ranges[0].Length, 3); return true; } }; IMPLEMENT_AI_INSTANT_TEST(FArchetypeEntityCollection_CreateWithDuplicates, "System.Mass.ArchetypeEntityCollection.Create.Duplicates"); struct FArchetypeEntityCollection_CreateWithInvalidDuplicates : FArchetypeEntityCollectionTestBase { virtual bool InstantTest() override { { TArray EntitiesSubSet; EntitiesSubSet.Add(FMassEntityHandle()); EntitiesSubSet.Add(Entities[0]); EntitiesSubSet.Add(Entities[0]); EntitiesSubSet.Add(FMassEntityHandle()); FMassArchetypeEntityCollection Collection(FloatsArchetype, EntitiesSubSet, FMassArchetypeEntityCollection::FoldDuplicates); // The resulting Collection should have only a single Range consisting of a single entity (matching Entities[0]) AITEST_EQUAL(TEXT("We expect only a single resulting range"), Collection.GetRanges().Num(), 1); AITEST_EQUAL(TEXT("We expect only a single entity in the resulting range"), Collection.GetRanges()[0].SubchunkStart, 0); AITEST_EQUAL(TEXT("We expect only a single entity in the resulting range"), Collection.GetRanges()[0].Length, 1); } { TArray EntitiesSubSet; EntitiesSubSet.Add(Entities[4]); EntitiesSubSet.Add(FMassEntityHandle()); EntitiesSubSet.Add(FMassEntityHandle()); EntitiesSubSet.Add(Entities[3]); EntitiesSubSet.Add(FMassEntityHandle()); EntitiesSubSet.Add(Entities[1]); FMassArchetypeEntityCollection Collection(FloatsArchetype, EntitiesSubSet, FMassArchetypeEntityCollection::FoldDuplicates); // The resulting Collection should have two Ranges for a single archetype, one of them with two entities (3, 4). AITEST_EQUAL(TEXT("We expect two resulting range"), Collection.GetRanges().Num(), 2); AITEST_EQUAL(TEXT("We expect the first range to consist of a single entity"), Collection.GetRanges()[0].Length, 1); AITEST_EQUAL(TEXT("We expect the second range to consist of a two entities"), Collection.GetRanges()[1].Length, 2); // the specific composition of resulting ranges is being tested by other tests } return true; } }; IMPLEMENT_AI_INSTANT_TEST(FArchetypeEntityCollection_CreateWithInvalidDuplicates, "System.Mass.ArchetypeEntityCollection.Create.InvalidDuplicates"); struct FArchetypeEntityCollection_CreateWithInvalidDuplicatesWithPayload : FArchetypeEntityCollectionTestBase { virtual bool InstantTest() override { TArray EntitiesSubSet; TArray Payload; EntitiesSubSet.Add(FMassEntityHandle()); Payload.Add(FTestFragment_Int(int32(2))); EntitiesSubSet.Add(Entities[0]); Payload.Add(FTestFragment_Int(int32(0))); EntitiesSubSet.Add(Entities[0]); Payload.Add(FTestFragment_Int(int32(1))); EntitiesSubSet.Add(FMassEntityHandle()); Payload.Add(FTestFragment_Int(int32(3))); // transform typed payload array into generic one for sorting purposes FStructArrayView PaloadView(Payload); TArray Result; { AITEST_SCOPED_CHECK("Invalid entity handle passed in", 2); FMassArchetypeEntityCollectionWithPayload::CreateEntityRangesWithPayload(*EntityManager, EntitiesSubSet, FMassArchetypeEntityCollection::FoldDuplicates , FMassGenericPayloadView(MakeArrayView(&PaloadView, 1)), Result); } AITEST_EQUAL(TEXT("We expect only a single result"), Result.Num(), 1); AITEST_EQUAL(TEXT("We expect only a single resulting range"), Result[0].GetEntityCollection().GetRanges().Num(), 1); AITEST_EQUAL(TEXT("We expect only a single entity in the resulting range"), Result[0].GetEntityCollection().GetRanges()[0].SubchunkStart, 0); AITEST_EQUAL(TEXT("We expect only a single entity in the resulting range"), Result[0].GetEntityCollection().GetRanges()[0].Length, 1); return true; } }; IMPLEMENT_AI_INSTANT_TEST(FArchetypeEntityCollection_CreateWithInvalidDuplicatesWithPayload, "System.Mass.ArchetypeEntityCollection.Create.InvalidDuplicatesWithPayloadWithPayload"); #if WITH_MASSENTITY_DEBUG struct FArchetypeEntityCollection_WithPayloadBase : FArchetypeEntityCollectionTestBase { virtual bool SetUp() override { // skipping FEntityCollectionTestBase::SetUp on purpose to manually create entities FEntityTestBase::SetUp(); const int32 EntitiesPerChunk = EntityManager->DebugGetArchetypeEntitiesCountPerChunk(FloatsArchetype); EntityManager->BatchCreateEntities(FloatsArchetype, EntitiesPerChunk * 2, Entities); return true; } }; struct FArchetypeEntityCollection_TrivialDuplicatesWithPayload : FArchetypeEntityCollection_WithPayloadBase { virtual bool InstantTest() override { const int32 EntitiesPerChunk = EntityManager->DebugGetArchetypeEntitiesCountPerChunk(FloatsArchetype); const int32 TotalCount = Entities.Num(); const int32 SubSetCount = int(0.6 * Entities.Num()); // using >0.5 to ensure some entities picked being in sequence and/or in different chunks TArray EntitiesSubSet; TArray Payload; EntitiesSubSet.Add(Entities[EntitiesPerChunk + 20]); Payload.Add(FTestFragment_Int(int32(0))); EntitiesSubSet.Add(Entities[EntitiesPerChunk + 20]); Payload.Add(FTestFragment_Int(int32(1))); // transform typed payload array into generic one for sorting purposes FStructArrayView PaloadView(Payload); TArray Result; FMassArchetypeEntityCollectionWithPayload::CreateEntityRangesWithPayload(*EntityManager, EntitiesSubSet, FMassArchetypeEntityCollection::FoldDuplicates , FMassGenericPayloadView(MakeArrayView(&PaloadView, 1)), Result); AITEST_EQUAL(TEXT("We expect only a single result"), Result.Num(), 1); AITEST_EQUAL(TEXT("We expect only a single resulting range"), Result[0].GetEntityCollection().GetRanges().Num(), 1); AITEST_EQUAL(TEXT("We expect only a single entity in the resulting range"), Result[0].GetEntityCollection().GetRanges()[0].Length, 1); return true; } }; IMPLEMENT_AI_INSTANT_TEST(FArchetypeEntityCollection_TrivialDuplicatesWithPayload, "System.Mass.ArchetypeEntityCollection.Create.TrivialDuplicatesWithPayload"); // @todo also add another archetype struct FArchetypeEntityCollection_MultiDuplicatesWithPayload : FArchetypeEntityCollection_WithPayloadBase { virtual bool InstantTest() override { const int32 EntitiesPerChunk = EntityManager->DebugGetArchetypeEntitiesCountPerChunk(FloatsArchetype); const int32 TotalCount = Entities.Num(); const int32 SubSetCount = int(0.6 * Entities.Num()); // using >0.5 to ensure some entities picked being in sequence and/or in different chunks TArray EntitiesSubSet; TArray Payload; constexpr int32 NumUniques = 3; constexpr int32 NumDuplicatesEach = 4; int32 FragmentValue = 0; for (int32 Iteration = 0; Iteration < NumDuplicatesEach; ++Iteration, ++FragmentValue) { for (int32 Unique = 0; Unique < NumUniques; ++Unique) { EntitiesSubSet.Add(Entities[EntitiesPerChunk + 20 + Unique]); Payload.Add(FTestFragment_Int(Unique)); } } // transform typed payload array into generic one for sorting purposes FStructArrayView PaloadView(Payload); TArray Result; FMassArchetypeEntityCollectionWithPayload::CreateEntityRangesWithPayload(*EntityManager, EntitiesSubSet, FMassArchetypeEntityCollection::FoldDuplicates , FMassGenericPayloadView(MakeArrayView(&PaloadView, 1)), Result); AITEST_EQUAL(TEXT("We expect only a single result"), Result.Num(), 1); AITEST_EQUAL(TEXT("We expect only a single resulting range"), Result[0].GetEntityCollection().GetRanges().Num(), 1); AITEST_EQUAL(TEXT("We expect exatly NumUniques entities in the resulting range"), Result[0].GetEntityCollection().GetRanges()[0].Length, NumUniques); const FMassGenericPayloadViewSlice& PayloadSlice = Result[0].GetPayload(); for (int32 Unique = 0; Unique < NumUniques; ++Unique) { AITEST_EQUAL("The surviving payload value should match the expected", PayloadSlice[0].GetAt(Unique).Value, Unique); } return true; } }; IMPLEMENT_AI_INSTANT_TEST(FArchetypeEntityCollection_MultiDuplicatesWithPayload, "System.Mass.ArchetypeEntityCollection.Create.MultiDuplicatesWithPayload"); struct FArchetypeEntityCollection_WithPayload : FArchetypeEntityCollection_WithPayloadBase { virtual bool InstantTest() override { const int32 TotalCount = Entities.Num(); const int32 SubSetCount = int(0.6 * Entities.Num()); // using >0.5 to ensure some entities picked being in sequence and/or in different chunks TArray EntitiesSubSet; TArray Payload; TArray Indices; Indices.AddUninitialized(TotalCount); for (int32 i = 0; i < Indices.Num(); ++i) { Indices[i] = i; } FMath::SRandInit(TotalCount); Algo::RandomShuffle(Indices); Indices.SetNum(SubSetCount); for (int32 i : Indices) { EntitiesSubSet.Add(Entities[i]); Payload.Add(FTestFragment_Int(i)); } // transform typed payload array into generic one for sorting purposes FStructArrayView PaloadView(Payload); TArray Result; FMassArchetypeEntityCollectionWithPayload::CreateEntityRangesWithPayload(*EntityManager, EntitiesSubSet, FMassArchetypeEntityCollection::FoldDuplicates , FMassGenericPayloadView(MakeArrayView(&PaloadView, 1)), Result); // at this point Payload should be sorted ascending // and the values in Payload should match entities at given locations for (int i = 1; i < Payload.Num(); ++i) { AITEST_TRUE("Items in Payload should be arranged in an ascending manner", Payload[i].Value >= Payload[i-1].Value); } return true; } }; IMPLEMENT_AI_INSTANT_TEST(FArchetypeEntityCollection_WithPayload, "System.Mass.ArchetypeEntityCollection.Create.WithPayload"); struct FArchetypeEntityCollection_ZeroLength_Destroy : FEntityTestBase { virtual bool InstantTest() override { CA_ASSUME(EntityManager); const FMassArchetypeHandle SourceArchetype = FloatsArchetype; const int32 EntitiesPerChunk = FMassArchetypeHelper::ArchetypeDataFromHandleChecked(SourceArchetype).GetNumEntitiesPerChunk(); const int32 NumChunks = 3; const int32 EntityCount = NumChunks * EntitiesPerChunk; TArray Entities; EntityManager->BatchCreateEntities(SourceArchetype, {}, EntityCount, Entities); AITEST_EQUAL("Entity creation generated expected number of entities", EntityCount, FMassArchetypeHelper::ArchetypeDataFromHandleChecked(SourceArchetype).GetNumEntities()); FMassEntityQuery Query(EntityManager); Query.CacheArchetypes(); FMassArchetypeEntityCollection Collection(SourceArchetype); AITEST_EQUAL("Created collection has expected number of ranges (matching number of chunks created" , Collection.GetRanges().Num(), NumChunks); EntityManager->BatchDestroyEntityChunks(Collection); AITEST_EQUAL("Entity destruction destroyed all entities", FMassArchetypeHelper::ArchetypeDataFromHandleChecked(SourceArchetype).GetNumEntities(), 0); return true; } }; IMPLEMENT_AI_INSTANT_TEST(FArchetypeEntityCollection_ZeroLength_Destroy, "System.Mass.Entity.ZeroLengthRanges.Destroy"); struct FArchetypeEntityCollection_ZeroLength_Execute : FEntityTestBase { virtual bool InstantTest() override { CA_ASSUME(EntityManager); const FMassArchetypeHandle SourceArchetype = FloatsArchetype; const int32 EntitiesPerChunk = FMassArchetypeHelper::ArchetypeDataFromHandleChecked(SourceArchetype).GetNumEntitiesPerChunk(); const int32 NumChunks = 3; const int32 EntityCount = NumChunks * EntitiesPerChunk; TArray Entities; EntityManager->BatchCreateEntities(SourceArchetype, {}, EntityCount, Entities); FMassArchetypeEntityCollection Collection(SourceArchetype); AITEST_EQUAL("Entity creation generated expected number of entities", EntityCount, FMassArchetypeHelper::ArchetypeDataFromHandleChecked(SourceArchetype).GetNumEntities()); int32 ProcessedEntitiesCount = 0; UMassTestProcessorBase* Processor = NewTestProcessor(EntityManager); Processor->ForEachEntityChunkExecutionFunction = [&ProcessedEntitiesCount](FMassExecutionContext& Context) { ProcessedEntitiesCount += Context.GetNumEntities(); }; Processor->EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); FMassProcessingContext ProcessingContext(EntityManager, /*DeltaSeconds=*/0.f); UE::Mass::Executor::RunProcessorsView(MakeArrayView((UMassProcessor**)&Processor, 1), ProcessingContext, MakeConstArrayView(&Collection, 1)); AITEST_EQUAL("Number of entities processed matches expectations" , ProcessedEntitiesCount, EntityCount); return true; } }; IMPLEMENT_AI_INSTANT_TEST(FArchetypeEntityCollection_ZeroLength_Execute, "System.Mass.Entity.ZeroLengthRanges.Execute"); struct FArchetypeEntityCollection_ZeroLength_BatchMove : FEntityTestBase { virtual bool InstantTest() override { CA_ASSUME(EntityManager); const FMassArchetypeHandle SourceArchetype = FloatsArchetype; const int32 EntitiesPerChunk = FMassArchetypeHelper::ArchetypeDataFromHandleChecked(SourceArchetype).GetNumEntitiesPerChunk(); const int32 NumChunks = 3; const int32 EntityCount = NumChunks * EntitiesPerChunk; TArray Entities; EntityManager->BatchCreateEntities(SourceArchetype, {}, EntityCount, Entities); FMassArchetypeEntityCollection Collection(SourceArchetype); const UScriptStruct* AddedTag = FTestTag_A::StaticStruct(); AITEST_EQUAL("Entity creation generated expected number of entities", EntityCount, FMassArchetypeHelper::ArchetypeDataFromHandleChecked(SourceArchetype).GetNumEntities()); check(EntityCount == Entities.Num()); EntityManager->BatchChangeTagsForEntities(MakeArrayView(&Collection, 1), FMassTagBitSet(*AddedTag), {}); const FMassArchetypeHandle NewArchetype = EntityManager->GetArchetypeForEntity(Entities[0]); AITEST_NOT_EQUAL("Entities have changed their host archetype", NewArchetype, SourceArchetype); const FMassArchetypeHandle ExpectedArchetype = EntityManager->CreateArchetype(SourceArchetype, MakeArrayView(&AddedTag, 1)); AITEST_EQUAL("The new archetype matches expectations", NewArchetype, ExpectedArchetype); AITEST_EQUAL("All the entities have been moved to the expected archetype", EntityManager->DebugGetArchetypeEntitiesCount(NewArchetype), EntityCount); return true; } }; IMPLEMENT_AI_INSTANT_TEST(FArchetypeEntityCollection_ZeroLength_BatchMove, "System.Mass.Entity.ZeroLengthRanges.BatchMove"); struct FArchetypeEntityCollection_ZeroLength_BatchSetFragmentValues : FEntityTestBase { virtual bool InstantTest() override { CA_ASSUME(EntityManager); const FMassArchetypeHandle SourceArchetype = FloatsArchetype; const int32 EntitiesPerChunk = FMassArchetypeHelper::ArchetypeDataFromHandleChecked(SourceArchetype).GetNumEntitiesPerChunk(); const int32 NumChunks = 3; const int32 EntityCount = NumChunks * EntitiesPerChunk; TArray Entities; EntityManager->BatchCreateEntities(SourceArchetype, {}, EntityCount, Entities); AITEST_EQUAL("Entity creation generated expected number of entities", EntityCount, FMassArchetypeHelper::ArchetypeDataFromHandleChecked(SourceArchetype).GetNumEntities()); check(EntityCount == Entities.Num()); FMassArchetypeEntityCollectionWithPayload EntityCollectionWithEmptyPayload{FMassArchetypeEntityCollection(SourceArchetype)}; const FMassFragmentBitSet AddedFragmentsBitSet(*FTestFragment_Int::StaticStruct()); EntityManager->BatchAddFragmentInstancesForEntities(MakeArrayView(&EntityCollectionWithEmptyPayload, 1), AddedFragmentsBitSet); const FMassArchetypeHandle NewArchetype = EntityManager->GetArchetypeForEntity(Entities[0]); AITEST_NOT_EQUAL("Entities have changed their host archetype", NewArchetype, SourceArchetype); const FMassArchetypeHandle ExpectedArchetype = FloatsIntsArchetype; AITEST_EQUAL("The new archetype matches expectations", NewArchetype, ExpectedArchetype); AITEST_EQUAL("All the entities have been moved to the expected archetype", EntityManager->DebugGetArchetypeEntitiesCount(NewArchetype), EntityCount); return true; } }; IMPLEMENT_AI_INSTANT_TEST(FArchetypeEntityCollection_ZeroLength_BatchSetFragmentValues, "System.Mass.Entity.ZeroLengthRanges.BatchSetFragmentValues"); struct FArchetypeEntityCollection_ExportHandles_Created : FEntityTestBase { virtual bool InstantTest() override { CA_ASSUME(EntityManager); const FMassArchetypeHandle SourceArchetype = FloatsArchetype; const int32 EntitiesPerChunk = FMassArchetypeHelper::ArchetypeDataFromHandleChecked(SourceArchetype).GetNumEntitiesPerChunk(); const int32 NumChunks = 3; const int32 EntityCount = NumChunks * EntitiesPerChunk; TArray Entities; EntityManager->BatchCreateEntities(SourceArchetype, {}, EntityCount, Entities); // deterministically removing half of the entities FRandomStream RandomStream(0); const int32 RemainingCount = Algo::StableRemoveIf(Entities, [&RandomStream](const FMassEntityHandle& Element) { return RandomStream.FRand() < 0.5f; }); Entities.SetNum(RemainingCount); FMassArchetypeEntityCollection Collection(SourceArchetype, Entities, FMassArchetypeEntityCollection::EDuplicatesHandling::NoDuplicates); TArray ExportedHandles; Collection.ExportEntityHandles(ExportedHandles); AITEST_TRUE("Exported handles are the same as the input data", Algo::Compare(ExportedHandles, Entities)); return true; } }; IMPLEMENT_AI_INSTANT_TEST(FArchetypeEntityCollection_ExportHandles_Created, "System.Mass.ArchetypeEntityCollection.ExportHandles.CreatedEntities"); #endif // WITH_MASSENTITY_DEBUG // } UE_ENABLE_OPTIMIZATION_SHIP #undef LOCTEXT_NAMESPACE