// Copyright Epic Games, Inc. All Rights Reserved. #include "CoreTypes.h" #include "MovieSceneFwd.h" #include "Misc/AutomationTest.h" #include "MovieSceneTestObjects.h" #include "TrackInstancePropertyBindings.h" #if WITH_DEV_AUTOMATION_TESTS #define UTEST_CACHED_BINDING(Message, Bindings, Object)\ {\ FTrackInstancePropertyBindings::FResolvedPropertyAndFunction PropAndFunction = Bindings.FindOrAdd(Object);\ UTEST_EQUAL(Message, PropAndFunction.ResolvedProperty.IsType(), true);\ } #define UTEST_VOLATILE_BINDING(Message, Bindings, Object)\ {\ FTrackInstancePropertyBindings::FResolvedPropertyAndFunction PropAndFunction = Bindings.FindOrAdd(Object);\ UTEST_EQUAL(Message, PropAndFunction.ResolvedProperty.IsType(), true);\ } IMPLEMENT_SIMPLE_AUTOMATION_TEST(FTrackInstancePropertyBindingsTests, "System.Engine.Sequencer.TrackInstancePropertyBindings", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter) bool FTrackInstancePropertyBindingsTests::RunTest(const FString& Parameters) { using namespace UE::MovieScene; // TestBool property { ATestMovieSceneArrayPropertiesActor* TestActor = NewObject(); FTrackInstancePropertyBindings Bindings(TEXT("TestBool"), TEXT("TestBool")); UTEST_EQUAL("", TestActor->TestBool, false); Bindings.CallFunction(*TestActor, true); UTEST_EQUAL("", TestActor->TestBool, true); UTEST_CACHED_BINDING("", Bindings, *TestActor); } // TestEnum property { ATestMovieSceneArrayPropertiesActor* TestActor = NewObject(); FTrackInstancePropertyBindings Bindings(TEXT("TestEnum"), TEXT("TestEnum")); UTEST_EQUAL("", TestActor->TestEnum, ETestMovieSceneEnum::One); Bindings.CallFunctionForEnum(*TestActor, (int64)ETestMovieSceneEnum::Two); UTEST_EQUAL("", TestActor->TestEnum, ETestMovieSceneEnum::Two); UTEST_CACHED_BINDING("", Bindings, *TestActor); } // TestInt32 property { ATestMovieSceneArrayPropertiesActor* TestActor = NewObject(); FTrackInstancePropertyBindings Bindings(TEXT("TestInt32"), TEXT("TestInt32")); UTEST_EQUAL("", TestActor->TestInt32, 0); Bindings.CallFunction(*TestActor, 42); UTEST_EQUAL("", TestActor->TestInt32, 42); UTEST_CACHED_BINDING("", Bindings, *TestActor); } // TestObject property { ATestMovieSceneArrayPropertiesActor* TestActor = NewObject(); UTestMovieSceneObject* TestObject = NewObject(); FTrackInstancePropertyBindings Bindings(TEXT("TestObject"), TEXT("TestObject")); UTEST_EQUAL("", TestActor->TestObject.Get(), (UTestMovieSceneObject*)nullptr); Bindings.CallFunction(*TestActor, (UObject*)TestObject); UTEST_EQUAL("", TestActor->TestObject.Get(), TestObject); UTEST_CACHED_BINDING("", Bindings, *TestActor); } // TestVector.X/Y property { ATestMovieSceneArrayPropertiesActor* TestActor = NewObject(); FTrackInstancePropertyBindings BindingsX(TEXT("X"), TEXT("TestVector.X")); UTEST_EQUAL("", TestActor->TestVector, FVector(0, 0, 0)); BindingsX.CallFunction(*TestActor, 1.0); UTEST_EQUAL("", TestActor->TestVector, FVector(1, 0, 0)); UTEST_CACHED_BINDING("", BindingsX, *TestActor); FTrackInstancePropertyBindings BindingsY(TEXT("Y"), TEXT("TestVector.Y")); UTEST_EQUAL("", TestActor->TestVector, FVector(1, 0, 0)); BindingsY.CallFunction(*TestActor, 2.0); UTEST_EQUAL("", TestActor->TestVector, FVector(1, 2, 0)); UTEST_CACHED_BINDING("", BindingsY, *TestActor); } // MultipleFloats property { ATestMovieSceneArrayPropertiesActor* TestActor = NewObject(); TestActor->MultipleFloats.Add(0); TestActor->MultipleFloats.Add(1); FTrackInstancePropertyBindings Bindings(TEXT("MultipleFloats[1]"), TEXT("MultipleFloats[1]")); UTEST_EQUAL("", TestActor->MultipleFloats[1], 1.f); Bindings.CallFunction(*TestActor, 11.f); UTEST_EQUAL("", TestActor->MultipleFloats[1], 11.f); UTEST_VOLATILE_BINDING("", Bindings, *TestActor); } // SingleStruct.Second property { ATestMovieSceneArrayPropertiesActor* TestActor = NewObject(); FTrackInstancePropertyBindings Bindings(TEXT("Second"), TEXT("SingleStruct.Second")); UTEST_EQUAL("", TestActor->SingleStruct.Second, 0.f); Bindings.CallFunction(*TestActor, 1.f); UTEST_EQUAL("", TestActor->SingleStruct.Second, 1.f); UTEST_CACHED_BINDING("", Bindings, *TestActor); } // SingleStruct.MultipleIntegers property { ATestMovieSceneArrayPropertiesActor* TestActor = NewObject(); TestActor->SingleStruct.MultipleIntegers.Add(2); TestActor->SingleStruct.MultipleIntegers.Add(3); FTrackInstancePropertyBindings Bindings(TEXT("MultipleIntegers[1]"), TEXT("SingleStruct.MultipleIntegers[1]")); UTEST_EQUAL("", TestActor->SingleStruct.MultipleIntegers[1], 3); Bindings.CallFunction(*TestActor, 4); UTEST_EQUAL("", TestActor->SingleStruct.MultipleIntegers[1], 4); UTEST_VOLATILE_BINDING("", Bindings, *TestActor); } // SingleStruct.MultipleVectors.X property { ATestMovieSceneArrayPropertiesActor* TestActor = NewObject(); TestActor->SingleStruct.MultipleVectors.Add(FVector(2, 3, 4)); TestActor->SingleStruct.MultipleVectors.Add(FVector(5, 6, 7)); FTrackInstancePropertyBindings Bindings(TEXT("X"), TEXT("SingleStruct.MultipleVectors[1].X")); UTEST_EQUAL("", TestActor->SingleStruct.MultipleVectors[1].X, 5.0); Bindings.CallFunction(*TestActor, 10.0); UTEST_EQUAL("", TestActor->SingleStruct.MultipleVectors[1].X, 10.0); UTEST_VOLATILE_BINDING("", Bindings, *TestActor); } // MultipleStruct.Second property { ATestMovieSceneArrayPropertiesActor* TestActor = NewObject(); TestActor->MultipleStructs.Reserve(2); TestActor->MultipleStructs.Add(FTestMovieSceneStruct()); TestActor->MultipleStructs.Add(FTestMovieSceneStruct{ 3.f, 5.f }); FTrackInstancePropertyBindings Bindings(TEXT("Second"), TEXT("MultipleStructs[1].Second")); UTEST_EQUAL("", TestActor->MultipleStructs[1].Second, 5.f); Bindings.CallFunction(*TestActor, 10.f); UTEST_EQUAL("", TestActor->MultipleStructs[1].Second, 10.f); UTEST_VOLATILE_BINDING("", Bindings, *TestActor); const FTrackInstancePropertyBindings::FResolvedPropertyAndFunction& PropAndFunction = Bindings.FindOrAdd(*TestActor); void* OldContainerAddress = PropAndFunction.GetContainerAddress(); UTEST_EQUAL("", OldContainerAddress, (void*)&TestActor->MultipleStructs[1]); // Force re-allocating the array and check that we haven't cached the old addresses. TestActor->MultipleStructs.Reserve(4); UTEST_NOT_EQUAL("", OldContainerAddress, (void*)&TestActor->MultipleStructs[1]); void* NewContainerAddress = PropAndFunction.GetContainerAddress(); UTEST_EQUAL("", NewContainerAddress, (void*)&TestActor->MultipleStructs[1]); } // MultipleStruct.Enum property { ATestMovieSceneArrayPropertiesActor* TestActor = NewObject(); TestActor->MultipleStructs.Reserve(2); TestActor->MultipleStructs.Add(FTestMovieSceneStruct()); TestActor->MultipleStructs.Add(FTestMovieSceneStruct()); FTrackInstancePropertyBindings Bindings(TEXT("Enum"), TEXT("MultipleStructs[1].Enum")); UTEST_EQUAL("", TestActor->MultipleStructs[1].Enum, ETestMovieSceneEnum::One); Bindings.CallFunctionForEnum(*TestActor, (int64)ETestMovieSceneEnum::Two); UTEST_EQUAL("", TestActor->MultipleStructs[1].Enum, ETestMovieSceneEnum::Two); UTEST_VOLATILE_BINDING("", Bindings, *TestActor); const FTrackInstancePropertyBindings::FResolvedPropertyAndFunction& PropAndFunction = Bindings.FindOrAdd(*TestActor); void* OldContainerAddress = PropAndFunction.GetContainerAddress(); UTEST_EQUAL("", OldContainerAddress, (void*)&TestActor->MultipleStructs[1]); // Force re-allocating the array and check that we haven't cached the old addresses. TestActor->MultipleStructs.Reserve(4); UTEST_NOT_EQUAL("", OldContainerAddress, (void*)&TestActor->MultipleStructs[1]); void* NewContainerAddress = PropAndFunction.GetContainerAddress(); UTEST_EQUAL("", NewContainerAddress, (void*)&TestActor->MultipleStructs[1]); } // MultipleStruct.MultipleIntegers property { ATestMovieSceneArrayPropertiesActor* TestActor = NewObject(); TestActor->MultipleStructs.Reserve(2); TestActor->MultipleStructs.Add(FTestMovieSceneStruct()); TestActor->MultipleStructs.Add(FTestMovieSceneStruct()); TestActor->MultipleStructs[1].MultipleIntegers.Add(7); TestActor->MultipleStructs[1].MultipleIntegers.Add(8); FTrackInstancePropertyBindings Bindings(TEXT("MultipleIntegers[1]"), TEXT("MultipleStructs[1].MultipleIntegers[1]")); UTEST_EQUAL("", TestActor->MultipleStructs[1].MultipleIntegers[1], 8); Bindings.CallFunction(*TestActor, 10); UTEST_EQUAL("", TestActor->MultipleStructs[1].MultipleIntegers[1], 10); UTEST_VOLATILE_BINDING("", Bindings, *TestActor); } // MultipleStruct.MultipleVectors.X property { ATestMovieSceneArrayPropertiesActor* TestActor = NewObject(); TestActor->MultipleStructs.Reserve(2); TestActor->MultipleStructs.Add(FTestMovieSceneStruct()); TestActor->MultipleStructs.Add(FTestMovieSceneStruct{ 3.f, 5.f }); TestActor->MultipleStructs[1].MultipleVectors.Add(FVector(11, 12, 13)); TestActor->MultipleStructs[1].MultipleVectors.Add(FVector(14, 15, 16)); FTrackInstancePropertyBindings Bindings(TEXT("X"), TEXT("MultipleStructs[1].MultipleVectors[1].X")); UTEST_EQUAL("", TestActor->MultipleStructs[1].MultipleVectors[1].X, 14.0); Bindings.CallFunction(*TestActor, 20.0); UTEST_EQUAL("", TestActor->MultipleStructs[1].MultipleVectors[1].X, 20.0); UTEST_VOLATILE_BINDING("", Bindings, *TestActor); } // SingleInstancedStruct.Second property { ATestMovieSceneArrayPropertiesActor* TestActor = NewObject(); FTestMovieSceneStruct& TestStruct = TestActor->SingleInstancedStruct.InitializeAs(); TestStruct.First = 2.f; TestStruct.Second = 4.f; FTrackInstancePropertyBindings Bindings(TEXT("Second"), TEXT("SingleInstancedStruct.Second")); UTEST_EQUAL("", TestStruct.Second, 4.f); Bindings.CallFunction(*TestActor, 8.f); UTEST_EQUAL("", TestStruct.Second, 8.f); UTEST_VOLATILE_BINDING("", Bindings, *TestActor); FMovieSceneEasingSettings& Easing = TestActor->SingleInstancedStruct.InitializeAs(); Easing.AutoEaseInDuration = 5000; Easing.AutoEaseOutDuration = 5000; Bindings.CallFunction(*TestActor, 8.f); // The struct is different so it should now fail. // Everything else should be untouched (this checks for memory corruption) UTEST_EQUAL("", Easing.AutoEaseInDuration, 5000); UTEST_EQUAL("", Easing.AutoEaseOutDuration, 5000); UTEST_EQUAL("", Easing.EaseIn, TScriptInterface()); UTEST_EQUAL("", Easing.bManualEaseIn, false); UTEST_EQUAL("", Easing.ManualEaseInDuration, 0); UTEST_EQUAL("", Easing.EaseOut, TScriptInterface()); UTEST_EQUAL("", Easing.bManualEaseOut, false); UTEST_EQUAL("", Easing.ManualEaseOutDuration, 0); } // SingleInstancedStruct.Second property { ATestMovieSceneArrayPropertiesActor* TestActor = NewObject(); FTestMovieSceneStruct& TestStruct = TestActor->SingleInstancedStruct.InitializeAs(); TestStruct.First = 2.f; TestStruct.Second = 4.f; FTrackInstancePropertyBindings Bindings(TEXT("Second"), TEXT("SingleInstancedStruct.Second")); UTEST_EQUAL("", TestStruct.Second, 4.f); Bindings.CallFunction(*TestActor, 8.f); UTEST_EQUAL("", TestStruct.Second, 8.f); UTEST_VOLATILE_BINDING("", Bindings, *TestActor); FTestMovieSceneStruct2& TestStruct2 = TestActor->SingleInstancedStruct.InitializeAs(); TestStruct2.Second = 12.f; Bindings.CallFunction(*TestActor, 18.f); // The struct is different but duck typing should let us key this. UTEST_EQUAL("", TestStruct2.Second, 18.f); UTEST_VOLATILE_BINDING("", Bindings, *TestActor); } // SingleInstancedStruct.MultipleIntegers property { ATestMovieSceneArrayPropertiesActor* TestActor = NewObject(); FTestMovieSceneStruct& TestStruct = TestActor->SingleInstancedStruct.InitializeAs(); TestStruct.MultipleIntegers.Add(3); TestStruct.MultipleIntegers.Add(4); FTrackInstancePropertyBindings Bindings(TEXT("MultipleIntegers[1]"), TEXT("SingleInstancedStruct.MultipleIntegers[1]")); UTEST_EQUAL("", TestStruct.MultipleIntegers[1], 4); Bindings.CallFunction(*TestActor, 8); UTEST_EQUAL("", TestStruct.MultipleIntegers[1], 8); UTEST_VOLATILE_BINDING("", Bindings, *TestActor); } // SingleInstancedStruct.MultipleVectors.X property { ATestMovieSceneArrayPropertiesActor* TestActor = NewObject(); FTestMovieSceneStruct& TestStruct = TestActor->SingleInstancedStruct.InitializeAs(); TestStruct.MultipleVectors.Add(FVector(2, 3, 4)); TestStruct.MultipleVectors.Add(FVector(5, 6, 7)); FTrackInstancePropertyBindings Bindings(TEXT("X"), TEXT("SingleInstancedStruct.MultipleVectors[1].X")); UTEST_EQUAL("", TestStruct.MultipleVectors[1].X, 5.0); Bindings.CallFunction(*TestActor, 10.0); UTEST_EQUAL("", TestStruct.MultipleVectors[1].X, 10.0); UTEST_VOLATILE_BINDING("", Bindings, *TestActor); } // MultipleInstancedStructs.Second property { ATestMovieSceneArrayPropertiesActor* TestActor = NewObject(); TestActor->MultipleInstancedStructs.Reserve(2); TestActor->MultipleInstancedStructs.Emplace(); TestActor->MultipleInstancedStructs.Emplace(); FTestMovieSceneStruct& TestStruct = TestActor->MultipleInstancedStructs[1].InitializeAs(); TestStruct.First = 5.f; TestStruct.Second = 9.f; FTrackInstancePropertyBindings Bindings(TEXT("Second"), TEXT("MultipleInstancedStructs[1].Second")); UTEST_EQUAL("", TestStruct.Second, 9.f); Bindings.CallFunction(*TestActor, 12.f); UTEST_EQUAL("", TestStruct.Second, 12.f); UTEST_VOLATILE_BINDING("", Bindings, *TestActor); } // MultipleInstancedStructs.MultipleIntegers property { ATestMovieSceneArrayPropertiesActor* TestActor = NewObject(); TestActor->MultipleInstancedStructs.Reserve(2); TestActor->MultipleInstancedStructs.Emplace(); TestActor->MultipleInstancedStructs.Emplace(); FTestMovieSceneStruct& TestStruct = TestActor->MultipleInstancedStructs[1].InitializeAs(); TestStruct.MultipleIntegers.Add(8); TestStruct.MultipleIntegers.Add(9); FTrackInstancePropertyBindings Bindings(TEXT("MultipleIntegers[1]"), TEXT("MultipleInstancedStructs[1].MultipleIntegers[1]")); UTEST_EQUAL("", TestStruct.MultipleIntegers[1], 9); Bindings.CallFunction(*TestActor, 12); UTEST_EQUAL("", TestStruct.MultipleIntegers[1], 12); UTEST_VOLATILE_BINDING("", Bindings, *TestActor); } // MultipleInstancedStructs.MultipleVectors.Y property { ATestMovieSceneArrayPropertiesActor* TestActor = NewObject(); TestActor->MultipleInstancedStructs.Reserve(2); TestActor->MultipleInstancedStructs.Emplace(); TestActor->MultipleInstancedStructs.Emplace(); FTestMovieSceneStruct& TestStruct = TestActor->MultipleInstancedStructs[1].InitializeAs(); TestStruct.MultipleVectors.Add(FVector(5, 4, 3)); TestStruct.MultipleVectors.Add(FVector(2, 1, 0)); FTrackInstancePropertyBindings Bindings(TEXT("Y"), TEXT("MultipleInstancedStructs[1].MultipleVectors[1].Y")); UTEST_EQUAL("", TestStruct.MultipleVectors[1].Y, 1.0); Bindings.CallFunction(*TestActor, 11.0); UTEST_EQUAL("", TestStruct.MultipleVectors[1].Y, 11.0); UTEST_VOLATILE_BINDING("", Bindings, *TestActor); } return true; } #endif // WITH_DEV_AUTOMATION_TESTS