diff --git a/src/Avalonia.Base/Animation/AnimationInstance`1.cs b/src/Avalonia.Base/Animation/AnimationInstance`1.cs index 682629c801..92d2c2c8b5 100644 --- a/src/Avalonia.Base/Animation/AnimationInstance`1.cs +++ b/src/Avalonia.Base/Animation/AnimationInstance`1.cs @@ -53,10 +53,10 @@ namespace Avalonia.Animation private void FetchProperties() { if (_animation.SpeedRatio < 0d) - throw new ArgumentOutOfRangeException("SpeedRatio value should not be negative."); + throw new InvalidOperationException("SpeedRatio value should not be negative."); - if (_animation.Duration.TotalSeconds <= 0) - throw new InvalidOperationException("Duration value cannot be negative or zero."); + if (_animation.Duration < TimeSpan.Zero) + throw new InvalidOperationException("Duration value cannot be negative."); _easeFunc = _animation.Easing; @@ -110,8 +110,8 @@ namespace Avalonia.Animation { if (_animator.Property is null) throw new InvalidOperationException("Animator has no property specified."); - if (_fillMode == FillMode.Forward || _fillMode == FillMode.Both) - _targetControl.SetValue(_animator.Property, _lastInterpValue, BindingPriority.LocalValue); + if (_fillMode is FillMode.Forward or FillMode.Both) + _targetControl.SetValue(_animator.Property, _lastInterpValue); } private void DoComplete() @@ -123,11 +123,8 @@ namespace Avalonia.Animation private void DoDelay() { - if (_fillMode == FillMode.Backward || _fillMode == FillMode.Both) - if (_currentIteration == 0) - PublishNext(_firstKFValue); - else - PublishNext(_lastInterpValue); + if (_fillMode is not (FillMode.Backward or FillMode.Both)) return; + PublishNext(_currentIteration == 0 ? _firstKFValue : _lastInterpValue); } private void DoPlayStates() @@ -167,9 +164,9 @@ namespace Avalonia.Animation _currentIteration = (ulong)(opsTime / iterationTime); - // Stop animation when the current iteration is beyond the iteration count - // and snap the last iteration value to exact values. - if ((_currentIteration + 1) > _iterationCount) + // Stop animation when the current iteration is beyond the iteration count or + // when the duration is set to zero while animating and snap to the last iterated value. + if (_currentIteration + 1 > _iterationCount || _duration == TimeSpan.Zero) { var easedTime = _easeFunc!.Ease(_playbackReversed ? 0.0 : 1.0); _lastInterpValue = _interpolator(easedTime, _neutralValue); @@ -192,10 +189,10 @@ namespace Avalonia.Animation _playbackReversed = true; break; case PlaybackDirection.Alternate: - _playbackReversed = (_currentIteration % 2 == 0) ? false : true; + _playbackReversed = _currentIteration % 2 != 0; break; case PlaybackDirection.AlternateReverse: - _playbackReversed = (_currentIteration % 2 == 0) ? true : false; + _playbackReversed = _currentIteration % 2 == 0; break; default: throw new InvalidOperationException($"Animation direction value is unknown: {_playbackDirection}"); @@ -215,7 +212,7 @@ namespace Avalonia.Animation iterDelay > 0) { // The last iteration's trailing delay should be skipped. - if ((_currentIteration + 1) < _iterationCount) + if (_currentIteration + 1 < _iterationCount) DoDelay(); else DoComplete(); diff --git a/tests/Avalonia.Base.UnitTests/Animation/AnimatableTests.cs b/tests/Avalonia.Base.UnitTests/Animation/AnimatableTests.cs index d81c6e75f3..7ceaddfa16 100644 --- a/tests/Avalonia.Base.UnitTests/Animation/AnimatableTests.cs +++ b/tests/Avalonia.Base.UnitTests/Animation/AnimatableTests.cs @@ -5,7 +5,6 @@ using Avalonia.Controls.Shapes; using Avalonia.Data; using Avalonia.Layout; using Avalonia.Media; -using Avalonia.PropertyStore; using Avalonia.Styling; using Avalonia.UnitTests; using Moq; @@ -19,18 +18,15 @@ namespace Avalonia.Base.UnitTests.Animation public void Transition_Is_Not_Applied_When_Not_Attached_To_Visual_Tree() { var target = CreateTarget(); - var control = new Control - { - Transitions = new Transitions { target.Object }, - }; + var control = new Control { Transitions = new Transitions { target.Object }, }; control.Opacity = 0.5; target.Verify(x => x.Apply( - control, - It.IsAny(), - 1.0, - 0.5), + control, + It.IsAny(), + 1.0, + 0.5), Times.Never); } @@ -40,10 +36,7 @@ namespace Avalonia.Base.UnitTests.Animation using (Start()) { var target = CreateTarget(); - var control = new Control - { - Transitions = new Transitions { target.Object }, - }; + var control = new Control { Transitions = new Transitions { target.Object }, }; var root = new TestRoot { @@ -51,10 +44,7 @@ namespace Avalonia.Base.UnitTests.Animation { new Style(x => x.OfType()) { - Setters = - { - new Setter(Visual.OpacityProperty, 0.8), - } + Setters = { new Setter(Visual.OpacityProperty, 0.8), } } } }; @@ -64,10 +54,10 @@ namespace Avalonia.Base.UnitTests.Animation Assert.Equal(0.8, control.Opacity); target.Verify(x => x.Apply( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny()), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny()), Times.Never); } } @@ -97,10 +87,10 @@ namespace Avalonia.Base.UnitTests.Animation control.SetValue(Visual.OpacityProperty, 0.5, BindingPriority.Animation); target.Verify(x => x.Apply( - control, - It.IsAny(), - 1.0, - 0.5), + control, + It.IsAny(), + 1.0, + 0.5), Times.Never); } @@ -112,53 +102,34 @@ namespace Avalonia.Base.UnitTests.Animation { var keyframe1 = new KeyFrame() { - Setters = - { - new Setter(Layoutable.WidthProperty, 1d), - }, - KeyTime = TimeSpan.FromSeconds(0) + Setters = { new Setter(Layoutable.WidthProperty, 1d), }, KeyTime = TimeSpan.FromSeconds(0) }; var keyframe2 = new KeyFrame() { - Setters = - { - new Setter(Layoutable.WidthProperty, 2d), - }, - KeyTime = TimeSpan.FromSeconds(2), + Setters = { new Setter(Layoutable.WidthProperty, 2d), }, KeyTime = TimeSpan.FromSeconds(2), }; var keyframe3 = new KeyFrame() { - Setters = - { - new Setter(Layoutable.WidthProperty, invalidValue), - }, + Setters = { new Setter(Layoutable.WidthProperty, invalidValue), }, KeyTime = TimeSpan.FromSeconds(3), }; var animation = new Avalonia.Animation.Animation() { Duration = TimeSpan.FromSeconds(3), - Children = - { - keyframe1, - keyframe2, - keyframe3 - }, + Children = { keyframe1, keyframe2, keyframe3 }, IterationCount = new IterationCount(5), PlaybackDirection = PlaybackDirection.Alternate, }; - var rect = new Rectangle() - { - Width = 11, - }; + var rect = new Rectangle() { Width = 11, }; var originalValue = rect.Width; var clock = new TestClock(); - var animationRun = animation.RunAsync(rect, clock); + animation.RunAsync(rect, clock); clock.Step(TimeSpan.Zero); Assert.Equal(rect.Width, 1); @@ -188,10 +159,10 @@ namespace Avalonia.Base.UnitTests.Animation control.SetValue(Visual.OpacityProperty, 0.8, BindingPriority.StyleTrigger); target.Verify(x => x.Apply( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny()), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny()), Times.Never); } @@ -258,14 +229,15 @@ namespace Avalonia.Base.UnitTests.Animation target.Invocations.Clear(); var root = (TestRoot)control.Parent; + Assert.NotNull(root); root.Child = null; control.Opacity = 0.8; target.Verify(x => x.Apply( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny()), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny()), Times.Never); } @@ -284,6 +256,8 @@ namespace Avalonia.Base.UnitTests.Animation It.IsAny())).Returns(sub.Object); control.Opacity = 0.5; + Assert.NotNull(control.Transitions); + control.Transitions.RemoveAt(0); sub.Verify(x => x.Dispose()); @@ -307,10 +281,10 @@ namespace Avalonia.Base.UnitTests.Animation control.Opacity = 0.5; target.Verify(x => x.Apply( - control, - It.IsAny(), - 1.0, - 0.5), + control, + It.IsAny(), + 1.0, + 0.5), Times.Once); control.Classes.Add("foo"); @@ -324,18 +298,17 @@ namespace Avalonia.Base.UnitTests.Animation { using (Start()) { - var target = CreateTransition(Control.WidthProperty); + var target = CreateTransition(Layoutable.WidthProperty); var control = CreateStyledControl(transition2: target.Object); - var sub = new Mock(); control.Classes.Add("foo"); control.Width = 100; target.Verify(x => x.Apply( - control, - It.IsAny(), - double.NaN, - 100.0), + control, + It.IsAny(), + double.NaN, + 100.0), Times.Once); } } @@ -357,12 +330,12 @@ namespace Avalonia.Base.UnitTests.Animation { Setters = { - new Setter(Border.TransitionsProperty, + new Setter(Animatable.TransitionsProperty, new Transitions { new DoubleTransition { - Property = Border.OpacityProperty, + Property = Visual.OpacityProperty, Duration = TimeSpan.FromSeconds(1), }, }), @@ -372,23 +345,20 @@ namespace Avalonia.Base.UnitTests.Animation { Setters = { - new Setter(Border.TransitionsProperty, + new Setter(Animatable.TransitionsProperty, new Transitions { new DoubleTransition { - Property = Border.OpacityProperty, + Property = Visual.OpacityProperty, Duration = TimeSpan.FromSeconds(1), }, }), - new Setter(Border.OpacityProperty, 0.0), + new Setter(Visual.OpacityProperty, 0.0), }, }, }, - Child = target = new Border - { - Background = Brushes.Red, - } + Child = target = new Border { Background = Brushes.Red, } }; root.Measure(Size.Infinity); @@ -421,7 +391,7 @@ namespace Avalonia.Base.UnitTests.Animation // Assigning and then clearing Transitions ensures we have a transition state // collection created. - control.ClearValue(Control.TransitionsProperty); + control.ClearValue(Animatable.TransitionsProperty); control.GetValueStore().BeginStyling(); @@ -431,8 +401,8 @@ namespace Avalonia.Base.UnitTests.Animation { Setters = { - new Setter(Control.OpacityProperty, 0.5), - new Setter(Control.TransitionsProperty, new Transitions { target.Object }), + new Setter(Visual.OpacityProperty, 0.5), + new Setter(Animatable.TransitionsProperty, new Transitions { target.Object }), } }; @@ -450,28 +420,17 @@ namespace Avalonia.Base.UnitTests.Animation var opacityTransition = new DoubleTransition { - Property = Control.OpacityProperty, - Duration = TimeSpan.FromSeconds(1), + Property = Visual.OpacityProperty, Duration = TimeSpan.FromSeconds(1), }; var transitions = new Transitions { opacityTransition }; var borderTheme = new ControlTheme(typeof(Border)) { - Setters = - { - new Setter(Control.TransitionsProperty, transitions), - } + Setters = { new Setter(Animatable.TransitionsProperty, transitions), } }; var clock = new TestClock(); - var root = new TestRoot - { - Clock = clock, - Resources = - { - { typeof(Border), borderTheme }, - } - }; + var root = new TestRoot { Clock = clock, Resources = { { typeof(Border), borderTheme }, } }; var border = new Border(); root.Child = border; @@ -489,13 +448,214 @@ namespace Avalonia.Base.UnitTests.Animation // Now clear the property; a transition is now in progress but no local value is // set. - border.ClearValue(Border.OpacityProperty); + border.ClearValue(Visual.OpacityProperty); // Remove the transition by removing the control from the logical tree. This was // causing an exception. root.Child = null; } + [Fact] + public void Run_Normal_Use_Case_Animation() + { + using (Start()) + { + var keyframe1 = new KeyFrame() + { + Setters = { new Setter(Visual.OpacityProperty, 1d), }, KeyTime = TimeSpan.FromSeconds(0) + }; + + var keyframe2 = new KeyFrame() + { + Setters = { new Setter(Visual.OpacityProperty, 0.5d), }, KeyTime = TimeSpan.FromSeconds(1) + }; + + var animation = new Avalonia.Animation.Animation() + { + Duration = TimeSpan.FromSeconds(10), Children = { keyframe1, keyframe2 }, + }; + + Border target; + var clock = new TestClock(); + var root = new TestRoot + { + Clock = clock, + Styles = { new Style(x => x.OfType()) { Animations = { animation }, } }, + Child = target = new Border { Background = Brushes.Red, } + }; + + root.Measure(Size.Infinity); + root.Arrange(new Rect(root.DesiredSize)); + + clock.Step(TimeSpan.FromSeconds(0)); + clock.Step(TimeSpan.FromSeconds(0.99)); + + Assert.InRange(target.Opacity, 0.5d, 0.51d); + } + } + + [Fact] + public void Run_Normal_Use_Case_Animation_With_Infinite_Iteration() + { + using (Start()) + { + var keyframe1 = new KeyFrame() + { + Setters = { new Setter(Visual.OpacityProperty, 0d), }, KeyTime = TimeSpan.FromSeconds(0) + }; + + var keyframe2 = new KeyFrame() + { + Setters = { new Setter(Visual.OpacityProperty, 1d), }, KeyTime = TimeSpan.FromSeconds(1) + }; + + var animation = new Avalonia.Animation.Animation() + { + Duration = TimeSpan.FromSeconds(1), + IterationCount = IterationCount.Infinite, + Children = { keyframe1, keyframe2 }, + }; + + Border target; + var clock = new TestClock(); + var root = new TestRoot + { + Clock = clock, + Styles = { new Style(x => x.OfType()) { Animations = { animation }, } }, + Child = target = new Border { Background = Brushes.Red, } + }; + + root.Measure(Size.Infinity); + root.Arrange(new Rect(root.DesiredSize)); + + clock.Step(TimeSpan.FromSeconds(0)); + + clock.Step(TimeSpan.FromSeconds(0.5)); + Assert.Equal(0.5, target.Opacity); + + clock.Step(TimeSpan.FromSeconds(1)); + Assert.Equal(0, target.Opacity); + + clock.Step(TimeSpan.FromSeconds(1.5)); + Assert.Equal(0.5, target.Opacity); + + clock.Step(TimeSpan.FromSeconds(2)); + Assert.Equal(0, target.Opacity); + } + } + + [Fact] + public void Zero_Duration_Should_Finish_Animation() + { + using (Start()) + { + var keyframe1 = new KeyFrame() + { + Setters = { new Setter(Visual.OpacityProperty, 1d), }, KeyTime = TimeSpan.FromSeconds(0) + }; + + var keyframe2 = new KeyFrame() + { + Setters = { new Setter(Visual.OpacityProperty, 0.5d), }, KeyTime = TimeSpan.FromSeconds(2) + }; + + var animation = new Avalonia.Animation.Animation() + { + Duration = TimeSpan.FromSeconds(2), + Children = { keyframe1, keyframe2 }, + FillMode = FillMode.Both + }; + + Border target; + var clock = new TestClock(); + var root = new TestRoot + { + Clock = clock, + Styles = { new Style(x => x.OfType()) { Animations = { animation }, } }, + Child = target = new Border { Background = Brushes.Red, } + }; + + root.Measure(Size.Infinity); + root.Arrange(new Rect(root.DesiredSize)); + + clock.Step(TimeSpan.FromSeconds(0)); + clock.Step(TimeSpan.FromSeconds(1)); + + Assert.True(target.IsAnimating(Visual.OpacityProperty)); + + Assert.Equal(0.75, target.Opacity); + + // This is not the normal way to access and set the animations + // object's Duration property to zero that is defined in styles + // but this is still valid for the RunAsync version. + animation.Duration = TimeSpan.Zero; + + clock.Step(TimeSpan.FromSeconds(1.2)); + + Assert.Equal(0.5, target.Opacity); + Assert.False(target.IsAnimating(Visual.OpacityProperty)); + } + } + + [Fact] + public void Zero_Duration_Should_Finish_Animation_With_Infinite_Iteration() + { + using (Start()) + { + var keyframe1 = new KeyFrame() + { + Setters = { new Setter(Visual.OpacityProperty, 0d), }, KeyTime = TimeSpan.FromSeconds(0) + }; + + var keyframe2 = new KeyFrame() + { + Setters = { new Setter(Visual.OpacityProperty, 1d), }, KeyTime = TimeSpan.FromSeconds(1) + }; + + var animation = new Avalonia.Animation.Animation() + { + Duration = TimeSpan.FromSeconds(1), + IterationCount = IterationCount.Infinite, + Children = { keyframe1, keyframe2 }, + }; + + Border target; + var clock = new TestClock(); + var root = new TestRoot + { + Clock = clock, + Styles = { new Style(x => x.OfType()) { Animations = { animation }, } }, + Child = target = new Border { Background = Brushes.Red, } + }; + + root.Measure(Size.Infinity); + root.Arrange(new Rect(root.DesiredSize)); + + clock.Step(TimeSpan.FromSeconds(0)); + Assert.True(target.IsAnimating(Visual.OpacityProperty)); + + clock.Step(TimeSpan.FromSeconds(0.5)); + Assert.Equal(0.5, target.Opacity); + + clock.Step(TimeSpan.FromSeconds(1)); + Assert.Equal(0, target.Opacity); + + clock.Step(TimeSpan.FromSeconds(1.5)); + Assert.Equal(0.5, target.Opacity); + + clock.Step(TimeSpan.FromSeconds(2)); + Assert.Equal(0, target.Opacity); + + // This is not the normal way to access and set the animations + // object's Duration property to zero that is defined in styles + // but this is still valid for the RunAsync version. + animation.Duration = TimeSpan.Zero; + clock.Step(TimeSpan.FromSeconds(1.2)); + Assert.Equal(1, target.Opacity); + Assert.False(target.IsAnimating(Visual.OpacityProperty)); + } + } + private static IDisposable Start() { var clock = new MockGlobalClock(); @@ -510,12 +670,9 @@ namespace Avalonia.Base.UnitTests.Animation private static Control CreateControl(ITransition transition) { - var control = new Control - { - Transitions = new Transitions { transition }, - }; + var control = new Control { Transitions = new Transitions { transition }, }; - var root = new TestRoot(control); + var _ = new TestRoot(control); return control; } @@ -524,7 +681,7 @@ namespace Avalonia.Base.UnitTests.Animation ITransition transition2 = null) { transition1 = transition1 ?? CreateTarget().Object; - transition2 = transition2 ?? CreateTransition(Control.WidthProperty).Object; + transition2 = transition2 ?? CreateTransition(Layoutable.WidthProperty).Object; var control = new Control { @@ -536,7 +693,7 @@ namespace Avalonia.Base.UnitTests.Animation { new Setter { - Property = Control.TransitionsProperty, + Property = Animatable.TransitionsProperty, Value = new Transitions { transition1 }, } } @@ -547,7 +704,7 @@ namespace Avalonia.Base.UnitTests.Animation { new Setter { - Property = Control.TransitionsProperty, + Property = Animatable.TransitionsProperty, Value = new Transitions { transition2 }, } } @@ -555,7 +712,7 @@ namespace Avalonia.Base.UnitTests.Animation } }; - var root = new TestRoot(control); + var _ = new TestRoot(control); return control; } diff --git a/tests/Avalonia.Base.UnitTests/Animation/AnimationIterationTests.cs b/tests/Avalonia.Base.UnitTests/Animation/AnimationIterationTests.cs index 58e908aca9..dde59365a1 100644 --- a/tests/Avalonia.Base.UnitTests/Animation/AnimationIterationTests.cs +++ b/tests/Avalonia.Base.UnitTests/Animation/AnimationIterationTests.cs @@ -1,16 +1,13 @@ using System; -using System.Linq; -using System.Text; using System.Threading.Tasks; using Avalonia.Animation; using Avalonia.Controls; using Avalonia.Styling; -using Avalonia.UnitTests; -using Avalonia.Data; using Xunit; using Avalonia.Animation.Easings; using System.Threading; using System.Reactive.Linq; +using Avalonia.Layout; namespace Avalonia.Base.UnitTests.Animation { @@ -23,47 +20,27 @@ namespace Avalonia.Base.UnitTests.Animation { var keyframe1 = new KeyFrame() { - Setters = - { - new Setter(Border.WidthProperty, 100d), - }, - KeyTime = TimeSpan.FromSeconds(0.5) + Setters = { new Setter(Layoutable.WidthProperty, 100d), }, KeyTime = TimeSpan.FromSeconds(0.5) }; var keyframe2 = new KeyFrame() { - Setters = - { - new Setter(Border.WidthProperty, 0d), - }, - KeyTime = TimeSpan.FromSeconds(0) + Setters = { new Setter(Layoutable.WidthProperty, 0d), }, KeyTime = TimeSpan.FromSeconds(0) }; - var animation = new Avalonia.Animation.Animation() - { - Duration = TimeSpan.FromSeconds(1), - Children = - { - keyframe2, - keyframe1 - } - }; + var animation = new Animation() { Duration = TimeSpan.FromSeconds(1), Children = { keyframe2, keyframe1 } }; - var border = new Border() - { - Height = 100d, - Width = 100d - }; + var border = new Border() { Height = 100d, Width = 100d }; var clock = new TestClock(); - var animationRun = animation.RunAsync(border, clock); - clock.Step(TimeSpan.Zero); + animation.RunAsync(border, clock); + + clock.Step(TimeSpan.Zero); Assert.Equal(border.Width, 0d); - clock.Step(TimeSpan.FromSeconds(1)); + clock.Step(TimeSpan.FromSeconds(1)); Assert.Equal(border.Width, 100d); - } @@ -72,40 +49,24 @@ namespace Avalonia.Base.UnitTests.Animation { var keyframe1 = new KeyFrame() { - Setters = - { - new Setter(Border.WidthProperty, 200d), - }, - Cue = new Cue(1d) + Setters = { new Setter(Layoutable.WidthProperty, 200d), }, Cue = new Cue(1d) }; var keyframe2 = new KeyFrame() { - Setters = - { - new Setter(Border.WidthProperty, 100d), - }, - Cue = new Cue(0d) + Setters = { new Setter(Layoutable.WidthProperty, 100d), }, Cue = new Cue(0d) }; - var animation = new Avalonia.Animation.Animation() + var animation = new Animation() { Duration = TimeSpan.FromSeconds(3), Delay = TimeSpan.FromSeconds(3), DelayBetweenIterations = TimeSpan.FromSeconds(3), IterationCount = new IterationCount(2), - Children = - { - keyframe2, - keyframe1 - } + Children = { keyframe2, keyframe1 } }; - var border = new Border() - { - Height = 100d, - Width = 100d - }; + var border = new Border() { Height = 100d, Width = 100d }; var clock = new TestClock(); var animationRun = animation.RunAsync(border, clock); @@ -133,43 +94,28 @@ namespace Avalonia.Base.UnitTests.Animation { var keyframe1 = new KeyFrame() { - Setters = - { - new Setter(Border.WidthProperty, 0d), - }, - Cue = new Cue(0.0d) + Setters = { new Setter(Layoutable.WidthProperty, 0d), }, Cue = new Cue(0.0d) }; var keyframe2 = new KeyFrame() { - Setters = - { - new Setter(Border.WidthProperty, 300d), - }, - Cue = new Cue(1.0d) + Setters = { new Setter(Layoutable.WidthProperty, 300d), }, Cue = new Cue(1.0d) }; - var animation = new Avalonia.Animation.Animation() + var animation = new Animation() { Duration = TimeSpan.FromSeconds(0.05d), Delay = TimeSpan.FromSeconds(0.05d), Easing = new SineEaseInOut(), FillMode = FillMode.Both, - Children = - { - keyframe1, - keyframe2 - } + Children = { keyframe1, keyframe2 } }; - var border = new Border() - { - Height = 100d, - Width = 100d, - }; + var border = new Border() { Height = 100d, Width = 100d, }; var clock = new TestClock(); - var animationRun = animation.RunAsync(border, clock); + + animation.RunAsync(border, clock); clock.Step(TimeSpan.FromSeconds(0d)); Assert.Equal(border.Width, 0d); @@ -186,20 +132,12 @@ namespace Avalonia.Base.UnitTests.Animation { var keyframe1 = new KeyFrame() { - Setters = - { - new Setter(Border.WidthProperty, 200d), - }, - Cue = new Cue(1d) + Setters = { new Setter(Layoutable.WidthProperty, 200d), }, Cue = new Cue(1d) }; var keyframe2 = new KeyFrame() { - Setters = - { - new Setter(Border.WidthProperty, 100d), - }, - Cue = new Cue(0d) + Setters = { new Setter(Layoutable.WidthProperty, 100d), }, Cue = new Cue(0d) }; var animation = new Animation() @@ -208,23 +146,15 @@ namespace Avalonia.Base.UnitTests.Animation Delay = TimeSpan.FromSeconds(0), DelayBetweenIterations = TimeSpan.FromSeconds(0), IterationCount = new IterationCount(1), - Children = - { - keyframe2, - keyframe1 - } + Children = { keyframe2, keyframe1 } }; - var border = new Border() - { - Height = 100d, - Width = 50d - }; + var border = new Border() { Height = 100d, Width = 50d }; var propertyChangedCount = 0; var animationCompletedCount = 0; - border.PropertyChanged += (sender, e) => + border.PropertyChanged += (_, e) => { - if (e.Property == Control.WidthProperty) + if (e.Property == Layoutable.WidthProperty) { propertyChangedCount++; } @@ -257,20 +187,12 @@ namespace Avalonia.Base.UnitTests.Animation { var keyframe1 = new KeyFrame() { - Setters = - { - new Setter(Border.WidthProperty, 200d), - }, - Cue = new Cue(1d) + Setters = { new Setter(Layoutable.WidthProperty, 200d), }, Cue = new Cue(1d) }; var keyframe2 = new KeyFrame() { - Setters = - { - new Setter(Border.WidthProperty, 100d), - }, - Cue = new Cue(0d) + Setters = { new Setter(Layoutable.WidthProperty, 100d), }, Cue = new Cue(0d) }; var animation = new Animation() @@ -279,22 +201,14 @@ namespace Avalonia.Base.UnitTests.Animation Delay = TimeSpan.FromSeconds(0), DelayBetweenIterations = TimeSpan.FromSeconds(0), IterationCount = new IterationCount(1), - Children = - { - keyframe2, - keyframe1 - } + Children = { keyframe2, keyframe1 } }; - var border = new Border() - { - Height = 100d, - Width = 100d - }; + var border = new Border() { Height = 100d, Width = 100d }; var propertyChangedCount = 0; - border.PropertyChanged += (sender, e) => + border.PropertyChanged += (_, e) => { - if (e.Property == Control.WidthProperty) + if (e.Property == Layoutable.WidthProperty) { propertyChangedCount++; } @@ -315,20 +229,12 @@ namespace Avalonia.Base.UnitTests.Animation { var keyframe1 = new KeyFrame() { - Setters = - { - new Setter(Border.WidthProperty, 200d), - }, - Cue = new Cue(1d) + Setters = { new Setter(Layoutable.WidthProperty, 200d), }, Cue = new Cue(1d) }; var keyframe2 = new KeyFrame() { - Setters = - { - new Setter(Border.WidthProperty, 100d), - }, - Cue = new Cue(0d) + Setters = { new Setter(Layoutable.WidthProperty, 100d), }, Cue = new Cue(0d) }; var animation = new Animation() @@ -337,22 +243,14 @@ namespace Avalonia.Base.UnitTests.Animation Delay = TimeSpan.FromSeconds(0), DelayBetweenIterations = TimeSpan.FromSeconds(0), IterationCount = new IterationCount(1), - Children = - { - keyframe2, - keyframe1 - } + Children = { keyframe2, keyframe1 } }; - var border = new Border() - { - Height = 100d, - Width = 50d - }; + var border = new Border() { Height = 100d, Width = 50d }; var propertyChangedCount = 0; - border.PropertyChanged += (sender, e) => + border.PropertyChanged += (_, e) => { - if (e.Property == Control.WidthProperty) + if (e.Property == Layoutable.WidthProperty) { propertyChangedCount++; } @@ -361,6 +259,7 @@ namespace Avalonia.Base.UnitTests.Animation var clock = new TestClock(); var cancellationTokenSource = new CancellationTokenSource(); var animationRun = animation.RunAsync(border, clock, cancellationTokenSource.Token); + Assert.False(animationRun.IsCompleted); Assert.Equal(0, propertyChangedCount); @@ -381,24 +280,16 @@ namespace Avalonia.Base.UnitTests.Animation } [Fact] - public void Cancellation_Of_Completed_Animation_Does_Not_Fail() + public void Dont_Run_Infinite_Iteration_Animation_On_RunAsync_Method() { var keyframe1 = new KeyFrame() { - Setters = - { - new Setter(Border.WidthProperty, 200d), - }, - Cue = new Cue(1d) + Setters = { new Setter(Layoutable.WidthProperty, 200d), }, Cue = new Cue(1d) }; var keyframe2 = new KeyFrame() { - Setters = - { - new Setter(Border.WidthProperty, 100d), - }, - Cue = new Cue(0d) + Setters = { new Setter(Layoutable.WidthProperty, 100d), }, Cue = new Cue(0d) }; var animation = new Animation() @@ -406,23 +297,47 @@ namespace Avalonia.Base.UnitTests.Animation Duration = TimeSpan.FromSeconds(10), Delay = TimeSpan.FromSeconds(0), DelayBetweenIterations = TimeSpan.FromSeconds(0), - IterationCount = new IterationCount(1), - Children = - { - keyframe2, - keyframe1 - } + IterationCount = IterationCount.Infinite, + Children = { keyframe2, keyframe1 } }; - var border = new Border() + var border = new Border() { Height = 100d, Width = 50d }; + var clock = new TestClock(); + var cancellationTokenSource = new CancellationTokenSource(); + var animationRun = animation.RunAsync(border, clock, cancellationTokenSource.Token); + + + Assert.True(animationRun.IsCompleted); + Assert.NotNull(animationRun.Exception); + } + + [Fact] + public void Cancellation_Of_Completed_Animation_Does_Not_Fail() + { + var keyframe1 = new KeyFrame() + { + Setters = { new Setter(Layoutable.WidthProperty, 200d), }, Cue = new Cue(1d) + }; + + var keyframe2 = new KeyFrame() { - Height = 100d, - Width = 50d + Setters = { new Setter(Layoutable.WidthProperty, 100d), }, Cue = new Cue(0d) }; + + var animation = new Animation() + { + Duration = TimeSpan.FromSeconds(10), + Delay = TimeSpan.FromSeconds(0), + DelayBetweenIterations = TimeSpan.FromSeconds(0), + IterationCount = new IterationCount(1), + Children = { keyframe2, keyframe1 } + }; + + var border = new Border() { Height = 100d, Width = 50d }; var propertyChangedCount = 0; - border.PropertyChanged += (sender, e) => + border.PropertyChanged += (_, e) => { - if (e.Property == Control.WidthProperty) + if (e.Property == Layoutable.WidthProperty) { propertyChangedCount++; } diff --git a/tests/Avalonia.Base.UnitTests/Animation/BrushTransitionTests.cs b/tests/Avalonia.Base.UnitTests/Animation/BrushTransitionTests.cs index acc32fd6a2..363ecafddb 100644 --- a/tests/Avalonia.Base.UnitTests/Animation/BrushTransitionTests.cs +++ b/tests/Avalonia.Base.UnitTests/Animation/BrushTransitionTests.cs @@ -36,8 +36,7 @@ namespace Avalonia.Base.UnitTests.Animation var border = new Border() { Background = oldBrush }; BrushTransition sut = new BrushTransition { - Duration = TimeSpan.FromSeconds(1), - Property = Border.BackgroundProperty + Duration = TimeSpan.FromSeconds(1), Property = Border.BackgroundProperty }; sut.Apply(border, clock, oldBrush, newBrush); @@ -45,7 +44,8 @@ namespace Avalonia.Base.UnitTests.Animation clock.Pulse(sut.Duration * progress); Assert.NotNull(border.Background); - Assert.Equal(oldBrush.Opacity + (newBrush.Opacity - oldBrush.Opacity) * progress, border.Background.Opacity); + Assert.Equal(oldBrush.Opacity + (newBrush.Opacity - oldBrush.Opacity) * progress, + border.Background.Opacity); } } } diff --git a/tests/Avalonia.Base.UnitTests/Animation/KeySplineTests.cs b/tests/Avalonia.Base.UnitTests/Animation/KeySplineTests.cs index 3cd12d92ec..61e8103bd5 100644 --- a/tests/Avalonia.Base.UnitTests/Animation/KeySplineTests.cs +++ b/tests/Avalonia.Base.UnitTests/Animation/KeySplineTests.cs @@ -21,6 +21,8 @@ namespace Avalonia.Base.UnitTests.Animation var keySpline = (KeySpline)conv.ConvertFrom(input); + Assert.NotNull(keySpline); + Assert.Equal(1, keySpline.ControlPointX1); Assert.Equal(2, keySpline.ControlPointY1); Assert.Equal(3, keySpline.ControlPointX2); @@ -28,8 +30,8 @@ namespace Avalonia.Base.UnitTests.Animation } [Theory] - [InlineData("1,2F,3,4")] - [InlineData("Foo,Bar,Fee,Buzz")] + [InlineData("1,2F,3,4")] + [InlineData("Foo,Bar,Fee,Buzz")] public void Can_Handle_Invalid_String_KeySpline_Via_TypeConverter(string input) { var conv = new KeySplineTypeConverter(); @@ -104,46 +106,33 @@ namespace Avalonia.Base.UnitTests.Animation { var keyframe1 = new KeyFrame() { - Setters = - { - new Setter(RotateTransform.AngleProperty, -2.5d), - }, - KeyTime = TimeSpan.FromSeconds(0) + Setters = { new Setter(RotateTransform.AngleProperty, -2.5d), }, KeyTime = TimeSpan.FromSeconds(0) }; var keyframe2 = new KeyFrame() { - Setters = - { - new Setter(RotateTransform.AngleProperty, 2.5d), - }, + Setters = { new Setter(RotateTransform.AngleProperty, 2.5d), }, KeyTime = TimeSpan.FromSeconds(5), KeySpline = new KeySpline(0.1123555056179775, - 0.657303370786517, - 0.8370786516853934, - 0.499999999999999999) + 0.657303370786517, + 0.8370786516853934, + 0.499999999999999999) }; var animation = new Avalonia.Animation.Animation() { Duration = TimeSpan.FromSeconds(5), - Children = - { - keyframe1, - keyframe2 - }, + Children = { keyframe1, keyframe2 }, IterationCount = new IterationCount(5), PlaybackDirection = PlaybackDirection.Alternate }; var rotateTransform = new RotateTransform(-2.5); - var rect = new Rectangle() - { - RenderTransform = rotateTransform - }; + var rect = new Rectangle() { RenderTransform = rotateTransform }; var clock = new TestClock(); - var animationRun = animation.RunAsync(rect, clock); + + animation.RunAsync(rect, clock); // position is what you'd expect at end and beginning clock.Step(TimeSpan.Zero); @@ -169,49 +158,36 @@ namespace Avalonia.Base.UnitTests.Animation expected = 1.8016358493761722; Assert.True(Math.Abs(rotateTransform.Angle - expected) <= tolerance); } - + [Fact] public void Check_KeySpline_Parsing_Is_Correct() { var keyframe1 = new KeyFrame() { - Setters = - { - new Setter(RotateTransform.AngleProperty, -2.5d), - }, - KeyTime = TimeSpan.FromSeconds(0) + Setters = { new Setter(RotateTransform.AngleProperty, -2.5d), }, KeyTime = TimeSpan.FromSeconds(0) }; var keyframe2 = new KeyFrame() { - Setters = - { - new Setter(RotateTransform.AngleProperty, 2.5d), - }, - KeyTime = TimeSpan.FromSeconds(5), + Setters = { new Setter(RotateTransform.AngleProperty, 2.5d), }, KeyTime = TimeSpan.FromSeconds(5), }; var animation = new Avalonia.Animation.Animation() { Duration = TimeSpan.FromSeconds(5), - Children = - { - keyframe1, - keyframe2 - }, + Children = { keyframe1, keyframe2 }, IterationCount = new IterationCount(5), PlaybackDirection = PlaybackDirection.Alternate, - Easing = Easing.Parse("0.1123555056179775,0.657303370786517,0.8370786516853934,0.499999999999999999") + Easing = Easing.Parse( + "0.1123555056179775,0.657303370786517,0.8370786516853934,0.499999999999999999") }; var rotateTransform = new RotateTransform(-2.5); - var rect = new Rectangle() - { - RenderTransform = rotateTransform - }; + var rect = new Rectangle() { RenderTransform = rotateTransform }; var clock = new TestClock(); - var animationRun = animation.RunAsync(rect, clock); + + animation.RunAsync(rect, clock); // position is what you'd expect at end and beginning clock.Step(TimeSpan.Zero); diff --git a/tests/Avalonia.Base.UnitTests/Animation/SpringTests.cs b/tests/Avalonia.Base.UnitTests/Animation/SpringTests.cs index 47c0e48033..ed2c00e63c 100644 --- a/tests/Avalonia.Base.UnitTests/Animation/SpringTests.cs +++ b/tests/Avalonia.Base.UnitTests/Animation/SpringTests.cs @@ -18,6 +18,7 @@ public class SpringTests var spring = (Spring)conv.ConvertFrom(input); + Assert.NotNull(spring); Assert.Equal(1, spring.Mass); Assert.Equal(2, spring.Stiffness); Assert.Equal(3, spring.Damping); @@ -25,8 +26,8 @@ public class SpringTests } [Theory] - [InlineData("1,2F,3,4")] - [InlineData("Foo,Bar,Fee,Buzz")] + [InlineData("1,2F,3,4")] + [InlineData("Foo,Bar,Fee,Buzz")] public void Can_Handle_Invalid_String_Via_TypeConverter(string input) { var conv = new SpringTypeConverter(); @@ -37,7 +38,7 @@ public class SpringTests [Fact] public void SplineEasing_Can_Be_Mutated() { - var easing = new SpringEasing(1, 1, 1, 0); + var easing = new SpringEasing(1, 1, 1); Assert.Equal(0, easing.Ease(0)); Assert.Equal(0.34029984660829826, easing.Ease(1)); @@ -55,43 +56,28 @@ public class SpringTests { var keyframe1 = new KeyFrame() { - Setters = - { - new Setter(RotateTransform.AngleProperty, -2.5d), - }, - KeyTime = TimeSpan.FromSeconds(0) + Setters = { new Setter(RotateTransform.AngleProperty, -2.5d), }, KeyTime = TimeSpan.FromSeconds(0) }; var keyframe2 = new KeyFrame() { - Setters = - { - new Setter(RotateTransform.AngleProperty, 2.5d), - }, - KeyTime = TimeSpan.FromSeconds(5) + Setters = { new Setter(RotateTransform.AngleProperty, 2.5d), }, KeyTime = TimeSpan.FromSeconds(5) }; var animation = new Avalonia.Animation.Animation() { Duration = TimeSpan.FromSeconds(5), - Children = - { - keyframe1, - keyframe2 - }, + Children = { keyframe1, keyframe2 }, IterationCount = new IterationCount(5), PlaybackDirection = PlaybackDirection.Alternate, - Easing = new SpringEasing(1, 10, 1, 0) + Easing = new SpringEasing(1, 10, 1) }; var rotateTransform = new RotateTransform(-2.5); - var rect = new Rectangle() - { - RenderTransform = rotateTransform - }; + var rect = new Rectangle() { RenderTransform = rotateTransform }; var clock = new TestClock(); - var animationRun = animation.RunAsync(rect, clock); + animation.RunAsync(rect, clock); clock.Step(TimeSpan.Zero); Assert.Equal(rotateTransform.Angle, -2.5); diff --git a/tests/Avalonia.Base.UnitTests/Animation/TransitionsTests.cs b/tests/Avalonia.Base.UnitTests/Animation/TransitionsTests.cs index 65cf90b642..2737c2cebf 100644 --- a/tests/Avalonia.Base.UnitTests/Animation/TransitionsTests.cs +++ b/tests/Avalonia.Base.UnitTests/Animation/TransitionsTests.cs @@ -15,13 +15,12 @@ namespace Avalonia.Base.UnitTests.Animation var border = new Border { Transitions = new Transitions + { + new DoubleTransition { - new DoubleTransition - { - Duration = TimeSpan.FromSeconds(1), - Property = Border.OpacityProperty, - } + Duration = TimeSpan.FromSeconds(1), Property = Visual.OpacityProperty, } + } }; border.Opacity = 0; @@ -30,7 +29,6 @@ namespace Avalonia.Base.UnitTests.Animation clock.Pulse(TimeSpan.FromSeconds(-0.5)); Assert.Equal(0, border.Opacity); - } [Fact] @@ -41,13 +39,12 @@ namespace Avalonia.Base.UnitTests.Animation var border = new Border { Transitions = new Transitions + { + new DoubleTransition { - new DoubleTransition - { - Duration = TimeSpan.FromSeconds(1), - Property = Border.OpacityProperty, - } + Duration = TimeSpan.FromSeconds(1), Property = Visual.OpacityProperty, } + } }; border.Opacity = 0; @@ -56,7 +53,6 @@ namespace Avalonia.Base.UnitTests.Animation clock.Pulse(TimeSpan.FromMilliseconds(1001)); Assert.Equal(0, border.Opacity); - } [Fact] @@ -64,18 +60,22 @@ namespace Avalonia.Base.UnitTests.Animation { var clock = new TestClock(); - int i = 0; - var inst = new TransitionInstance(clock, TimeSpan.Zero, TimeSpan.Zero).Subscribe(nextValue => + var i = 0; + + new TransitionInstance(clock, TimeSpan.Zero, TimeSpan.Zero).Subscribe(nextValue => { switch (i++) { - case 0: Assert.Equal(0, nextValue); break; - case 1: Assert.Equal(1d, nextValue); break; + case 0: + Assert.Equal(0, nextValue); + break; + case 1: + Assert.Equal(1d, nextValue); + break; } }); clock.Pulse(TimeSpan.FromMilliseconds(10)); - } [Fact] @@ -83,26 +83,50 @@ namespace Avalonia.Base.UnitTests.Animation { var clock = new TestClock(); - int i = -1; - var inst = new TransitionInstance(clock, TimeSpan.FromMilliseconds(30), TimeSpan.FromMilliseconds(70)).Subscribe(nextValue => - { - switch (i++) + var i = -1; + + new TransitionInstance(clock, TimeSpan.FromMilliseconds(30), TimeSpan.FromMilliseconds(70)).Subscribe( + nextValue => { - case 0: Assert.Equal(0, nextValue); break; - case 1: Assert.Equal(0, nextValue); break; - case 2: Assert.Equal(0, nextValue); break; - case 3: Assert.Equal(0, nextValue); break; - case 4: Assert.Equal(Math.Round(10d / 70d, 4), Math.Round(nextValue, 4)); break; - case 5: Assert.Equal(Math.Round(20d / 70d, 4), Math.Round(nextValue, 4)); break; - case 6: Assert.Equal(Math.Round(30d / 70d, 4), Math.Round(nextValue, 4)); break; - case 7: Assert.Equal(Math.Round(40d / 70d, 4), Math.Round(nextValue, 4)); break; - case 8: Assert.Equal(Math.Round(50d / 70d, 4), Math.Round(nextValue, 4)); break; - case 9: Assert.Equal(Math.Round(60d / 70d, 4), Math.Round(nextValue, 4)); break; - case 10: Assert.Equal(1d, nextValue); break; - } - }); + switch (i++) + { + case 0: + Assert.Equal(0, nextValue); + break; + case 1: + Assert.Equal(0, nextValue); + break; + case 2: + Assert.Equal(0, nextValue); + break; + case 3: + Assert.Equal(0, nextValue); + break; + case 4: + Assert.Equal(Math.Round(10d / 70d, 4), Math.Round(nextValue, 4)); + break; + case 5: + Assert.Equal(Math.Round(20d / 70d, 4), Math.Round(nextValue, 4)); + break; + case 6: + Assert.Equal(Math.Round(30d / 70d, 4), Math.Round(nextValue, 4)); + break; + case 7: + Assert.Equal(Math.Round(40d / 70d, 4), Math.Round(nextValue, 4)); + break; + case 8: + Assert.Equal(Math.Round(50d / 70d, 4), Math.Round(nextValue, 4)); + break; + case 9: + Assert.Equal(Math.Round(60d / 70d, 4), Math.Round(nextValue, 4)); + break; + case 10: + Assert.Equal(1d, nextValue); + break; + } + }); - for (int z = 0; z <= 10; z++) + for (var z = 0; z <= 10; z++) { clock.Pulse(TimeSpan.FromMilliseconds(10)); }