// Copyright Epic Games, Inc. All Rights Reserved. #include "MovieSceneTestObjects.h" #include "CoreMinimal.h" #include "Misc/AutomationTest.h" #include "Compilation/MovieSceneSegmentCompiler.h" #include "Compilation/MovieSceneCompilerRules.h" #include "Evaluation/MovieSceneEvaluationTrack.h" #include "Evaluation/MovieSceneEvaluationField.h" #include "UObject/Package.h" #include "MovieSceneTimeHelpers.h" // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // TODO: Reimplement or remove segment blender automation tests // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ UE_MOVIESCENE_TODO(Reimplement or remove segment blender automation tests) #if 0 #if WITH_DEV_AUTOMATION_TESTS namespace Impl { static const TRangeBound Inf = TRangeBound::Open(); typedef TTuple, TArray> FEvaluationTreeIteratorResult; /** Compiler rules to sort by priority */ struct FSortByPrioritySegmentBlender : FMovieSceneTrackSegmentBlender { FSortByPrioritySegmentBlender(bool bInAllowEmptySegments) { bAllowEmptySegments = bInAllowEmptySegments; } virtual TOptional InsertEmptySpace(const TRange& Range, const FMovieSceneSegment* PreviousSegment, const FMovieSceneSegment* NextSegment) const override { return bAllowEmptySegments ? FMovieSceneSegment(Range) : TOptional(); } virtual void Blend(FSegmentBlendData& BlendData) const { BlendData.Sort( [](const FMovieSceneSectionData& A, const FMovieSceneSectionData& B) { // Sort by template index and flags when we don't have any sections if (!A.Section || !B.Section) { if (A.Flags == ESectionEvaluationFlags::None && B.Flags == ESectionEvaluationFlags::None) { return A.TemplateIndex < B.TemplateIndex; } return A.Flags != ESectionEvaluationFlags::None; } return A.Section->GetOverlapPriority() > B.Section->GetOverlapPriority(); } ); } }; FString Join(TArrayView Stuff) { FString Result; for (const FSectionEvaluationData& Thing : Stuff) { if (Result.Len()) { Result += TEXT(", "); } Result += FString::Printf(TEXT("(Impl: %d, ForcedTime: %i, Flags: %u)"), Thing.ImplIndex, Thing.ForcedTime.Value, (uint8)Thing.Flags); } return Result; } FORCEINLINE_DEBUGGABLE void AssertSegmentImpls(FAutomationTestBase* Test, TArrayView ExpectedImpls, TArrayView ActualImpls, const TCHAR* AdditionalText = TEXT("")) { FString ActualImplsString = Join(ActualImpls); FString ExpectedImplsString = Join(ExpectedImpls); if (ActualImplsString != ExpectedImplsString) { Test->AddError(FString::Printf(TEXT("Compiled data does not match for segment %s.\nExpected: %s\nActual: %s."), AdditionalText, *ExpectedImplsString, *ActualImplsString)); } } FORCEINLINE_DEBUGGABLE void AssertSegmentValues(FAutomationTestBase* Test, TArrayView Expected, TArrayView Actual) { for (const FMovieSceneSegment& Segment : Actual) { Test->AddInfo(FString::Printf(TEXT("Actual %s, Impls: %s"), *LexToString(Segment.Range), *Join(Segment.Impls))); } for (const FMovieSceneSegment& Segment : Expected) { Test->AddInfo(FString::Printf(TEXT("Expected %s, Impls: %s"), *LexToString(Segment.Range), *Join(Segment.Impls))); } if (Actual.Num() != Expected.Num()) { Test->AddError(FString::Printf(TEXT("Wrong number of compiled segments. Expected %d, actual %d."), Expected.Num(), Actual.Num())); } else for (int32 Index = 0; Index < Expected.Num(); ++Index) { const FMovieSceneSegment& ExpectedSegment = Expected[Index]; const FMovieSceneSegment& ActualSegment = Actual[Index]; if (ExpectedSegment.Range != ActualSegment.Range) { Test->AddError(FString::Printf(TEXT("Incorrect compiled segment range at segment index %d. Expected:\n%s\nActual:\n%s"), Index, *LexToString(ExpectedSegment.Range), *LexToString(ActualSegment.Range))); } AssertSegmentImpls(Test, ExpectedSegment.Impls, ActualSegment.Impls, *FString::Printf(TEXT("index %d"), Index)); } } FORCEINLINE_DEBUGGABLE void AssertSegmentAtTime(FAutomationTestBase* Test, FMovieSceneEvaluationTrack& InTrack, FFrameNumber InTime, TArrayView ExpectedImpls) { FMovieSceneSegmentIdentifier ID = InTrack.GetSegmentFromTime(InTime); if (!ID.IsValid()) { Test->AddError(TEXT("No segment compiled for frame %i"), InTime.Value); } else { AssertSegmentImpls(Test, ExpectedImpls, InTrack.GetSegment(ID).Impls, *FString::Printf(TEXT("frame %i"), InTime.Value)); } } FORCEINLINE void AssertSegmentAtTime(FAutomationTestBase* Test, FMovieSceneEvaluationTrack& InTrack, FFrameNumber InTime, std::initializer_list ExpectedImpls) { AssertSegmentAtTime(Test, InTrack, InTime, MakeArrayView(ExpectedImpls)); } FORCEINLINE_DEBUGGABLE void AssertEvaluationTree(FAutomationTestBase* Test, const TMovieSceneEvaluationTree& Tree, TArrayView Expected) { int32 Index = 0; for (FMovieSceneEvaluationTreeRangeIterator It(Tree); It; ++It, ++Index) { if (Index >= Expected.Num()) { Test->AddError(FString::Printf(TEXT("Too many ranges iterated (expected %d, iterated >= %d)"), Expected.Num(), Index + 1)); break; } if (It.Range() != Expected[Index].Get<0>()) { Test->AddError(FString::Printf(TEXT("Incorrect range iterated at index %d. %s != %s"), Index, *LexToString(It.Range()), *LexToString(Expected[Index].Get<0>()))); continue; } int32 Count = 0; const TArray& ExpectedData = Expected[Index].Get<1>(); for (auto AllDataIt = Tree.GetAllData(It.Node()); AllDataIt; ++AllDataIt, ++Count) { if (!ExpectedData.Contains(*AllDataIt)) { Test->AddError(FString::Printf(TEXT("Incorrect data encountered at index %d. Count not find %d in expected data."), Index, *AllDataIt)); continue; } } if (Count != ExpectedData.Num()) { Test->AddError(FString::Printf(TEXT("Incorrect number of data entries iterated at index %d. Found %d entries, expected %d"), Index, Count, ExpectedData.Num())); } } } } IMPLEMENT_SIMPLE_AUTOMATION_TEST(FMovieSceneCompilerBasicTest, "System.Engine.Sequencer.Compiler.Basic", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter) bool FMovieSceneCompilerBasicTest::RunTest(const FString& Parameters) { using namespace Impl; // Specify descending priorities on the segments so we always sort the compiled segments in the order they're defined within the data // This is our test layout // Time -inf 10 20 25 30 inf // [============= 0 ===========] // [============== 1 ==================] // [========================== 2 ==================================] // [================== 3 ==========================] // [================== 4 ==========================] //---------------------------------------------------------------------------------------------------------------------------------------- // Expected Impls [ 3 | 0,2,3 | 1,2 | 1,2,4 | 4 ] // Override the blender (so it doesn't try and get it from the null track ptr) FScopedOverrideTrackSegmentBlender ScopedBlenderOverride(FSortByPrioritySegmentBlender(false)); FMovieSceneEvaluationTrack Track; Track.AddTreeData(TRange(10, 20), FSectionEvaluationData(0)); Track.AddTreeData(TRange(20, 30), FSectionEvaluationData(1)); Track.AddTreeData(TRange(10, 30), FSectionEvaluationData(2)); Track.AddTreeData(TRange(Inf, TRangeBound::Exclusive(20)), FSectionEvaluationData(3)); Track.AddTreeData(TRange(TRangeBound::Inclusive(25), Inf), FSectionEvaluationData(4)); FMovieSceneSegment Expected[] = { FMovieSceneSegment(TRange(Inf, TRangeBound::Exclusive(10)), { FSectionEvaluationData(3) }), FMovieSceneSegment(TRange(10, 20), { FSectionEvaluationData(0), FSectionEvaluationData(2), FSectionEvaluationData(3) }), FMovieSceneSegment(TRange(20, 25), { FSectionEvaluationData(1), FSectionEvaluationData(2) }), FMovieSceneSegment(TRange(25, 30), { FSectionEvaluationData(1), FSectionEvaluationData(2), FSectionEvaluationData(4) }), FMovieSceneSegment(TRange(TRangeBound::Inclusive(30),Inf), { FSectionEvaluationData(4) }) }; // Compile all the segments Track.GetSegmentsInRange(TRange::All()); AssertSegmentValues(this, Expected, Track.GetSortedSegments()); return true; } IMPLEMENT_SIMPLE_AUTOMATION_TEST(FMovieSceneCompilerEmptySpaceTest, "System.Engine.Sequencer.Compiler.Empty Space", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter) bool FMovieSceneCompilerEmptySpaceTest::RunTest(const FString& Parameters) { using namespace Impl; // Specify descending priorities on the segments so we always sort the compiled segments in the order they're defined within the data // This is our test layout // Time -Inf 10 20 30 40 Inf // [====== 0 ======] [====== 1 ======] //---------------------------------------------------------------------------------------------------------------------------------------- // Expected Impls [ Empty | 0 | Empty | 1 | Empty ] FMovieSceneEvaluationTrack Track; Track.AddTreeData(TRange(10, 20), FSectionEvaluationData(0)); Track.AddTreeData(TRange(30, 40), FSectionEvaluationData(1)); // Override the blender (so it doesn't try and get it from the null track ptr) FScopedOverrideTrackSegmentBlender ScopedBlenderOverride(FSortByPrioritySegmentBlender(true)); // Compile all the segments Track.GetSegmentsInRange(TRange::All()); FMovieSceneSegment Expected[] = { FMovieSceneSegment(TRange(Inf, TRangeBound::Exclusive(10)), { }), FMovieSceneSegment(TRange(10, 20), { FSectionEvaluationData(0) }), FMovieSceneSegment(TRange(20, 30), { }), FMovieSceneSegment(TRange(30, 40), { FSectionEvaluationData(1) }), FMovieSceneSegment(TRange(TRangeBound::Inclusive(40), Inf), { }) }; AssertSegmentValues(this, Expected, Track.GetSortedSegments()); return true; } IMPLEMENT_SIMPLE_AUTOMATION_TEST(FMovieSceneCustomCompilerTest, "System.Engine.Sequencer.Compiler.Custom", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter) bool FMovieSceneCustomCompilerTest::RunTest(const FString& Parameters) { using namespace Impl; // Test that we don't get duplicate entries in the resulting segments except for differing flag sets // This is our test layout // Time -Inf 10 15 20 25 30 40 Inf // [===== 0 (preroll) =====] // [========== 0 ==========] // [========== 0 ==========][========== 0 =========] FMovieSceneEvaluationTrack Track; Track.AddUniqueTreeData(TRange(TRangeBound::Inclusive(10), TRangeBound::Exclusive(20)), FSectionEvaluationData(0, ESectionEvaluationFlags::PreRoll)); Track.AddUniqueTreeData(TRange(TRangeBound::Inclusive(15), TRangeBound::Exclusive(25)), FSectionEvaluationData(0)); Track.AddUniqueTreeData(TRange(TRangeBound::Inclusive(20), TRangeBound::Exclusive(30)), FSectionEvaluationData(0)); Track.AddUniqueTreeData(TRange(TRangeBound::Inclusive(30), TRangeBound::Exclusive(40)), FSectionEvaluationData(0)); // Override the blender (so it doesn't try and get it from the null track ptr) FScopedOverrideTrackSegmentBlender ScopedBlenderOverride(FSortByPrioritySegmentBlender(false)); AssertSegmentAtTime(this, Track, 12, { FSectionEvaluationData(0, ESectionEvaluationFlags::PreRoll) }); AssertSegmentAtTime(this, Track, 17, { FSectionEvaluationData(0, ESectionEvaluationFlags::PreRoll), FSectionEvaluationData(0) }); AssertSegmentAtTime(this, Track, 22, { FSectionEvaluationData(0) }); AssertSegmentAtTime(this, Track, 27, { FSectionEvaluationData(0) }); AssertSegmentAtTime(this, Track, 30, { FSectionEvaluationData(0) }); return true; } IMPLEMENT_SIMPLE_AUTOMATION_TEST(FMovieSceneTrackCompilerTest, "System.Engine.Sequencer.Compiler.Tracks", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter) bool FMovieSceneTrackCompilerTest::RunTest(const FString& Parameters) { using namespace Impl; // Track 0 test layout: // Time -inf 10 20 25 30 inf // ------------------------------------------------------------------------------------------------------------------------------------------------------ // Track 0: [====================== 0 ==================] // [=============== 1 =================] // ------------------------------------------------------------------------------------------------------------------------------------------------------ // Additive Camera Rules Expected [ | 0 | (0,1) | 1 | ] // Nearest Section Expected [ 0 (10) | 0 | (0,1) | 1 | 1 (30) ] // No Nearest Section Expected [ | 0 | (0,1) | 1 | ] // High-pass Filter Expected [ | 0 | 1 | ] { UTestMovieSceneTrack* Track = NewObject(GetTransientPackage()); Track->EvalOptions.bCanEvaluateNearestSection = true; UTestMovieSceneSection* Section0 = NewObject(Track); Section0->SetRange(TRange( TRangeBound::Inclusive(10), TRangeBound::Exclusive(25) )); Section0->SetRowIndex(0); UTestMovieSceneSection* Section1 = NewObject(Track); Section1->SetRange(TRange( TRangeBound::Inclusive(20), TRangeBound::Exclusive(30) )); Section1->SetRowIndex(1); Track->SectionArray.Add(Section0); Track->SectionArray.Add(Section1); // Test compiling the track with the additive camera rules { FScopedOverrideTrackSegmentBlender ScopedBlenderOverride{FMovieSceneAdditiveCameraTrackBlender()}; FMovieSceneEvaluationTrack EvalTrack = Track->GenerateTrackTemplate(); EvalTrack.GetSegmentsInRange(TRange::All()); FMovieSceneSegment Expected[] = { FMovieSceneSegment(TRange(TRangeBound::Inclusive(10), TRangeBound::Exclusive(20)), { FSectionEvaluationData(0) }), FMovieSceneSegment(TRange(TRangeBound::Inclusive(20), TRangeBound::Exclusive(25)), { FSectionEvaluationData(0), FSectionEvaluationData(1) }), FMovieSceneSegment(TRange(TRangeBound::Inclusive(25), TRangeBound::Exclusive(30)), { FSectionEvaluationData(1) }), }; AssertSegmentValues(this, Expected, EvalTrack.GetSortedSegments()); } // Test compiling with 'evaluate nearest section' enabled { Track->EvalOptions.bEvalNearestSection = true; FMovieSceneEvaluationTrack EvalTrack = Track->GenerateTrackTemplate(); EvalTrack.GetSegmentsInRange(TRange::All()); FMovieSceneSegment Expected[] = { FMovieSceneSegment(TRange(Inf, TRangeBound::Exclusive(10)), { FSectionEvaluationData(0, 10) }), FMovieSceneSegment(TRange(TRangeBound::Inclusive(10), TRangeBound::Exclusive(20)), { FSectionEvaluationData(0) }), FMovieSceneSegment(TRange(TRangeBound::Inclusive(20), TRangeBound::Exclusive(25)), { FSectionEvaluationData(0), FSectionEvaluationData(1) }), FMovieSceneSegment(TRange(TRangeBound::Inclusive(25), TRangeBound::Exclusive(30)), { FSectionEvaluationData(1) }), FMovieSceneSegment(TRange(TRangeBound::Inclusive(30), Inf), { FSectionEvaluationData(1, 30) }), }; AssertSegmentValues(this, Expected, EvalTrack.GetSortedSegments()); } // Test compiling without 'evaluate nearest section' enabled { Track->EvalOptions.bEvalNearestSection = false; FMovieSceneEvaluationTrack EvalTrack = Track->GenerateTrackTemplate(); EvalTrack.GetSegmentsInRange(TRange::All()); FMovieSceneSegment Expected[] = { FMovieSceneSegment(TRange(TRangeBound::Inclusive(10), TRangeBound::Exclusive(20)), { FSectionEvaluationData(0) }), FMovieSceneSegment(TRange(TRangeBound::Inclusive(20), TRangeBound::Exclusive(25)), { FSectionEvaluationData(0), FSectionEvaluationData(1) }), FMovieSceneSegment(TRange(TRangeBound::Inclusive(25), TRangeBound::Exclusive(30)), { FSectionEvaluationData(1) }), }; AssertSegmentValues(this, Expected, EvalTrack.GetSortedSegments()); } // Test high-pass filter { Track->EvalOptions.bEvalNearestSection = false; Track->bHighPassFilter = true; FMovieSceneEvaluationTrack EvalTrack = Track->GenerateTrackTemplate(); EvalTrack.GetSegmentsInRange(TRange::All()); FMovieSceneSegment Expected[] = { FMovieSceneSegment(TRange(TRangeBound::Inclusive(10), TRangeBound::Exclusive(25)), { FSectionEvaluationData(0) }), FMovieSceneSegment(TRange(TRangeBound::Inclusive(25), TRangeBound::Exclusive(30)), { FSectionEvaluationData(1) }), }; AssertSegmentValues(this, Expected, EvalTrack.GetSortedSegments()); } } // Track 1 test layout: // Time -inf 10 15 20 25 30 inf // ----------------------------------------------------------------------------------------------------------------------------------------------------- // Track 1: [==== 3 ====(==== 3,2 ======)======= 2 =========] // [============================ 0 ============================] // [================================================ 1 ========================================================] // ----------------------------------------------------------------------------------------------------------------------------------------------------- // Additive Camera Rules Expected [ 1 | (1,0) | (1,0,3) | (1,0,2) | 1 ] // Nearest Section Expected [ 1 | (0,1) | (3,0,1) | (2,0,1) | 1 ] // No Nearest Section Expected [ 1 | (0,1) | (3,0,1) | (2,0,1) | 1 ] // High-Pass Filter Expected [ 1 | 0 | 3 | 2 | 1 ] { UTestMovieSceneTrack* Track = NewObject(GetTransientPackage()); UTestMovieSceneSection* Section0 = NewObject(Track); Section0->SetRange(TRange(10, 30)); Section0->SetRowIndex(1); UTestMovieSceneSection* Section1 = NewObject(Track); Section1->SetRange(TRange::All()); Section1->SetRowIndex(2); UTestMovieSceneSection* Section2 = NewObject(Track); Section2->SetRange(TRange(20, 30)); Section2->SetRowIndex(0); UTestMovieSceneSection* Section3 = NewObject(Track); Section3->SetRange(TRange(15, 25)); Section3->SetRowIndex(0); Section3->SetOverlapPriority(100.f); Track->SectionArray.Add(Section0); Track->SectionArray.Add(Section1); Track->SectionArray.Add(Section2); Track->SectionArray.Add(Section3); // Additive camera rules prescribe that they are evaluated in order of start time FMovieSceneSegment Expected[] = { FMovieSceneSegment(TRange(Inf, TRangeBound::Exclusive(10)), { }), FMovieSceneSegment(TRange(TRangeBound::Inclusive(10), TRangeBound::Exclusive(15)), { }), FMovieSceneSegment(TRange(TRangeBound::Inclusive(15), TRangeBound::Exclusive(25)), { }), FMovieSceneSegment(TRange(TRangeBound::Inclusive(25), TRangeBound::Exclusive(30)), { }), FMovieSceneSegment(TRange(TRangeBound::Inclusive(30), Inf), { }), }; // Test compiling the track with the additive camera rules { FScopedOverrideTrackSegmentBlender ScopedBlenderOverride{FMovieSceneAdditiveCameraTrackBlender()}; FMovieSceneEvaluationTrack EvalTrack = Track->GenerateTrackTemplate(); EvalTrack.GetSegmentsInRange(TRange::All()); // Additive camera rules prescribe that they are evaluated in order of start time Expected[0].Impls = { FSectionEvaluationData(1) }; Expected[1].Impls = { FSectionEvaluationData(1), FSectionEvaluationData(0) }; Expected[2].Impls = { FSectionEvaluationData(1), FSectionEvaluationData(0), FSectionEvaluationData(3) }; Expected[3].Impls = { FSectionEvaluationData(1), FSectionEvaluationData(0), FSectionEvaluationData(2) }; Expected[4].Impls = { FSectionEvaluationData(1) }; AssertSegmentValues(this, Expected, EvalTrack.GetSortedSegments()); } // Test compiling with 'evaluate nearest section' enabled { Track->EvalOptions.bEvalNearestSection = true; FMovieSceneEvaluationTrack EvalTrack = Track->GenerateTrackTemplate(); EvalTrack.GetSegmentsInRange(TRange::All()); Expected[0].Impls = { FSectionEvaluationData(1) }; Expected[1].Impls = { FSectionEvaluationData(0), FSectionEvaluationData(1) }; Expected[2].Impls = { FSectionEvaluationData(3), FSectionEvaluationData(0), FSectionEvaluationData(1) }; Expected[3].Impls = { FSectionEvaluationData(2), FSectionEvaluationData(0), FSectionEvaluationData(1) }; Expected[4].Impls = { FSectionEvaluationData(1) }; AssertSegmentValues(this, Expected, EvalTrack.GetSortedSegments()); } // Test compiling without 'evaluate nearest section' enabled { Track->EvalOptions.bEvalNearestSection = false; FMovieSceneEvaluationTrack EvalTrack = Track->GenerateTrackTemplate(); EvalTrack.GetSegmentsInRange(TRange::All()); Expected[0].Impls = { FSectionEvaluationData(1) }; Expected[1].Impls = { FSectionEvaluationData(0), FSectionEvaluationData(1) }; Expected[2].Impls = { FSectionEvaluationData(3), FSectionEvaluationData(0), FSectionEvaluationData(1) }; Expected[3].Impls = { FSectionEvaluationData(2), FSectionEvaluationData(0), FSectionEvaluationData(1) }; Expected[4].Impls = { FSectionEvaluationData(1) }; AssertSegmentValues(this, Expected, EvalTrack.GetSortedSegments()); } // Test compiling with high pass filter { Track->EvalOptions.bEvalNearestSection = false; Track->bHighPassFilter = true; FMovieSceneEvaluationTrack EvalTrack = Track->GenerateTrackTemplate(); EvalTrack.GetSegmentsInRange(TRange::All()); Expected[0].Impls = { FSectionEvaluationData(1) }; Expected[1].Impls = { FSectionEvaluationData(0) }; Expected[2].Impls = { FSectionEvaluationData(3) }; Expected[3].Impls = { FSectionEvaluationData(2) }; Expected[4].Impls = { FSectionEvaluationData(1) }; AssertSegmentValues(this, Expected, EvalTrack.GetSortedSegments()); } } return true; } IMPLEMENT_SIMPLE_AUTOMATION_TEST(FMovieSceneCompilerTreeBasicTest, "System.Engine.Sequencer.Compiler.Tree Basic", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter) bool FMovieSceneCompilerTreeBasicTest::RunTest(const FString& Parameters) { using namespace Impl; // Specify descending priorities on the segments so we always sort the compiled segments in the order they're defined within the data // This is our test layout // Time -inf 10 20 25 30 inf // [============= 0 ===========] // [============== 1 ==================] // [========================== 2 ==================================] // [================== 3 ==========================] // [================== 4 ==========================] //---------------------------------------------------------------------------------------------------------------------------------------- // Expected Impls [ 3 | 0,2,3 | 1,2 | 1,2,4 | 4 ] TMovieSceneEvaluationTree EvalTree; EvalTree.Add(TRange(10, 20), FSectionEvaluationData(0)); EvalTree.Add(TRange(20, 30), FSectionEvaluationData(1)); EvalTree.Add(TRange(10, 30), FSectionEvaluationData(2)); EvalTree.Add(TRange(Inf, TRangeBound::Exclusive(20)), FSectionEvaluationData(3)); EvalTree.Add(TRange(TRangeBound::Inclusive(25), Inf), FSectionEvaluationData(4)); EvalTree.Compact(); TArray Segments; for (FMovieSceneEvaluationTreeRangeIterator It(EvalTree); It; ++It) { FMovieSceneSegment NewSegment(It.Range()); for (FSectionEvaluationData EvalData : EvalTree.GetAllData(It.Node())) { NewSegment.Impls.Add(EvalData); } Algo::SortBy(NewSegment.Impls, &FSectionEvaluationData::ImplIndex); Segments.Add(NewSegment); } FMovieSceneSegment Expected[] = { FMovieSceneSegment(TRange(Inf, TRangeBound::Exclusive(10)), { FSectionEvaluationData(3) }), FMovieSceneSegment(TRange(10, 20), { FSectionEvaluationData(0), FSectionEvaluationData(2), FSectionEvaluationData(3) }), FMovieSceneSegment(TRange(20, 25), { FSectionEvaluationData(1), FSectionEvaluationData(2) }), FMovieSceneSegment(TRange(25, 30), { FSectionEvaluationData(1), FSectionEvaluationData(2), FSectionEvaluationData(4) }), FMovieSceneSegment(TRange(TRangeBound::Inclusive(30), Inf), { FSectionEvaluationData(4) }) }; AssertSegmentValues(this, Expected, Segments); return true; } IMPLEMENT_SIMPLE_AUTOMATION_TEST(FMovieSceneCompilerTreeIteratorTest, "System.Engine.Sequencer.Compiler.Tree Iterator", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter) bool FMovieSceneCompilerTreeIteratorTest::RunTest(const FString& Parameters) { using namespace Impl; // Time -inf 10 20 25 30 inf // [============= 0 ===========] // [============== 1 ==================] // [========================== 2 ==================================] // [================== 3 ==========================] // [================== 4 ==========================] //---------------------------------------------------------------------------------------------------------------------------------------- // Expected Impls [ 3 | 0,2,3 | 1,2 | 1,2,4 | 4 ] TMovieSceneEvaluationTree Tree; Tree.Add(TRange(10, 20), 0); Tree.Add(TRange(20, 30), 1); Tree.Add(TRange(10, 30), 2); Tree.Add(TRange(Inf, TRangeBound::Exclusive(20)), 3); Tree.Add(TRange(TRangeBound::Inclusive(25), Inf), 4); FEvaluationTreeIteratorResult Expected[] = { MakeTuple( TRange(Inf, TRangeBound::Exclusive(10)), TArray({ 3 }) ), MakeTuple( TRange(10, 20), TArray({ 0, 2, 3 }) ), MakeTuple( TRange(20, 25), TArray({ 1, 2 }) ), MakeTuple( TRange(25, 30), TArray({ 1, 2, 4 }) ), MakeTuple( TRange(TRangeBound::Inclusive(30), Inf), TArray({ 4 }) ), }; AssertEvaluationTree(this, Tree, Expected); return true; } IMPLEMENT_SIMPLE_AUTOMATION_TEST(FMovieSceneCompilerTreeIteratorBoundsTest, "System.Engine.Sequencer.Compiler.Tree Iterator Bounds", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter) bool FMovieSceneCompilerTreeIteratorBoundsTest::RunTest(const FString& Parameters) { using namespace Impl; // Time -inf 10 20 25 30 inf // [======== 0 ========][========= 1 ==============][============= 2 ==================][=========== 3 =============] TMovieSceneEvaluationTree Tree; Tree.Add(TRange(Inf, TRangeBound::Exclusive(10)), 0); Tree.Add(TRange(FFrameNumber(10), TRangeBound::Exclusive(20)), 1); Tree.Add(TRange(FFrameNumber(20), TRangeBound::Exclusive(30)), 2); Tree.Add(TRange(TRangeBound::Inclusive(30), Inf), 3); FEvaluationTreeIteratorResult Expected[] = { MakeTuple( TRange(Inf, TRangeBound::Exclusive(10)), TArray({ 0 }) ), MakeTuple( TRange(FFrameNumber(10), TRangeBound::Exclusive(20)), TArray({ 1 }) ), MakeTuple( TRange(FFrameNumber(20), TRangeBound::Exclusive(30)), TArray({ 2 }) ), MakeTuple( TRange(TRangeBound::Inclusive(30), Inf), TArray({ 3 }) ), }; AssertEvaluationTree(this, Tree, Expected); return true; } #endif // WITH_DEV_AUTOMATION_TESTS #endif // #if 0