// Copyright Epic Games, Inc. All Rights Reserved. #include "CoreMinimal.h" #include "AnimNextRigVMAssetEditorData.h" #include "AnimNextTest.h" #include "UncookedOnlyUtils.h" #include "Compilation/AnimNextRigVMAssetCompileContext.h" #include "Compilation/AnimNextGetVariableCompileContext.h" #include "DataInterface/AnimNextDataInterface.h" #include "Misc/AutomationTest.h" #include "DataInterface/AnimNextDataInterfaceFactory.h" #include "DataInterface/AnimNextDataInterface_EditorData.h" #include "Entries/AnimNextDataInterfaceEntry.h" #include "Entries/AnimNextVariableEntry.h" // AnimNext Data Interface Tests #if WITH_DEV_AUTOMATION_TESTS && WITH_EDITOR namespace UE::AnimNext::Tests { IMPLEMENT_SIMPLE_AUTOMATION_TEST(FDataInterfaceCompile, "Animation.AnimNext.DataInterface.Compile", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter) bool FDataInterfaceCompile::RunTest(const FString& InParameters) { using namespace UE::AnimNext; UFactory* Factory = NewObject(GetTransientPackage(), UAnimNextDataInterfaceFactory::StaticClass()); UAnimNextDataInterface* A = CastChecked(Factory->FactoryCreateNew(UAnimNextDataInterface::StaticClass(), GetTransientPackage(), TEXT("TestDataInterfaceA"), RF_Transient, nullptr, nullptr, NAME_None)); UE_RETURN_ON_ERROR(A != nullptr, "FDataInterfaceCompile -> Failed to create asset"); UAnimNextDataInterface* B = CastChecked(Factory->FactoryCreateNew(UAnimNextDataInterface::StaticClass(), GetTransientPackage(), TEXT("TestDataInterfaceB"), RF_Transient, nullptr, nullptr, NAME_None)); UE_RETURN_ON_ERROR(B != nullptr, "FDataInterfaceCompile -> Failed to create asset"); UAnimNextDataInterface* C = CastChecked(Factory->FactoryCreateNew(UAnimNextDataInterface::StaticClass(), GetTransientPackage(), TEXT("TestDataInterfaceC"), RF_Transient, nullptr, nullptr, NAME_None)); UE_RETURN_ON_ERROR(C != nullptr, "FDataInterfaceCompile -> Failed to create asset"); UAnimNextDataInterface* D = CastChecked(Factory->FactoryCreateNew(UAnimNextDataInterface::StaticClass(), GetTransientPackage(), TEXT("TestDataInterfaceD"), RF_Transient, nullptr, nullptr, NAME_None)); UE_RETURN_ON_ERROR(D != nullptr, "FDataInterfaceCompile -> Failed to create asset"); auto CompileAndTest = [A, B, C, D](TFunctionRef InTestFunction) { FRigVMCompileSettings TempSettings = {}; FAnimNextRigVMAssetCompileContext TempContext = {}; FAnimNextGetVariableCompileContext TempGetVariableContext = FAnimNextGetVariableCompileContext(TempContext); UncookedOnly::FUtils::CompileVariables(TempSettings, A, TempGetVariableContext); UncookedOnly::FUtils::CompileVariables(TempSettings, B, TempGetVariableContext); UncookedOnly::FUtils::CompileVariables(TempSettings, C, TempGetVariableContext); UncookedOnly::FUtils::CompileVariables(TempSettings, D, TempGetVariableContext); InTestFunction(); }; auto ClearVariables = [A, B, C, D]() { UAnimNextRigVMAssetLibrary::RemoveAllEntries(A, false, false); UAnimNextRigVMAssetLibrary::RemoveAllEntries(B, false, false); UAnimNextRigVMAssetLibrary::RemoveAllEntries(C, false, false); UAnimNextRigVMAssetLibrary::RemoveAllEntries(D, false, false); }; auto RunTest = [this, &ClearVariables](TFunctionRef InTestFunction) { InTestFunction(); ClearVariables(); }; auto AddPublicVariable = [this](UAnimNextDataInterface* InDataInterface, FName InName, const auto& InValue) { using ValueType = std::remove_reference_t; FAnimNextParamType Type = FAnimNextParamType::GetType(); UAnimNextVariableEntry* VariableEntry = UAnimNextRigVMAssetLibrary::AddVariable(InDataInterface, InName, Type.GetValueType(), Type.GetContainerType(), Type.GetValueTypeObject(), TEXT(""), false, false); UE_RETURN_ON_ERROR(VariableEntry != nullptr, "FDataInterfaceCompile::AddPublicVariable -> Failed to create variable"); VariableEntry->SetExportAccessSpecifier(EAnimNextExportAccessSpecifier::Public, false); UE_RETURN_ON_ERROR(VariableEntry->SetDefaultValue(InValue, false), "FDataInterfaceCompile::AddPublicVariable -> Failed to set variable default value"); return true; }; auto OverrideVariable = [this](UAnimNextDataInterface* InDataInterface, FName InName, const auto& InValue) { UAnimNextDataInterface_EditorData* EditorData = UncookedOnly::FUtils::GetEditorData(InDataInterface); bool bSuccessfulOverride = false; EditorData->ForEachEntryOfType([this, &InName, &InValue, &bSuccessfulOverride](UAnimNextDataInterfaceEntry* InEntry) { bSuccessfulOverride = InEntry->SetValueOverride(InName, InValue, false); return !bSuccessfulOverride; }); UE_RETURN_ON_ERROR(bSuccessfulOverride, "FDataInterfaceCompile::OverrideVariable -> Failed to override default value"); return false; }; auto Implement = [this](UAnimNextDataInterface* InA, UAnimNextDataInterface* InB) { UAnimNextDataInterfaceEntry* DataInterfaceEntry = UAnimNextRigVMAssetLibrary::AddDataInterface(InA, InB, false, false); UE_RETURN_ON_ERROR(DataInterfaceEntry != nullptr, "FDataInterfaceCompile::Implement -> Failed to add data interface"); return true; }; auto CheckValue = [this](UAnimNextDataInterface* InDataInterface, FName InName, const auto& InValue) { const FPropertyBagPropertyDesc* Desc = InDataInterface->VariableDefaults.FindPropertyDescByName(InName); UE_RETURN_ON_ERROR(Desc != nullptr, "FDataInterfaceCompile::CheckValue -> Failed to find value"); using ValueType = std::remove_reference_t; FAnimNextParamType DesiredType = FAnimNextParamType::GetType(); FAnimNextParamType FoundType = FAnimNextParamType(Desc->ValueType, Desc->ContainerTypes.GetFirstContainerType(), Desc->ValueTypeObject); UE_RETURN_ON_ERROR(DesiredType == FoundType, "FDataInterfaceCompile::CheckValue -> Type was incorrect"); check(Desc->CachedProperty); const uint8* ValuePtr = Desc->CachedProperty->ContainerPtrToValuePtr(InDataInterface->VariableDefaults.GetValue().GetMemory()); UE_RETURN_ON_ERROR(Desc->CachedProperty->Identical(ValuePtr, &InValue), "FDataInterfaceCompile::CheckValue -> Values were not equal"); return true; }; // Add a variable, dont override it, check its value RunTest([&]() { AddPublicVariable(A, "A", 1); CompileAndTest([&]() { CheckValue(A, "A", 1); }); }); // Add a variable, override it, check its value RunTest([&]() { AddPublicVariable(A, "A", 1); Implement(B, A); OverrideVariable(B, "A", 2); CompileAndTest([&]() { CheckValue(A, "A", 1); CheckValue(B, "A", 2); }); }); // Add a variable, override it twice, check its value at each step RunTest([&]() { AddPublicVariable(A, "A", 1); Implement(B, A); Implement(C, A); OverrideVariable(B, "A", 2); OverrideVariable(C, "A", 3); CompileAndTest([&]() { CheckValue(A, "A", 1); CheckValue(B, "A", 2); CheckValue(C, "A", 3); }); }); // Diamond inheritance // A=1 // / \ // B=2 C=3 // \ / // D=3 RunTest([&]() { AddPublicVariable(A, "A", 1); Implement(B, A); Implement(C, A); Implement(D, B); Implement(D, C); OverrideVariable(B, "A", 2); OverrideVariable(C, "A", 3); CompileAndTest([&]() { CheckValue(A, "A", 1); CheckValue(B, "A", 2); CheckValue(C, "A", 3); CheckValue(D, "A", 3); }); }); FUtils::CleanupAfterTests(); return true; } } #endif // WITH_DEV_AUTOMATION_TESTS && WITH_EDITOR