using System; using System.Collections; using System.Collections.Generic; using NUnit.Framework; using UnityEngine; using UnityEngine.TestTools; using UnityEngine.UI; namespace AibisDream.FrameAnimation.Tests.PlayMode { public sealed class FrameAnimationPlayerTests { private readonly List createdObjects = new List(); [TearDown] public void TearDown() { foreach (var createdObject in createdObjects) { if (createdObject != null) { UnityEngine.Object.DestroyImmediate(createdObject); } } createdObjects.Clear(); } [Test] public void SpriteRenderer_PlayerShowsFirstFrameAndCompletesWithClear() { var first = CreateSprite(Color.red); var second = CreateSprite(Color.blue); var clip = CreateClip("Clip", FrameClipEndBehavior.Clear, first, second); var graph = CreateDirectGraph(clip); var gameObject = Track(new GameObject("Sprite Player")); var renderer = gameObject.AddComponent(); var player = gameObject.AddComponent(); player.ConfigureForAuthoring(graph, false, 1f); var handle = player.Play(clip.Id); Assert.That(renderer.sprite, Is.EqualTo(first)); player.EvaluateForTests(0.2d); Assert.That(handle.IsCompleted, Is.True); Assert.That(handle.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Completed)); Assert.That(renderer.sprite, Is.Null); Assert.That(renderer.enabled, Is.True); } [Test] public void FrameClipPlayer_PlaysDefaultClipWithoutGraph() { var first = CreateSprite(Color.magenta); var second = CreateSprite(Color.cyan); var clip = CreateClip("Standalone", FrameClipEndBehavior.HoldLastFrame, first, second); var gameObject = Track(new GameObject("Standalone Clip Player")); var renderer = gameObject.AddComponent(); var player = gameObject.AddComponent(); player.ConfigureForAuthoring(clip, false, 1f); var handle = player.Play(); Assert.That(renderer.sprite, Is.SameAs(first)); Assert.That(player.Clip, Is.SameAs(clip)); Assert.That(player.CurrentClip, Is.SameAs(clip)); player.EvaluateForTests(0.2d); Assert.That(handle.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Completed)); Assert.That(renderer.sprite, Is.SameAs(second)); } [Test] public void FrameClipPlayer_TemporaryClipDoesNotReplaceDefaultAndFailedPlayKeepsActive() { var defaultClip = CreateClip("Default", FrameClipEndBehavior.Loop, CreateSprite(Color.red)); var temporary = CreateClip("Temporary", FrameClipEndBehavior.Loop, CreateSprite(Color.blue)); var invalid = Track(ScriptableObject.CreateInstance()); var gameObject = Track(new GameObject("Temporary Clip Player")); gameObject.AddComponent(); var player = gameObject.AddComponent(); player.ConfigureForAuthoring(defaultClip, false, 1f); var active = player.Play(temporary); var failed = player.Play(invalid); Assert.That(failed.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Failed)); Assert.That(active.IsCompleted, Is.False); Assert.That(player.Clip, Is.SameAs(defaultClip)); Assert.That(player.CurrentClip, Is.SameAs(temporary)); } [Test] public void FrameClipPlayer_RestoreTerminalStateAppliesLastFrame() { var first = CreateSprite(Color.red); var last = CreateSprite(Color.green); var clip = CreateClip("RestoreStandalone", FrameClipEndBehavior.HoldLastFrame, first, last); var gameObject = Track(new GameObject("Restore Standalone")); var renderer = gameObject.AddComponent(); var player = gameObject.AddComponent(); player.ConfigureForAuthoring(clip, false, 1f); var handle = player.RestoreTerminalState(); Assert.That(handle.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Completed)); Assert.That(renderer.sprite, Is.SameAs(last)); } [Test] public void FrameClipPlayer_ImageLoopWaitsForFirstPassAndKeepsPlaying() { var first = CreateSprite(Color.yellow); var second = CreateSprite(Color.black); var clip = CreateClip("StandaloneLoop", FrameClipEndBehavior.Loop, first, second); var gameObject = Track(new GameObject("Standalone Image", typeof(RectTransform), typeof(CanvasRenderer))); var image = gameObject.AddComponent(); var player = gameObject.AddComponent(); player.ConfigureForAuthoring(clip, false, 1f); var handle = player.Play(); var wait = player.WaitForFirstPass(handle); player.EvaluateForTests(0.2d); Assert.That(wait.keepWaiting, Is.False); Assert.That(handle.IsCompleted, Is.False); Assert.That(player.State, Is.EqualTo(FrameAnimationPlaybackState.Playing)); Assert.That(image.sprite, Is.SameAs(first)); } [Test] public void GraphAndClipPlayersOnSameObjectReturnConflict() { var clip = CreateClip("ConflictClip", FrameClipEndBehavior.Loop, CreateSprite(Color.white)); var graph = CreateDirectGraph(clip); var gameObject = Track(new GameObject("Player Conflict")); gameObject.AddComponent(); var graphPlayer = gameObject.AddComponent(); graphPlayer.ConfigureForAuthoring(graph, false, 1f); var clipPlayer = gameObject.AddComponent(); clipPlayer.ConfigureForAuthoring(clip, false, 1f); Assert.That(graphPlayer.Play().Result.Error.Code, Is.EqualTo(FrameAnimationPlaybackErrorCode.TargetConflict)); Assert.That(clipPlayer.Play().Result.Error.Code, Is.EqualTo(FrameAnimationPlaybackErrorCode.TargetConflict)); } [Test] public void Image_PlayerSupportsHideAndNextPlayReEnablesTarget() { var sprite = CreateSprite(Color.green); var clip = CreateClip("Clip", FrameClipEndBehavior.HideTarget, sprite); var graph = CreateDirectGraph(clip); var gameObject = Track(new GameObject("Image Player", typeof(RectTransform), typeof(CanvasRenderer))); var image = gameObject.AddComponent(); var player = gameObject.AddComponent(); player.ConfigureForAuthoring(graph, false, 1f); player.Play(clip.Id); player.EvaluateForTests(0.1d); Assert.That(image.enabled, Is.False); player.Play(clip.Id, new FrameAnimationPlayOptions(FrameClipEndBehavior.HoldLastFrame)); Assert.That(image.enabled, Is.True); Assert.That(image.sprite, Is.EqualTo(sprite)); } [Test] public void FailedPlayDoesNotReplaceActiveRequest() { var clip = CreateClip("Loop", FrameClipEndBehavior.Loop, CreateSprite(Color.white)); var graph = CreateDirectGraph(clip); var gameObject = Track(new GameObject("Player")); gameObject.AddComponent(); var player = gameObject.AddComponent(); player.ConfigureForAuthoring(graph, false, 1f); var active = player.Play(clip.Id); var failed = player.Play("Missing"); Assert.That(failed.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Failed)); Assert.That(failed.Result.Error.Code, Is.EqualTo(FrameAnimationPlaybackErrorCode.PlayableNotFound)); Assert.That(active.IsCompleted, Is.False); Assert.That(player.State, Is.EqualTo(FrameAnimationPlaybackState.Playing)); Assert.That(player.CurrentPlayableId, Is.EqualTo(clip.Id)); } [Test] public void SuccessfulPlayReplacesPreviousRequestExactlyOnce() { var first = CreateClip("First", FrameClipEndBehavior.Loop, CreateSprite(Color.white)); var second = CreateClip("Second", FrameClipEndBehavior.Loop, CreateSprite(Color.black)); var graph = Track(ScriptableObject.CreateInstance()); graph.Configure( "Graph", "Graph", new[] { first, second }, Array.Empty(), Array.Empty(), Array.Empty(), first.Id); var gameObject = Track(new GameObject("Replace")); gameObject.AddComponent(); var player = gameObject.AddComponent(); player.ConfigureForAuthoring(graph, false, 1f); var firstHandle = player.Play(first.Id); var secondHandle = player.Play(second.Id); Assert.That(firstHandle.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Replaced)); Assert.That(secondHandle.IsCompleted, Is.False); Assert.That(player.CurrentPlayableId, Is.EqualTo(second.Id)); } [Test] public void WaitForFirstPass_DirectLoopReleasesAfterFirstWrapAndKeepsPlaying() { var first = CreateSprite(Color.red); var second = CreateSprite(Color.blue); var clip = CreateClip("Loop", FrameClipEndBehavior.Loop, first, second); var graph = CreateDirectGraph(clip); var gameObject = Track(new GameObject("Wait Direct Loop")); var renderer = gameObject.AddComponent(); var player = gameObject.AddComponent(); player.ConfigureForAuthoring(graph, false, 1f); var handle = player.Play(clip.Id); var wait = player.WaitForFirstPass(handle); Assert.That(wait.keepWaiting, Is.True); player.EvaluateForTests(0.1d); Assert.That(wait.keepWaiting, Is.True); player.EvaluateForTests(0.1d); Assert.That(wait.keepWaiting, Is.False); Assert.That(handle.IsCompleted, Is.False); Assert.That(player.State, Is.EqualTo(FrameAnimationPlaybackState.Playing)); Assert.That(player.CurrentFrameIndex, Is.EqualTo(0)); Assert.That(renderer.sprite, Is.SameAs(first)); } [Test] public void WaitForFirstPass_FlowWaitsForIntroAndOneTerminalLoopCycle() { var introClip = CreateClip( "Intro", FrameClipEndBehavior.HoldLastFrame, CreateSprite(Color.red)); var loopFirst = CreateSprite(Color.green); var loopClip = CreateClip( "Loop", FrameClipEndBehavior.Loop, loopFirst, CreateSprite(Color.blue)); var introNode = new AnimationNode(introClip.Id); var loopNode = new AnimationNode(loopClip.Id); var edge = new AnimationEdge(introNode.InternalId, loopNode.InternalId); var flow = new AnimationFlow("Flow", "Flow", introNode.InternalId); var graph = Track(ScriptableObject.CreateInstance()); graph.Configure( "Graph", "Graph", new[] { introClip, loopClip }, new[] { introNode, loopNode }, new[] { edge }, new[] { flow }, flow.Id); var gameObject = Track(new GameObject("Wait Flow Loop")); var renderer = gameObject.AddComponent(); var player = gameObject.AddComponent(); player.ConfigureForAuthoring(graph, false, 1f); var handle = player.Play(flow.Id); var wait = player.WaitForFirstPass(handle); player.EvaluateForTests(0.1d); Assert.That(player.CurrentClipId, Is.EqualTo(loopClip.Id)); Assert.That(wait.keepWaiting, Is.True); player.EvaluateForTests(0.2d); Assert.That(wait.keepWaiting, Is.False); Assert.That(handle.IsCompleted, Is.False); Assert.That(player.State, Is.EqualTo(FrameAnimationPlaybackState.Playing)); Assert.That(player.CurrentFrameIndex, Is.EqualTo(0)); Assert.That(renderer.sprite, Is.SameAs(loopFirst)); } [Test] public void WaitForFirstPass_LargeDeltaCrossesIntroAndTerminalLoopCycle() { var introClip = CreateClip( "Intro", FrameClipEndBehavior.HoldLastFrame, CreateSprite(Color.red)); var loopClip = CreateClip( "Loop", FrameClipEndBehavior.Loop, CreateSprite(Color.green), CreateSprite(Color.blue)); var introNode = new AnimationNode(introClip.Id); var loopNode = new AnimationNode(loopClip.Id); var graph = Track(ScriptableObject.CreateInstance()); graph.Configure( "Graph", "Graph", new[] { introClip, loopClip }, new[] { introNode, loopNode }, new[] { new AnimationEdge(introNode.InternalId, loopNode.InternalId) }, new[] { new AnimationFlow("Flow", "Flow", introNode.InternalId) }, "Flow"); var gameObject = Track(new GameObject("Wait Large Delta")); gameObject.AddComponent(); var player = gameObject.AddComponent(); player.ConfigureForAuthoring(graph, false, 1f); var handle = player.Play("Flow"); var wait = player.WaitForFirstPass(handle); player.EvaluateForTests(1.35d); Assert.That(wait.keepWaiting, Is.False); Assert.That(handle.IsCompleted, Is.False); Assert.That(player.State, Is.EqualTo(FrameAnimationPlaybackState.Playing)); } [Test] public void WaitForFirstPass_NonLoopMirrorsNaturalCompletion() { var clip = CreateClip( "Once", FrameClipEndBehavior.HoldLastFrame, CreateSprite(Color.red), CreateSprite(Color.blue)); var graph = CreateDirectGraph(clip); var gameObject = Track(new GameObject("Wait Once")); gameObject.AddComponent(); var player = gameObject.AddComponent(); player.ConfigureForAuthoring(graph, false, 1f); var handle = player.Play(clip.Id); var wait = player.WaitForFirstPass(handle); player.EvaluateForTests(0.2d); Assert.That(wait.keepWaiting, Is.False); Assert.That(handle.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Completed)); Assert.That(player.State, Is.EqualTo(FrameAnimationPlaybackState.Stopped)); } [Test] public void WaitForFirstPass_ReleasesWhenRequestIsReplacedOrStopped() { var first = CreateClip("First", FrameClipEndBehavior.Loop, CreateSprite(Color.red)); var second = CreateClip("Second", FrameClipEndBehavior.Loop, CreateSprite(Color.blue)); var graph = Track(ScriptableObject.CreateInstance()); graph.Configure( "Graph", "Graph", new[] { first, second }, Array.Empty(), Array.Empty(), Array.Empty(), first.Id); var gameObject = Track(new GameObject("Wait Termination")); gameObject.AddComponent(); var player = gameObject.AddComponent(); player.ConfigureForAuthoring(graph, false, 1f); var replacedHandle = player.Play(first.Id); var replacedWait = player.WaitForFirstPass(replacedHandle); var stoppedHandle = player.Play(second.Id); var stoppedWait = player.WaitForFirstPass(stoppedHandle); Assert.That(replacedWait.keepWaiting, Is.False); Assert.That(replacedHandle.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Replaced)); Assert.That(stoppedWait.keepWaiting, Is.True); player.Stop(); Assert.That(stoppedWait.keepWaiting, Is.False); Assert.That(stoppedHandle.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Stopped)); } [Test] public void WaitForFirstPass_PauseAndZeroSpeedDoNotReleaseEarly() { var clip = CreateClip( "Loop", FrameClipEndBehavior.Loop, CreateSprite(Color.red), CreateSprite(Color.blue)); var graph = CreateDirectGraph(clip); var gameObject = Track(new GameObject("Wait Paused")); gameObject.AddComponent(); var player = gameObject.AddComponent(); player.ConfigureForAuthoring(graph, false, 0f); var handle = player.Play(clip.Id); var wait = player.WaitForFirstPass(handle); player.EvaluateForTests(1d); Assert.That(wait.keepWaiting, Is.True); player.SetSpeed(1f); player.Pause(); player.EvaluateForTests(1d); Assert.That(wait.keepWaiting, Is.True); player.Resume(); player.EvaluateForTests(0.2d); Assert.That(wait.keepWaiting, Is.False); Assert.That(handle.IsCompleted, Is.False); } [Test] public void WaitForFirstPass_FailedRequestDoesNotWait() { var clip = CreateClip("Loop", FrameClipEndBehavior.Loop, CreateSprite(Color.white)); var graph = CreateDirectGraph(clip); var gameObject = Track(new GameObject("Wait Failed")); gameObject.AddComponent(); var player = gameObject.AddComponent(); player.ConfigureForAuthoring(graph, false, 1f); var handle = player.Play("Missing"); var wait = player.WaitForFirstPass(handle); Assert.That(wait.keepWaiting, Is.False); Assert.That(handle.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Failed)); Assert.That(handle.Result.Error.Code, Is.EqualTo(FrameAnimationPlaybackErrorCode.PlayableNotFound)); } [Test] public void FailedTargetProbeDoesNotDiscardCurrentTargetOrRequest() { var clip = CreateClip("Loop", FrameClipEndBehavior.Loop, CreateSprite(Color.white)); var graph = CreateDirectGraph(clip); var gameObject = Track(new GameObject("Player", typeof(RectTransform), typeof(CanvasRenderer))); gameObject.AddComponent(); var player = gameObject.AddComponent(); player.ConfigureForAuthoring(graph, false, 1f); var active = player.Play(clip.Id); gameObject.AddComponent(); var failed = player.Play(clip.Id); Assert.That(failed.Result.Error.Code, Is.EqualTo(FrameAnimationPlaybackErrorCode.TargetConflict)); Assert.That(active.IsCompleted, Is.False); Assert.That(player.State, Is.EqualTo(FrameAnimationPlaybackState.Playing)); player.EvaluateForTests(0.05d); Assert.That(active.IsCompleted, Is.False); } [Test] public void ZeroSpeedKeepsRequestPlayingWithoutAdvancing() { var first = CreateSprite(Color.red); var second = CreateSprite(Color.blue); var clip = CreateClip("Clip", FrameClipEndBehavior.HoldLastFrame, first, second); var graph = CreateDirectGraph(clip); var gameObject = Track(new GameObject("Zero Speed")); var renderer = gameObject.AddComponent(); var player = gameObject.AddComponent(); player.ConfigureForAuthoring(graph, false, 0f); var handle = player.Play(clip.Id); player.EvaluateForTests(10d); Assert.That(handle.IsCompleted, Is.False); Assert.That(player.State, Is.EqualTo(FrameAnimationPlaybackState.Playing)); Assert.That(player.CurrentFrameIndex, Is.EqualTo(0)); Assert.That(renderer.sprite, Is.EqualTo(first)); } [Test] public void PauseAndResumePreserveFrameProgress() { var clip = CreateClip( "Clip", FrameClipEndBehavior.HoldLastFrame, CreateSprite(Color.red), CreateSprite(Color.blue)); var graph = CreateDirectGraph(clip); var gameObject = Track(new GameObject("Pause")); gameObject.AddComponent(); var player = gameObject.AddComponent(); player.ConfigureForAuthoring(graph, false, 1f); player.Play(clip.Id); player.EvaluateForTests(0.05d); player.Pause(); player.EvaluateForTests(1d); Assert.That(player.CurrentFrameIndex, Is.EqualTo(0)); player.Resume(); player.EvaluateForTests(0.05d); Assert.That(player.CurrentFrameIndex, Is.EqualTo(1)); Assert.That(player.State, Is.EqualTo(FrameAnimationPlaybackState.Playing)); } [Test] public void TargetConflictReturnsStructuredFailure() { var clip = CreateClip("Clip", FrameClipEndBehavior.HoldLastFrame, CreateSprite(Color.white)); var graph = CreateDirectGraph(clip); var gameObject = Track(new GameObject("Conflict", typeof(RectTransform), typeof(CanvasRenderer))); gameObject.AddComponent(); gameObject.AddComponent(); var player = gameObject.AddComponent(); player.ConfigureForAuthoring(graph, false, 1f); var handle = player.Play(clip.Id); Assert.That(handle.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Failed)); Assert.That(handle.Result.Error.Code, Is.EqualTo(FrameAnimationPlaybackErrorCode.TargetConflict)); } [Test] public void StopAppliesVisualModeWithoutActiveRequest() { var gameObject = Track(new GameObject("Stop")); var renderer = gameObject.AddComponent(); renderer.sprite = CreateSprite(Color.yellow); var player = gameObject.AddComponent(); player.Stop(FrameAnimationStopMode.Clear); Assert.That(renderer.sprite, Is.Null); Assert.That(renderer.enabled, Is.True); } [Test] public void TryBindGraph_AllowsInitialBindAndRejectsReplacementAfterPlayback() { var firstGraph = CreateDirectGraph( CreateClip("First", FrameClipEndBehavior.Loop, CreateSprite(Color.red))); var secondGraph = CreateDirectGraph( CreateClip("Second", FrameClipEndBehavior.Loop, CreateSprite(Color.blue))); var gameObject = Track(new GameObject("Runtime Bind")); gameObject.AddComponent(); var player = gameObject.AddComponent(); Assert.That(player.TryBindGraph(firstGraph, out var bindError), Is.True); Assert.That(bindError.Code, Is.EqualTo(FrameAnimationPlaybackErrorCode.None)); player.Play("First"); Assert.That(player.TryBindGraph(secondGraph, out var replaceError), Is.False); Assert.That( replaceError.Code, Is.EqualTo(FrameAnimationPlaybackErrorCode.GraphBindingNotAllowed)); Assert.That(player.Graph, Is.SameAs(firstGraph)); } [Test] public void RestoreTerminalState_HoldLastFrameAppliesLastFrameImmediately() { var first = CreateSprite(Color.red); var last = CreateSprite(Color.blue); var clip = CreateClip("Terminal", FrameClipEndBehavior.HoldLastFrame, first, last); var graph = CreateDirectGraph(clip); var gameObject = Track(new GameObject("Restore Terminal")); var renderer = gameObject.AddComponent(); var player = gameObject.AddComponent(); player.ConfigureForAuthoring(graph, false, 1f); var handle = player.RestoreTerminalState(clip.Id); Assert.That(handle.IsCompleted, Is.True); Assert.That(handle.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Completed)); Assert.That(player.State, Is.EqualTo(FrameAnimationPlaybackState.Stopped)); Assert.That(renderer.sprite, Is.SameAs(last)); Assert.That(renderer.enabled, Is.True); } [Test] public void RestoreTerminalState_AppliesClearAndHideTargetEndBehaviors() { var clearClip = CreateClip( "Clear", FrameClipEndBehavior.Clear, CreateSprite(Color.red)); var hideClip = CreateClip( "Hide", FrameClipEndBehavior.HideTarget, CreateSprite(Color.blue)); var graph = Track(ScriptableObject.CreateInstance()); graph.Configure( "Graph", "Graph", new[] { clearClip, hideClip }, Array.Empty(), Array.Empty(), Array.Empty(), clearClip.Id); var gameObject = Track(new GameObject("Restore End Behaviors")); var renderer = gameObject.AddComponent(); var player = gameObject.AddComponent(); player.ConfigureForAuthoring(graph, false, 1f); player.RestoreTerminalState(clearClip.Id); Assert.That(renderer.sprite, Is.Null); Assert.That(renderer.enabled, Is.True); player.RestoreTerminalState(hideClip.Id); Assert.That(renderer.enabled, Is.False); } [Test] public void RestoreTerminalState_LoopRestartsFromFirstFrameWithoutCompleting() { var first = CreateSprite(Color.red); var second = CreateSprite(Color.blue); var clip = CreateClip("Loop", FrameClipEndBehavior.Loop, first, second); var graph = CreateDirectGraph(clip); var gameObject = Track(new GameObject("Restore Loop")); var renderer = gameObject.AddComponent(); var player = gameObject.AddComponent(); player.ConfigureForAuthoring(graph, false, 1f); var handle = player.RestoreTerminalState(clip.Id); Assert.That(handle.IsCompleted, Is.False); Assert.That(player.State, Is.EqualTo(FrameAnimationPlaybackState.Playing)); Assert.That(player.CurrentFrameIndex, Is.EqualTo(0)); Assert.That(renderer.sprite, Is.SameAs(first)); } [Test] public void FailedTerminalRestoreDoesNotReplaceActiveRequest() { var clip = CreateClip("Loop", FrameClipEndBehavior.Loop, CreateSprite(Color.white)); var graph = CreateDirectGraph(clip); var gameObject = Track(new GameObject("Failed Restore")); gameObject.AddComponent(); var player = gameObject.AddComponent(); player.ConfigureForAuthoring(graph, false, 1f); var active = player.Play(clip.Id); var failed = player.RestoreTerminalState("Missing"); Assert.That(failed.IsCompleted, Is.True); Assert.That(failed.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Failed)); Assert.That( failed.Result.Error.Code, Is.EqualTo(FrameAnimationPlaybackErrorCode.PlayableNotFound)); Assert.That(active.IsCompleted, Is.False); Assert.That(player.State, Is.EqualTo(FrameAnimationPlaybackState.Playing)); Assert.That(player.CurrentPlayableId, Is.EqualTo(clip.Id)); } [Test] public void RestoreTerminalState_FlowUsesTerminalStepEndState() { var firstClip = CreateClip( "First", FrameClipEndBehavior.HoldLastFrame, CreateSprite(Color.red)); var terminalSprite = CreateSprite(Color.green); var terminalClip = CreateClip( "Terminal", FrameClipEndBehavior.HoldLastFrame, CreateSprite(Color.blue), terminalSprite); var firstNode = new AnimationNode(firstClip.Id); var terminalNode = new AnimationNode(terminalClip.Id); var edge = new AnimationEdge(firstNode.InternalId, terminalNode.InternalId); var flow = new AnimationFlow("Flow", "Flow", firstNode.InternalId); var graph = Track(ScriptableObject.CreateInstance()); graph.Configure( "Graph", "Graph", new[] { firstClip, terminalClip }, new[] { firstNode, terminalNode }, new[] { edge }, new[] { flow }, flow.Id); var gameObject = Track(new GameObject("Restore Flow")); var renderer = gameObject.AddComponent(); var player = gameObject.AddComponent(); player.ConfigureForAuthoring(graph, false, 1f); var handle = player.RestoreTerminalState(flow.Id); Assert.That(handle.IsCompleted, Is.True); Assert.That(renderer.sprite, Is.SameAs(terminalSprite)); } [UnityTest] public IEnumerator OnDisableSettlesActiveRequestAsStopped() { var clip = CreateClip("Loop", FrameClipEndBehavior.Loop, CreateSprite(Color.white)); var graph = CreateDirectGraph(clip); var gameObject = Track(new GameObject("Disable")); gameObject.AddComponent(); var player = gameObject.AddComponent(); player.ConfigureForAuthoring(graph, false, 1f); var handle = player.Play(clip.Id); player.enabled = false; yield return null; Assert.That(handle.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Stopped)); Assert.That(player.State, Is.EqualTo(FrameAnimationPlaybackState.Stopped)); } [UnityTest] public IEnumerator PlayOnEnableStartsDefaultPlayableFromBeginning() { var clip = CreateClip("Loop", FrameClipEndBehavior.Loop, CreateSprite(Color.cyan)); var graph = CreateDirectGraph(clip); var gameObject = Track(new GameObject("Auto Play")); gameObject.SetActive(false); gameObject.AddComponent(); var player = gameObject.AddComponent(); player.ConfigureForAuthoring(graph, true, 1f); gameObject.SetActive(true); yield return null; Assert.That(player.State, Is.EqualTo(FrameAnimationPlaybackState.Playing)); Assert.That(player.CurrentPlayableId, Is.EqualTo(clip.Id)); Assert.That(player.CurrentFrameIndex, Is.EqualTo(0)); } private FrameClip CreateClip(string id, FrameClipEndBehavior endBehavior, params Sprite[] sprites) { var clip = Track(ScriptableObject.CreateInstance()); var frames = new List(); for (var index = 0; index < sprites.Length; index++) { frames.Add(new FrameAnimationFrame(sprites[index], 100, $"frame_{index}", index)); } clip.Configure(id, id, frames, 1f, endBehavior); return clip; } private FrameAnimationGraph CreateDirectGraph(FrameClip clip) { var graph = Track(ScriptableObject.CreateInstance()); graph.Configure( "Graph", "Graph", new[] { clip }, Array.Empty(), Array.Empty(), Array.Empty(), clip.Id); return graph; } private Sprite CreateSprite(Color color) { var texture = Track(new Texture2D(2, 2)); texture.SetPixels(new[] { color, color, color, color }); texture.Apply(); return Track(Sprite.Create(texture, new Rect(0, 0, 2, 2), new Vector2(0.5f, 0.5f))); } private T Track(T value) where T : UnityEngine.Object { createdObjects.Add(value); return value; } } }