Files
UnrealEngine/Engine/Source/Runtime/Online/BuildPatchServices/Private/Tests/Unit/ChunkEvictionPolicy.spec.cpp
2025-05-18 13:04:45 +08:00

168 lines
5.7 KiB
C++

// 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<BuildPatchServices::IChunkEvictionPolicy> ChunkEvictionPolicy;
// Mock
TUniquePtr<BuildPatchServices::FFakeChunkReferenceTracker> MockChunkReferenceTracker;
TUniquePtr<BuildPatchServices::FFakeChunkDataAccess> MockChunkDataAccess;
// Data
TSet<FGuid> ReferencedChunks;
TMap<FGuid, int32> ReferenceCounts;
TArray<FGuid> NextReferences;
TMap<FGuid, TUniquePtr<BuildPatchServices::IChunkDataAccess>> CurrentMap;
TArray<uint8> 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<FGuid> 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<FGuid> 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<FGuid> 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<FGuid> 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<FGuid> 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<FGuid> 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<FGuid> 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<FGuid> 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