// Copyright Epic Games, Inc. All Rights Reserved. #include "CoreTypes.h" #include "Misc/CoreMiscDefines.h" #include "MovieSceneFwd.h" #include "Misc/AutomationTest.h" #include "Evaluation/MovieScenePlaybackCapabilities.h" #if WITH_DEV_AUTOMATION_TESTS namespace UE::MovieScene::Tests { // A couple of simple test capabilities. struct FTestSimpleCapability { static int32 TimesDestroyed; int32 IntValue; FString StringValue; FTestSimpleCapability() : IntValue(0) {} FTestSimpleCapability(int32 InIntValue, const FString& InStringValue) : IntValue(InIntValue) , StringValue(InStringValue) {} ~FTestSimpleCapability() { ++TimesDestroyed; } UE_DECLARE_MOVIESCENE_PLAYBACK_CAPABILITY(FTestSimpleCapability) }; UE_DEFINE_MOVIESCENE_PLAYBACK_CAPABILITY(FTestSimpleCapability) int32 FTestSimpleCapability::TimesDestroyed = 0; struct FTestOtherSimpleCapability { float FloatValue = 0.f; UE_DECLARE_MOVIESCENE_PLAYBACK_CAPABILITY(FTestOtherSimpleCapability) }; UE_DEFINE_MOVIESCENE_PLAYBACK_CAPABILITY(FTestOtherSimpleCapability) // Interface capability and sample implementation. struct FTestCapabilityBase { static int32 TimesDestroyedBase; float FloatValue = 0.f; virtual ~FTestCapabilityBase() { ++TimesDestroyedBase; } virtual int32 GetIntValue() { return 0; } virtual FString GetStringValue() { return FString(); } virtual void DoSomething() = 0; UE_DECLARE_MOVIESCENE_PLAYBACK_CAPABILITY(FTestCapabilityBase) }; UE_DEFINE_MOVIESCENE_PLAYBACK_CAPABILITY(FTestCapabilityBase) int32 FTestCapabilityBase::TimesDestroyedBase = 0; struct FTestCapabilityDerived : FTestCapabilityBase { static int32 TimesDestroyedDerived; int32 IntValue; FString StringValue; FTestCapabilityDerived(int32 InIntValue, const FString& InStringValue) : IntValue(InIntValue) , StringValue(InStringValue) {} virtual ~FTestCapabilityDerived() { ++TimesDestroyedDerived; } virtual int32 GetIntValue() override { return IntValue; } virtual FString GetStringValue() override { return StringValue; } virtual void DoSomething() override { ++IntValue; } }; int32 FTestCapabilityDerived::TimesDestroyedDerived = 0; // Base capability that implements the IPlaybackCapability interface. struct FTestInvalidatableCapability : IPlaybackCapability { int32 TimesInvalidated = 0; virtual void InvalidateCachedData(UMovieSceneEntitySystemLinker* Linker) override { ++TimesInvalidated; } UE_DECLARE_MOVIESCENE_PLAYBACK_CAPABILITY(FTestInvalidatableCapability) }; UE_DEFINE_MOVIESCENE_PLAYBACK_CAPABILITY(FTestInvalidatableCapability) // Derived version of the simple test capability, but with specific destructor counter struct FTestSimpleCapabilityDerived : FTestSimpleCapability { static int32 TimesDestroyedDerived; FTestSimpleCapabilityDerived() {} FTestSimpleCapabilityDerived(int32 InIntValue, const FString& InStringValue) : FTestSimpleCapability(InIntValue, InStringValue) {} ~FTestSimpleCapabilityDerived() { ++TimesDestroyedDerived; } }; int32 FTestSimpleCapabilityDerived::TimesDestroyedDerived = 0; // Derived version of the simple test capability, but with interface struct FTestSimpleCapabilityDerivedWithInterface : FTestSimpleCapability, IPlaybackCapability { int32 TimesInvalidated = 0; virtual void InvalidateCachedData(UMovieSceneEntitySystemLinker* Linker) override { ++TimesInvalidated; } }; } // namespace UE::MovieScene::Tests IMPLEMENT_SIMPLE_AUTOMATION_TEST(FMovieScenePlaybackCapabilitiesSimpleTest, "System.Engine.Sequencer.Capabilities.Simple", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter) bool FMovieScenePlaybackCapabilitiesSimpleTest::RunTest(const FString& Parameters) { using namespace UE::MovieScene; using namespace UE::MovieScene::Tests; FTestSimpleCapability::TimesDestroyed = 0; { FPlaybackCapabilities Caps; UTEST_FALSE("No capability", Caps.HasCapability()); FTestSimpleCapability& SimpleCap = Caps.AddCapability(42, TEXT("Just a test")); UTEST_EQUAL("Int value", SimpleCap.IntValue, 42); UTEST_EQUAL("String value", SimpleCap.StringValue, TEXT("Just a test")); UTEST_TRUE("Found capability", Caps.HasCapability()); FTestSimpleCapability* SimpleCap2 = Caps.FindCapability(); UTEST_NOT_NULL("Retrived test capacity", SimpleCap2); UTEST_EQUAL("Same capacity?", SimpleCap2, &SimpleCap); } UTEST_EQUAL("Destroyed", FTestSimpleCapability::TimesDestroyed, 1); return true; } IMPLEMENT_SIMPLE_AUTOMATION_TEST(FMovieScenePlaybackCapabilitiesInlineInterfaceTest, "System.Engine.Sequencer.Capabilities.InlineInterface", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter) bool FMovieScenePlaybackCapabilitiesInlineInterfaceTest::RunTest(const FString& Parameters) { using namespace UE::MovieScene; using namespace UE::MovieScene::Tests; FTestCapabilityBase::TimesDestroyedBase = 0; FTestCapabilityDerived::TimesDestroyedDerived = 0; { FPlaybackCapabilities Caps; UTEST_FALSE("No capability", Caps.HasCapability()); FTestCapabilityBase& InterfaceCap = Caps.AddCapability(42, TEXT("Just a test")); UTEST_EQUAL("Int value", InterfaceCap.GetIntValue(), 42); UTEST_EQUAL("String value", InterfaceCap.GetStringValue(), TEXT("Just a test")); UTEST_TRUE("Found capability", Caps.HasCapability()); FTestCapabilityBase* InterfaceCap2 = Caps.FindCapability(); UTEST_NOT_NULL("Retrived interface capacity", InterfaceCap2); UTEST_EQUAL("Same capacity?", InterfaceCap2, &InterfaceCap); } UTEST_EQUAL("Times destroyed", FTestCapabilityBase::TimesDestroyedBase, 1); UTEST_EQUAL("Times destroyed", FTestCapabilityDerived::TimesDestroyedDerived, 1); return true; } IMPLEMENT_SIMPLE_AUTOMATION_TEST(FMovieScenePlaybackCapabilitiesRawPointerTest, "System.Engine.Sequencer.Capabilities.RawPointer", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter) bool FMovieScenePlaybackCapabilitiesRawPointerTest::RunTest(const FString& Parameters) { using namespace UE::MovieScene; using namespace UE::MovieScene::Tests; FTestSimpleCapability::TimesDestroyed = 0; { FTestSimpleCapability ActualCap; { FPlaybackCapabilities Caps; UTEST_FALSE("No capability", Caps.HasCapability()); FTestSimpleCapability& PtrCap = Caps.AddCapabilityRaw(&ActualCap); UTEST_TRUE("Found capability", Caps.HasCapability()); FTestSimpleCapability* PtrCap2 = Caps.FindCapability(); UTEST_NOT_NULL("Retrived test capacity", PtrCap2); UTEST_EQUAL("Same capacity?", PtrCap2, &PtrCap); UTEST_EQUAL("Same capacity?", PtrCap2, &ActualCap); } UTEST_EQUAL("Not destroyed", FTestSimpleCapability::TimesDestroyed, 0); } UTEST_EQUAL("Destroyed", FTestSimpleCapability::TimesDestroyed, 1); return true; } IMPLEMENT_SIMPLE_AUTOMATION_TEST(FMovieScenePlaybackCapabilitiesSharedTest, "System.Engine.Sequencer.Capabilities.Shared", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter) bool FMovieScenePlaybackCapabilitiesSharedTest::RunTest(const FString& Parameters) { using namespace UE::MovieScene; using namespace UE::MovieScene::Tests; FTestSimpleCapability::TimesDestroyed = 0; { TSharedRef ActualCap = MakeShared(); UTEST_EQUAL("Ref count", ActualCap.GetSharedReferenceCount(), 1); { FPlaybackCapabilities Caps; UTEST_FALSE("No capability", Caps.HasCapability()); FTestSimpleCapability& PtrCap = Caps.AddCapabilityShared(ActualCap); UTEST_TRUE("Found capability", Caps.HasCapability()); UTEST_EQUAL("Ref count", ActualCap.GetSharedReferenceCount(), 2); FTestSimpleCapability* PtrCap2 = Caps.FindCapability(); UTEST_NOT_NULL("Retrived test capacity", PtrCap2); UTEST_EQUAL("Same capacity?", PtrCap2, &PtrCap); UTEST_EQUAL("Same capacity?", PtrCap2, ActualCap.ToSharedPtr().Get()); } UTEST_EQUAL("Not destroyed", FTestSimpleCapability::TimesDestroyed, 0); UTEST_EQUAL("Ref count", ActualCap.GetSharedReferenceCount(), 1); } UTEST_EQUAL("Destroyed", FTestSimpleCapability::TimesDestroyed, 1); return true; } IMPLEMENT_SIMPLE_AUTOMATION_TEST(FMovieScenePlaybackCapabilitiesInvalidateTest, "System.Engine.Sequencer.Capabilities.Invalidate", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter) bool FMovieScenePlaybackCapabilitiesInvalidateTest::RunTest(const FString& Parameters) { using namespace UE::MovieScene; using namespace UE::MovieScene::Tests; // Test simple inline { FPlaybackCapabilities Caps; FTestInvalidatableCapability& Cap = Caps.AddCapability(); UTEST_EQUAL("Not invalidated", Cap.TimesInvalidated, 0); Caps.InvalidateCachedData(nullptr); UTEST_EQUAL("Invalidated", Cap.TimesInvalidated, 1); } // Test inline subclass implementing interface when base class doesn't { FPlaybackCapabilities Caps; FTestSimpleCapability& Cap = Caps.AddCapability(); FTestSimpleCapabilityDerivedWithInterface* ActualCap = static_cast(&Cap); UTEST_EQUAL("Not invalidated", ActualCap->TimesInvalidated, 0); Caps.InvalidateCachedData(nullptr); UTEST_EQUAL("Invalidated", ActualCap->TimesInvalidated, 1); } // Test raw pointer { FTestInvalidatableCapability ActualCap; FPlaybackCapabilities Caps; Caps.AddCapabilityRaw(&ActualCap); UTEST_EQUAL("Not invalidated", ActualCap.TimesInvalidated, 0); Caps.InvalidateCachedData(nullptr); UTEST_EQUAL("Invalidated", ActualCap.TimesInvalidated, 1); } // Test shared pointer { TSharedRef ActualCap = MakeShared(); FPlaybackCapabilities Caps; Caps.AddCapabilityShared(ActualCap); UTEST_EQUAL("Not invalidated", ActualCap->TimesInvalidated, 0); Caps.InvalidateCachedData(nullptr); UTEST_EQUAL("Invalidated", ActualCap->TimesInvalidated, 1); } return true; } IMPLEMENT_SIMPLE_AUTOMATION_TEST(FMovieScenePlaybackCapabilitiesMultipleTest, "System.Engine.Sequencer.Capabilities.Multiple", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter) bool FMovieScenePlaybackCapabilitiesMultipleTest::RunTest(const FString& Parameters) { using namespace UE::MovieScene; using namespace UE::MovieScene::Tests; FTestOtherSimpleCapability RawPtrCap; TSharedRef SharedCap = MakeShared(); { FPlaybackCapabilities Caps; Caps.AddCapabilityRaw(&RawPtrCap); Caps.AddCapabilityShared(SharedCap); UTEST_FALSE("Found capability", Caps.HasCapability()); UTEST_FALSE("Found capability", Caps.HasCapability()); UTEST_TRUE("Found capability", Caps.HasCapability()); UTEST_TRUE("Found capability", Caps.HasCapability()); Caps.AddCapability(8, TEXT("Other simple test")); Caps.AddCapability(12, TEXT("Another implementation test")); UTEST_TRUE("Found capability", Caps.HasCapability()); UTEST_TRUE("Found capability", Caps.HasCapability()); UTEST_TRUE("Found capability", Caps.HasCapability()); UTEST_TRUE("Found capability", Caps.HasCapability()); UTEST_EQUAL("Before invalidation", SharedCap->TimesInvalidated, 0); Caps.InvalidateCachedData(nullptr); UTEST_EQUAL("After invalidation", SharedCap->TimesInvalidated, 1); } return true; } IMPLEMENT_SIMPLE_AUTOMATION_TEST(FMovieScenePlaybackCapabilitiesInheritanceTest, "System.Engine.Sequencer.Capabilities.Inheritance", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter) bool FMovieScenePlaybackCapabilitiesInheritanceTest::RunTest(const FString& Parameters) { using namespace UE::MovieScene; using namespace UE::MovieScene::Tests; struct ICapabilityAccessor { virtual ~ICapabilityAccessor() {} virtual FTestCapabilityBase& GetCapability() = 0; }; struct FCapabilityAccessor : ICapabilityAccessor { FTestCapabilityDerived Derived; FCapabilityAccessor() : Derived(42, TEXT("Life, the universe, everything")) {} virtual FTestCapabilityBase& GetCapability() override { return Derived; } }; FCapabilityAccessor Accessor; { FPlaybackCapabilities Caps; Caps.AddCapabilityRaw(&Accessor.GetCapability()); UTEST_TRUE("Found capability", Caps.HasCapability()); FTestCapabilityBase* Base = Caps.FindCapability(); UTEST_NOT_NULL("Got capability pointer", Base); UTEST_EQUAL("Same capability pointer", Base, static_cast(&Accessor.Derived)); UTEST_EQUAL("Checking IntValue", Base->GetIntValue(), 42); Base->DoSomething(); UTEST_EQUAL("Checking IntValue", Base->GetIntValue(), 43); } return true; } IMPLEMENT_SIMPLE_AUTOMATION_TEST(FMovieScenePlaybackCapabilitiesOverwriteTest, "System.Engine.Sequencer.Capabilities.Overwrite", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter) bool FMovieScenePlaybackCapabilitiesOverwriteTest::RunTest(const FString& Parameters) { using namespace UE::MovieScene; using namespace UE::MovieScene::Tests; FTestSimpleCapability Cap1(1, "First"); FTestSimpleCapability Cap2(2, "Second"); { FPlaybackCapabilities Caps; Caps.AddCapabilityRaw(&Cap1); FTestSimpleCapability* ActualCap = Caps.FindCapability(); UTEST_TRUE("Found capability", Caps.HasCapability()); UTEST_EQUAL("Same capability pointer", ActualCap, &Cap1); UTEST_EQUAL("Checking IntValue", ActualCap->IntValue, 1); UTEST_EQUAL("Checking StringValue", ActualCap->StringValue, TEXT("First")); Caps.OverwriteCapabilityRaw(&Cap2); ActualCap = Caps.FindCapability(); UTEST_TRUE("Found capability", Caps.HasCapability()); UTEST_EQUAL("Same capability pointer", ActualCap, &Cap2); UTEST_EQUAL("Checking IntValue", ActualCap->IntValue, 2); UTEST_EQUAL("Checking StringValue", ActualCap->StringValue, TEXT("Second")); } FTestSimpleCapability::TimesDestroyed = 0; FTestSimpleCapabilityDerived::TimesDestroyedDerived = 0; { FPlaybackCapabilities Caps; // Add the base, overwrite with the derived // (this is only allowed because they both have the same size) Caps.AddCapability(1, TEXT("Base")); FTestSimpleCapability* BaseCap = Caps.FindCapability(); UTEST_NOT_NULL("Found capabality", BaseCap); UTEST_EQUAL("Checking IntValue", BaseCap->IntValue, 1); UTEST_EQUAL("Checking StringValue", BaseCap->StringValue, TEXT("Base")); Caps.OverwriteCapability(2, TEXT("Derived")); UTEST_EQUAL("Times destroyed", FTestSimpleCapability::TimesDestroyed, 1); UTEST_EQUAL("Times destroyed", FTestSimpleCapabilityDerived::TimesDestroyedDerived, 0); FTestSimpleCapability* DerivedCap = Caps.FindCapability(); UTEST_NOT_NULL("Found capabality", DerivedCap); UTEST_EQUAL("Checking IntValue", DerivedCap->IntValue, 2); UTEST_EQUAL("Checking StringValue", DerivedCap->StringValue, TEXT("Derived")); } UTEST_EQUAL("Times destroyed", FTestSimpleCapability::TimesDestroyed, 2); UTEST_EQUAL("Times destroyed", FTestSimpleCapabilityDerived::TimesDestroyedDerived, 1); FTestSimpleCapability::TimesDestroyed = 0; FTestSimpleCapabilityDerived::TimesDestroyedDerived = 0; { FPlaybackCapabilities Caps; // Add the derived, overwrite with the base // (this is only allowed because they both have the same size) Caps.AddCapability(2, TEXT("Derived")); FTestSimpleCapability* DerivedCap = Caps.FindCapability(); UTEST_NOT_NULL("Found capabality", DerivedCap); UTEST_EQUAL("Checking IntValue", DerivedCap->IntValue, 2); UTEST_EQUAL("Checking StringValue", DerivedCap->StringValue, TEXT("Derived")); Caps.OverwriteCapability(1, TEXT("Base")); UTEST_EQUAL("Times destroyed", FTestSimpleCapability::TimesDestroyed, 1); UTEST_EQUAL("Times destroyed", FTestSimpleCapabilityDerived::TimesDestroyedDerived, 1); FTestSimpleCapability* BaseCap = Caps.FindCapability(); UTEST_NOT_NULL("Found capabality", BaseCap); UTEST_EQUAL("Checking IntValue", BaseCap->IntValue, 1); UTEST_EQUAL("Checking StringValue", BaseCap->StringValue, TEXT("Base")); } UTEST_EQUAL("Times destroyed", FTestSimpleCapability::TimesDestroyed, 2); UTEST_EQUAL("Times destroyed", FTestSimpleCapabilityDerived::TimesDestroyedDerived, 1); return true; } #endif // WITH_DEV_AUTOMATION_TESTS