// Copyright (c) 2026 Unreal Directive. Licensed under the MIT License. #include "Libraries/DirectiveUtilMapFunctionLibrary.h" #include "Tests/DirectiveUtilTestObject.h" #include "Misc/AutomationTest.h" namespace { template PropertyType* GetTestProperty(const FName PropertyName) { return FindFProperty(UDirectiveUtilTestObject::StaticClass(), PropertyName); } TSet MakeIntegerSet(const TArray& Values) { TSet Result; for (const int32 Value : Values) { Result.Add(Value); } return Result; } TSet MakeStringSet(const TArray& Values) { TSet Result; for (const FString& Value : Values) { Result.Add(Value); } return Result; } TSet MakeNameSet(const TArray& Values) { TSet Result; for (const FName Value : Values) { Result.Add(Value); } return Result; } template bool HaveSameValues(const TSet& Left, const TSet& Right) { if (Left.Num() != Right.Num()) { return false; } for (const ValueType& Value : Left) { if (!Right.Contains(Value)) { return false; } } return true; } } IMPLEMENT_SIMPLE_AUTOMATION_TEST( FDirectiveUtilMapScenarioTest, "DirectiveUtilities.MapScenarios.CardinalityAndTypes", EAutomationTestFlags::EditorContext | EAutomationTestFlags::ClientContext | EAutomationTestFlags::EngineFilter) bool FDirectiveUtilMapScenarioTest::RunTest(const FString& Parameters) { FMapProperty* IntegerMapProperty = GetTestProperty(GET_MEMBER_NAME_CHECKED(UDirectiveUtilTestObject, TestMap)); FArrayProperty* IntegerArrayProperty = GetTestProperty(GET_MEMBER_NAME_CHECKED(UDirectiveUtilTestObject, TestArray)); FMapProperty* StringMapProperty = GetTestProperty(GET_MEMBER_NAME_CHECKED(UDirectiveUtilTestObject, TestStringMap)); FMapProperty* StringMapProperty2 = GetTestProperty(GET_MEMBER_NAME_CHECKED(UDirectiveUtilTestObject, TestStringMap2)); FArrayProperty* StringArrayProperty = GetTestProperty(GET_MEMBER_NAME_CHECKED(UDirectiveUtilTestObject, TestStringArray)); FMapProperty* StructMapProperty = GetTestProperty(GET_MEMBER_NAME_CHECKED(UDirectiveUtilTestObject, TestStructValueMap)); FArrayProperty* NameArrayProperty = GetTestProperty(GET_MEMBER_NAME_CHECKED(UDirectiveUtilTestObject, TestNameArray)); if (!TestNotNull("Integer map property should be available", IntegerMapProperty) || !TestNotNull("Integer array property should be available", IntegerArrayProperty) || !TestNotNull("String map property should be available", StringMapProperty) || !TestNotNull("Second string map property should be available", StringMapProperty2) || !TestNotNull("String array property should be available", StringArrayProperty) || !TestNotNull("Struct map property should be available", StructMapProperty) || !TestNotNull("Name array property should be available", NameArrayProperty)) { return false; } UDirectiveUtilTestObject* TestObject = NewObject(); for (const int32 ItemCount : {0, 1, 2, 16, 257, 4096}) { TestObject->TestMap.Reset(); for (int32 Key = 0; Key < ItemCount; ++Key) { TestObject->TestMap.Add(Key, Key % 7); } const FString Label = FString::Printf(TEXT("integer items=%d"), ItemCount); const int32 QueryValue = 3; TArray ExpectedKeys; for (int32 Key = 0; Key < ItemCount; ++Key) { if (Key % 7 == QueryValue) { ExpectedKeys.Add(Key); } } TestObject->TestArray.Reset(); UDirectiveUtilMapFunctionLibrary::GenericMap_GetKeysByValue( &TestObject->TestMap, IntegerMapProperty, &QueryValue, &TestObject->TestArray, IntegerArrayProperty); TestEqual(Label + TEXT(" GetKeysByValue count"), TestObject->TestArray.Num(), ExpectedKeys.Num()); TestTrue(Label + TEXT(" GetKeysByValue values"), HaveSameValues(MakeIntegerSet(TestObject->TestArray), MakeIntegerSet(ExpectedKeys))); TestEqual( Label + TEXT(" HasValue"), UDirectiveUtilMapFunctionLibrary::GenericMap_HasValue(&TestObject->TestMap, IntegerMapProperty, &QueryValue), !ExpectedKeys.IsEmpty()); const int32 ExistingKey = ItemCount > 0 ? ItemCount / 2 : 0; int32 FoundValue = -1; UDirectiveUtilMapFunctionLibrary::GenericMap_FindOrAdd( &TestObject->TestMap, IntegerMapProperty, &ExistingKey, &FoundValue); TestEqual(Label + TEXT(" FindOrAdd value"), FoundValue, ItemCount > 0 ? ExistingKey % 7 : 0); UDirectiveUtilMapFunctionLibrary::GenericMap_ClearValues(&TestObject->TestMap, IntegerMapProperty); TestEqual(Label + TEXT(" ClearValues count"), TestObject->TestMap.Num(), FMath::Max(ItemCount, 1)); for (const TPair& Pair : TestObject->TestMap) { TestEqual(Label + TEXT(" ClearValues value"), Pair.Value, 0); } TestObject->TestArray.Reset(); for (int32 Key = 0; Key < ItemCount; Key += 3) { TestObject->TestArray.Add(Key); } const int32 ExpectedRemoved = TestObject->TestArray.Num(); const int32 Removed = UDirectiveUtilMapFunctionLibrary::GenericMap_RemoveKeys( &TestObject->TestMap, IntegerMapProperty, &TestObject->TestArray, IntegerArrayProperty); TestEqual(Label + TEXT(" RemoveKeys count"), Removed, ExpectedRemoved); TestEqual(Label + TEXT(" RemoveKeys remaining"), TestObject->TestMap.Num(), FMath::Max(ItemCount, 1) - ExpectedRemoved); } for (const int32 ItemCount : {0, 1, 16, 257}) { TestObject->TestStringMap.Reset(); TestObject->TestStringMap2.Reset(); for (int32 Index = 0; Index < ItemCount; ++Index) { const FString Key = FString::Printf(TEXT("Key%d"), Index); TestObject->TestStringMap.Add(Key, FString::Printf(TEXT("Original%d"), Index)); TestObject->TestStringMap2.Add(Key, FString::Printf(TEXT("Replacement%d"), Index)); } TestObject->TestStringMap2.Add(TEXT("Added"), TEXT("AddedValue")); const FString Label = FString::Printf(TEXT("string items=%d"), ItemCount); UDirectiveUtilMapFunctionLibrary::GenericMap_Append( &TestObject->TestStringMap, StringMapProperty, &TestObject->TestStringMap2, StringMapProperty2, false); TestEqual(Label + TEXT(" Append count"), TestObject->TestStringMap.Num(), ItemCount + 1); TestEqual(Label + TEXT(" Append new value"), TestObject->TestStringMap.FindRef(TEXT("Added")), FString(TEXT("AddedValue"))); if (ItemCount > 0) { TestEqual(Label + TEXT(" Append preserve"), TestObject->TestStringMap.FindRef(TEXT("Key0")), FString(TEXT("Original0"))); } UDirectiveUtilMapFunctionLibrary::GenericMap_Append( &TestObject->TestStringMap, StringMapProperty, &TestObject->TestStringMap2, StringMapProperty2, true); if (ItemCount > 0) { TestEqual(Label + TEXT(" Append overwrite"), TestObject->TestStringMap.FindRef(TEXT("Key0")), FString(TEXT("Replacement0"))); } const int32 CountBeforeSelfAppend = TestObject->TestStringMap.Num(); UDirectiveUtilMapFunctionLibrary::GenericMap_Append( &TestObject->TestStringMap, StringMapProperty, &TestObject->TestStringMap, StringMapProperty, true); TestEqual(Label + TEXT(" self append"), TestObject->TestStringMap.Num(), CountBeforeSelfAppend); const FString QueryValue = ItemCount > 0 ? TEXT("Replacement0") : TEXT("missing"); TestObject->TestStringArray.Reset(); UDirectiveUtilMapFunctionLibrary::GenericMap_GetKeysByValue( &TestObject->TestStringMap, StringMapProperty, &QueryValue, &TestObject->TestStringArray, StringArrayProperty); const TArray ExpectedKeys = ItemCount > 0 ? TArray({TEXT("Key0")}) : TArray(); TestTrue(Label + TEXT(" managed GetKeysByValue"), HaveSameValues(MakeStringSet(TestObject->TestStringArray), MakeStringSet(ExpectedKeys))); UDirectiveUtilMapFunctionLibrary::GenericMap_ClearValues(&TestObject->TestStringMap, StringMapProperty); for (const TPair& Pair : TestObject->TestStringMap) { TestTrue(Label + TEXT(" managed ClearValues"), Pair.Value.IsEmpty()); } } FDirectiveUtilCollisionValue FirstStructValue; FirstStructValue.Value = 1; FDirectiveUtilCollisionValue SecondStructValue; SecondStructValue.Value = 2; TestObject->TestStructValueMap = { {TEXT("Alpha"), FirstStructValue}, {TEXT("Beta"), SecondStructValue}, {TEXT("Gamma"), FirstStructValue} }; const FDirectiveUtilCollisionValue StructQuery = FirstStructValue; TestObject->TestNameArray.Reset(); UDirectiveUtilMapFunctionLibrary::GenericMap_GetKeysByValue( &TestObject->TestStructValueMap, StructMapProperty, &StructQuery, &TestObject->TestNameArray, NameArrayProperty); TestTrue( "Struct GetKeysByValue", HaveSameValues( MakeNameSet(TestObject->TestNameArray), MakeNameSet(TArray({TEXT("Alpha"), TEXT("Gamma")})))); TestTrue( "Struct HasValue", UDirectiveUtilMapFunctionLibrary::GenericMap_HasValue( &TestObject->TestStructValueMap, StructMapProperty, &StructQuery)); UDirectiveUtilMapFunctionLibrary::GenericMap_ClearValues(&TestObject->TestStructValueMap, StructMapProperty); for (const TPair& Pair : TestObject->TestStructValueMap) { TestEqual("Struct ClearValues", Pair.Value.Value, 0); } return true; }