Files
UnrealEngine/Engine/Plugins/Experimental/SubtitlesAndClosedCaptions/Source/SubtitlesAndClosedCaptionsTest/Private/SubtitlesAndClosedCaptionsMovieSceneTests.cpp
2025-05-18 13:04:45 +08:00

178 lines
9.0 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Misc/AutomationTest.h"
#include "Engine/World.h"
#include "MovieSceneSubtitlesSystem.h"
#include "Subtitles/SubtitlesAndClosedCaptionsDelegates.h"
#include "SubtitlesSubsystem.h"
#include "UObject/ObjectMacros.h"
#include "UObject/Package.h"
#include "UObject/UObjectGlobals.h"
#include "TimerManager.h"
#include "SubtitlesAndClosedCaptions/Public/MovieSceneSubtitlesSystem.h" // included to test internal FEvaluateSubtitles struct
#if 0 // Temporarily disabling these tests as they have a dangling reference that trips up the static analysis on certain build configurations.
#if WITH_DEV_AUTOMATION_TESTS
using namespace UE::MovieScene;
namespace SubtitlesAndClosedCaptions::Test
{
const FFrameRate TestFrameRate(30, 1);
constexpr float SubtitleDurationSeconds = 4.f;
constexpr int SubtitleDurationFrames = SubtitleDurationSeconds * 30;
// Helper struct for initializing tests so the same thing doesn't have to be done in every class
struct FMovieSceneSubtitlesTest
{
FMovieSceneSubtitlesTest()
: World(NewObject<UWorld>(GetTransientPackage(), NAME_None, RF_Transient))
, Subsystem(NewObject<USubtitlesSubsystem>(World, NAME_None, RF_Transient))
, Subtitle(NewObject<USubtitleAssetUserData>(GetTransientPackage(), NAME_None, RF_Transient))
, Section(NewObject<UMovieSceneSubtitleSection>(GetTransientPackage(), NAME_None, RF_Transient))
, PlaybackState(MakeShared<FSharedPlaybackState>())
, SequenceInstance(PlaybackState.ToSharedRef())
, SubtitleData({ Section })
, EvaluateTask(SequenceInstance.GetSharedPlaybackState()->GetLinker())
{
Subtitle->Duration = SubtitleDurationSeconds;
Section->SetRange(TRange<FFrameNumber>(1, SubtitleDurationFrames));
Subsystem->BindDelegates();
Section->SetSubtitle(*Subtitle);
// Reset TSharedRef FPlaybackState to avoid tripping an ensure in FSequenceInstance
// Necessary because the tests are only using as little of the MovieScene system as possible
PlaybackState.Reset();
}
UPROPERTY(Transient)
UWorld* World = nullptr;
UPROPERTY(Transient)
USubtitlesSubsystem* Subsystem = nullptr;
UPROPERTY(Transient)
USubtitleAssetUserData* Subtitle = nullptr;
UPROPERTY(Transient)
UMovieSceneSubtitleSection* Section = nullptr;
UPROPERTY(Transient)
TSharedPtr<FSharedPlaybackState> PlaybackState;
FSequenceInstance SequenceInstance;
FSubtitleDataComponent SubtitleData;
EMovieScenePlayerStatus::Type LastStatus;
FEvaluateSubtitles EvaluateTask;
};
// Verify base case that a subtitle can be queued (and therefore displayed) by the subtitle subsystem
// Ensures basic functionality that subtitles can actually be added to the subsystem.
// Test content is all transient and uses a new USubtitlesSubsystem that has no UWorld.
IMPLEMENT_SIMPLE_AUTOMATION_TEST(UMovieSceneSubtitlesSystem_EvaluateForwardsQueuesSubtitle, "Subtitles.MovieSceneSubtitlesSystem.EvaluateForwardsQueuesSubtitle", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
bool UMovieSceneSubtitlesSystem_EvaluateForwardsQueuesSubtitle::RunTest(const FString&)
{
FMovieSceneSubtitlesTest Test;
// Evaluate a playing frame.
const EMovieScenePlayerStatus::Type CurrentStatus = EMovieScenePlayerStatus::Type::Playing;
Test.SequenceInstance.SetContext(FMovieSceneContext(FMovieSceneEvaluationRange(FFrameTime(1), TestFrameRate), CurrentStatus));
Test.EvaluateTask.Evaluate(Test.SequenceInstance, Test.SubtitleData, Test.LastStatus);
const FActiveSubtitle* FoundSubtitle = Test.Subsystem->GetActiveSubtitles().FindByPredicate(
[Subtitle = Test.Subtitle](const FActiveSubtitle& ActiveSubtitle) {return Subtitle == ActiveSubtitle.Subtitle; });
UTEST_NOT_NULL("Subtitle is active", FoundSubtitle);
return true;
}
// Verify that all subtitles can be stopped by the subtitle movie scene system
// Ensures that when playing/stopping/scrubbing in editor that any active subtitles can be reset on movie scene status change.
// e.g. When scrubbing through sequencer during evaluation subtitles can be enqueued. Stop all of these and start fresh when playing forwards.
// Test content is all transient and uses a new USubtitlesSubsystem that has no UWorld.
IMPLEMENT_SIMPLE_AUTOMATION_TEST(UMovieSceneSubtitlesSystem_EvaluateHasStartedStopsAllSubtitles, "Subtitles.MovieSceneSubtitlesSystem.EvaluateHasStartedStopsAllSubtitles", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
bool UMovieSceneSubtitlesSystem_EvaluateHasStartedStopsAllSubtitles::RunTest(const FString&)
{
FMovieSceneSubtitlesTest Test;
const USubtitleAssetUserData* Subtitle1 = NewObject<USubtitleAssetUserData>(GetTransientPackage(), NAME_None, RF_Transient);
const USubtitleAssetUserData* Subtitle2 = NewObject<USubtitleAssetUserData>(GetTransientPackage(), NAME_None, RF_Transient);
const USubtitleAssetUserData* Subtitle3 = NewObject<USubtitleAssetUserData>(GetTransientPackage(), NAME_None, RF_Transient);
Test.Subsystem->QueueSubtitle(FQueueSubtitleParameters{ *Subtitle1 });
Test.Subsystem->QueueSubtitle(FQueueSubtitleParameters{ *Subtitle2 });
Test.Subsystem->QueueSubtitle(FQueueSubtitleParameters{ *Subtitle3 });
UTEST_EQUAL("All subtitles active", Test.Subsystem->GetActiveSubtitles().Num(), 3);
Test.LastStatus = EMovieScenePlayerStatus::Type::Stopped;
EMovieScenePlayerStatus::Type CurrentStatus = EMovieScenePlayerStatus::Type::Playing;
Test.SequenceInstance.SetContext(FMovieSceneContext(FMovieSceneEvaluationRange(FFrameTime(0), TestFrameRate), CurrentStatus));
Test.EvaluateTask.Evaluate(Test.SequenceInstance, Test.SubtitleData, Test.LastStatus);
const FActiveSubtitle* FoundSubtitle = Test.Subsystem->GetActiveSubtitles().FindByPredicate(
[Subtitle = Test.Subtitle](const FActiveSubtitle& ActiveSubtitle) {return Subtitle == ActiveSubtitle.Subtitle; });
UTEST_NOT_NULL("All subtitles removed before queueing new subtitle", FoundSubtitle);
return true;
}
// Verify that all subtitles can be displayed indefinitely
// Ensures that when you pause on an active subtitle that its duration does not expire
// Test content is all transient and uses a new USubtitlesSubsystem that has no UWorld.
IMPLEMENT_SIMPLE_AUTOMATION_TEST(UMovieSceneSubtitlesSystem_EvaluateStoppedQueuesInfiniteDuration, "Subtitles.MovieSceneSubtitlesSystem.EvaluateStoppedQueuesInfiniteDuration", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
bool UMovieSceneSubtitlesSystem_EvaluateStoppedQueuesInfiniteDuration::RunTest(const FString&)
{
FMovieSceneSubtitlesTest Test;
Test.LastStatus = EMovieScenePlayerStatus::Type::Playing;
EMovieScenePlayerStatus::Type CurrentStatus = EMovieScenePlayerStatus::Type::Stopped;
Test.SequenceInstance.SetContext(FMovieSceneContext(FMovieSceneEvaluationRange(FFrameTime(30), TestFrameRate), CurrentStatus));
Test.EvaluateTask.Evaluate(Test.SequenceInstance, Test.SubtitleData, Test.LastStatus);
UTEST_TRUE("A Subtitle is active", !Test.Subsystem->GetActiveSubtitles().IsEmpty());
const float ExpectedDuration = FLT_MAX;
const float ActualDuration = Test.World->GetTimerManager().GetTimerRemaining(Test.Subsystem->GetActiveSubtitles()[0].DurationTimerHandle);
UTEST_EQUAL_TOLERANCE("Subtitle played mid-way is queued with remaining duration", ActualDuration, ExpectedDuration, FLT_EPSILON);
return true;
}
// Verify that the subtitle section only queues its subtitle once
// Ensures that the subtitle section doesn't spam the subtitle subsystem
// Test content is all transient and uses a new USubtitlesSubsystem that has no UWorld.
IMPLEMENT_SIMPLE_AUTOMATION_TEST(UMovieSceneSubtitlesSystem_EvaluateDontQueueIfSubtitleAlreadyActive, "Subtitles.MovieSceneSubtitlesSystem.EvaluateDontQueueIfSubtitleAlreadyActive", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)
bool UMovieSceneSubtitlesSystem_EvaluateDontQueueIfSubtitleAlreadyActive::RunTest(const FString&)
{
FMovieSceneSubtitlesTest Test;
Test.Subtitle->Duration = 1.f;
EMovieScenePlayerStatus::Type CurrentStatus = EMovieScenePlayerStatus::Type::Playing;
Test.SequenceInstance.SetContext(FMovieSceneContext(FMovieSceneEvaluationRange(FFrameTime(0), TestFrameRate), CurrentStatus));
Test.EvaluateTask.Evaluate(Test.SequenceInstance, Test.SubtitleData, Test.LastStatus);
Test.SequenceInstance.SetContext(FMovieSceneContext(FMovieSceneEvaluationRange(FFrameTime(1), TestFrameRate), CurrentStatus));
Test.EvaluateTask.Evaluate(Test.SequenceInstance, Test.SubtitleData, Test.LastStatus);
Test.SequenceInstance.SetContext(FMovieSceneContext(FMovieSceneEvaluationRange(FFrameTime(2), TestFrameRate), CurrentStatus));
Test.EvaluateTask.Evaluate(Test.SequenceInstance, Test.SubtitleData, Test.LastStatus);
Test.SequenceInstance.SetContext(FMovieSceneContext(FMovieSceneEvaluationRange(FFrameTime(3), TestFrameRate), CurrentStatus));
Test.EvaluateTask.Evaluate(Test.SequenceInstance, Test.SubtitleData, Test.LastStatus);
UTEST_EQUAL("SubtitleSection's Subtitle is only queued once", Test.Subsystem->GetActiveSubtitles().Num(), 1);
return true;
}
} // namespace SubtitlesAndClosedCaptions::Test
#endif
#endif