Files
UnrealEngine/Engine/Source/Runtime/AssetRegistry/Private/DependsNodeTest.cpp
2025-05-18 13:04:45 +08:00

1220 lines
52 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Misc/AutomationTest.h"
#if WITH_DEV_AUTOMATION_TESTS
#include "AssetRegistry/AssetRegistryState.h"
#include "DependsNode.h"
#include "Serialization/MemoryReader.h"
#include "Serialization/MemoryWriter.h"
IMPLEMENT_SIMPLE_AUTOMATION_TEST(FDependsNodeTest, "System.AssetRegistry.DependsNode", EAutomationTestFlags_ApplicationContextMask | EAutomationTestFlags::EngineFilter);
namespace
{
template <typename ElementType>
bool CombinationEquals(TArrayView<const ElementType> A, TArrayView<const ElementType> B)
{
if (A.Num() != B.Num())
{
return false;
}
for (const ElementType& AObj : A)
{
int ACount = 0;
for (const ElementType& AObj2 : A)
{
if (AObj2 == AObj)
{
++ACount;
}
}
int BCount = 0;
for (const ElementType& BObj : B)
{
if (BObj == AObj)
{
++BCount;
}
}
if (ACount != BCount)
{
return false;
}
}
return true;
}
template <typename ElementType>
static bool CombinationEquals(TArrayView<const ElementType> A, const ElementType& B)
{
return CombinationEquals(A, TArrayView<const ElementType>(&B, 1));
}
}
bool FDependsNodeTest::RunTest(const FString& Parameters)
{
using namespace UE::AssetRegistry;
TArray<FDependsNode*> Dependencies;
TArray<FDependsNode*> Referencers;
EDependencyCategory AllCategories[] = { EDependencyCategory::Package, EDependencyCategory::SearchableName, EDependencyCategory::Manage };
constexpr int CategoryNum = UE_ARRAY_COUNT(AllCategories);
FDependsNode ScratchNodes[10];
// Added dependency is found by GetDependencies and ContainsDependency and GetReferencers
{
for (EDependencyCategory Category : {EDependencyCategory::Package, EDependencyCategory::SearchableName, EDependencyCategory::Manage})
{
FDependsNode A;
FDependsNode* PointerA = &A;
FDependsNode* B = &ScratchNodes[0];
A.AddDependency(B, Category, EDependencyProperty::None);
B->AddReferencer(PointerA);
Dependencies.Reset();
A.GetDependencies(Dependencies);
Referencers.Reset();
B->GetReferencers(Referencers);
TestTrue(TEXT("GetDependencies single"), CombinationEquals<FDependsNode*>(Dependencies, B));
TestTrue(TEXT("ContainsDependency AllCategory single"), A.ContainsDependency(B, EDependencyCategory::All));
TestTrue(TEXT("ContainsDependency SpecificCategory single"), A.ContainsDependency(B, Category));
TestTrue(TEXT("GetReferencers single"), CombinationEquals<FDependsNode*>(Referencers, PointerA));
A.RemoveDependency(B, Category);
B->RemoveReferencer(PointerA);
Dependencies.Reset();
A.GetDependencies(Dependencies);
Referencers.Reset();
B->GetReferencers(Referencers);
TestTrue(TEXT("RemoveDependency"), Dependencies.Num() == 0);
TestTrue(TEXT("ContainsDependency AllCategory single"), Referencers.Num() == 0);
}
}
// Added dependency is found by GetDependencies and ContainsDependency when multiple dependencies are present
{
int NodeIndex = 0;
FDependsNode A;
FDependsNode* PointerA = &A;
TArray<FDependsNode*> Expected[CategoryNum];
int CategoryIndex = 0;
for (EDependencyCategory Category : AllCategories)
{
check(NodeIndex < UE_ARRAY_COUNT(ScratchNodes));
Expected[CategoryIndex].Add(&ScratchNodes[NodeIndex++]);
Expected[CategoryIndex].Add(&ScratchNodes[NodeIndex++]);
A.AddDependency(Expected[CategoryIndex][0], Category, EDependencyProperty::None);
A.AddDependency(Expected[CategoryIndex][1], Category, EDependencyProperty::None);
Expected[CategoryIndex][0]->AddReferencer(&A);
Expected[CategoryIndex][1]->AddReferencer(&A);
++CategoryIndex;
}
CategoryIndex = 0;
for (EDependencyCategory Category : AllCategories)
{
Dependencies.Reset();
A.GetDependencies(Dependencies, Category);
TestTrue(TEXT("GetDependencies multiple"), CombinationEquals<FDependsNode*>(Dependencies, Expected[CategoryIndex]));
for (FDependsNode* ExpectedNode : Expected[CategoryIndex])
{
TestTrue(TEXT("ContainsDependency AllCategory multiple"), A.ContainsDependency(ExpectedNode, EDependencyCategory::All));
TestTrue(TEXT("ContainsDependency SpecificCategory multiple"), A.ContainsDependency(ExpectedNode, Category));
Referencers.Reset();
ExpectedNode->GetReferencers(Referencers, Category);
TestTrue(TEXT("GetReferencers multiple"), CombinationEquals<FDependsNode*>(Referencers, PointerA));
}
++CategoryIndex;
}
CategoryIndex = 0;
for (EDependencyCategory Category : AllCategories)
{
Expected[CategoryIndex][0]->RemoveReferencer(&A);
Expected[CategoryIndex][1]->RemoveReferencer(&A);
++CategoryIndex;
}
}
// Added dependency and referencer is found when filtering by Flags
enum class ESortTestType
{
AlwaysSorted,
AlwaysUnsorted,
UnsortedThenSorted,
};
for (ESortTestType SortTestType : { ESortTestType::AlwaysSorted, ESortTestType::AlwaysUnsorted, ESortTestType::UnsortedThenSorted })
{
TArray<FAssetDependency> ReferencersStructs;
int NodeIndex = 0;
FDependsNode A;
FName IdentifierA(TEXT("IdentifierA"));
A.SetIdentifier(IdentifierA);
FDependsNode* PointerA = &A;
int NumScratch = 6;
if (SortTestType == ESortTestType::AlwaysUnsorted || SortTestType == ESortTestType::UnsortedThenSorted)
{
A.SetIsDependencyListSorted(EDependencyCategory::All, false);
for (int n = 0; n < NumScratch; ++n)
{
ScratchNodes[n].SetIsReferencersSorted(false);
}
}
for (EDependencyCategory Category : { EDependencyCategory::Package, EDependencyCategory::SearchableName, EDependencyCategory::Manage})
{
TestEqual(TEXT("IsDependencyListSorted"), A.IsDependencyListSorted(Category), SortTestType == ESortTestType::AlwaysSorted);
}
TestEqual(TEXT("IsReferencersSorted"), ScratchNodes[0].IsReferencersSorted(), SortTestType == ESortTestType::AlwaysSorted);
struct FTestDependency
{
FDependsNode* Node;
EDependencyCategory Category;
EDependencyProperty Properties;
FTestDependency(const FTestDependency& Other, FDependsNode* NewNode)
:Node(NewNode), Category(Other.Category), Properties(Other.Properties)
{}
FTestDependency(FDependsNode* InNode, EDependencyCategory InCategory, EDependencyProperty InProperties)
:Node(InNode), Category(InCategory), Properties(InProperties)
{}
bool operator==(const FTestDependency& Other) const
{
return Node == Other.Node && Category == Other.Category && Properties == Other.Properties;
}
FAssetDependency AsAssetDependency() const
{
return FAssetDependency{ Node->GetIdentifier(), Category, Properties };
}
};
FTestDependency TestDependencies[] = {
{&ScratchNodes[0], EDependencyCategory::Package, EDependencyProperty::Hard | EDependencyProperty::Game | EDependencyProperty::Build},
{&ScratchNodes[1], EDependencyCategory::Package, EDependencyProperty::Hard},
{&ScratchNodes[2], EDependencyCategory::Package, EDependencyProperty::None},
{&ScratchNodes[3], EDependencyCategory::SearchableName, EDependencyProperty::None},
{&ScratchNodes[4], EDependencyCategory::Manage, EDependencyProperty::Direct},
{&ScratchNodes[5], EDependencyCategory::Manage, EDependencyProperty::None }
};
for (FTestDependency& TestDependency : TestDependencies)
{
A.AddDependency(TestDependency.Node, TestDependency.Category, TestDependency.Properties);
}
for (int n = 0; n < NumScratch; ++n)
{
ScratchNodes[n].AddReferencer(&A);
}
if (SortTestType == ESortTestType::UnsortedThenSorted)
{
A.SetIsDependencyListSorted(UE::AssetRegistry::EDependencyCategory::All, true);
for (int n = 0; n < NumScratch; ++n)
{
ScratchNodes[n].SetIsReferencersSorted(true);
}
}
// Package All
{
FTestDependency MatchingDeps[] = { TestDependencies[0], TestDependencies[1], TestDependencies[2] };
FDependsNode* Matching[] = { &ScratchNodes[0], &ScratchNodes[1], &ScratchNodes[2] };
Dependencies.Reset();
A.GetDependencies(Dependencies, EDependencyCategory::Package);
TestTrue(TEXT("Package dependency with no flags"), CombinationEquals<FDependsNode*>(Dependencies, Matching));
for (int MatchingIndex = 0; MatchingIndex < UE_ARRAY_COUNT(Matching); ++MatchingIndex)
{
FDependsNode* Dependency = Matching[MatchingIndex];
FTestDependency& TestDependency = MatchingDeps[MatchingIndex];
Referencers.Reset();
ReferencersStructs.Reset();
Dependency->GetReferencers(Referencers, EDependencyCategory::Package);
Dependency->GetReferencers(ReferencersStructs, EDependencyCategory::Package);
TestTrue(TEXT("Package referencers with no flags"), CombinationEquals<FDependsNode*>(Referencers, PointerA));
TestTrue(TEXT("Package referencers with no flags"), CombinationEquals<FAssetDependency>(ReferencersStructs, FTestDependency(TestDependency, PointerA).AsAssetDependency()));
int IterateCount = 0;
Dependency->IterateOverReferencers([this, &IterateCount, &PointerA](FDependsNode* Referencer)
{
++IterateCount;
if (Referencer != PointerA)
{
AddError(TEXT("IterateReferencers Package, no flags"));
}
});
TestEqual(TEXT("IterateReferencers Package no flags, Count"), IterateCount, 1);
}
{
int IterateCount = 0;
A.IterateOverDependencies([this, &IterateCount, &ScratchNodes](FDependsNode* Dependency, EDependencyCategory Category, EDependencyProperty Properties, bool bDuplicate)
{
TestTrue(TEXT("IterateDependencies Package, no flags, Category"), Category == EDependencyCategory::Package);
++IterateCount;
if (Dependency == &ScratchNodes[0])
{
TestTrue(TEXT("IterateDependencies Package, no flags"), Properties == (EDependencyProperty::Hard | EDependencyProperty::Game | EDependencyProperty::Build) && !bDuplicate);
}
else if (Dependency == &ScratchNodes[1])
{
TestTrue(TEXT("IterateDependencies Package, no flags"), Properties == EDependencyProperty::Hard && !bDuplicate);
}
else if (Dependency == &ScratchNodes[2])
{
TestTrue(TEXT("IterateDependencies Package, no flags"), Properties == EDependencyProperty::None && !bDuplicate);
}
else
{
AddError(TEXT("IterateDependencies Package, no flags"));
}
}, EDependencyCategory::Package);
TestEqual(TEXT("IterateDependencies Package, no flags, Count"), IterateCount, 3);
}
}
// Package Hard
{
FTestDependency MatchingDeps[] = { TestDependencies[0], TestDependencies[1] };
FDependsNode* Matching[] = { &ScratchNodes[0], &ScratchNodes[1] };
FDependsNode* NonMatching[] = { &ScratchNodes[2] };
Dependencies.Reset();
A.GetDependencies(Dependencies, EDependencyCategory::Package, EDependencyQuery::Hard);
TestTrue(TEXT("Package dependency with flags, Hard"), CombinationEquals<FDependsNode*>(Dependencies, Matching));
for (int MatchingIndex = 0; MatchingIndex < UE_ARRAY_COUNT(Matching); ++MatchingIndex)
{
FDependsNode* Dependency = Matching[MatchingIndex];
FTestDependency TestDependency = MatchingDeps[MatchingIndex];
Referencers.Reset();
Dependency->GetReferencers(Referencers, EDependencyCategory::Package, EDependencyQuery::Hard);
ReferencersStructs.Reset();
Dependency->GetReferencers(ReferencersStructs, EDependencyCategory::Package, EDependencyQuery::Hard);
TestTrue(TEXT("Package referencers with flags, Hard, matching"), CombinationEquals<FDependsNode*>(Referencers, PointerA));
TestTrue(TEXT("Package referencers with flags, Hard, matching"), CombinationEquals<FAssetDependency>(ReferencersStructs, FTestDependency(TestDependency, PointerA).AsAssetDependency()));
int IterateCount = 0;
Dependency->IterateOverReferencers([this, &IterateCount, PointerA](FDependsNode* Referencer)
{
++IterateCount;
if (Referencer != PointerA)
{
AddError(TEXT("IterateReferencers Package, Hard, matching"));
}
});
TestEqual(TEXT("IterateReferencers Package Hard, matching, Count"), IterateCount, 1);
}
for (FDependsNode* Dependency : NonMatching)
{
Referencers.Reset();
Dependency->GetReferencers(Referencers, EDependencyCategory::Package, EDependencyQuery::Hard);
TestTrue(TEXT("Package referencers with flags, Hard, nonmatching"), Referencers.Num() == 0);
int IterateCount = 0;
Dependency->IterateOverReferencers([this, &IterateCount, PointerA](FDependsNode* Referencer)
{
++IterateCount;
if (Referencer != PointerA)
{
AddError(TEXT("IterateReferencers Package, Hard, nonmatching"));
}
});
TestEqual(TEXT("IterateReferencers Package Hard, nonmatching, Count"), IterateCount, 1);
}
{
int IterateCount = 0;
A.IterateOverDependencies([this, &IterateCount, &ScratchNodes](FDependsNode* Dependency, EDependencyCategory Category, EDependencyProperty Properties, bool bDuplicate)
{
TestTrue(TEXT("IterateDependencies Package, Hard, Category"), Category == EDependencyCategory::Package);
++IterateCount;
if (Dependency == &ScratchNodes[0])
{
TestTrue(TEXT("IterateDependencies Package, Hard"), Properties == (EDependencyProperty::Hard | EDependencyProperty::Game | EDependencyProperty::Build) && !bDuplicate);
}
else if (Dependency == &ScratchNodes[1])
{
TestTrue(TEXT("IterateDependencies Package, Hard"), Properties == EDependencyProperty::Hard && !bDuplicate);
}
else
{
AddError(TEXT("IterateDependencies Package, Hard"));
}
}, EDependencyCategory::Package, EDependencyQuery::Hard);
TestEqual(TEXT("IterateDependencies Package, Hard, Count"), IterateCount, 2);
}
}
// Package Soft
{
FTestDependency MatchingDeps[] = { TestDependencies[2] };
FDependsNode* Matching[] = { &ScratchNodes[2] };
FDependsNode* NonMatching[] = { &ScratchNodes[0], &ScratchNodes[1] };
Dependencies.Reset();
A.GetDependencies(Dependencies, EDependencyCategory::Package, EDependencyQuery::Soft);
TestTrue(TEXT("Package dependency with flags, Soft"), CombinationEquals<FDependsNode*>(Dependencies, Matching));
for (int MatchingIndex = 0; MatchingIndex < UE_ARRAY_COUNT(Matching); ++MatchingIndex)
{
FDependsNode* Dependency = Matching[MatchingIndex];
FTestDependency TestDependency = MatchingDeps[MatchingIndex];
Referencers.Reset();
Dependency->GetReferencers(Referencers, EDependencyCategory::Package, EDependencyQuery::Soft);
ReferencersStructs.Reset();
Dependency->GetReferencers(ReferencersStructs, EDependencyCategory::Package, EDependencyQuery::Soft);
TestTrue(TEXT("Package referencers with flags, Soft, matching"), CombinationEquals<FDependsNode*>(Referencers, PointerA));
TestTrue(TEXT("Package referencers with flags, Soft, matching"), CombinationEquals<FAssetDependency>(ReferencersStructs, FTestDependency(TestDependency, PointerA).AsAssetDependency()));
int IterateCount = 0;
Dependency->IterateOverReferencers([this, &IterateCount, PointerA](FDependsNode* Referencer)
{
++IterateCount;
if (Referencer != PointerA)
{
AddError(TEXT("IterateReferencers Package, Soft, matching"));
}
});
TestEqual(TEXT("IterateReferencers Package Soft, matching, Count"), IterateCount, 1);
}
for (FDependsNode* Dependency : NonMatching)
{
Referencers.Reset();
Dependency->GetReferencers(Referencers, EDependencyCategory::Package, EDependencyQuery::Soft);
TestTrue(TEXT("Package referencers with flags, Soft, nonmatching"), Referencers.Num() == 0);
int IterateCount = 0;
Dependency->IterateOverReferencers([this, &IterateCount, PointerA](FDependsNode* Referencer)
{
++IterateCount;
if (Referencer != PointerA)
{
AddError(TEXT("IterateReferencers Package, Soft, nonmatching"));
}
});
TestEqual(TEXT("IterateReferencers Package Soft, nonmatching, Count"), IterateCount, 1);
}
{
int IterateCount = 0;
A.IterateOverDependencies([this, &IterateCount, &ScratchNodes](FDependsNode* Dependency, EDependencyCategory Category, EDependencyProperty Properties, bool bDuplicate)
{
TestTrue(TEXT("IterateDependencies Package, Soft, Category"), Category == EDependencyCategory::Package);
++IterateCount;
if (Dependency == &ScratchNodes[2])
{
TestTrue(TEXT("IterateDependencies Package, Soft"), Properties == EDependencyProperty::None && !bDuplicate);
}
else
{
AddError(TEXT("IterateDependencies Package, Soft"));
}
}, EDependencyCategory::Package, EDependencyQuery::Soft);
TestEqual(TEXT("IterateDependencies Package, Soft, Count"), IterateCount, 1);
}
}
// SearchableName All
{
FTestDependency MatchingDeps[] = { TestDependencies[3] };
FDependsNode* Matching[] = { &ScratchNodes[3] };
Dependencies.Reset();
A.GetDependencies(Dependencies, EDependencyCategory::SearchableName);
TestTrue(TEXT("SearchableName dependency with no flags"), CombinationEquals<FDependsNode*>(Dependencies, Matching));
for (int MatchingIndex = 0; MatchingIndex < UE_ARRAY_COUNT(Matching); ++MatchingIndex)
{
FDependsNode* Dependency = Matching[MatchingIndex];
FTestDependency TestDependency = MatchingDeps[MatchingIndex];
Referencers.Reset();
Dependency->GetReferencers(Referencers, EDependencyCategory::SearchableName);
ReferencersStructs.Reset();
Dependency->GetReferencers(ReferencersStructs, EDependencyCategory::SearchableName);
TestTrue(TEXT("SearchableName referencers with no flags"), CombinationEquals<FDependsNode*>(Referencers,PointerA));
TestTrue(TEXT("SearchableName referencers with no flags"), CombinationEquals<FAssetDependency>(ReferencersStructs, FTestDependency(TestDependency, PointerA).AsAssetDependency()));
int IterateCount = 0;
Dependency->IterateOverReferencers([this, &IterateCount, PointerA](FDependsNode* Referencer)
{
++IterateCount;
if (Referencer != PointerA)
{
AddError(TEXT("IterateReferencers SearchableName, no flags"));
}
});
TestEqual(TEXT("IterateReferencers SearchableName no flags, Count"), IterateCount, 1);
}
int IterateCount = 0;
A.IterateOverDependencies([this, &IterateCount, &ScratchNodes](FDependsNode* Dependency, EDependencyCategory Category, EDependencyProperty Properties, bool bDuplicate)
{
TestTrue(TEXT("IterateDependencies SearchableName Category"), Category == EDependencyCategory::SearchableName);
++IterateCount;
if (Dependency == &ScratchNodes[3])
{
TestTrue(TEXT("IterateDependencies SearchableName"), Properties == EDependencyProperty::None && !bDuplicate);
}
else
{
AddError(TEXT("IterateDependencies SearchableName"));
}
}, EDependencyCategory::SearchableName);
TestEqual(TEXT("IterateDependencies SearchableName Count"), IterateCount, 1);
}
// Manage All
{
FTestDependency MatchingDeps[] = { TestDependencies[4], TestDependencies[5] };
FDependsNode* Matching[] = { &ScratchNodes[4], &ScratchNodes[5] };
Dependencies.Reset();
A.GetDependencies(Dependencies, EDependencyCategory::Manage);
TestTrue(TEXT("Manage dependency with no flags"), CombinationEquals<FDependsNode*>(Dependencies, Matching));
for (int MatchingIndex = 0; MatchingIndex < UE_ARRAY_COUNT(Matching); ++MatchingIndex)
{
FDependsNode* Dependency = Matching[MatchingIndex];
FTestDependency TestDependency = MatchingDeps[MatchingIndex];
Referencers.Reset();
Dependency->GetReferencers(Referencers, EDependencyCategory::Manage);
ReferencersStructs.Reset();
Dependency->GetReferencers(ReferencersStructs, EDependencyCategory::Manage);
TestTrue(TEXT("Manage referencers with no flags"), CombinationEquals<FDependsNode*>(Referencers, PointerA));
TestTrue(TEXT("Manage referencers with no flags"), CombinationEquals<FAssetDependency>(ReferencersStructs, FTestDependency(TestDependency, PointerA).AsAssetDependency()));
int IterateCount = 0;
Dependency->IterateOverReferencers([this, &IterateCount, PointerA](FDependsNode* Referencer)
{
++IterateCount;
if (Referencer != PointerA)
{
AddError(TEXT("IterateReferencers Manage, no flags"));
}
});
TestEqual(TEXT("IterateReferencers Manage no flags, Count"), IterateCount, 1);
}
int IterateCount = 0;
A.IterateOverDependencies([this, &IterateCount, &ScratchNodes](FDependsNode* Dependency, EDependencyCategory Category, EDependencyProperty Properties, bool bDuplicate)
{
TestTrue(TEXT("IterateDependencies Manage, no flags, Category"), Category == EDependencyCategory::Manage);
++IterateCount;
if (Dependency == &ScratchNodes[4])
{
TestTrue(TEXT("IterateDependencies Manage, no flags"), Properties == EDependencyProperty::Direct && !bDuplicate);
}
else if (Dependency == &ScratchNodes[5])
{
TestTrue(TEXT("IterateDependencies Manage, no flags"), Properties == EDependencyProperty::None && !bDuplicate);
}
else
{
AddError(TEXT("IterateDependencies Manage, no flags"));
}
}, EDependencyCategory::Manage);
TestEqual(TEXT("IterateDependencies Manage, no flags, Count"), IterateCount, 2);
}
// Manage Direct
{
FTestDependency MatchingDeps[] = { TestDependencies[4] };
FDependsNode* Matching[] = { &ScratchNodes[4] };
FDependsNode* NonMatching[] = { &ScratchNodes[5] };
Dependencies.Reset();
A.GetDependencies(Dependencies, EDependencyCategory::Manage, EDependencyQuery::Direct);
TestTrue(TEXT("Manage dependency with flags, Direct"), CombinationEquals<FDependsNode*>(Dependencies, Matching));
for (int MatchingIndex = 0; MatchingIndex < UE_ARRAY_COUNT(Matching); ++MatchingIndex)
{
FDependsNode* Dependency = Matching[MatchingIndex];
FTestDependency TestDependency = MatchingDeps[MatchingIndex];
Referencers.Reset();
Dependency->GetReferencers(Referencers, EDependencyCategory::Manage, EDependencyQuery::Direct);
ReferencersStructs.Reset();
Dependency->GetReferencers(ReferencersStructs, EDependencyCategory::Manage, EDependencyQuery::Direct);
TestTrue(TEXT("Manage referencers with flags, Direct, matching"), CombinationEquals<FDependsNode*>(Referencers, PointerA));
TestTrue(TEXT("Manage referencers with flags, Direct, matching"), CombinationEquals<FAssetDependency>(ReferencersStructs, FTestDependency(TestDependency, PointerA).AsAssetDependency()));
int IterateCount = 0;
Dependency->IterateOverReferencers([this, &IterateCount, PointerA](FDependsNode* Referencer)
{
++IterateCount;
if (Referencer != PointerA)
{
AddError(TEXT("IterateReferencers Manage, Direct, matching"));
}
});
TestEqual(TEXT("IterateReferencers Manage Direct, matching, Count"), IterateCount, 1);
}
for (FDependsNode* Dependency : NonMatching)
{
Referencers.Reset();
Dependency->GetReferencers(Referencers, EDependencyCategory::Manage, EDependencyQuery::Direct);
TestTrue(TEXT("Manage referencers with flags, Direct, nonmatching"), Referencers.Num() == 0);
int IterateCount = 0;
Dependency->IterateOverReferencers([this, &IterateCount, PointerA](FDependsNode* Referencer)
{
++IterateCount;
if (Referencer != PointerA)
{
AddError(TEXT("IterateReferencers Manage, Direct, nonmatching"));
}
});
TestEqual(TEXT("IterateReferencers Manage Direct, nonmatching, Count"), IterateCount, 1);
}
int IterateCount = 0;
A.IterateOverDependencies([this, &IterateCount, &ScratchNodes](FDependsNode* Dependency, EDependencyCategory Category, EDependencyProperty Properties, bool bDuplicate)
{
TestTrue(TEXT("IterateDependencies Manage, Direct, Category"), Category == EDependencyCategory::Manage);
++IterateCount;
if (Dependency == &ScratchNodes[4])
{
TestTrue(TEXT("IterateDependencies Manage, Direct"), Properties == EDependencyProperty::Direct && !bDuplicate);
}
else
{
AddError(TEXT("IterateDependencies Manage, Direct"));
}
}, EDependencyCategory::Manage, EDependencyQuery::Direct);
TestEqual(TEXT("IterateDependencies Manage, Direct, Count"), IterateCount, 1);
}
// Manage Indirect
{
FTestDependency MatchingDeps[] = { TestDependencies[5] };
FDependsNode* Matching[] = { &ScratchNodes[5] };
FDependsNode* NonMatching[] = { &ScratchNodes[4] };
Dependencies.Reset();
A.GetDependencies(Dependencies, EDependencyCategory::Manage, EDependencyQuery::Indirect);
TestTrue(TEXT("Manage dependency with flags, Indirect"), CombinationEquals<FDependsNode*>(Dependencies, Matching));
for (int MatchingIndex = 0; MatchingIndex < UE_ARRAY_COUNT(Matching); ++MatchingIndex)
{
FDependsNode* Dependency = Matching[MatchingIndex];
FTestDependency TestDependency = MatchingDeps[MatchingIndex];
Referencers.Reset();
Dependency->GetReferencers(Referencers, EDependencyCategory::Manage, EDependencyQuery::Indirect);
ReferencersStructs.Reset();
Dependency->GetReferencers(ReferencersStructs, EDependencyCategory::Manage, EDependencyQuery::Indirect);
TestTrue(TEXT("Manage referencers with flags, Indirect, matching"), CombinationEquals<FDependsNode*>(Referencers, PointerA));
TestTrue(TEXT("Manage referencers with flags, Indirect, matching"), CombinationEquals<FAssetDependency>(ReferencersStructs, FTestDependency(TestDependency, PointerA).AsAssetDependency()));
int IterateCount = 0;
Dependency->IterateOverReferencers([this, &IterateCount, PointerA](FDependsNode* Referencer)
{
++IterateCount;
if (Referencer != PointerA)
{
AddError(TEXT("IterateReferencers Manage, Indirect, matching"));
}
});
TestEqual(TEXT("IterateReferencers Manage Indirect, matching, Count"), IterateCount, 1);
}
for (FDependsNode* Dependency : NonMatching)
{
Referencers.Reset();
Dependency->GetReferencers(Referencers, EDependencyCategory::Manage, EDependencyQuery::Indirect);
TestTrue(TEXT("Manage referencers with flags, Indirect, nonmatching"), Referencers.Num() == 0);
int IterateCount = 0;
Dependency->IterateOverReferencers([this, &IterateCount, PointerA](FDependsNode* Referencer)
{
++IterateCount;
if (Referencer != PointerA)
{
AddError(TEXT("IterateReferencers Manage, Indirect, nonmatching"));
}
});
TestEqual(TEXT("IterateReferencers Manage Indirect, nonmatching, Count"), IterateCount, 1);
}
int IterateCount = 0;
A.IterateOverDependencies([this, &IterateCount, &ScratchNodes](FDependsNode* Dependency, EDependencyCategory Category, EDependencyProperty Properties, bool bDuplicate)
{
TestTrue(TEXT("IterateDependencies Manage, Indirect, Category"), Category == EDependencyCategory::Manage);
++IterateCount;
if (Dependency == &ScratchNodes[5])
{
TestTrue(TEXT("IterateDependencies Manage, Indirect"), Properties == EDependencyProperty::None && !bDuplicate);
}
else
{
AddError(TEXT("IterateDependencies Manage, Indirect"));
}
}, EDependencyCategory::Manage, EDependencyQuery::Indirect);
TestEqual(TEXT("IterateDependencies Manage, Indirect, Count"), IterateCount, 1);
}
TestEqual(TEXT("RemoveDependency in dependency and referencer filtering loop"), A.GetConnectionCount(), NumScratch);
for (int n = 0; n < NumScratch; ++n)
{
ScratchNodes[n].RemoveReferencer(&A);
ScratchNodes[n].SetIsReferencersSorted(true);
A.RemoveDependency(&ScratchNodes[n]);
TestEqual(TEXT("RemoveDependency in dependency and referencer filtering loop"), A.GetConnectionCount(), NumScratch - 1 - n);
}
}
// Unsorted Dependency lists with duplicate copies of a node
{
FDependsNode A;
FDependsNode B;
FDependsNode C;
for (int Order = 0; Order < 2; ++Order)
{
A.ClearDependencies();
B.ClearReferencers();
A.SetIsDependencyListSorted(EDependencyCategory::Package, false);
B.SetIsReferencersSorted(false);
if (Order == 0)
{
A.AddDependency(&B, EDependencyCategory::Package, EDependencyProperty::None);
A.AddDependency(&B, EDependencyCategory::Package, EDependencyProperty::None);
A.AddDependency(&C, EDependencyCategory::Package, EDependencyProperty::None);
B.AddReferencer(&A);
B.AddReferencer(&A);
B.AddReferencer(&C);
}
else
{
A.AddDependency(&B, EDependencyCategory::Package, EDependencyProperty::None);
A.AddDependency(&C, EDependencyCategory::Package, EDependencyProperty::None);
A.AddDependency(&C, EDependencyCategory::Package, EDependencyProperty::None);
B.AddReferencer(&A);
B.AddReferencer(&C);
B.AddReferencer(&C);
}
A.SetIsDependencyListSorted(EDependencyCategory::Package, true);
B.SetIsReferencersSorted(true);
Dependencies.Reset(0);
A.GetDependencies(Dependencies);
TestTrue(TEXT("Sorting a list removes duplicates"), CombinationEquals<FDependsNode* const>(Dependencies, TArrayView<FDependsNode* const>({ &B, &C })));
Referencers.Reset();
B.GetReferencers(Referencers);
TestTrue(TEXT("Sorting a list removes duplicates"), CombinationEquals<FDependsNode* const>(Referencers, TArrayView<FDependsNode* const>({ &A, &C })));
}
}
{
FDependsNode A;
FDependsNode B;
FDependsNode C;
A.SetIsDependencyListSorted(EDependencyCategory::Package, false);
B.SetIsReferencersSorted(false);
A.AddDependency(&B, EDependencyCategory::Package, EDependencyProperty::None);
A.AddDependency(&C, EDependencyCategory::Package, EDependencyProperty::None);
A.AddDependency(&B, EDependencyCategory::Package, EDependencyProperty::None);
B.AddReferencer(&A);
B.AddReferencer(&C);
B.AddReferencer(&A);
B.AddReferencer(&C);
C.AddReferencer(&A);
A.SetIsDependencyListSorted(EDependencyCategory::Package, true);
B.SetIsReferencersSorted(true);
int IterationIndex = 0;
A.IterateOverDependencies([&IterationIndex](FDependsNode* Dependency, EDependencyCategory Category, EDependencyProperty Properties, bool bDuplicate)
{
++IterationIndex;
});
TestTrue(TEXT("Sorting a list removes duplicates"), IterationIndex == 2);
Referencers.Reset();
B.GetReferencers(Referencers);
TestTrue(TEXT("Sorting a list removes duplicates"), Referencers.Num() == 2 && Referencers[0] != Referencers[1]);
}
{
FDependsNode A;
FDependsNode B;
FDependsNode C;
A.SetIsDependencyListSorted(EDependencyCategory::Package, false);
B.SetIsReferencersSorted(false);
A.AddDependency(&B, EDependencyCategory::Package, EDependencyProperty::Hard);
A.AddDependency(&C, EDependencyCategory::Package, EDependencyProperty::None);
A.AddDependency(&B, EDependencyCategory::Package, EDependencyProperty::Game);
A.AddDependency(&C, EDependencyCategory::Package, EDependencyProperty::None);
A.AddDependency(&B, EDependencyCategory::Package, EDependencyProperty::Hard);
B.AddReferencer(&A);
B.AddReferencer(&A);
B.AddReferencer(&A);
C.AddReferencer(&A);
C.AddReferencer(&A);
A.SetIsDependencyListSorted(EDependencyCategory::Package, true);
B.SetIsReferencersSorted(true);
int IterationIndex = 0;
int DuplicateCount = 0;
A.IterateOverDependencies([&IterationIndex, &DuplicateCount](FDependsNode* Dependency, EDependencyCategory Category, EDependencyProperty Properties, bool bDuplicate)
{
++IterationIndex;
DuplicateCount += bDuplicate != 0;
});
TestTrue(TEXT("Sorting a list removes duplicates but keeps nonredundant corners"), IterationIndex == 3 && DuplicateCount == 1);
Referencers.Reset();
B.GetReferencers(Referencers);
TestTrue(TEXT("Sorting a list removes duplicates"), Referencers.Num() == 1);
}
// GetPackageReferencers
{
FDependsNode A;
FName AName(TEXT("FDependsNodeA"));
A.SetIdentifier(AName);
FDependsNode B;
A.AddDependency(&B, EDependencyCategory::Package, EDependencyProperty::None);
B.AddReferencer(&A);
TArray<TPair<FAssetIdentifier, FDependsNode::FPackageFlagSet>> ReferencersResult;
B.GetPackageReferencers(ReferencersResult);
TestTrue(TEXT("GetPackageReferencers expecting 1"), ReferencersResult.Num() == 1 && ReferencersResult[0].Key.PackageName == AName);
ReferencersResult.Reset();
A.GetPackageReferencers(ReferencersResult);
TestTrue(TEXT("GetPackageReferencers expecting 0"), ReferencersResult.Num() == 0);
}
// iterating over the same dependency with two nonredundant property combinations
{
FDependsNode A;
FDependsNode B;
A.AddDependency(&B, EDependencyCategory::Package, EDependencyProperty::Game);
A.AddDependency(&B, EDependencyCategory::Package, EDependencyProperty::Hard);
B.AddReferencer(&A);
int IterationIndex = 0;
bool bHasGame = false;
bool bHasHard = false;
A.IterateOverDependencies([this, &B, &IterationIndex, &bHasGame, &bHasHard](FDependsNode* Dependency, EDependencyCategory Category, EDependencyProperty Properties, bool bDuplicate)
{
TestTrue(TEXT("IterateOverDependencies two property corners only two elements"), IterationIndex < 2);
TestTrue(TEXT("IterateOverDependencies two property corners expected values"), Dependency == &B && Category == EDependencyCategory::Package && (Properties == EDependencyProperty::Game || Properties == EDependencyProperty::Hard));
TestTrue(TEXT("IterateOverDependencies two property corners bDuplicate is set correctly"), IterationIndex == 0 || bDuplicate);
bHasGame = bHasGame || Properties == EDependencyProperty::Game;
bHasHard = bHasHard || Properties == EDependencyProperty::Hard;
IterationIndex++;
});
TestTrue(TEXT("IterateOverDependencies two property corners hits both corners"), bHasGame && bHasHard);
IterationIndex = 0;
bHasGame = false;
bHasHard = false;
A.IterateOverDependencies([this, &B, &IterationIndex, &bHasGame, &bHasHard](FDependsNode* Dependency, EDependencyCategory Category, EDependencyProperty Properties, bool bDuplicate)
{
TestTrue(TEXT("IterateOverDependencies two property corners, node-specific, only two elements"), IterationIndex < 2);
TestTrue(TEXT("IterateOverDependencies two property corners, node-specific, expected values"), Dependency == &B && Category == EDependencyCategory::Package && (Properties == EDependencyProperty::Game || Properties == EDependencyProperty::Hard));
TestTrue(TEXT("IterateOverDependencies two property corners, node-specific, bDuplicate is set correctly"), IterationIndex == 0 || bDuplicate);
bHasGame = bHasGame || Properties == EDependencyProperty::Game;
bHasHard = bHasHard || Properties == EDependencyProperty::Hard;
IterationIndex++;
}, &B);
TestTrue(TEXT("IterateOverDependencies two property corners, node-specific, hits both corners"), bHasGame&& bHasHard);
TArray<FAssetIdentifier> Assets;
A.GetDependencies(Assets);
TestEqual(TEXT("GetDependencies with a lists of assets removes duplicates"), Assets.Num(), 1);
TArray<FAssetDependency> StructDependencies;
A.GetDependencies(StructDependencies);
TestEqual(TEXT("GetDependencies with a lists of dependency structs keeps duplicates"), StructDependencies.Num(), 2);
}
// Redundant property combinations are skipped when iterating
{
FDependsNode A;
FDependsNode B;
A.AddDependency(&B, EDependencyCategory::Package, EDependencyProperty::Game);
A.AddDependency(&B, EDependencyCategory::Package, EDependencyProperty::Game | EDependencyProperty::Hard);
B.AddReferencer(&A);
int IterationIndex = 0;
A.IterateOverDependencies([this, &B, &IterationIndex](FDependsNode* Dependency, EDependencyCategory Category, EDependencyProperty Properties, bool bDuplicate)
{
TestTrue(TEXT("IterateOverDependencies redundant corner only one element"), IterationIndex < 1);
TestTrue(TEXT("IterateOverDependencies redundant corner expected values"), Dependency == &B && Category == EDependencyCategory::Package && (Properties == (EDependencyProperty::Game | EDependencyProperty::Hard)));
TestTrue(TEXT("IterateOverDependencies redundant corner bDuplicate is set correctly"), IterationIndex == 0 || bDuplicate);
IterationIndex++;
});
TestTrue(TEXT("IterateOverDependencies two property corners hits the one non-redundant corners"), IterationIndex == 1);
}
// AddPackageDependencySet
{
FDependsNode A;
FDependsNode B;
FDependsNode::FPackageFlagSet PackageFlagSet;
PackageFlagSet.Add(FDependsNode::PackagePropertiesToByte(EDependencyProperty::Game));
PackageFlagSet.Add(FDependsNode::PackagePropertiesToByte(EDependencyProperty::Hard));
A.AddPackageDependencySet(&B, PackageFlagSet);
B.AddReferencer(&A);
int IterationIndex = 0;
bool bHasGame = false;
bool bHasHard = false;
A.IterateOverDependencies([this, &B, &IterationIndex, &bHasGame, &bHasHard](FDependsNode* Dependency, EDependencyCategory Category, EDependencyProperty Properties, bool bDuplicate)
{
TestTrue(TEXT("AddPackageDependencySet only two elements"), IterationIndex < 2);
TestTrue(TEXT("AddPackageDependencySets expected values"), Dependency == &B && Category == EDependencyCategory::Package && (Properties == EDependencyProperty::Game || Properties == EDependencyProperty::Hard));
bHasGame = bHasGame || Properties == EDependencyProperty::Game;
bHasHard = bHasHard || Properties == EDependencyProperty::Hard;
IterationIndex++;
});
TestTrue(TEXT("IterateOverDependencies two property corners hits both corners"), bHasGame && bHasHard);
}
// ClearDependencies
{
FDependsNode A;
FDependsNode B;
A.AddDependency(&B, EDependencyCategory::Package, EDependencyProperty::Game);
A.AddDependency(&B, EDependencyCategory::Package, EDependencyProperty::Hard);
B.AddReferencer(&A);
A.ClearDependencies(EDependencyCategory::All);
int IterationIndex = 0;
A.IterateOverDependencies([&IterationIndex](FDependsNode* Dependency, EDependencyCategory Category, EDependencyProperty Properties, bool bDuplicate)
{
++IterationIndex;
});
TestTrue(TEXT("ClearDependencies(All) results in 0 iteration"), IterationIndex == 0);
A.AddDependency(&B, EDependencyCategory::Package, EDependencyProperty::None);
A.AddDependency(&B, EDependencyCategory::Manage, EDependencyProperty::None);
A.AddDependency(&B, EDependencyCategory::SearchableName, EDependencyProperty::None);
IterationIndex = 0;
A.IterateOverDependencies([&IterationIndex](FDependsNode* Dependency, EDependencyCategory Category, EDependencyProperty Properties, bool bDuplicate)
{
++IterationIndex;
});
TestTrue(TEXT("ClearDependencies partial, setup"), IterationIndex == 3);
A.ClearDependencies(EDependencyCategory::Package);
IterationIndex = 0;
A.IterateOverDependencies([this, &IterationIndex](FDependsNode* Dependency, EDependencyCategory Category, EDependencyProperty Properties, bool bDuplicate)
{
TestTrue(TEXT("ClearDependencies partial, removed Package"), Category != EDependencyCategory::Package);
++IterationIndex;
});
TestTrue(TEXT("ClearDependencies partial, removed one"), IterationIndex == 2);
A.ClearDependencies(EDependencyCategory::Manage);
IterationIndex = 0;
A.IterateOverDependencies([this, &IterationIndex](FDependsNode* Dependency, EDependencyCategory Category, EDependencyProperty Properties, bool bDuplicate)
{
TestTrue(TEXT("ClearDependencies partial, removed Package+Manage"), Category != EDependencyCategory::Package && Category != EDependencyCategory::Manage);
++IterationIndex;
});
TestTrue(TEXT("ClearDependencies partial, removed two"), IterationIndex == 1);
A.ClearDependencies(EDependencyCategory::SearchableName);
IterationIndex = 0;
A.IterateOverDependencies([this, &IterationIndex](FDependsNode* Dependency, EDependencyCategory Category, EDependencyProperty Properties, bool bDuplicate)
{
++IterationIndex;
});
TestTrue(TEXT("ClearDependencies partial, removed three"), IterationIndex == 0);
}
// RemoveManageReferencesToNode
for (ESortTestType SortTestType : { ESortTestType::AlwaysSorted, ESortTestType::AlwaysUnsorted, ESortTestType::UnsortedThenSorted })
{
FDependsNode A;
FDependsNode B;
FDependsNode C;
FDependsNode D;
if (SortTestType == ESortTestType::AlwaysUnsorted || SortTestType == ESortTestType::UnsortedThenSorted)
{
A.SetIsDependencyListSorted(UE::AssetRegistry::EDependencyCategory::All, false);
B.SetIsReferencersSorted(false);
C.SetIsDependencyListSorted(UE::AssetRegistry::EDependencyCategory::All, false);
D.SetIsReferencersSorted(false);
}
A.AddDependency(&B, EDependencyCategory::Package, EDependencyProperty::None);
A.AddDependency(&B, EDependencyCategory::Manage, EDependencyProperty::None);
A.AddDependency(&B, EDependencyCategory::SearchableName, EDependencyProperty::None);
B.AddReferencer(&A);
C.AddDependency(&D, EDependencyCategory::Manage, EDependencyProperty::None);
D.AddReferencer(&C);
if (SortTestType == ESortTestType::UnsortedThenSorted)
{
A.SetIsDependencyListSorted(UE::AssetRegistry::EDependencyCategory::All, true);
B.SetIsReferencersSorted(true);
C.SetIsDependencyListSorted(UE::AssetRegistry::EDependencyCategory::All, true);
D.SetIsReferencersSorted(true);
}
B.RemoveManageReferencesToNode();
int IterationIndex = 0;
A.IterateOverDependencies([this, &IterationIndex](FDependsNode* Dependency, EDependencyCategory Category, EDependencyProperty Properties, bool bDuplicate)
{
TestTrue(TEXT("RemoveManageReferencesToNode removed the manage dependency"), Category != EDependencyCategory::Manage);
++IterationIndex;
});
TestTrue(TEXT("RemoveManageReferencesToNode removed just the manage dependency"), IterationIndex == 2);
Referencers.Reset();
B.GetReferencers(Referencers);
TestTrue(TEXT("RemoveManageReferencesToNode did not remove the referencer when dependencies still existed"), Referencers.Num() == 1);
D.RemoveManageReferencesToNode();
IterationIndex = 0;
C.IterateOverDependencies([this, &IterationIndex](FDependsNode* Dependency, EDependencyCategory Category, EDependencyProperty Properties, bool bDuplicate)
{
++IterationIndex;
});
TestTrue(TEXT("RemoveManageReferencesToNode only manage reference - removed the manage dependency"), IterationIndex == 0);
Referencers.Reset();
D.GetReferencers(Referencers);
TestTrue(TEXT("RemoveManageReferencesToNode only manage reference - removed the referencer"), Referencers.Num() == 0);
}
// RemoveLinks
{
constexpr int NumLinkNodes = 5;
EDependencyProperty PackageProperties[NumLinkNodes];
EDependencyProperty ManageProperties[NumLinkNodes];
int UnsortedOrder[] = { 4,1,3,0,2 };
for (int n = 0; n < NumLinkNodes; ++n)
{
PackageProperties[n] = (n & 0x1) ? EDependencyProperty::Hard : EDependencyProperty::None;
PackageProperties[n] = (n & 0x2) ? EDependencyProperty::Game : EDependencyProperty::None;
PackageProperties[n] = (n & 0x4) ? EDependencyProperty::Build : EDependencyProperty::None;
ManageProperties[n] = (n & 0x1) ? EDependencyProperty::Direct : EDependencyProperty::None;
}
for (ESortTestType SortTestType : { ESortTestType::AlwaysSorted, ESortTestType::UnsortedThenSorted, ESortTestType::AlwaysUnsorted})
{
for (int FirstRemove = 0; FirstRemove < NumLinkNodes; ++FirstRemove)
{
for (int SecondRemove = -1; SecondRemove < NumLinkNodes; ++SecondRemove)
{
if (SecondRemove == FirstRemove)
{
continue;
}
int NumRemoves = SecondRemove == -1 ? 1 : 2;
for (int NumElements : { 1, 2, NumLinkNodes})
{
if (NumElements == 2 && SecondRemove != -1)
{
continue;
}
FDependsNode LinkHaver;
FDependsNode LinkNodes[NumLinkNodes];
auto AddLink = [&LinkHaver, PackageProperties, ManageProperties, &LinkNodes](int NodeIndex)
{
FDependsNode* LinkNode = &LinkNodes[NodeIndex];
LinkHaver.AddReferencer(LinkNode);
LinkHaver.AddDependency(LinkNode, EDependencyCategory::Package, PackageProperties[NodeIndex]);
LinkHaver.AddDependency(LinkNode, EDependencyCategory::SearchableName, EDependencyProperty::None);
LinkHaver.AddDependency(LinkNode, EDependencyCategory::Manage, ManageProperties[NodeIndex]);
};
auto GetNodeIndex = [&LinkNodes](const FDependsNode* Existing) { return UE_PTRDIFF_TO_INT32(Existing - &LinkNodes[0]); };
if (SortTestType != ESortTestType::AlwaysSorted)
{
for (EDependencyCategory Category : { EDependencyCategory::Manage, EDependencyCategory::SearchableName, EDependencyCategory::Package})
{
LinkHaver.SetIsDependencyListSorted(Category, false);
}
LinkHaver.SetIsReferencersSorted(false);
}
if (NumElements <= 2)
{
AddLink(FirstRemove);
if (NumElements > 1)
{
AddLink(SecondRemove);
}
}
else
{
if (SortTestType == ESortTestType::AlwaysUnsorted)
{
for (int n = 0; n < NumLinkNodes; ++n)
{
AddLink(UnsortedOrder[n]);
}
}
else
{
for (int n = 0; n < NumLinkNodes; ++n)
{
AddLink(n);
}
}
}
TUniqueFunction<bool(const FDependsNode*)> ShouldRemove = [FirstRemove, SecondRemove, &LinkNodes, &GetNodeIndex](const FDependsNode* ExistingNode)
{
int NodeIndex = GetNodeIndex(ExistingNode);
return NodeIndex == FirstRemove || NodeIndex == SecondRemove;
};
LinkHaver.RemoveLinks(ShouldRemove);
if (NumElements <= 2)
{
TestTrue(TEXT("RemoveLinks with NumElements removes all"), LinkHaver.GetConnectionCount() == 0);
}
else
{
int ExpectedCount = NumElements - NumRemoves;
int Count = 0;
LinkHaver.IterateOverReferencers([this, &ShouldRemove, &Count](const FDependsNode* ExistingLink)
{
TestTrue(TEXT("RemoveLinks removes all requested referencers"), !ShouldRemove(ExistingLink));
++Count;
});
TestEqual(TEXT("RemoveLinks keeps the proper number of referencers"), ExpectedCount, Count);
for (EDependencyCategory Category : {EDependencyCategory::Package, EDependencyCategory::SearchableName, EDependencyCategory::Manage})
{
Count = 0;
LinkHaver.IterateOverDependencies([this, &ShouldRemove, &GetNodeIndex, Category, &Count, &PackageProperties, &ManageProperties](const FDependsNode* ExistingLink, EDependencyCategory ExistingCategory, EDependencyProperty ExistingProperty, bool bIsDuplicate)
{
TestTrue(TEXT("RemoveLinks removes all requested referencers"), !ShouldRemove(ExistingLink));
EDependencyProperty* Properties = nullptr;
if (Category == EDependencyCategory::Package) Properties = PackageProperties;
else if (Category == EDependencyCategory::Manage) Properties = ManageProperties;
if (Properties)
{
TestTrue(TEXT("RemoveLinks keeps the proper properties"), ExistingProperty == Properties[GetNodeIndex(ExistingLink)]);
}
++Count;
}, Category);
TestEqual(TEXT("RemoveLinks keeps the proper number of dependencies"), ExpectedCount, Count);
}
}
}
}
}
}
}
// GetConnectionCount, GetAllocatedSize
{
FDependsNode A;
FDependsNode B;
FDependsNode C;
A.AddDependency(&B, EDependencyCategory::Package, EDependencyProperty::None);
A.AddDependency(&B, EDependencyCategory::Manage, EDependencyProperty::None);
A.AddDependency(&B, EDependencyCategory::SearchableName, EDependencyProperty::None);
A.AddDependency(&C, EDependencyCategory::Package, EDependencyProperty::None);
B.AddDependency(&C, EDependencyCategory::Package, EDependencyProperty::None);
B.AddReferencer(&A);
C.AddReferencer(&A);
C.AddReferencer(&B);
TestEqual(TEXT("GetConnectionCount - Referencer - A"), A.GetConnectionCount(), 4);
TestEqual(TEXT("GetConnectionCount - Referencer - B"), B.GetConnectionCount(), 2);
TestEqual(TEXT("GetConnectionCount - Referencer - C"), C.GetConnectionCount(), 2);
TestTrue(TEXT("GetAllocatedSize"), A.GetAllocatedSize() > 0 && C.GetAllocatedSize() > 0);
}
// Serialize
enum class EKeepNode2
{
KeepAlways,
KeepNever,
KeepSaveButNotLoad,
};
for (EKeepNode2 KeepNode2 : { EKeepNode2::KeepAlways, EKeepNode2::KeepNever, EKeepNode2::KeepSaveButNotLoad })
{
for (bool bKeepManageDependencies : { true, false})
{
for (bool bKeepNameDependencies : { true, false})
{
FDependsNode::FSaveScratch SaveScratch;
FDependsNode::FLoadScratch LoadScratch;
FDependsNode A;
FDependsNode B;
A.AddDependency(&B, EDependencyCategory::Package, EDependencyProperty::None);
A.AddDependency(&B, EDependencyCategory::Manage, EDependencyProperty::None);
A.AddDependency(&B, EDependencyCategory::SearchableName, EDependencyProperty::None);
B.AddReferencer(&A);
TUniqueFunction<int32(FDependsNode*, bool bAsReferencer)> GetSerializeIndexFromNode = [&A, &B, KeepNode2](FDependsNode* DependsNode, bool bAsReferencer) -> int32
{
if (DependsNode == &A) return 0;
if (KeepNode2 == EKeepNode2::KeepAlways || KeepNode2 == EKeepNode2::KeepSaveButNotLoad)
{
if (DependsNode == &B) return 1;
}
return -1;
};
FDependsNode LoadedA;
FDependsNode LoadedB;
TUniqueFunction<FDependsNode* (int32)> GetNodeFromSerializeIndex = [&LoadedA, &LoadedB, KeepNode2](int32 Index) -> FDependsNode*
{
if (Index == 0) return &LoadedA;
if (KeepNode2 == EKeepNode2::KeepAlways)
{
if (Index == 1) return &LoadedB;
}
return nullptr;
};
FAssetRegistrySerializationOptions Options(UE::AssetRegistry::ESerializationTarget::ForDevelopment);
Options.bSerializeSearchableNameDependencies = bKeepNameDependencies;
Options.bSerializeManageDependencies = bKeepManageDependencies;
TArray<uint8> Bytes;
{
FMemoryWriter Writer(Bytes);
A.SerializeSave(Writer, GetSerializeIndexFromNode, SaveScratch, Options);
if (KeepNode2 == EKeepNode2::KeepAlways || KeepNode2 == EKeepNode2::KeepSaveButNotLoad)
{
B.SerializeSave(Writer, GetSerializeIndexFromNode, SaveScratch, Options);
}
}
{
FMemoryReader Reader(Bytes);
LoadedA.SerializeLoad(Reader, GetNodeFromSerializeIndex, LoadScratch);
if (KeepNode2 == EKeepNode2::KeepAlways)
{
LoadedB.SerializeLoad(Reader, GetNodeFromSerializeIndex, LoadScratch);
}
}
int AIterationIndex = 0;
bool bHasPackage = false;
bool bHasManage = false;
bool bHasName = false;
LoadedA.IterateOverDependencies([this, &AIterationIndex, &bHasPackage, &bHasManage, &bHasName, &LoadedB](FDependsNode* Dependency, EDependencyCategory Category, EDependencyProperty Properties, bool bDuplicate)
{
bHasPackage = bHasPackage || Category == EDependencyCategory::Package;
bHasName = bHasName || Category == EDependencyCategory::SearchableName;
bHasManage = bHasManage || Category == EDependencyCategory::Manage;
TestTrue(TEXT("Serialize - ADependencies - Category one at a time"), Category == EDependencyCategory::Package || Category == EDependencyCategory::SearchableName || Category == EDependencyCategory::Manage);
TestTrue(TEXT("Serialize - ADependencies - Expected values"), Dependency == &LoadedB && Properties == EDependencyProperty::None);
++AIterationIndex;
});
TArray<FDependsNode*> AReferencers, BReferencers;
LoadedA.GetReferencers(AReferencers);
LoadedB.GetReferencers(BReferencers);
int BIterationIndex = 0;
LoadedB.IterateOverDependencies([&BIterationIndex](FDependsNode* Dependency, EDependencyCategory Category, EDependencyProperty Properties, bool bDuplicate)
{
++BIterationIndex;
});
TestTrue(TEXT("Serialize - AReferencers"), AReferencers.Num() == 0);
TestTrue(TEXT("Serialize - BDependencies"), BIterationIndex == 0);
if (KeepNode2 == EKeepNode2::KeepAlways)
{
TestTrue(TEXT("Serialize - ADependencies - All expected types"), bHasPackage && bHasName == bKeepNameDependencies && bHasManage == bKeepManageDependencies);
TestTrue(TEXT("Serialize - BReferencers"), BReferencers.Num() == 1 && BReferencers[0] == &LoadedA);
}
else
{
TestTrue(TEXT("Serialize - ADependencies - filtered out node2"), AIterationIndex == 0);
}
}
}
}
// RefreshReferencers
for (ESortTestType SortTestType : { ESortTestType::AlwaysSorted, ESortTestType::AlwaysUnsorted, ESortTestType::UnsortedThenSorted })
{
FDependsNode Nodes[8];
FDependsNode* NodeWithReferencers = &Nodes[7];
if (SortTestType != ESortTestType::AlwaysSorted)
{
NodeWithReferencers->SetIsReferencersSorted(false);
}
Nodes[0].AddDependency(&Nodes[7], EDependencyCategory::Package, EDependencyProperty::None);
Nodes[6].AddDependency(&Nodes[7], EDependencyCategory::Package, EDependencyProperty::None);
for (int Count = 0; Count < 2; ++Count)
{
for (int n = 0; n < UE_ARRAY_COUNT(Nodes); ++n)
{
if (&Nodes[n] != NodeWithReferencers)
{
NodeWithReferencers->AddReferencer(&Nodes[n]);
}
}
}
NodeWithReferencers->RefreshReferencers();
if (SortTestType != ESortTestType::AlwaysUnsorted)
{
NodeWithReferencers->SetIsReferencersSorted(true);
}
Referencers.Reset();
NodeWithReferencers->GetReferencers(Referencers);
if (SortTestType != ESortTestType::AlwaysUnsorted)
{
TestTrue(TEXT("RefreshReferencers"), CombinationEquals<FDependsNode* const>(Referencers, TArrayView<FDependsNode* const>({ &Nodes[0], &Nodes[6] })));
}
else
{
TestTrue(TEXT("RefreshReferencers"), CombinationEquals<FDependsNode* const>(Referencers, TArrayView<FDependsNode* const>({ &Nodes[0], &Nodes[6], &Nodes[0], &Nodes[6] })));
}
}
return true;
}
#endif