Browse Source

Merge pull request #11800 from AvaloniaUI/fixes/zero-dur-and-unittestfixes

Implement stopping animations with zero duration + UI Animations maintenance sweep
pull/11811/head
Jumar Macato 3 years ago
committed by GitHub
parent
commit
58ed081812
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 29
      src/Avalonia.Base/Animation/AnimationInstance`1.cs
  2. 365
      tests/Avalonia.Base.UnitTests/Animation/AnimatableTests.cs
  3. 241
      tests/Avalonia.Base.UnitTests/Animation/AnimationIterationTests.cs
  4. 6
      tests/Avalonia.Base.UnitTests/Animation/BrushTransitionTests.cs
  5. 68
      tests/Avalonia.Base.UnitTests/Animation/KeySplineTests.cs
  6. 34
      tests/Avalonia.Base.UnitTests/Animation/SpringTests.cs
  7. 94
      tests/Avalonia.Base.UnitTests/Animation/TransitionsTests.cs

29
src/Avalonia.Base/Animation/AnimationInstance`1.cs

@ -53,10 +53,10 @@ namespace Avalonia.Animation
private void FetchProperties() private void FetchProperties()
{ {
if (_animation.SpeedRatio < 0d) 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) if (_animation.Duration < TimeSpan.Zero)
throw new InvalidOperationException("Duration value cannot be negative or zero."); throw new InvalidOperationException("Duration value cannot be negative.");
_easeFunc = _animation.Easing; _easeFunc = _animation.Easing;
@ -110,8 +110,8 @@ namespace Avalonia.Animation
{ {
if (_animator.Property is null) if (_animator.Property is null)
throw new InvalidOperationException("Animator has no property specified."); throw new InvalidOperationException("Animator has no property specified.");
if (_fillMode == FillMode.Forward || _fillMode == FillMode.Both) if (_fillMode is FillMode.Forward or FillMode.Both)
_targetControl.SetValue(_animator.Property, _lastInterpValue, BindingPriority.LocalValue); _targetControl.SetValue(_animator.Property, _lastInterpValue);
} }
private void DoComplete() private void DoComplete()
@ -123,11 +123,8 @@ namespace Avalonia.Animation
private void DoDelay() private void DoDelay()
{ {
if (_fillMode == FillMode.Backward || _fillMode == FillMode.Both) if (_fillMode is not (FillMode.Backward or FillMode.Both)) return;
if (_currentIteration == 0) PublishNext(_currentIteration == 0 ? _firstKFValue : _lastInterpValue);
PublishNext(_firstKFValue);
else
PublishNext(_lastInterpValue);
} }
private void DoPlayStates() private void DoPlayStates()
@ -167,9 +164,9 @@ namespace Avalonia.Animation
_currentIteration = (ulong)(opsTime / iterationTime); _currentIteration = (ulong)(opsTime / iterationTime);
// Stop animation when the current iteration is beyond the iteration count // Stop animation when the current iteration is beyond the iteration count or
// and snap the last iteration value to exact values. // when the duration is set to zero while animating and snap to the last iterated value.
if ((_currentIteration + 1) > _iterationCount) if (_currentIteration + 1 > _iterationCount || _duration == TimeSpan.Zero)
{ {
var easedTime = _easeFunc!.Ease(_playbackReversed ? 0.0 : 1.0); var easedTime = _easeFunc!.Ease(_playbackReversed ? 0.0 : 1.0);
_lastInterpValue = _interpolator(easedTime, _neutralValue); _lastInterpValue = _interpolator(easedTime, _neutralValue);
@ -192,10 +189,10 @@ namespace Avalonia.Animation
_playbackReversed = true; _playbackReversed = true;
break; break;
case PlaybackDirection.Alternate: case PlaybackDirection.Alternate:
_playbackReversed = (_currentIteration % 2 == 0) ? false : true; _playbackReversed = _currentIteration % 2 != 0;
break; break;
case PlaybackDirection.AlternateReverse: case PlaybackDirection.AlternateReverse:
_playbackReversed = (_currentIteration % 2 == 0) ? true : false; _playbackReversed = _currentIteration % 2 == 0;
break; break;
default: default:
throw new InvalidOperationException($"Animation direction value is unknown: {_playbackDirection}"); throw new InvalidOperationException($"Animation direction value is unknown: {_playbackDirection}");
@ -215,7 +212,7 @@ namespace Avalonia.Animation
iterDelay > 0) iterDelay > 0)
{ {
// The last iteration's trailing delay should be skipped. // The last iteration's trailing delay should be skipped.
if ((_currentIteration + 1) < _iterationCount) if (_currentIteration + 1 < _iterationCount)
DoDelay(); DoDelay();
else else
DoComplete(); DoComplete();

365
tests/Avalonia.Base.UnitTests/Animation/AnimatableTests.cs

@ -5,7 +5,6 @@ using Avalonia.Controls.Shapes;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.PropertyStore;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.UnitTests; using Avalonia.UnitTests;
using Moq; using Moq;
@ -19,18 +18,15 @@ namespace Avalonia.Base.UnitTests.Animation
public void Transition_Is_Not_Applied_When_Not_Attached_To_Visual_Tree() public void Transition_Is_Not_Applied_When_Not_Attached_To_Visual_Tree()
{ {
var target = CreateTarget(); var target = CreateTarget();
var control = new Control var control = new Control { Transitions = new Transitions { target.Object }, };
{
Transitions = new Transitions { target.Object },
};
control.Opacity = 0.5; control.Opacity = 0.5;
target.Verify(x => x.Apply( target.Verify(x => x.Apply(
control, control,
It.IsAny<IClock>(), It.IsAny<IClock>(),
1.0, 1.0,
0.5), 0.5),
Times.Never); Times.Never);
} }
@ -40,10 +36,7 @@ namespace Avalonia.Base.UnitTests.Animation
using (Start()) using (Start())
{ {
var target = CreateTarget(); var target = CreateTarget();
var control = new Control var control = new Control { Transitions = new Transitions { target.Object }, };
{
Transitions = new Transitions { target.Object },
};
var root = new TestRoot var root = new TestRoot
{ {
@ -51,10 +44,7 @@ namespace Avalonia.Base.UnitTests.Animation
{ {
new Style(x => x.OfType<Control>()) new Style(x => x.OfType<Control>())
{ {
Setters = Setters = { new Setter(Visual.OpacityProperty, 0.8), }
{
new Setter(Visual.OpacityProperty, 0.8),
}
} }
} }
}; };
@ -64,10 +54,10 @@ namespace Avalonia.Base.UnitTests.Animation
Assert.Equal(0.8, control.Opacity); Assert.Equal(0.8, control.Opacity);
target.Verify(x => x.Apply( target.Verify(x => x.Apply(
It.IsAny<Control>(), It.IsAny<Control>(),
It.IsAny<IClock>(), It.IsAny<IClock>(),
It.IsAny<object>(), It.IsAny<object>(),
It.IsAny<object>()), It.IsAny<object>()),
Times.Never); Times.Never);
} }
} }
@ -97,10 +87,10 @@ namespace Avalonia.Base.UnitTests.Animation
control.SetValue(Visual.OpacityProperty, 0.5, BindingPriority.Animation); control.SetValue(Visual.OpacityProperty, 0.5, BindingPriority.Animation);
target.Verify(x => x.Apply( target.Verify(x => x.Apply(
control, control,
It.IsAny<IClock>(), It.IsAny<IClock>(),
1.0, 1.0,
0.5), 0.5),
Times.Never); Times.Never);
} }
@ -112,53 +102,34 @@ namespace Avalonia.Base.UnitTests.Animation
{ {
var keyframe1 = new KeyFrame() var keyframe1 = new KeyFrame()
{ {
Setters = Setters = { new Setter(Layoutable.WidthProperty, 1d), }, KeyTime = TimeSpan.FromSeconds(0)
{
new Setter(Layoutable.WidthProperty, 1d),
},
KeyTime = TimeSpan.FromSeconds(0)
}; };
var keyframe2 = new KeyFrame() var keyframe2 = new KeyFrame()
{ {
Setters = Setters = { new Setter(Layoutable.WidthProperty, 2d), }, KeyTime = TimeSpan.FromSeconds(2),
{
new Setter(Layoutable.WidthProperty, 2d),
},
KeyTime = TimeSpan.FromSeconds(2),
}; };
var keyframe3 = new KeyFrame() var keyframe3 = new KeyFrame()
{ {
Setters = Setters = { new Setter(Layoutable.WidthProperty, invalidValue), },
{
new Setter(Layoutable.WidthProperty, invalidValue),
},
KeyTime = TimeSpan.FromSeconds(3), KeyTime = TimeSpan.FromSeconds(3),
}; };
var animation = new Avalonia.Animation.Animation() var animation = new Avalonia.Animation.Animation()
{ {
Duration = TimeSpan.FromSeconds(3), Duration = TimeSpan.FromSeconds(3),
Children = Children = { keyframe1, keyframe2, keyframe3 },
{
keyframe1,
keyframe2,
keyframe3
},
IterationCount = new IterationCount(5), IterationCount = new IterationCount(5),
PlaybackDirection = PlaybackDirection.Alternate, PlaybackDirection = PlaybackDirection.Alternate,
}; };
var rect = new Rectangle() var rect = new Rectangle() { Width = 11, };
{
Width = 11,
};
var originalValue = rect.Width; var originalValue = rect.Width;
var clock = new TestClock(); var clock = new TestClock();
var animationRun = animation.RunAsync(rect, clock); animation.RunAsync(rect, clock);
clock.Step(TimeSpan.Zero); clock.Step(TimeSpan.Zero);
Assert.Equal(rect.Width, 1); Assert.Equal(rect.Width, 1);
@ -188,10 +159,10 @@ namespace Avalonia.Base.UnitTests.Animation
control.SetValue(Visual.OpacityProperty, 0.8, BindingPriority.StyleTrigger); control.SetValue(Visual.OpacityProperty, 0.8, BindingPriority.StyleTrigger);
target.Verify(x => x.Apply( target.Verify(x => x.Apply(
It.IsAny<Control>(), It.IsAny<Control>(),
It.IsAny<IClock>(), It.IsAny<IClock>(),
It.IsAny<object>(), It.IsAny<object>(),
It.IsAny<object>()), It.IsAny<object>()),
Times.Never); Times.Never);
} }
@ -258,14 +229,15 @@ namespace Avalonia.Base.UnitTests.Animation
target.Invocations.Clear(); target.Invocations.Clear();
var root = (TestRoot)control.Parent; var root = (TestRoot)control.Parent;
Assert.NotNull(root);
root.Child = null; root.Child = null;
control.Opacity = 0.8; control.Opacity = 0.8;
target.Verify(x => x.Apply( target.Verify(x => x.Apply(
It.IsAny<Control>(), It.IsAny<Control>(),
It.IsAny<IClock>(), It.IsAny<IClock>(),
It.IsAny<object>(), It.IsAny<object>(),
It.IsAny<object>()), It.IsAny<object>()),
Times.Never); Times.Never);
} }
@ -284,6 +256,8 @@ namespace Avalonia.Base.UnitTests.Animation
It.IsAny<object>())).Returns(sub.Object); It.IsAny<object>())).Returns(sub.Object);
control.Opacity = 0.5; control.Opacity = 0.5;
Assert.NotNull(control.Transitions);
control.Transitions.RemoveAt(0); control.Transitions.RemoveAt(0);
sub.Verify(x => x.Dispose()); sub.Verify(x => x.Dispose());
@ -307,10 +281,10 @@ namespace Avalonia.Base.UnitTests.Animation
control.Opacity = 0.5; control.Opacity = 0.5;
target.Verify(x => x.Apply( target.Verify(x => x.Apply(
control, control,
It.IsAny<IClock>(), It.IsAny<IClock>(),
1.0, 1.0,
0.5), 0.5),
Times.Once); Times.Once);
control.Classes.Add("foo"); control.Classes.Add("foo");
@ -324,18 +298,17 @@ namespace Avalonia.Base.UnitTests.Animation
{ {
using (Start()) using (Start())
{ {
var target = CreateTransition(Control.WidthProperty); var target = CreateTransition(Layoutable.WidthProperty);
var control = CreateStyledControl(transition2: target.Object); var control = CreateStyledControl(transition2: target.Object);
var sub = new Mock<IDisposable>();
control.Classes.Add("foo"); control.Classes.Add("foo");
control.Width = 100; control.Width = 100;
target.Verify(x => x.Apply( target.Verify(x => x.Apply(
control, control,
It.IsAny<IClock>(), It.IsAny<IClock>(),
double.NaN, double.NaN,
100.0), 100.0),
Times.Once); Times.Once);
} }
} }
@ -357,12 +330,12 @@ namespace Avalonia.Base.UnitTests.Animation
{ {
Setters = Setters =
{ {
new Setter(Border.TransitionsProperty, new Setter(Animatable.TransitionsProperty,
new Transitions new Transitions
{ {
new DoubleTransition new DoubleTransition
{ {
Property = Border.OpacityProperty, Property = Visual.OpacityProperty,
Duration = TimeSpan.FromSeconds(1), Duration = TimeSpan.FromSeconds(1),
}, },
}), }),
@ -372,23 +345,20 @@ namespace Avalonia.Base.UnitTests.Animation
{ {
Setters = Setters =
{ {
new Setter(Border.TransitionsProperty, new Setter(Animatable.TransitionsProperty,
new Transitions new Transitions
{ {
new DoubleTransition new DoubleTransition
{ {
Property = Border.OpacityProperty, Property = Visual.OpacityProperty,
Duration = TimeSpan.FromSeconds(1), Duration = TimeSpan.FromSeconds(1),
}, },
}), }),
new Setter(Border.OpacityProperty, 0.0), new Setter(Visual.OpacityProperty, 0.0),
}, },
}, },
}, },
Child = target = new Border Child = target = new Border { Background = Brushes.Red, }
{
Background = Brushes.Red,
}
}; };
root.Measure(Size.Infinity); root.Measure(Size.Infinity);
@ -421,7 +391,7 @@ namespace Avalonia.Base.UnitTests.Animation
// Assigning and then clearing Transitions ensures we have a transition state // Assigning and then clearing Transitions ensures we have a transition state
// collection created. // collection created.
control.ClearValue(Control.TransitionsProperty); control.ClearValue(Animatable.TransitionsProperty);
control.GetValueStore().BeginStyling(); control.GetValueStore().BeginStyling();
@ -431,8 +401,8 @@ namespace Avalonia.Base.UnitTests.Animation
{ {
Setters = Setters =
{ {
new Setter(Control.OpacityProperty, 0.5), new Setter(Visual.OpacityProperty, 0.5),
new Setter(Control.TransitionsProperty, new Transitions { target.Object }), new Setter(Animatable.TransitionsProperty, new Transitions { target.Object }),
} }
}; };
@ -450,28 +420,17 @@ namespace Avalonia.Base.UnitTests.Animation
var opacityTransition = new DoubleTransition var opacityTransition = new DoubleTransition
{ {
Property = Control.OpacityProperty, Property = Visual.OpacityProperty, Duration = TimeSpan.FromSeconds(1),
Duration = TimeSpan.FromSeconds(1),
}; };
var transitions = new Transitions { opacityTransition }; var transitions = new Transitions { opacityTransition };
var borderTheme = new ControlTheme(typeof(Border)) var borderTheme = new ControlTheme(typeof(Border))
{ {
Setters = Setters = { new Setter(Animatable.TransitionsProperty, transitions), }
{
new Setter(Control.TransitionsProperty, transitions),
}
}; };
var clock = new TestClock(); var clock = new TestClock();
var root = new TestRoot var root = new TestRoot { Clock = clock, Resources = { { typeof(Border), borderTheme }, } };
{
Clock = clock,
Resources =
{
{ typeof(Border), borderTheme },
}
};
var border = new Border(); var border = new Border();
root.Child = 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 // Now clear the property; a transition is now in progress but no local value is
// set. // set.
border.ClearValue(Border.OpacityProperty); border.ClearValue(Visual.OpacityProperty);
// Remove the transition by removing the control from the logical tree. This was // Remove the transition by removing the control from the logical tree. This was
// causing an exception. // causing an exception.
root.Child = null; 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<Border>()) { 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<Border>()) { 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<Border>()) { 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<Border>()) { 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() private static IDisposable Start()
{ {
var clock = new MockGlobalClock(); var clock = new MockGlobalClock();
@ -510,12 +670,9 @@ namespace Avalonia.Base.UnitTests.Animation
private static Control CreateControl(ITransition transition) private static Control CreateControl(ITransition transition)
{ {
var control = new Control var control = new Control { Transitions = new Transitions { transition }, };
{
Transitions = new Transitions { transition },
};
var root = new TestRoot(control); var _ = new TestRoot(control);
return control; return control;
} }
@ -524,7 +681,7 @@ namespace Avalonia.Base.UnitTests.Animation
ITransition transition2 = null) ITransition transition2 = null)
{ {
transition1 = transition1 ?? CreateTarget().Object; transition1 = transition1 ?? CreateTarget().Object;
transition2 = transition2 ?? CreateTransition(Control.WidthProperty).Object; transition2 = transition2 ?? CreateTransition(Layoutable.WidthProperty).Object;
var control = new Control var control = new Control
{ {
@ -536,7 +693,7 @@ namespace Avalonia.Base.UnitTests.Animation
{ {
new Setter new Setter
{ {
Property = Control.TransitionsProperty, Property = Animatable.TransitionsProperty,
Value = new Transitions { transition1 }, Value = new Transitions { transition1 },
} }
} }
@ -547,7 +704,7 @@ namespace Avalonia.Base.UnitTests.Animation
{ {
new Setter new Setter
{ {
Property = Control.TransitionsProperty, Property = Animatable.TransitionsProperty,
Value = new Transitions { transition2 }, Value = new Transitions { transition2 },
} }
} }
@ -555,7 +712,7 @@ namespace Avalonia.Base.UnitTests.Animation
} }
}; };
var root = new TestRoot(control); var _ = new TestRoot(control);
return control; return control;
} }

241
tests/Avalonia.Base.UnitTests/Animation/AnimationIterationTests.cs

@ -1,16 +1,13 @@
using System; using System;
using System.Linq;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Animation; using Avalonia.Animation;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.UnitTests;
using Avalonia.Data;
using Xunit; using Xunit;
using Avalonia.Animation.Easings; using Avalonia.Animation.Easings;
using System.Threading; using System.Threading;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.Layout;
namespace Avalonia.Base.UnitTests.Animation namespace Avalonia.Base.UnitTests.Animation
{ {
@ -23,47 +20,27 @@ namespace Avalonia.Base.UnitTests.Animation
{ {
var keyframe1 = new KeyFrame() var keyframe1 = new KeyFrame()
{ {
Setters = Setters = { new Setter(Layoutable.WidthProperty, 100d), }, KeyTime = TimeSpan.FromSeconds(0.5)
{
new Setter(Border.WidthProperty, 100d),
},
KeyTime = TimeSpan.FromSeconds(0.5)
}; };
var keyframe2 = new KeyFrame() var keyframe2 = new KeyFrame()
{ {
Setters = Setters = { new Setter(Layoutable.WidthProperty, 0d), }, KeyTime = TimeSpan.FromSeconds(0)
{
new Setter(Border.WidthProperty, 0d),
},
KeyTime = TimeSpan.FromSeconds(0)
}; };
var animation = new Avalonia.Animation.Animation() var animation = new Animation() { Duration = TimeSpan.FromSeconds(1), Children = { keyframe2, keyframe1 } };
{
Duration = TimeSpan.FromSeconds(1),
Children =
{
keyframe2,
keyframe1
}
};
var border = new Border() var border = new Border() { Height = 100d, Width = 100d };
{
Height = 100d,
Width = 100d
};
var clock = new TestClock(); 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); Assert.Equal(border.Width, 0d);
clock.Step(TimeSpan.FromSeconds(1)); clock.Step(TimeSpan.FromSeconds(1));
Assert.Equal(border.Width, 100d); Assert.Equal(border.Width, 100d);
} }
@ -72,40 +49,24 @@ namespace Avalonia.Base.UnitTests.Animation
{ {
var keyframe1 = new KeyFrame() var keyframe1 = new KeyFrame()
{ {
Setters = Setters = { new Setter(Layoutable.WidthProperty, 200d), }, Cue = new Cue(1d)
{
new Setter(Border.WidthProperty, 200d),
},
Cue = new Cue(1d)
}; };
var keyframe2 = new KeyFrame() var keyframe2 = new KeyFrame()
{ {
Setters = Setters = { new Setter(Layoutable.WidthProperty, 100d), }, Cue = new Cue(0d)
{
new Setter(Border.WidthProperty, 100d),
},
Cue = new Cue(0d)
}; };
var animation = new Avalonia.Animation.Animation() var animation = new Animation()
{ {
Duration = TimeSpan.FromSeconds(3), Duration = TimeSpan.FromSeconds(3),
Delay = TimeSpan.FromSeconds(3), Delay = TimeSpan.FromSeconds(3),
DelayBetweenIterations = TimeSpan.FromSeconds(3), DelayBetweenIterations = TimeSpan.FromSeconds(3),
IterationCount = new IterationCount(2), IterationCount = new IterationCount(2),
Children = Children = { keyframe2, keyframe1 }
{
keyframe2,
keyframe1
}
}; };
var border = new Border() var border = new Border() { Height = 100d, Width = 100d };
{
Height = 100d,
Width = 100d
};
var clock = new TestClock(); var clock = new TestClock();
var animationRun = animation.RunAsync(border, clock); var animationRun = animation.RunAsync(border, clock);
@ -133,43 +94,28 @@ namespace Avalonia.Base.UnitTests.Animation
{ {
var keyframe1 = new KeyFrame() var keyframe1 = new KeyFrame()
{ {
Setters = Setters = { new Setter(Layoutable.WidthProperty, 0d), }, Cue = new Cue(0.0d)
{
new Setter(Border.WidthProperty, 0d),
},
Cue = new Cue(0.0d)
}; };
var keyframe2 = new KeyFrame() var keyframe2 = new KeyFrame()
{ {
Setters = Setters = { new Setter(Layoutable.WidthProperty, 300d), }, Cue = new Cue(1.0d)
{
new Setter(Border.WidthProperty, 300d),
},
Cue = new Cue(1.0d)
}; };
var animation = new Avalonia.Animation.Animation() var animation = new Animation()
{ {
Duration = TimeSpan.FromSeconds(0.05d), Duration = TimeSpan.FromSeconds(0.05d),
Delay = TimeSpan.FromSeconds(0.05d), Delay = TimeSpan.FromSeconds(0.05d),
Easing = new SineEaseInOut(), Easing = new SineEaseInOut(),
FillMode = FillMode.Both, FillMode = FillMode.Both,
Children = Children = { keyframe1, keyframe2 }
{
keyframe1,
keyframe2
}
}; };
var border = new Border() var border = new Border() { Height = 100d, Width = 100d, };
{
Height = 100d,
Width = 100d,
};
var clock = new TestClock(); var clock = new TestClock();
var animationRun = animation.RunAsync(border, clock);
animation.RunAsync(border, clock);
clock.Step(TimeSpan.FromSeconds(0d)); clock.Step(TimeSpan.FromSeconds(0d));
Assert.Equal(border.Width, 0d); Assert.Equal(border.Width, 0d);
@ -186,20 +132,12 @@ namespace Avalonia.Base.UnitTests.Animation
{ {
var keyframe1 = new KeyFrame() var keyframe1 = new KeyFrame()
{ {
Setters = Setters = { new Setter(Layoutable.WidthProperty, 200d), }, Cue = new Cue(1d)
{
new Setter(Border.WidthProperty, 200d),
},
Cue = new Cue(1d)
}; };
var keyframe2 = new KeyFrame() var keyframe2 = new KeyFrame()
{ {
Setters = Setters = { new Setter(Layoutable.WidthProperty, 100d), }, Cue = new Cue(0d)
{
new Setter(Border.WidthProperty, 100d),
},
Cue = new Cue(0d)
}; };
var animation = new Animation() var animation = new Animation()
@ -208,23 +146,15 @@ namespace Avalonia.Base.UnitTests.Animation
Delay = TimeSpan.FromSeconds(0), Delay = TimeSpan.FromSeconds(0),
DelayBetweenIterations = TimeSpan.FromSeconds(0), DelayBetweenIterations = TimeSpan.FromSeconds(0),
IterationCount = new IterationCount(1), IterationCount = new IterationCount(1),
Children = Children = { keyframe2, keyframe1 }
{
keyframe2,
keyframe1
}
}; };
var border = new Border() var border = new Border() { Height = 100d, Width = 50d };
{
Height = 100d,
Width = 50d
};
var propertyChangedCount = 0; var propertyChangedCount = 0;
var animationCompletedCount = 0; var animationCompletedCount = 0;
border.PropertyChanged += (sender, e) => border.PropertyChanged += (_, e) =>
{ {
if (e.Property == Control.WidthProperty) if (e.Property == Layoutable.WidthProperty)
{ {
propertyChangedCount++; propertyChangedCount++;
} }
@ -257,20 +187,12 @@ namespace Avalonia.Base.UnitTests.Animation
{ {
var keyframe1 = new KeyFrame() var keyframe1 = new KeyFrame()
{ {
Setters = Setters = { new Setter(Layoutable.WidthProperty, 200d), }, Cue = new Cue(1d)
{
new Setter(Border.WidthProperty, 200d),
},
Cue = new Cue(1d)
}; };
var keyframe2 = new KeyFrame() var keyframe2 = new KeyFrame()
{ {
Setters = Setters = { new Setter(Layoutable.WidthProperty, 100d), }, Cue = new Cue(0d)
{
new Setter(Border.WidthProperty, 100d),
},
Cue = new Cue(0d)
}; };
var animation = new Animation() var animation = new Animation()
@ -279,22 +201,14 @@ namespace Avalonia.Base.UnitTests.Animation
Delay = TimeSpan.FromSeconds(0), Delay = TimeSpan.FromSeconds(0),
DelayBetweenIterations = TimeSpan.FromSeconds(0), DelayBetweenIterations = TimeSpan.FromSeconds(0),
IterationCount = new IterationCount(1), IterationCount = new IterationCount(1),
Children = Children = { keyframe2, keyframe1 }
{
keyframe2,
keyframe1
}
}; };
var border = new Border() var border = new Border() { Height = 100d, Width = 100d };
{
Height = 100d,
Width = 100d
};
var propertyChangedCount = 0; var propertyChangedCount = 0;
border.PropertyChanged += (sender, e) => border.PropertyChanged += (_, e) =>
{ {
if (e.Property == Control.WidthProperty) if (e.Property == Layoutable.WidthProperty)
{ {
propertyChangedCount++; propertyChangedCount++;
} }
@ -315,20 +229,12 @@ namespace Avalonia.Base.UnitTests.Animation
{ {
var keyframe1 = new KeyFrame() var keyframe1 = new KeyFrame()
{ {
Setters = Setters = { new Setter(Layoutable.WidthProperty, 200d), }, Cue = new Cue(1d)
{
new Setter(Border.WidthProperty, 200d),
},
Cue = new Cue(1d)
}; };
var keyframe2 = new KeyFrame() var keyframe2 = new KeyFrame()
{ {
Setters = Setters = { new Setter(Layoutable.WidthProperty, 100d), }, Cue = new Cue(0d)
{
new Setter(Border.WidthProperty, 100d),
},
Cue = new Cue(0d)
}; };
var animation = new Animation() var animation = new Animation()
@ -337,22 +243,14 @@ namespace Avalonia.Base.UnitTests.Animation
Delay = TimeSpan.FromSeconds(0), Delay = TimeSpan.FromSeconds(0),
DelayBetweenIterations = TimeSpan.FromSeconds(0), DelayBetweenIterations = TimeSpan.FromSeconds(0),
IterationCount = new IterationCount(1), IterationCount = new IterationCount(1),
Children = Children = { keyframe2, keyframe1 }
{
keyframe2,
keyframe1
}
}; };
var border = new Border() var border = new Border() { Height = 100d, Width = 50d };
{
Height = 100d,
Width = 50d
};
var propertyChangedCount = 0; var propertyChangedCount = 0;
border.PropertyChanged += (sender, e) => border.PropertyChanged += (_, e) =>
{ {
if (e.Property == Control.WidthProperty) if (e.Property == Layoutable.WidthProperty)
{ {
propertyChangedCount++; propertyChangedCount++;
} }
@ -361,6 +259,7 @@ namespace Avalonia.Base.UnitTests.Animation
var clock = new TestClock(); var clock = new TestClock();
var cancellationTokenSource = new CancellationTokenSource(); var cancellationTokenSource = new CancellationTokenSource();
var animationRun = animation.RunAsync(border, clock, cancellationTokenSource.Token); var animationRun = animation.RunAsync(border, clock, cancellationTokenSource.Token);
Assert.False(animationRun.IsCompleted);
Assert.Equal(0, propertyChangedCount); Assert.Equal(0, propertyChangedCount);
@ -381,24 +280,16 @@ namespace Avalonia.Base.UnitTests.Animation
} }
[Fact] [Fact]
public void Cancellation_Of_Completed_Animation_Does_Not_Fail() public void Dont_Run_Infinite_Iteration_Animation_On_RunAsync_Method()
{ {
var keyframe1 = new KeyFrame() var keyframe1 = new KeyFrame()
{ {
Setters = Setters = { new Setter(Layoutable.WidthProperty, 200d), }, Cue = new Cue(1d)
{
new Setter(Border.WidthProperty, 200d),
},
Cue = new Cue(1d)
}; };
var keyframe2 = new KeyFrame() var keyframe2 = new KeyFrame()
{ {
Setters = Setters = { new Setter(Layoutable.WidthProperty, 100d), }, Cue = new Cue(0d)
{
new Setter(Border.WidthProperty, 100d),
},
Cue = new Cue(0d)
}; };
var animation = new Animation() var animation = new Animation()
@ -406,23 +297,47 @@ namespace Avalonia.Base.UnitTests.Animation
Duration = TimeSpan.FromSeconds(10), Duration = TimeSpan.FromSeconds(10),
Delay = TimeSpan.FromSeconds(0), Delay = TimeSpan.FromSeconds(0),
DelayBetweenIterations = TimeSpan.FromSeconds(0), DelayBetweenIterations = TimeSpan.FromSeconds(0),
IterationCount = new IterationCount(1), IterationCount = IterationCount.Infinite,
Children = Children = { keyframe2, keyframe1 }
{
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, Setters = { new Setter(Layoutable.WidthProperty, 100d), }, Cue = new Cue(0d)
Width = 50d
}; };
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; var propertyChangedCount = 0;
border.PropertyChanged += (sender, e) => border.PropertyChanged += (_, e) =>
{ {
if (e.Property == Control.WidthProperty) if (e.Property == Layoutable.WidthProperty)
{ {
propertyChangedCount++; propertyChangedCount++;
} }

6
tests/Avalonia.Base.UnitTests/Animation/BrushTransitionTests.cs

@ -36,8 +36,7 @@ namespace Avalonia.Base.UnitTests.Animation
var border = new Border() { Background = oldBrush }; var border = new Border() { Background = oldBrush };
BrushTransition sut = new BrushTransition BrushTransition sut = new BrushTransition
{ {
Duration = TimeSpan.FromSeconds(1), Duration = TimeSpan.FromSeconds(1), Property = Border.BackgroundProperty
Property = Border.BackgroundProperty
}; };
sut.Apply(border, clock, oldBrush, newBrush); sut.Apply(border, clock, oldBrush, newBrush);
@ -45,7 +44,8 @@ namespace Avalonia.Base.UnitTests.Animation
clock.Pulse(sut.Duration * progress); clock.Pulse(sut.Duration * progress);
Assert.NotNull(border.Background); 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);
} }
} }
} }

68
tests/Avalonia.Base.UnitTests/Animation/KeySplineTests.cs

@ -21,6 +21,8 @@ namespace Avalonia.Base.UnitTests.Animation
var keySpline = (KeySpline)conv.ConvertFrom(input); var keySpline = (KeySpline)conv.ConvertFrom(input);
Assert.NotNull(keySpline);
Assert.Equal(1, keySpline.ControlPointX1); Assert.Equal(1, keySpline.ControlPointX1);
Assert.Equal(2, keySpline.ControlPointY1); Assert.Equal(2, keySpline.ControlPointY1);
Assert.Equal(3, keySpline.ControlPointX2); Assert.Equal(3, keySpline.ControlPointX2);
@ -28,8 +30,8 @@ namespace Avalonia.Base.UnitTests.Animation
} }
[Theory] [Theory]
[InlineData("1,2F,3,4")] [InlineData("1,2F,3,4")]
[InlineData("Foo,Bar,Fee,Buzz")] [InlineData("Foo,Bar,Fee,Buzz")]
public void Can_Handle_Invalid_String_KeySpline_Via_TypeConverter(string input) public void Can_Handle_Invalid_String_KeySpline_Via_TypeConverter(string input)
{ {
var conv = new KeySplineTypeConverter(); var conv = new KeySplineTypeConverter();
@ -104,46 +106,33 @@ namespace Avalonia.Base.UnitTests.Animation
{ {
var keyframe1 = new KeyFrame() var keyframe1 = new KeyFrame()
{ {
Setters = Setters = { new Setter(RotateTransform.AngleProperty, -2.5d), }, KeyTime = TimeSpan.FromSeconds(0)
{
new Setter(RotateTransform.AngleProperty, -2.5d),
},
KeyTime = TimeSpan.FromSeconds(0)
}; };
var keyframe2 = new KeyFrame() var keyframe2 = new KeyFrame()
{ {
Setters = Setters = { new Setter(RotateTransform.AngleProperty, 2.5d), },
{
new Setter(RotateTransform.AngleProperty, 2.5d),
},
KeyTime = TimeSpan.FromSeconds(5), KeyTime = TimeSpan.FromSeconds(5),
KeySpline = new KeySpline(0.1123555056179775, KeySpline = new KeySpline(0.1123555056179775,
0.657303370786517, 0.657303370786517,
0.8370786516853934, 0.8370786516853934,
0.499999999999999999) 0.499999999999999999)
}; };
var animation = new Avalonia.Animation.Animation() var animation = new Avalonia.Animation.Animation()
{ {
Duration = TimeSpan.FromSeconds(5), Duration = TimeSpan.FromSeconds(5),
Children = Children = { keyframe1, keyframe2 },
{
keyframe1,
keyframe2
},
IterationCount = new IterationCount(5), IterationCount = new IterationCount(5),
PlaybackDirection = PlaybackDirection.Alternate PlaybackDirection = PlaybackDirection.Alternate
}; };
var rotateTransform = new RotateTransform(-2.5); var rotateTransform = new RotateTransform(-2.5);
var rect = new Rectangle() var rect = new Rectangle() { RenderTransform = rotateTransform };
{
RenderTransform = rotateTransform
};
var clock = new TestClock(); var clock = new TestClock();
var animationRun = animation.RunAsync(rect, clock);
animation.RunAsync(rect, clock);
// position is what you'd expect at end and beginning // position is what you'd expect at end and beginning
clock.Step(TimeSpan.Zero); clock.Step(TimeSpan.Zero);
@ -169,49 +158,36 @@ namespace Avalonia.Base.UnitTests.Animation
expected = 1.8016358493761722; expected = 1.8016358493761722;
Assert.True(Math.Abs(rotateTransform.Angle - expected) <= tolerance); Assert.True(Math.Abs(rotateTransform.Angle - expected) <= tolerance);
} }
[Fact] [Fact]
public void Check_KeySpline_Parsing_Is_Correct() public void Check_KeySpline_Parsing_Is_Correct()
{ {
var keyframe1 = new KeyFrame() var keyframe1 = new KeyFrame()
{ {
Setters = Setters = { new Setter(RotateTransform.AngleProperty, -2.5d), }, KeyTime = TimeSpan.FromSeconds(0)
{
new Setter(RotateTransform.AngleProperty, -2.5d),
},
KeyTime = TimeSpan.FromSeconds(0)
}; };
var keyframe2 = new KeyFrame() var keyframe2 = new KeyFrame()
{ {
Setters = Setters = { new Setter(RotateTransform.AngleProperty, 2.5d), }, KeyTime = TimeSpan.FromSeconds(5),
{
new Setter(RotateTransform.AngleProperty, 2.5d),
},
KeyTime = TimeSpan.FromSeconds(5),
}; };
var animation = new Avalonia.Animation.Animation() var animation = new Avalonia.Animation.Animation()
{ {
Duration = TimeSpan.FromSeconds(5), Duration = TimeSpan.FromSeconds(5),
Children = Children = { keyframe1, keyframe2 },
{
keyframe1,
keyframe2
},
IterationCount = new IterationCount(5), IterationCount = new IterationCount(5),
PlaybackDirection = PlaybackDirection.Alternate, 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 rotateTransform = new RotateTransform(-2.5);
var rect = new Rectangle() var rect = new Rectangle() { RenderTransform = rotateTransform };
{
RenderTransform = rotateTransform
};
var clock = new TestClock(); var clock = new TestClock();
var animationRun = animation.RunAsync(rect, clock);
animation.RunAsync(rect, clock);
// position is what you'd expect at end and beginning // position is what you'd expect at end and beginning
clock.Step(TimeSpan.Zero); clock.Step(TimeSpan.Zero);

34
tests/Avalonia.Base.UnitTests/Animation/SpringTests.cs

@ -18,6 +18,7 @@ public class SpringTests
var spring = (Spring)conv.ConvertFrom(input); var spring = (Spring)conv.ConvertFrom(input);
Assert.NotNull(spring);
Assert.Equal(1, spring.Mass); Assert.Equal(1, spring.Mass);
Assert.Equal(2, spring.Stiffness); Assert.Equal(2, spring.Stiffness);
Assert.Equal(3, spring.Damping); Assert.Equal(3, spring.Damping);
@ -25,8 +26,8 @@ public class SpringTests
} }
[Theory] [Theory]
[InlineData("1,2F,3,4")] [InlineData("1,2F,3,4")]
[InlineData("Foo,Bar,Fee,Buzz")] [InlineData("Foo,Bar,Fee,Buzz")]
public void Can_Handle_Invalid_String_Via_TypeConverter(string input) public void Can_Handle_Invalid_String_Via_TypeConverter(string input)
{ {
var conv = new SpringTypeConverter(); var conv = new SpringTypeConverter();
@ -37,7 +38,7 @@ public class SpringTests
[Fact] [Fact]
public void SplineEasing_Can_Be_Mutated() 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, easing.Ease(0));
Assert.Equal(0.34029984660829826, easing.Ease(1)); Assert.Equal(0.34029984660829826, easing.Ease(1));
@ -55,43 +56,28 @@ public class SpringTests
{ {
var keyframe1 = new KeyFrame() var keyframe1 = new KeyFrame()
{ {
Setters = Setters = { new Setter(RotateTransform.AngleProperty, -2.5d), }, KeyTime = TimeSpan.FromSeconds(0)
{
new Setter(RotateTransform.AngleProperty, -2.5d),
},
KeyTime = TimeSpan.FromSeconds(0)
}; };
var keyframe2 = new KeyFrame() var keyframe2 = new KeyFrame()
{ {
Setters = Setters = { new Setter(RotateTransform.AngleProperty, 2.5d), }, KeyTime = TimeSpan.FromSeconds(5)
{
new Setter(RotateTransform.AngleProperty, 2.5d),
},
KeyTime = TimeSpan.FromSeconds(5)
}; };
var animation = new Avalonia.Animation.Animation() var animation = new Avalonia.Animation.Animation()
{ {
Duration = TimeSpan.FromSeconds(5), Duration = TimeSpan.FromSeconds(5),
Children = Children = { keyframe1, keyframe2 },
{
keyframe1,
keyframe2
},
IterationCount = new IterationCount(5), IterationCount = new IterationCount(5),
PlaybackDirection = PlaybackDirection.Alternate, PlaybackDirection = PlaybackDirection.Alternate,
Easing = new SpringEasing(1, 10, 1, 0) Easing = new SpringEasing(1, 10, 1)
}; };
var rotateTransform = new RotateTransform(-2.5); var rotateTransform = new RotateTransform(-2.5);
var rect = new Rectangle() var rect = new Rectangle() { RenderTransform = rotateTransform };
{
RenderTransform = rotateTransform
};
var clock = new TestClock(); var clock = new TestClock();
var animationRun = animation.RunAsync(rect, clock); animation.RunAsync(rect, clock);
clock.Step(TimeSpan.Zero); clock.Step(TimeSpan.Zero);
Assert.Equal(rotateTransform.Angle, -2.5); Assert.Equal(rotateTransform.Angle, -2.5);

94
tests/Avalonia.Base.UnitTests/Animation/TransitionsTests.cs

@ -15,13 +15,12 @@ namespace Avalonia.Base.UnitTests.Animation
var border = new Border var border = new Border
{ {
Transitions = new Transitions Transitions = new Transitions
{
new DoubleTransition
{ {
new DoubleTransition Duration = TimeSpan.FromSeconds(1), Property = Visual.OpacityProperty,
{
Duration = TimeSpan.FromSeconds(1),
Property = Border.OpacityProperty,
}
} }
}
}; };
border.Opacity = 0; border.Opacity = 0;
@ -30,7 +29,6 @@ namespace Avalonia.Base.UnitTests.Animation
clock.Pulse(TimeSpan.FromSeconds(-0.5)); clock.Pulse(TimeSpan.FromSeconds(-0.5));
Assert.Equal(0, border.Opacity); Assert.Equal(0, border.Opacity);
} }
[Fact] [Fact]
@ -41,13 +39,12 @@ namespace Avalonia.Base.UnitTests.Animation
var border = new Border var border = new Border
{ {
Transitions = new Transitions Transitions = new Transitions
{
new DoubleTransition
{ {
new DoubleTransition Duration = TimeSpan.FromSeconds(1), Property = Visual.OpacityProperty,
{
Duration = TimeSpan.FromSeconds(1),
Property = Border.OpacityProperty,
}
} }
}
}; };
border.Opacity = 0; border.Opacity = 0;
@ -56,7 +53,6 @@ namespace Avalonia.Base.UnitTests.Animation
clock.Pulse(TimeSpan.FromMilliseconds(1001)); clock.Pulse(TimeSpan.FromMilliseconds(1001));
Assert.Equal(0, border.Opacity); Assert.Equal(0, border.Opacity);
} }
[Fact] [Fact]
@ -64,18 +60,22 @@ namespace Avalonia.Base.UnitTests.Animation
{ {
var clock = new TestClock(); var clock = new TestClock();
int i = 0; var i = 0;
var inst = new TransitionInstance(clock, TimeSpan.Zero, TimeSpan.Zero).Subscribe(nextValue =>
new TransitionInstance(clock, TimeSpan.Zero, TimeSpan.Zero).Subscribe(nextValue =>
{ {
switch (i++) switch (i++)
{ {
case 0: Assert.Equal(0, nextValue); break; case 0:
case 1: Assert.Equal(1d, nextValue); break; Assert.Equal(0, nextValue);
break;
case 1:
Assert.Equal(1d, nextValue);
break;
} }
}); });
clock.Pulse(TimeSpan.FromMilliseconds(10)); clock.Pulse(TimeSpan.FromMilliseconds(10));
} }
[Fact] [Fact]
@ -83,26 +83,50 @@ namespace Avalonia.Base.UnitTests.Animation
{ {
var clock = new TestClock(); var clock = new TestClock();
int i = -1; var i = -1;
var inst = new TransitionInstance(clock, TimeSpan.FromMilliseconds(30), TimeSpan.FromMilliseconds(70)).Subscribe(nextValue =>
{ new TransitionInstance(clock, TimeSpan.FromMilliseconds(30), TimeSpan.FromMilliseconds(70)).Subscribe(
switch (i++) nextValue =>
{ {
case 0: Assert.Equal(0, nextValue); break; switch (i++)
case 1: Assert.Equal(0, nextValue); break; {
case 2: Assert.Equal(0, nextValue); break; case 0:
case 3: Assert.Equal(0, nextValue); break; Assert.Equal(0, nextValue);
case 4: Assert.Equal(Math.Round(10d / 70d, 4), Math.Round(nextValue, 4)); break; break;
case 5: Assert.Equal(Math.Round(20d / 70d, 4), Math.Round(nextValue, 4)); break; case 1:
case 6: Assert.Equal(Math.Round(30d / 70d, 4), Math.Round(nextValue, 4)); break; Assert.Equal(0, nextValue);
case 7: Assert.Equal(Math.Round(40d / 70d, 4), Math.Round(nextValue, 4)); break; break;
case 8: Assert.Equal(Math.Round(50d / 70d, 4), Math.Round(nextValue, 4)); break; case 2:
case 9: Assert.Equal(Math.Round(60d / 70d, 4), Math.Round(nextValue, 4)); break; Assert.Equal(0, nextValue);
case 10: Assert.Equal(1d, nextValue); break; 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)); clock.Pulse(TimeSpan.FromMilliseconds(10));
} }

Loading…
Cancel
Save