Files
UnrealEngine/Engine/Source/Runtime/MovieSceneTracks/Private/Tests/MovieSceneCompiledDataManagerTests.cpp
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

169 lines
5.4 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "CoreTypes.h"
#include "Compilation/MovieSceneCompiledDataManager.h"
#include "Conditions/MovieScenePlatformCondition.h"
#include "Engine/Engine.h"
#include "Misc/AutomationTest.h"
#include "Sections/MovieSceneFadeSection.h"
#include "Sections/MovieSceneSubSection.h"
#include "Sections/MovieSceneTimeWarpSection.h"
#include "Tests/MovieSceneTestDataBuilders.h"
#include "Tracks/MovieSceneFadeTrack.h"
#include "Tracks/MovieSceneSubTrack.h"
#include "Tracks/MovieSceneTimeWarpTrack.h"
#include "UObject/Package.h"
#include "Variants/MovieScenePlayRateCurve.h"
#if WITH_DEV_AUTOMATION_TESTS
#define LOCTEXT_NAMESPACE "MovieSceneCompiledDataManagerTests"
IMPLEMENT_SIMPLE_AUTOMATION_TEST(FMovieSceneCompiledDataManagerGCReferenceTest,
"System.Engine.Sequencer.Compilation.GCReferenceTests",
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
bool FMovieSceneCompiledDataManagerGCReferenceTest::RunTest(const FString& Parameters)
{
using namespace UE::MovieScene;
using namespace UE::MovieScene::Test;
TStrongObjectPtr<UMovieSceneCompiledDataManager> CompiledData(
NewObject<UMovieSceneCompiledDataManager>(GetTransientPackage(), "GCReferenceTest")
);
UMovieSceneTimeWarpSection* TimeWarpSection = nullptr;
UMovieSceneSubSection* SubSection = nullptr;
UMovieSceneFadeSection* FadeSection = nullptr;
FSequenceBuilder RootSequenceBuilder;
{
RootSequenceBuilder
.AddRootTrack<UMovieSceneTimeWarpTrack>()
.AddSection(0, 96000)
.Assign(TimeWarpSection)
.Pop()
.Pop()
.AddRootTrack<UMovieSceneFadeTrack>()
.AddSection(0, 96000)
.Assign(FadeSection)
.Pop()
.Pop()
.AddRootTrack<UMovieSceneSubTrack>()
.AddSection(0, 2400)
.Assign(SubSection)
.Pop()
.Pop();
}
// Add a condition to ensure that they do not hold a strong reference in the compiled data
FadeSection->ConditionContainer.Condition = NewObject<UMovieScenePlatformCondition>(FadeSection);
// Build an empty sub sequence - we only need this so we can create a hierarchy and shove
// its timewarp into the compiled data manager for testing
FSequenceBuilder SubSequenceBuilder;
TWeakObjectPtr<UMovieSceneSequence> WeakRootSequence = RootSequenceBuilder.Sequence;
TWeakObjectPtr<UMovieSceneSequence> WeakSubSequence = SubSequenceBuilder.Sequence;
TWeakObjectPtr<UMovieScenePlayRateCurve> WeakRootTimeWarp;
TWeakObjectPtr<UMovieScenePlayRateCurve> WeakSubTimeWarp;
// Add root time warp
{
UMovieScenePlayRateCurve* PlayRate = NewObject<UMovieScenePlayRateCurve>(TimeWarpSection);
WeakRootTimeWarp = PlayRate;
TMovieSceneChannelData<FMovieSceneDoubleValue> ChannelData = PlayRate->PlayRate.GetData();
ChannelData.AddKey(0, FMovieSceneDoubleValue(1.0));
ChannelData.AddKey(24000, FMovieSceneDoubleValue(0.5));
ChannelData.AddKey(48000, FMovieSceneDoubleValue(1.0));
TimeWarpSection->TimeWarp.Set(PlayRate);
}
// Setup the subsequence and time warp
{
SubSection->SetSequence(SubSequenceBuilder.Sequence);
UMovieScenePlayRateCurve* SubPlayRate = NewObject<UMovieScenePlayRateCurve>(SubSection);
WeakSubTimeWarp = SubPlayRate;
TMovieSceneChannelData<FMovieSceneDoubleValue> ChannelData = SubPlayRate->PlayRate.GetData();
ChannelData.AddKey(0, FMovieSceneDoubleValue(0.25));
SubSection->Parameters.TimeScale.Set(SubPlayRate);
}
// Compile the sequence, run a GC (which should clean up all the sequences), and verify that the compiled data manager has removed everything
if (WeakRootSequence.Get() == nullptr
|| WeakSubSequence.Get() == nullptr
|| WeakRootTimeWarp.Get() == nullptr
|| WeakSubTimeWarp.Get() == nullptr)
{
AddError(TEXT("Test failed to initialize weak pointers correctly."));
return false;
}
CompiledData->Compile(RootSequenceBuilder.Sequence);
FMovieSceneCompiledDataID RootDataID = CompiledData->FindDataID(RootSequenceBuilder.Sequence);
FMovieSceneCompiledDataID SubDataID = CompiledData->FindDataID(SubSequenceBuilder.Sequence);
if (!RootDataID.IsValid() || !SubDataID.IsValid())
{
AddError(TEXT("Sequence was not compiled successfully."));
return false;
}
if (CompiledData->FindHierarchy(RootDataID) == nullptr)
{
AddError(TEXT("Sequence did not compile a hierarchy when it should have."));
return false;
}
CollectGarbage(GARBAGE_COLLECTION_KEEPFLAGS, true);
if (WeakRootSequence.Get() != nullptr
|| WeakSubSequence.Get() != nullptr
|| WeakRootTimeWarp.Get() != nullptr
|| WeakSubTimeWarp.Get() != nullptr)
{
if (UObject* Object = WeakRootSequence.Get())
{
GEngine->Exec(nullptr, *FString::Printf(TEXT("obj refs name=\"%s\""), *Object->GetPathName()));
}
if (UObject* Object = WeakSubSequence.Get())
{
GEngine->Exec(nullptr, *FString::Printf(TEXT("obj refs name=\"%s\""), *Object->GetPathName()));
}
if (UObject* Object = WeakRootTimeWarp.Get())
{
GEngine->Exec(nullptr, *FString::Printf(TEXT("obj refs name=\"%s\""), *Object->GetPathName()));
}
if (UObject* Object = WeakSubTimeWarp.Get())
{
GEngine->Exec(nullptr, *FString::Printf(TEXT("obj refs name=\"%s\""), *Object->GetPathName()));
}
AddError(TEXT("Objects were unexpectedly still alive after GC please see logs for references."));
return false;
}
if (CompiledData->FindHierarchy(RootDataID) != nullptr)
{
AddError(TEXT("Compiled data was not correctly cleaned up."));
return false;
}
return true;
}
#undef LOCTEXT_NAMESPACE
#endif // WITH_DEV_AUTOMATION_TESTS