using System; using System.Collections; using System.Collections.Generic; using NUnit.Framework; using UnityEngine; using UnityEngine.TestTools; namespace AibisDream.FrameAnimation.Tests.PlayMode { /// /// 帧动画组件的最小运行时集成测试。 /// 时间推进细节由 EditMode 的 PlaybackSession 契约覆盖。 /// public sealed class FrameAnimationPlayerTests { private readonly List createdObjects = new List(); [TearDown] public void TearDown() { for (var index = createdObjects.Count - 1; index >= 0; index--) { if (createdObjects[index] != null) UnityEngine.Object.DestroyImmediate(createdObjects[index]); } createdObjects.Clear(); } [Test] public void GraphPlayer_RendersAndAppliesTerminalBehavior() { var first = CreateSprite(Color.red); var second = CreateSprite(Color.blue); var clip = CreateClip( "Clip", FrameClipEndBehavior.Clear, first, second); var graph = CreateGraph(clip); var gameObject = Track(new GameObject("Graph 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.SameAs(first)); player.EvaluateForTests(0.2d); Assert.That(handle.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Completed)); Assert.That(renderer.sprite, Is.Null); Assert.That(renderer.enabled, Is.True); } [Test] public void StandalonePlayer_PlaysConfiguredClipWithoutGraph() { var first = CreateSprite(Color.magenta); var second = CreateSprite(Color.cyan); var clip = CreateClip( "Standalone", FrameClipEndBehavior.HoldLastFrame, first, second); var gameObject = Track(new GameObject("Standalone Player")); var renderer = gameObject.AddComponent(); var player = gameObject.AddComponent(); player.ConfigureForAuthoring(clip, false, 1f); var handle = player.Play(); 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 PlayRequest_FailureKeepsActiveAndSuccessReplacesIt() { var first = CreateClip( "First", FrameClipEndBehavior.Loop, CreateSprite(Color.white)); var second = CreateClip( "Second", FrameClipEndBehavior.Loop, CreateSprite(Color.black)); var graph = CreateGraph(first, second); var gameObject = Track(new GameObject("Request Arbitration")); gameObject.AddComponent(); var player = gameObject.AddComponent(); player.ConfigureForAuthoring(graph, false, 1f); var active = player.Play(first.Id); var failed = player.Play("Missing"); Assert.That(failed.Result.Error.Code, Is.EqualTo(FrameAnimationPlaybackErrorCode.PlayableNotFound)); Assert.That(active.IsCompleted, Is.False); Assert.That(player.CurrentPlayableId, Is.EqualTo(first.Id)); var replacement = player.Play(second.Id); Assert.That(active.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Replaced)); Assert.That(replacement.IsCompleted, Is.False); Assert.That(player.CurrentPlayableId, Is.EqualTo(second.Id)); } [Test] public void RestoreTerminalState_RebuildsSavedVisualState() { var terminalSprite = CreateSprite(Color.green); var hold = CreateClip( "Hold", FrameClipEndBehavior.HoldLastFrame, CreateSprite(Color.red), terminalSprite); var hidden = CreateClip( "Hidden", FrameClipEndBehavior.HideTarget, CreateSprite(Color.blue)); var graph = CreateGraph(hold, hidden); var gameObject = Track(new GameObject("Restore Terminal State")); var renderer = gameObject.AddComponent(); var player = gameObject.AddComponent(); player.ConfigureForAuthoring(graph, false, 1f); var holdResult = player.RestoreTerminalState(hold.Id); Assert.That(holdResult.Result.Reason, Is.EqualTo(FrameAnimationCompletionReason.Completed)); Assert.That(renderer.sprite, Is.SameAs(terminalSprite)); Assert.That(renderer.enabled, Is.True); player.RestoreTerminalState(hidden.Id); Assert.That(renderer.enabled, Is.False); } [UnityTest] public IEnumerator Disable_SettlesActiveRequestAsStopped() { var clip = CreateClip( "Loop", FrameClipEndBehavior.Loop, CreateSprite(Color.white)); var gameObject = Track(new GameObject("Disable Player")); gameObject.AddComponent(); var player = gameObject.AddComponent(); player.ConfigureForAuthoring(CreateGraph(clip), 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)); } 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 CreateGraph(params FrameClip[] clips) { var graph = Track(ScriptableObject.CreateInstance()); graph.Configure( "Graph", "Graph", clips, Array.Empty(), Array.Empty(), Array.Empty(), clips[0].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; } } }