Files
UnrealEngine/Engine/Source/Runtime/MovieScene/Private/Tests/MovieSceneSegmentCompilerTests.cpp
2025-05-18 13:04:45 +08:00

614 lines
29 KiB
C++

// 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<FFrameNumber> Inf = TRangeBound<FFrameNumber>::Open();
typedef TTuple<TRange<FFrameNumber>, TArray<int32>> FEvaluationTreeIteratorResult;
/** Compiler rules to sort by priority */
struct FSortByPrioritySegmentBlender : FMovieSceneTrackSegmentBlender
{
FSortByPrioritySegmentBlender(bool bInAllowEmptySegments)
{
bAllowEmptySegments = bInAllowEmptySegments;
}
virtual TOptional<FMovieSceneSegment> InsertEmptySpace(const TRange<FFrameNumber>& Range, const FMovieSceneSegment* PreviousSegment, const FMovieSceneSegment* NextSegment) const override
{
return bAllowEmptySegments ? FMovieSceneSegment(Range) : TOptional<FMovieSceneSegment>();
}
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<const FSectionEvaluationData> 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<const FSectionEvaluationData> ExpectedImpls, TArrayView<const FSectionEvaluationData> 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<const FMovieSceneSegment> Expected, TArrayView<const FMovieSceneSegment> 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<const FSectionEvaluationData> 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<FSectionEvaluationData> ExpectedImpls)
{
AssertSegmentAtTime(Test, InTrack, InTime, MakeArrayView(ExpectedImpls));
}
FORCEINLINE_DEBUGGABLE void AssertEvaluationTree(FAutomationTestBase* Test, const TMovieSceneEvaluationTree<int32>& Tree, TArrayView<const FEvaluationTreeIteratorResult> 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<int32>& 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<FFrameNumber>(10, 20), FSectionEvaluationData(0));
Track.AddTreeData(TRange<FFrameNumber>(20, 30), FSectionEvaluationData(1));
Track.AddTreeData(TRange<FFrameNumber>(10, 30), FSectionEvaluationData(2));
Track.AddTreeData(TRange<FFrameNumber>(Inf, TRangeBound<FFrameNumber>::Exclusive(20)), FSectionEvaluationData(3));
Track.AddTreeData(TRange<FFrameNumber>(TRangeBound<FFrameNumber>::Inclusive(25), Inf), FSectionEvaluationData(4));
FMovieSceneSegment Expected[] = {
FMovieSceneSegment(TRange<FFrameNumber>(Inf, TRangeBound<FFrameNumber>::Exclusive(10)), { FSectionEvaluationData(3) }),
FMovieSceneSegment(TRange<FFrameNumber>(10, 20), { FSectionEvaluationData(0), FSectionEvaluationData(2), FSectionEvaluationData(3) }),
FMovieSceneSegment(TRange<FFrameNumber>(20, 25), { FSectionEvaluationData(1), FSectionEvaluationData(2) }),
FMovieSceneSegment(TRange<FFrameNumber>(25, 30), { FSectionEvaluationData(1), FSectionEvaluationData(2), FSectionEvaluationData(4) }),
FMovieSceneSegment(TRange<FFrameNumber>(TRangeBound<FFrameNumber>::Inclusive(30),Inf), { FSectionEvaluationData(4) })
};
// Compile all the segments
Track.GetSegmentsInRange(TRange<FFrameNumber>::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<FFrameNumber>(10, 20), FSectionEvaluationData(0));
Track.AddTreeData(TRange<FFrameNumber>(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<FFrameNumber>::All());
FMovieSceneSegment Expected[] = {
FMovieSceneSegment(TRange<FFrameNumber>(Inf, TRangeBound<FFrameNumber>::Exclusive(10)), { }),
FMovieSceneSegment(TRange<FFrameNumber>(10, 20), { FSectionEvaluationData(0) }),
FMovieSceneSegment(TRange<FFrameNumber>(20, 30), { }),
FMovieSceneSegment(TRange<FFrameNumber>(30, 40), { FSectionEvaluationData(1) }),
FMovieSceneSegment(TRange<FFrameNumber>(TRangeBound<FFrameNumber>::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<FFrameNumber>(TRangeBound<FFrameNumber>::Inclusive(10), TRangeBound<FFrameNumber>::Exclusive(20)), FSectionEvaluationData(0, ESectionEvaluationFlags::PreRoll));
Track.AddUniqueTreeData(TRange<FFrameNumber>(TRangeBound<FFrameNumber>::Inclusive(15), TRangeBound<FFrameNumber>::Exclusive(25)), FSectionEvaluationData(0));
Track.AddUniqueTreeData(TRange<FFrameNumber>(TRangeBound<FFrameNumber>::Inclusive(20), TRangeBound<FFrameNumber>::Exclusive(30)), FSectionEvaluationData(0));
Track.AddUniqueTreeData(TRange<FFrameNumber>(TRangeBound<FFrameNumber>::Inclusive(30), TRangeBound<FFrameNumber>::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<UTestMovieSceneTrack>(GetTransientPackage());
Track->EvalOptions.bCanEvaluateNearestSection = true;
UTestMovieSceneSection* Section0 = NewObject<UTestMovieSceneSection>(Track);
Section0->SetRange(TRange<FFrameNumber>(
TRangeBound<FFrameNumber>::Inclusive(10),
TRangeBound<FFrameNumber>::Exclusive(25)
));
Section0->SetRowIndex(0);
UTestMovieSceneSection* Section1 = NewObject<UTestMovieSceneSection>(Track);
Section1->SetRange(TRange<FFrameNumber>(
TRangeBound<FFrameNumber>::Inclusive(20),
TRangeBound<FFrameNumber>::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<FFrameNumber>::All());
FMovieSceneSegment Expected[] = {
FMovieSceneSegment(TRange<FFrameNumber>(TRangeBound<FFrameNumber>::Inclusive(10), TRangeBound<FFrameNumber>::Exclusive(20)), { FSectionEvaluationData(0) }),
FMovieSceneSegment(TRange<FFrameNumber>(TRangeBound<FFrameNumber>::Inclusive(20), TRangeBound<FFrameNumber>::Exclusive(25)), { FSectionEvaluationData(0), FSectionEvaluationData(1) }),
FMovieSceneSegment(TRange<FFrameNumber>(TRangeBound<FFrameNumber>::Inclusive(25), TRangeBound<FFrameNumber>::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<FFrameNumber>::All());
FMovieSceneSegment Expected[] = {
FMovieSceneSegment(TRange<FFrameNumber>(Inf, TRangeBound<FFrameNumber>::Exclusive(10)), { FSectionEvaluationData(0, 10) }),
FMovieSceneSegment(TRange<FFrameNumber>(TRangeBound<FFrameNumber>::Inclusive(10), TRangeBound<FFrameNumber>::Exclusive(20)), { FSectionEvaluationData(0) }),
FMovieSceneSegment(TRange<FFrameNumber>(TRangeBound<FFrameNumber>::Inclusive(20), TRangeBound<FFrameNumber>::Exclusive(25)), { FSectionEvaluationData(0), FSectionEvaluationData(1) }),
FMovieSceneSegment(TRange<FFrameNumber>(TRangeBound<FFrameNumber>::Inclusive(25), TRangeBound<FFrameNumber>::Exclusive(30)), { FSectionEvaluationData(1) }),
FMovieSceneSegment(TRange<FFrameNumber>(TRangeBound<FFrameNumber>::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<FFrameNumber>::All());
FMovieSceneSegment Expected[] = {
FMovieSceneSegment(TRange<FFrameNumber>(TRangeBound<FFrameNumber>::Inclusive(10), TRangeBound<FFrameNumber>::Exclusive(20)), { FSectionEvaluationData(0) }),
FMovieSceneSegment(TRange<FFrameNumber>(TRangeBound<FFrameNumber>::Inclusive(20), TRangeBound<FFrameNumber>::Exclusive(25)), { FSectionEvaluationData(0), FSectionEvaluationData(1) }),
FMovieSceneSegment(TRange<FFrameNumber>(TRangeBound<FFrameNumber>::Inclusive(25), TRangeBound<FFrameNumber>::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<FFrameNumber>::All());
FMovieSceneSegment Expected[] = {
FMovieSceneSegment(TRange<FFrameNumber>(TRangeBound<FFrameNumber>::Inclusive(10), TRangeBound<FFrameNumber>::Exclusive(25)), { FSectionEvaluationData(0) }),
FMovieSceneSegment(TRange<FFrameNumber>(TRangeBound<FFrameNumber>::Inclusive(25), TRangeBound<FFrameNumber>::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<UTestMovieSceneTrack>(GetTransientPackage());
UTestMovieSceneSection* Section0 = NewObject<UTestMovieSceneSection>(Track);
Section0->SetRange(TRange<FFrameNumber>(10, 30));
Section0->SetRowIndex(1);
UTestMovieSceneSection* Section1 = NewObject<UTestMovieSceneSection>(Track);
Section1->SetRange(TRange<FFrameNumber>::All());
Section1->SetRowIndex(2);
UTestMovieSceneSection* Section2 = NewObject<UTestMovieSceneSection>(Track);
Section2->SetRange(TRange<FFrameNumber>(20, 30));
Section2->SetRowIndex(0);
UTestMovieSceneSection* Section3 = NewObject<UTestMovieSceneSection>(Track);
Section3->SetRange(TRange<FFrameNumber>(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<FFrameNumber>(Inf, TRangeBound<FFrameNumber>::Exclusive(10)), { }),
FMovieSceneSegment(TRange<FFrameNumber>(TRangeBound<FFrameNumber>::Inclusive(10), TRangeBound<FFrameNumber>::Exclusive(15)), { }),
FMovieSceneSegment(TRange<FFrameNumber>(TRangeBound<FFrameNumber>::Inclusive(15), TRangeBound<FFrameNumber>::Exclusive(25)), { }),
FMovieSceneSegment(TRange<FFrameNumber>(TRangeBound<FFrameNumber>::Inclusive(25), TRangeBound<FFrameNumber>::Exclusive(30)), { }),
FMovieSceneSegment(TRange<FFrameNumber>(TRangeBound<FFrameNumber>::Inclusive(30), Inf), { }),
};
// Test compiling the track with the additive camera rules
{
FScopedOverrideTrackSegmentBlender ScopedBlenderOverride{FMovieSceneAdditiveCameraTrackBlender()};
FMovieSceneEvaluationTrack EvalTrack = Track->GenerateTrackTemplate();
EvalTrack.GetSegmentsInRange(TRange<FFrameNumber>::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<FFrameNumber>::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<FFrameNumber>::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<FFrameNumber>::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<FSectionEvaluationData> EvalTree;
EvalTree.Add(TRange<FFrameNumber>(10, 20), FSectionEvaluationData(0));
EvalTree.Add(TRange<FFrameNumber>(20, 30), FSectionEvaluationData(1));
EvalTree.Add(TRange<FFrameNumber>(10, 30), FSectionEvaluationData(2));
EvalTree.Add(TRange<FFrameNumber>(Inf, TRangeBound<FFrameNumber>::Exclusive(20)), FSectionEvaluationData(3));
EvalTree.Add(TRange<FFrameNumber>(TRangeBound<FFrameNumber>::Inclusive(25), Inf), FSectionEvaluationData(4));
EvalTree.Compact();
TArray<FMovieSceneSegment> 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<FFrameNumber>(Inf, TRangeBound<FFrameNumber>::Exclusive(10)), { FSectionEvaluationData(3) }),
FMovieSceneSegment(TRange<FFrameNumber>(10, 20), { FSectionEvaluationData(0), FSectionEvaluationData(2), FSectionEvaluationData(3) }),
FMovieSceneSegment(TRange<FFrameNumber>(20, 25), { FSectionEvaluationData(1), FSectionEvaluationData(2) }),
FMovieSceneSegment(TRange<FFrameNumber>(25, 30), { FSectionEvaluationData(1), FSectionEvaluationData(2), FSectionEvaluationData(4) }),
FMovieSceneSegment(TRange<FFrameNumber>(TRangeBound<FFrameNumber>::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<int32> Tree;
Tree.Add(TRange<FFrameNumber>(10, 20), 0);
Tree.Add(TRange<FFrameNumber>(20, 30), 1);
Tree.Add(TRange<FFrameNumber>(10, 30), 2);
Tree.Add(TRange<FFrameNumber>(Inf, TRangeBound<FFrameNumber>::Exclusive(20)), 3);
Tree.Add(TRange<FFrameNumber>(TRangeBound<FFrameNumber>::Inclusive(25), Inf), 4);
FEvaluationTreeIteratorResult Expected[] = {
MakeTuple( TRange<FFrameNumber>(Inf, TRangeBound<FFrameNumber>::Exclusive(10)), TArray<int32>({ 3 }) ),
MakeTuple( TRange<FFrameNumber>(10, 20), TArray<int32>({ 0, 2, 3 }) ),
MakeTuple( TRange<FFrameNumber>(20, 25), TArray<int32>({ 1, 2 }) ),
MakeTuple( TRange<FFrameNumber>(25, 30), TArray<int32>({ 1, 2, 4 }) ),
MakeTuple( TRange<FFrameNumber>(TRangeBound<FFrameNumber>::Inclusive(30), Inf), TArray<int32>({ 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<int32> Tree;
Tree.Add(TRange<FFrameNumber>(Inf, TRangeBound<FFrameNumber>::Exclusive(10)), 0);
Tree.Add(TRange<FFrameNumber>(FFrameNumber(10), TRangeBound<FFrameNumber>::Exclusive(20)), 1);
Tree.Add(TRange<FFrameNumber>(FFrameNumber(20), TRangeBound<FFrameNumber>::Exclusive(30)), 2);
Tree.Add(TRange<FFrameNumber>(TRangeBound<FFrameNumber>::Inclusive(30), Inf), 3);
FEvaluationTreeIteratorResult Expected[] = {
MakeTuple( TRange<FFrameNumber>(Inf, TRangeBound<FFrameNumber>::Exclusive(10)), TArray<int32>({ 0 }) ),
MakeTuple( TRange<FFrameNumber>(FFrameNumber(10), TRangeBound<FFrameNumber>::Exclusive(20)), TArray<int32>({ 1 }) ),
MakeTuple( TRange<FFrameNumber>(FFrameNumber(20), TRangeBound<FFrameNumber>::Exclusive(30)), TArray<int32>({ 2 }) ),
MakeTuple( TRange<FFrameNumber>(TRangeBound<FFrameNumber>::Inclusive(30), Inf), TArray<int32>({ 3 }) ),
};
AssertEvaluationTree(this, Tree, Expected);
return true;
}
#endif // WITH_DEV_AUTOMATION_TESTS
#endif // #if 0