// Copyright Epic Games, Inc. All Rights Reserved. #include "Misc/AutomationTest.h" #include "Tests/TestHelpers.h" #include "Tests/Fake/ChunkReferenceTracker.fake.h" #include "Tests/Fake/ChunkDataAccess.fake.h" #include "Installer/ChunkEvictionPolicy.h" #if WITH_DEV_AUTOMATION_TESTS BEGIN_DEFINE_SPEC(FChunkEvictionPolicySpec, "BuildPatchServices.Unit", EAutomationTestFlags::ProductFilter | EAutomationTestFlags_ApplicationContextMask) const uint32 TestChunkSize = 128 * 1024; // Unit TUniquePtr ChunkEvictionPolicy; // Mock TUniquePtr MockChunkReferenceTracker; TUniquePtr MockChunkDataAccess; // Data TSet ReferencedChunks; TMap ReferenceCounts; TArray NextReferences; TMap> CurrentMap; TArray ChunkData; END_DEFINE_SPEC(FChunkEvictionPolicySpec) void FChunkEvictionPolicySpec::Define() { using namespace BuildPatchServices; // Data setup. for (int32 Idx = 0; Idx < 50; ++Idx) { NextReferences.Add(FGuid::NewGuid()); } for (int32 Idx = 0; Idx < 25; ++Idx) { NextReferences.Add(FGuid(NextReferences[Idx])); } for (const FGuid& NextReference : NextReferences) { ReferencedChunks.Add(NextReference); ++ReferenceCounts.FindOrAdd(NextReference); } ChunkData.SetNumUninitialized(TestChunkSize); MockChunkDataAccess.Reset(new FFakeChunkDataAccess()); MockChunkDataAccess->ChunkData = ChunkData.GetData(); // Specs. BeforeEach([this]() { for (int32 Idx = 0; Idx < 10; ++Idx) { CurrentMap.Emplace(NextReferences[Idx], new FFakeChunkDataAccess(*MockChunkDataAccess.Get())); } MockChunkReferenceTracker.Reset(new FFakeChunkReferenceTracker()); MockChunkReferenceTracker->ReferencedChunks = ReferencedChunks; MockChunkReferenceTracker->ReferenceCounts = ReferenceCounts; MockChunkReferenceTracker->NextReferences = NextReferences; ChunkEvictionPolicy.Reset(FChunkEvictionPolicyFactory::Create(MockChunkReferenceTracker.Get())); }); Describe("ChunkEvictionPolicy", [this]() { Describe("Query", [this]() { Describe("when chunk map has free space", [this]() { It("should provide cleanable chunks which have refcount 0.", [this]() { TSet ExpectedCleanable, Cleanable, Bootable; for (int32 Idx = 0; Idx < 5; ++Idx) { MockChunkReferenceTracker->ReferenceCounts.Remove(NextReferences[Idx]); ExpectedCleanable.Add(NextReferences[Idx]); } ChunkEvictionPolicy->Query(CurrentMap, CurrentMap.Num() + 10, Cleanable, Bootable); TEST_TRUE(SetsAreEqual(Cleanable, ExpectedCleanable)); }); It("should provide no chunks if all chunks are referenced.", [this]() { TSet Cleanable, Bootable; ChunkEvictionPolicy->Query(CurrentMap, CurrentMap.Num() + 10, Cleanable, Bootable); TEST_EQUAL(Cleanable.Num(), 0); TEST_EQUAL(Bootable.Num(), 0); }); }); Describe("when chunk map is exact desired size", [this]() { It("should provide cleanable chunks which have refcount 0.", [this]() { TSet ExpectedCleanable, Cleanable, Bootable; for (int32 Idx = 0; Idx < 5; ++Idx) { MockChunkReferenceTracker->ReferenceCounts.Remove(NextReferences[Idx]); ExpectedCleanable.Add(NextReferences[Idx]); } ChunkEvictionPolicy->Query(CurrentMap, CurrentMap.Num(), Cleanable, Bootable); TEST_TRUE(SetsAreEqual(Cleanable, ExpectedCleanable)); }); It("should provide no chunks if all chunks are referenced.", [this]() { TSet Cleanable, Bootable; ChunkEvictionPolicy->Query(CurrentMap, CurrentMap.Num(), Cleanable, Bootable); TEST_EQUAL(Cleanable.Num(), 0); TEST_EQUAL(Bootable.Num(), 0); }); }); Describe("when chunk map is full", [this]() { It("should provide cleanable chunks which have refcount 0.", [this]() { TSet ExpectedCleanable, Cleanable, Bootable; for (int32 Idx = 0; Idx < 5; ++Idx) { MockChunkReferenceTracker->ReferenceCounts.Remove(NextReferences[Idx]); ExpectedCleanable.Add(NextReferences[Idx]); } ChunkEvictionPolicy->Query(CurrentMap, CurrentMap.Num() - 3, Cleanable, Bootable); TEST_TRUE(SetsAreEqual(Cleanable, ExpectedCleanable)); }); It("should provide minimum number of bootable chunks.", [this]() { TSet Cleanable, Bootable; ChunkEvictionPolicy->Query(CurrentMap, CurrentMap.Num() - 3, Cleanable, Bootable); TEST_EQUAL(Bootable.Num(), 3); }); It("should provide cleanable chunks over bootable chunks.", [this]() { TSet ExpectedCleanable, Cleanable, ExpectedBootable, Bootable; MockChunkReferenceTracker->ReferenceCounts.Remove(NextReferences[0]); ExpectedCleanable.Add(NextReferences[0]); MockChunkReferenceTracker->ReferenceCounts.Remove(NextReferences[1]); ExpectedCleanable.Add(NextReferences[1]); ExpectedBootable.Add(NextReferences[9]); ChunkEvictionPolicy->Query(CurrentMap, CurrentMap.Num() - 3, Cleanable, Bootable); TEST_TRUE(SetsAreEqual(Cleanable, ExpectedCleanable)); TEST_TRUE(SetsAreEqual(Bootable, ExpectedBootable)); }); It("should provide bootable chunks which are needed the latest.", [this]() { TSet Cleanable, ExpectedBootable, Bootable; ExpectedBootable.Add(NextReferences[9]); ExpectedBootable.Add(NextReferences[8]); ExpectedBootable.Add(NextReferences[7]); ChunkEvictionPolicy->Query(CurrentMap, CurrentMap.Num() - 3, Cleanable, Bootable); TEST_TRUE(SetsAreEqual(Bootable, ExpectedBootable)); }); }); }); }); AfterEach([this]() { ChunkEvictionPolicy.Reset(); MockChunkReferenceTracker.Reset(); CurrentMap.Reset(); }); } #endif //WITH_DEV_AUTOMATION_TESTS