فهرست منبع

Merge pull request #9578 from AvaloniaUI/fixes/composition-keyframe-tests

Added some tests for composition animations
Max Katz 2 سال پیش
والد
کامیت
c5e2f0cec0

+ 6 - 1
src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimationInstance.cs

@@ -130,7 +130,12 @@ namespace Avalonia.Rendering.Composition.Animations
                     right = _keyFrames[c + 1];
                 }
                 else if (c == 0)
-                    return ExpressionVariant.Create(GetKeyFrame(ref ctx, kf));
+                {
+                    // The current progress is before the first frame, we implicitly use the starting value 
+                    // as the first frame in this case
+                    right = _keyFrames[c];
+                    break;
+                }
                 else
                     break;
             }

+ 104 - 0
tests/Avalonia.Base.UnitTests/Composition/CompositionAnimationTests.cs

@@ -0,0 +1,104 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Reflection;
+using Avalonia.Animation.Easings;
+using Avalonia.Base.UnitTests.Rendering;
+using Avalonia.Rendering;
+using Avalonia.Rendering.Composition;
+using Avalonia.Rendering.Composition.Expressions;
+using Avalonia.Rendering.Composition.Server;
+using Avalonia.Threading;
+using Xunit;
+using Xunit.Sdk;
+
+namespace Avalonia.Base.UnitTests.Composition;
+
+public class CompositionAnimationTests
+{
+
+    class AnimationDataProvider : DataAttribute
+    {
+        IEnumerable<AnimationData> Generate() =>
+            new AnimationData[]
+            {
+                new("3 frames starting from 0")
+                {
+                    Frames =
+                    {
+                        (0f, 10f),
+                        (0.5f, 30f),
+                        (1f, 20f)
+                    },
+                    Checks =
+                    {
+                        (0.25f, 20f),
+                        (0.5f, 30f),
+                        (0.75f, 25f),
+                        (1f, 20f)
+                    }
+                },
+                new("1 final frame")
+                {
+                    Frames =
+                    {
+                        (1f, 10f)
+                    },
+                    Checks =
+                    {
+                        (0f, 0f),
+                        (0.5f, 5f),
+                        (1f, 10f)
+                    }
+                }
+            };
+
+        public override IEnumerable<object[]> GetData(MethodInfo testMethod)
+        {
+            foreach (var ani in Generate())
+            {
+                yield return new Object[] { ani };
+            }
+        }
+    }
+    
+    [AnimationDataProvider]
+    [Theory]
+    public void GenericCheck(AnimationData data)
+    {
+        var compositor =
+            new Compositor(new RenderLoop(new CompositorTestsBase.ManualRenderTimer(), new Dispatcher(null)), null);
+        var target = compositor.CreateSolidColorVisual();
+        var ani = new ScalarKeyFrameAnimation(null);
+        foreach (var frame in data.Frames)
+            ani.InsertKeyFrame(frame.key, frame.value, new LinearEasing());
+        ani.Duration = TimeSpan.FromSeconds(1);
+        var instance = ani.CreateInstance(target.Server, null);
+        instance.Initialize(TimeSpan.Zero, data.StartingValue, ServerCompositionVisual.s_IdOfRotationAngleProperty);
+        var currentValue = ExpressionVariant.Create(data.StartingValue);
+        foreach (var check in data.Checks)
+        {
+            currentValue = instance.Evaluate(TimeSpan.FromSeconds(check.time), currentValue);
+            Assert.Equal(check.value, currentValue.Scalar);
+        }
+        
+    }
+    
+    public class AnimationData
+    {
+        public AnimationData(string name)
+        {
+            Name = name;
+        }
+
+        public string Name { get;  }
+        public List<(float key, float value)> Frames { get; set; } = new();
+        public List<(float time, float value)> Checks { get; set; } = new();
+        public float StartingValue { get; set; }
+        public float Duration { get; set; } = 1;
+        public override string ToString()
+        {
+            return Name;
+        }
+    }
+}