434 lines
24 KiB
C#
434 lines
24 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using AibisDream.FrameAnimation.Editor;
|
|
using NUnit.Framework;
|
|
using UnityEditor;
|
|
using UnityEditor.UIElements;
|
|
using UnityEngine;
|
|
using UnityEngine.UIElements;
|
|
|
|
namespace AibisDream.FrameAnimation.Tests.EditMode
|
|
{
|
|
public sealed class FrameAnimationPreviewTests
|
|
{
|
|
private readonly List<UnityEngine.Object> objects = new List<UnityEngine.Object>();
|
|
|
|
[TearDown]
|
|
public void TearDown()
|
|
{
|
|
foreach (var item in objects.Where(item => item != null))
|
|
{
|
|
UnityEngine.Object.DestroyImmediate(item);
|
|
}
|
|
objects.Clear();
|
|
}
|
|
|
|
[Test]
|
|
public void SessionSnapshotAndSeek_UseTheRuntimeEvaluator()
|
|
{
|
|
var clip = Clip("Clip", 2f, FrameClipEndBehavior.HoldLastFrame, 100, 200, 300);
|
|
var graph = Graph(new[] { clip }, Array.Empty<AnimationNode>(), Array.Empty<AnimationEdge>(),
|
|
Array.Empty<AnimationFlow>());
|
|
Assert.That(FrameAnimationResolver.TryResolve(graph, clip.Id, default, out var plan, out _), Is.True);
|
|
var session = new FrameAnimationPlaybackSession(plan);
|
|
session.Start(_ => { });
|
|
|
|
session.Seek(0.075d, _ => { });
|
|
|
|
Assert.That(session.Snapshot.StepIndex, Is.EqualTo(0));
|
|
Assert.That(session.Snapshot.FrameIndex, Is.EqualTo(1));
|
|
Assert.That(session.Snapshot.FrameElapsedSeconds, Is.EqualTo(0.05d).Within(0.000001d));
|
|
Assert.That(session.Snapshot.StepSpeed, Is.EqualTo(2f));
|
|
}
|
|
|
|
[Test]
|
|
public void NodeResolver_AppliesNodeOverridesWithoutChangingClip()
|
|
{
|
|
var clip = Clip("Clip", 0.5f, FrameClipEndBehavior.HoldLastFrame, 100);
|
|
var node = new AnimationNode(clip.Id, speedOverride: 3f,
|
|
endBehaviorOverride: FrameClipEndBehavior.HideTarget);
|
|
var graph = Graph(new[] { clip }, new[] { node }, Array.Empty<AnimationEdge>(),
|
|
Array.Empty<AnimationFlow>());
|
|
|
|
Assert.That(FrameAnimationResolver.TryResolveNode(graph, node, out var plan, out var error),
|
|
Is.True, error.ToString());
|
|
Assert.That(plan.Steps.Single().PlaybackSpeed, Is.EqualTo(3f));
|
|
Assert.That(plan.Steps.Single().TerminalEndBehavior, Is.EqualTo(FrameClipEndBehavior.HideTarget));
|
|
Assert.That(plan.Steps.Single().NodeId, Is.EqualTo(node.InternalId));
|
|
Assert.That(clip.Speed, Is.EqualTo(0.5f));
|
|
}
|
|
|
|
[Test]
|
|
public void Timeline_FlattensFlowUsingEffectiveStepDurations()
|
|
{
|
|
var first = Clip("First", 1f, FrameClipEndBehavior.HoldLastFrame, 100, 100);
|
|
var second = Clip("Second", 1f, FrameClipEndBehavior.HoldLastFrame, 300);
|
|
var firstNode = new AnimationNode(first.Id, speedOverride: 2f);
|
|
var secondNode = new AnimationNode(second.Id, speedOverride: 0.5f);
|
|
var edge = new AnimationEdge(firstNode.InternalId, secondNode.InternalId);
|
|
var flow = new AnimationFlow("Flow", "Flow", firstNode.InternalId);
|
|
var graph = Graph(new[] { first, second }, new[] { firstNode, secondNode }, new[] { edge }, new[] { flow });
|
|
Assert.That(FrameAnimationResolver.TryResolve(graph, flow.Id, default, out var plan, out _), Is.True);
|
|
|
|
var timeline = new FrameAnimationPreviewTimeline(plan);
|
|
|
|
Assert.That(timeline.Segments.Count, Is.EqualTo(3));
|
|
Assert.That(timeline.Segments[2].NodeId, Is.EqualTo(secondNode.InternalId));
|
|
Assert.That(timeline.Segments[2].StartSeconds, Is.EqualTo(0.1d).Within(0.000001d));
|
|
Assert.That(timeline.DisplayDurationSeconds, Is.EqualTo(0.7d).Within(0.000001d));
|
|
Assert.That(timeline.IsInfinite, Is.False);
|
|
}
|
|
|
|
[Test]
|
|
public void Timeline_LoopUsesOneCycle_AndZeroSpeedStopsReachability()
|
|
{
|
|
var loopClip = Clip("Loop", 1f, FrameClipEndBehavior.Loop, 100, 200);
|
|
var loopNode = new AnimationNode(loopClip.Id, endBehaviorOverride: FrameClipEndBehavior.Loop);
|
|
var loopFlow = new AnimationFlow("LoopFlow", "Loop", loopNode.InternalId);
|
|
var loopGraph = Graph(new[] { loopClip }, new[] { loopNode }, Array.Empty<AnimationEdge>(), new[] { loopFlow });
|
|
FrameAnimationResolver.TryResolve(loopGraph, loopFlow.Id, default, out var loopPlan, out _);
|
|
var loopTimeline = new FrameAnimationPreviewTimeline(loopPlan);
|
|
|
|
Assert.That(loopTimeline.IsInfinite, Is.True);
|
|
Assert.That(loopTimeline.Segments.Count, Is.EqualTo(2));
|
|
Assert.That(loopTimeline.DisplayDurationSeconds, Is.EqualTo(0.3d).Within(0.000001d));
|
|
|
|
var blockedClip = Clip("Blocked", 1f, FrameClipEndBehavior.HoldLastFrame, 100, 100);
|
|
var blockedNode = new AnimationNode(blockedClip.Id, speedOverride: 0f);
|
|
var blockedGraph = Graph(new[] { blockedClip }, new[] { blockedNode }, Array.Empty<AnimationEdge>(),
|
|
Array.Empty<AnimationFlow>());
|
|
FrameAnimationResolver.TryResolveNode(blockedGraph, blockedNode, out var blockedPlan, out _);
|
|
var blockedTimeline = new FrameAnimationPreviewTimeline(blockedPlan);
|
|
|
|
Assert.That(blockedTimeline.IsBlocked, Is.True);
|
|
Assert.That(blockedTimeline.IsInfinite, Is.True);
|
|
Assert.That(blockedTimeline.Segments.Count, Is.EqualTo(1));
|
|
Assert.That(blockedTimeline.Segments[0].IsBlocked, Is.True);
|
|
}
|
|
|
|
[Test]
|
|
public void Coordinator_FlowPreviewCrossesNodesAndRestoresAutomaticPolicyOnStop()
|
|
{
|
|
var first = Clip("First", 1f, FrameClipEndBehavior.HoldLastFrame, 100);
|
|
var second = Clip("Second", 1f, FrameClipEndBehavior.Loop, 100);
|
|
var firstNode = new AnimationNode(first.Id);
|
|
var secondNode = new AnimationNode(second.Id, endBehaviorOverride: FrameClipEndBehavior.Loop);
|
|
var edge = new AnimationEdge(firstNode.InternalId, secondNode.InternalId);
|
|
var flow = new AnimationFlow("Flow", "Flow", firstNode.InternalId);
|
|
var graph = Graph(new[] { first, second }, new[] { firstNode, secondNode }, new[] { edge }, new[] { flow });
|
|
var coordinator = new FrameAnimationPreviewCoordinator();
|
|
coordinator.SetGraph(graph);
|
|
coordinator.SetTarget(FrameAnimationPreviewTargetKind.Flow, flow);
|
|
|
|
coordinator.Play();
|
|
coordinator.Tick(0.1d, graph.Nodes);
|
|
|
|
Assert.That(coordinator.IsFlowPreview, Is.True);
|
|
Assert.That(coordinator.Snapshot.NodeId, Is.EqualTo(secondNode.InternalId));
|
|
Assert.That(coordinator.GetNodeDisplay(firstNode).FlowState, Is.EqualTo(FrameAnimationFlowElementState.Passed));
|
|
Assert.That(coordinator.GetNodeDisplay(secondNode).FlowState, Is.EqualTo(FrameAnimationFlowElementState.Current));
|
|
|
|
coordinator.Stop();
|
|
Assert.That(coordinator.IsFlowPreview, Is.False);
|
|
Assert.That(coordinator.NodePolicy, Is.EqualTo(NodePreviewPolicy.SelectedOnly));
|
|
}
|
|
|
|
[Test]
|
|
public void Coordinator_FlowSeekAndStepCrossNodeBoundaries()
|
|
{
|
|
var first = Clip("First", 1f, FrameClipEndBehavior.HoldLastFrame, 100);
|
|
var second = Clip("Second", 1f, FrameClipEndBehavior.HoldLastFrame, 200, 200);
|
|
var firstNode = new AnimationNode(first.Id);
|
|
var secondNode = new AnimationNode(second.Id);
|
|
var flow = new AnimationFlow("Flow", "Flow", firstNode.InternalId);
|
|
var graph = Graph(new[] { first, second }, new[] { firstNode, secondNode },
|
|
new[] { new AnimationEdge(firstNode.InternalId, secondNode.InternalId) }, new[] { flow });
|
|
var coordinator = new FrameAnimationPreviewCoordinator();
|
|
coordinator.SetGraph(graph);
|
|
coordinator.SetTarget(FrameAnimationPreviewTargetKind.Flow, flow);
|
|
|
|
coordinator.Seek(0.1d);
|
|
Assert.That(coordinator.Snapshot.NodeId, Is.EqualTo(secondNode.InternalId));
|
|
Assert.That(coordinator.Snapshot.FrameIndex, Is.EqualTo(0));
|
|
coordinator.Step(1);
|
|
Assert.That(coordinator.Snapshot.NodeId, Is.EqualTo(secondNode.InternalId));
|
|
Assert.That(coordinator.Snapshot.FrameIndex, Is.EqualTo(1));
|
|
coordinator.Step(-1);
|
|
Assert.That(coordinator.Snapshot.FrameIndex, Is.EqualTo(0));
|
|
}
|
|
|
|
[Test]
|
|
public void GraphViewPreview_RendersOnePreviewPerNodeWithoutMutatingGraph()
|
|
{
|
|
var clip = Clip("Clip", 1f, FrameClipEndBehavior.Loop, 100);
|
|
var first = new AnimationNode(clip.Id);
|
|
var second = new AnimationNode(clip.Id);
|
|
var graph = Graph(new[] { clip }, new[] { first, second }, Array.Empty<AnimationEdge>(),
|
|
Array.Empty<AnimationFlow>());
|
|
var before = JsonUtility.ToJson(graph);
|
|
var view = new FrameAnimationGraphView();
|
|
view.Bind(graph, Array.Empty<FrameAnimationEditorIssue>(), string.Empty);
|
|
var coordinator = new FrameAnimationPreviewCoordinator();
|
|
coordinator.SetGraph(graph);
|
|
coordinator.SetNodePolicy(NodePreviewPolicy.AllVisible);
|
|
|
|
view.ApplyPreview(coordinator);
|
|
|
|
Assert.That(view.Query<FrameAnimationPreviewElement>().ToList().Count, Is.EqualTo(2));
|
|
Assert.That(JsonUtility.ToJson(graph), Is.EqualTo(before));
|
|
}
|
|
|
|
[Test]
|
|
public void FlowColorPalette_IsOpaqueAndReadableForExtremeColors()
|
|
{
|
|
var dark = FrameAnimationFlowColorUtility.CreatePalette(new Color(0.005f, 0.01f, 0.02f, 0f));
|
|
var light = FrameAnimationFlowColorUtility.CreatePalette(new Color(1f, 0.98f, 0.9f, 0.1f));
|
|
|
|
Assert.That(dark.Raw.a, Is.EqualTo(1f));
|
|
Assert.That(dark.Stroke.maxColorComponent, Is.GreaterThan(dark.Raw.maxColorComponent));
|
|
Assert.That(light.Raw.a, Is.EqualTo(1f));
|
|
Assert.That(FrameAnimationFlowColorUtility.Luminance(light.Text),
|
|
Is.LessThan(FrameAnimationFlowColorUtility.Luminance(light.Header)));
|
|
}
|
|
|
|
[Test]
|
|
public void GraphView_FocusedFlowUsesItsColorAndShowAllKeepsNeutralEdges()
|
|
{
|
|
var clip = Clip("Clip", 1f, FrameClipEndBehavior.HoldLastFrame, 100);
|
|
var first = new AnimationNode(clip.Id, internalId: "first");
|
|
var second = new AnimationNode(clip.Id, internalId: "second");
|
|
var edge = new AnimationEdge(first.InternalId, second.InternalId, internalId: "edge");
|
|
var flow = new AnimationFlow("Flow", "Flow", first.InternalId);
|
|
var graph = Graph(new[] { clip }, new[] { first, second }, new[] { edge }, new[] { flow });
|
|
var stored = new Color(0.12f, 0.72f, 0.38f, 0.15f);
|
|
graph.EditorData.GetOrCreateFlowData(flow.Id, stored);
|
|
var palette = FrameAnimationFlowColorUtility.CreatePalette(stored);
|
|
var view = new FrameAnimationGraphView();
|
|
|
|
view.Bind(graph, Array.Empty<FrameAnimationEditorIssue>(), string.Empty);
|
|
var edgeView = view.Query<FrameAnimationEdgeView>().First();
|
|
Assert.That(edgeView.edgeControl.inputColor.r, Is.EqualTo(119f / 255f).Within(0.001f));
|
|
Assert.That(view.Query<Label>(className: "fa-flow-badge").ToList().Count, Is.EqualTo(2));
|
|
|
|
view.SetFocusedFlow(flow.Id);
|
|
|
|
Assert.That(edgeView.edgeControl.inputColor.r, Is.EqualTo(palette.Stroke.r).Within(0.003f));
|
|
Assert.That(edgeView.edgeControl.inputColor.g, Is.EqualTo(palette.Stroke.g).Within(0.003f));
|
|
Assert.That(view.Query<FrameAnimationClipNodeView>(className: "fa-node--focused").ToList().Count,
|
|
Is.EqualTo(2));
|
|
}
|
|
|
|
[Test]
|
|
public void GraphView_SharedPathSwitchesToNewFocusedFlowColor()
|
|
{
|
|
var clip = Clip("Clip", 1f, FrameClipEndBehavior.HoldLastFrame, 100);
|
|
var first = new AnimationNode(clip.Id, internalId: "first");
|
|
var second = new AnimationNode(clip.Id, internalId: "second");
|
|
var edge = new AnimationEdge(first.InternalId, second.InternalId, internalId: "edge");
|
|
var redFlow = new AnimationFlow("Red", "Red", first.InternalId);
|
|
var blueFlow = new AnimationFlow("Blue", "Blue", first.InternalId);
|
|
var graph = Graph(new[] { clip }, new[] { first, second }, new[] { edge }, new[] { redFlow, blueFlow });
|
|
graph.EditorData.GetOrCreateFlowData(redFlow.Id, Color.red);
|
|
graph.EditorData.GetOrCreateFlowData(blueFlow.Id, Color.blue);
|
|
var view = new FrameAnimationGraphView();
|
|
view.Bind(graph, Array.Empty<FrameAnimationEditorIssue>(), redFlow.Id);
|
|
var edgeView = view.Query<FrameAnimationEdgeView>().First();
|
|
var red = edgeView.edgeControl.inputColor;
|
|
|
|
view.SetFocusedFlow(blueFlow.Id);
|
|
var blue = edgeView.edgeControl.inputColor;
|
|
|
|
Assert.That(red.r, Is.GreaterThan(red.b));
|
|
Assert.That(blue.b, Is.GreaterThan(blue.r));
|
|
}
|
|
|
|
[Test]
|
|
public void GraphView_EdgeValidationColorOverridesFlowAndPreviewColor()
|
|
{
|
|
var clip = Clip("Clip", 1f, FrameClipEndBehavior.HoldLastFrame, 100);
|
|
var first = new AnimationNode(clip.Id, internalId: "first");
|
|
var second = new AnimationNode(clip.Id, internalId: "second");
|
|
var edge = new AnimationEdge(first.InternalId, second.InternalId, internalId: "edge");
|
|
var flow = new AnimationFlow("Flow", "Flow", first.InternalId);
|
|
var graph = Graph(new[] { clip }, new[] { first, second }, new[] { edge }, new[] { flow });
|
|
graph.EditorData.GetOrCreateFlowData(flow.Id, Color.green);
|
|
var issue = new FrameAnimationEditorIssue(FrameAnimationValidationSeverity.Error, "BrokenEdge",
|
|
new FrameAnimationEditorSelection(FrameAnimationEditorSelectionKind.Edge, edge), "Broken", string.Empty);
|
|
var view = new FrameAnimationGraphView();
|
|
view.Bind(graph, new[] { issue }, flow.Id);
|
|
var edgeView = view.Query<FrameAnimationEdgeView>().First();
|
|
|
|
edgeView.SetPreviewState(FrameAnimationFlowElementState.Current);
|
|
|
|
Assert.That(edgeView.edgeControl.inputColor.r, Is.EqualTo(223f / 255f).Within(0.003f));
|
|
Assert.That(edgeView.edgeControl.inputColor.g, Is.EqualTo(106f / 255f).Within(0.003f));
|
|
}
|
|
|
|
[Test]
|
|
public void PreviewSample_IsIsolatedAndStructurallyPlayable()
|
|
{
|
|
var sample = AssetDatabase.LoadAssetAtPath<FrameAnimationGraph>(
|
|
"Assets/GameContent/Test/FrameAnimation/Preview/PreviewSampleGraph.asset");
|
|
|
|
Assert.That(sample, Is.Not.Null);
|
|
Assert.That(sample.Clips.Count, Is.EqualTo(3));
|
|
Assert.That(sample.Flows.Count, Is.EqualTo(5));
|
|
Assert.That(sample.Clips.Any(item => item.Frames.Any(frame => frame.Sprite == null)), Is.True);
|
|
foreach (var flow in sample.Flows)
|
|
{
|
|
Assert.That(FrameAnimationResolver.TryResolve(sample, flow.Id, default, out _, out var error),
|
|
Is.True, error.ToString());
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void Workbench_CreateGUIBuildsTabsAndSelectionPreviewForClipNodeAndFlow()
|
|
{
|
|
var sample = AssetDatabase.LoadAssetAtPath<FrameAnimationGraph>(
|
|
"Assets/GameContent/Test/FrameAnimation/Preview/PreviewSampleGraph.asset");
|
|
var window = ScriptableObject.CreateInstance<FrameAnimationGraphEditorWindow>();
|
|
try
|
|
{
|
|
typeof(FrameAnimationGraphEditorWindow).GetField("graph",
|
|
BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(window, sample);
|
|
typeof(FrameAnimationGraphEditorWindow).GetMethod("CreateGUI",
|
|
BindingFlags.Instance | BindingFlags.NonPublic)?.Invoke(window, null);
|
|
|
|
Assert.That(window.rootVisualElement.Query<FrameAnimationPreviewElement>().ToList().Count,
|
|
Is.GreaterThanOrEqualTo(1));
|
|
Assert.That(window.rootVisualElement.Query<Slider>().ToList().Count,
|
|
Is.GreaterThanOrEqualTo(2));
|
|
Assert.That(window.rootVisualElement.Q<Button>("graph-properties-button"), Is.Not.Null);
|
|
Assert.That(window.rootVisualElement.Q<Button>("graph-breadcrumb-button"), Is.Not.Null);
|
|
Assert.That(window.rootVisualElement.Q<VisualElement>("canvas-toolbar").Query<Slider>().ToList(),
|
|
Is.Empty);
|
|
Assert.That(window.rootVisualElement.Query<IMGUIContainer>().ToList(), Is.Empty);
|
|
Assert.That(window.rootVisualElement.Q<VisualElement>("fa-workbench"), Is.Not.Null);
|
|
var clipsTab = window.rootVisualElement.Q<Button>("resource-tab-clips");
|
|
typeof(FrameAnimationGraphEditorWindow).GetMethod("SetResourceTab",
|
|
BindingFlags.Instance | BindingFlags.NonPublic)?.Invoke(window,
|
|
new[] { clipsTab.userData, (object)false });
|
|
Assert.That(clipsTab.ClassListContains("fa-tab--active"), Is.True);
|
|
var importDiffTab = window.rootVisualElement.Q<Button>("bottom-tab-import-diff");
|
|
typeof(FrameAnimationGraphEditorWindow).GetMethod("SetBottomTab",
|
|
BindingFlags.Instance | BindingFlags.NonPublic)?.Invoke(window,
|
|
new[] { importDiffTab.userData });
|
|
Assert.That(importDiffTab.ClassListContains("fa-tab--active"), Is.True);
|
|
|
|
var transport = window.rootVisualElement.Q<Toolbar>("preview-transport");
|
|
Assert.That(transport, Is.Not.Null);
|
|
Assert.That(transport.Q<Button>("preview-restart"), Is.Not.Null);
|
|
Assert.That(transport.Q<Button>("preview-previous-frame"), Is.Not.Null);
|
|
Assert.That(transport.Q<Button>("preview-play-pause"), Is.Not.Null);
|
|
Assert.That(transport.Q<Button>("preview-next-frame"), Is.Not.Null);
|
|
Assert.That(transport.Q<Button>("preview-stop"), Is.Not.Null);
|
|
Assert.That(transport.Q<Button>("preview-stop").Q<Image>().image, Is.Not.Null);
|
|
Assert.That(transport.Q<Slider>("preview-timeline"), Is.Not.Null);
|
|
Assert.That(transport.Q<Label>("preview-time"), Is.Not.Null);
|
|
Assert.That(transport.Q<Button>("preview-play-pause").enabledSelf, Is.False);
|
|
|
|
var flowsTab = window.rootVisualElement.Q<Button>("resource-tab-flows");
|
|
typeof(FrameAnimationGraphEditorWindow).GetMethod("SetResourceTab",
|
|
BindingFlags.Instance | BindingFlags.NonPublic)?.Invoke(window,
|
|
new[] { flowsTab.userData, (object)true });
|
|
Assert.That(flowsTab.ClassListContains("fa-tab--active"), Is.True);
|
|
Assert.That(window.rootVisualElement.Q<Button>("resource-tab-clips")
|
|
.ClassListContains("fa-tab--active"), Is.False);
|
|
|
|
var validationTab = window.rootVisualElement.Q<Button>("bottom-tab-validation");
|
|
typeof(FrameAnimationGraphEditorWindow).GetMethod("SetBottomTab",
|
|
BindingFlags.Instance | BindingFlags.NonPublic)?.Invoke(window,
|
|
new[] { validationTab.userData });
|
|
Assert.That(validationTab.ClassListContains("fa-tab--active"), Is.True);
|
|
Assert.That(window.rootVisualElement.Q<Button>("bottom-tab-import-diff")
|
|
.ClassListContains("fa-tab--active"), Is.False);
|
|
|
|
var clip = sample.Clips.First(item => item != null);
|
|
var setSelection = typeof(FrameAnimationGraphEditorWindow).GetMethod("SetSelection",
|
|
BindingFlags.Instance | BindingFlags.NonPublic);
|
|
setSelection?.Invoke(window, new object[]
|
|
{
|
|
new FrameAnimationEditorSelection(FrameAnimationEditorSelectionKind.Clip, clip),
|
|
false
|
|
});
|
|
AssertSelectionPreview(window, "Clip Preview");
|
|
var playButton = transport.Q<Button>("preview-play-pause");
|
|
Assert.That(playButton.enabledSelf, Is.True);
|
|
Assert.That(playButton.text, Is.Empty);
|
|
Assert.That(playButton.tooltip, Is.EqualTo("Play"));
|
|
Assert.That(playButton.Q<Image>(), Is.Not.Null);
|
|
Assert.That(playButton.Q<Image>().image, Is.Not.Null);
|
|
|
|
typeof(FrameAnimationGraphEditorWindow).GetMethod("PreviewPlayPause",
|
|
BindingFlags.Instance | BindingFlags.NonPublic)?.Invoke(window, null);
|
|
Assert.That(playButton.text, Is.Empty);
|
|
Assert.That(playButton.tooltip, Is.EqualTo("Pause"));
|
|
Assert.That(playButton.Q<Image>().image, Is.Not.Null);
|
|
|
|
var node = sample.Nodes.First(item => item != null);
|
|
setSelection?.Invoke(window, new object[]
|
|
{
|
|
new FrameAnimationEditorSelection(FrameAnimationEditorSelectionKind.Node, node),
|
|
false
|
|
});
|
|
AssertSelectionPreview(window, "Node Preview");
|
|
|
|
var flow = sample.Flows.First(item => item != null);
|
|
setSelection?.Invoke(window, new object[]
|
|
{
|
|
new FrameAnimationEditorSelection(FrameAnimationEditorSelectionKind.Flow, flow),
|
|
false
|
|
});
|
|
AssertSelectionPreview(window, "Flow Preview");
|
|
|
|
typeof(FrameAnimationGraphEditorWindow).GetMethod("SelectGraphProperties",
|
|
BindingFlags.Instance | BindingFlags.NonPublic)?.Invoke(window, null);
|
|
var selected = (FrameAnimationEditorSelection)typeof(FrameAnimationGraphEditorWindow)
|
|
.GetField("selection", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(window);
|
|
Assert.That(selected.Kind, Is.EqualTo(FrameAnimationEditorSelectionKind.Graph));
|
|
Assert.That(selected.Value, Is.SameAs(sample));
|
|
}
|
|
finally
|
|
{
|
|
UnityEngine.Object.DestroyImmediate(window);
|
|
}
|
|
}
|
|
|
|
private static void AssertSelectionPreview(FrameAnimationGraphEditorWindow window, string expectedTitle)
|
|
{
|
|
var panel = window.rootVisualElement.Q<VisualElement>("selection-preview-panel");
|
|
Assert.That(panel, Is.Not.Null);
|
|
Assert.That(panel.style.display.value, Is.EqualTo(DisplayStyle.Flex));
|
|
Assert.That(window.rootVisualElement.Q<Label>("selection-preview-title").text,
|
|
Is.EqualTo(expectedTitle));
|
|
}
|
|
|
|
private FrameClip Clip(string id, float speed, FrameClipEndBehavior end, params int[] durations)
|
|
{
|
|
var clip = Track(ScriptableObject.CreateInstance<FrameClip>());
|
|
clip.Configure(id, id, durations.Select((duration, index) =>
|
|
new FrameAnimationFrame(null, duration, $"frame_{index}", index)), speed, end);
|
|
return clip;
|
|
}
|
|
|
|
private FrameAnimationGraph Graph(
|
|
IEnumerable<FrameClip> clips,
|
|
IEnumerable<AnimationNode> nodes,
|
|
IEnumerable<AnimationEdge> edges,
|
|
IEnumerable<AnimationFlow> flows)
|
|
{
|
|
var graph = Track(ScriptableObject.CreateInstance<FrameAnimationGraph>());
|
|
graph.Configure("Graph", "Graph", clips, nodes, edges, flows, string.Empty);
|
|
return graph;
|
|
}
|
|
|
|
private T Track<T>(T value) where T : UnityEngine.Object
|
|
{
|
|
objects.Add(value);
|
|
return value;
|
|
}
|
|
}
|
|
}
|