diff --git a/src/Core/src/Converters/EasingTypeConverter.cs b/src/Core/src/Converters/EasingTypeConverter.cs index 2df10249bbe1..04f762d1623d 100644 --- a/src/Core/src/Converters/EasingTypeConverter.cs +++ b/src/Core/src/Converters/EasingTypeConverter.cs @@ -1,8 +1,6 @@ using System; using System.ComponentModel; using System.Globalization; -using System.Linq; -using System.Reflection; using static Microsoft.Maui.Easing; #nullable disable @@ -20,50 +18,36 @@ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinati public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { - var strValue = value?.ToString(); + if (value is null) + return null; + + var strValue = value as string ?? value.ToString(); if (string.IsNullOrWhiteSpace(strValue)) return null; - strValue = strValue?.Trim() ?? ""; var parts = strValue.Split('.'); - - if (parts.Length == 2 && parts[0] == nameof(Easing)) - strValue = parts[parts.Length - 1]; - - if (strValue.Equals(nameof(Linear), StringComparison.OrdinalIgnoreCase)) - return Linear; - if (strValue.Equals(nameof(SinIn), StringComparison.OrdinalIgnoreCase)) - return SinIn; - if (strValue.Equals(nameof(SinOut), StringComparison.OrdinalIgnoreCase)) - return SinOut; - if (strValue.Equals(nameof(SinInOut), StringComparison.OrdinalIgnoreCase)) - return SinInOut; - if (strValue.Equals(nameof(CubicIn), StringComparison.OrdinalIgnoreCase)) - return CubicIn; - if (strValue.Equals(nameof(CubicOut), StringComparison.OrdinalIgnoreCase)) - return CubicOut; - if (strValue.Equals(nameof(CubicInOut), StringComparison.OrdinalIgnoreCase)) - return CubicInOut; - if (strValue.Equals(nameof(BounceIn), StringComparison.OrdinalIgnoreCase)) - return BounceIn; - if (strValue.Equals(nameof(BounceOut), StringComparison.OrdinalIgnoreCase)) - return BounceOut; - if (strValue.Equals(nameof(SpringIn), StringComparison.OrdinalIgnoreCase)) - return SpringIn; - if (strValue.Equals(nameof(SpringOut), StringComparison.OrdinalIgnoreCase)) - return SpringOut; - - var fallbackValue = typeof(Easing) - .GetTypeInfo() - .DeclaredFields - .FirstOrDefault(f => f.Name.Equals(strValue, StringComparison.OrdinalIgnoreCase)) - ?.GetValue(null); - - if (fallbackValue is Easing fallbackEasing) - return fallbackEasing; - - throw new InvalidOperationException($"Cannot convert \"{strValue}\" into {typeof(Easing)}"); + if (parts.Length == 2 && Compare(parts[0], nameof(Easing))) + strValue = parts[1]; + + return strValue switch + { + _ when Compare(strValue, nameof(Linear)) => Linear, + _ when Compare(strValue, nameof(SinIn)) => SinIn, + _ when Compare(strValue, nameof(SinOut)) => SinOut, + _ when Compare(strValue, nameof(SinInOut)) => SinInOut, + _ when Compare(strValue, nameof(CubicIn)) => CubicIn, + _ when Compare(strValue, nameof(CubicOut)) => CubicOut, + _ when Compare(strValue, nameof(CubicInOut)) => CubicInOut, + _ when Compare(strValue, nameof(BounceIn)) => BounceIn, + _ when Compare(strValue, nameof(BounceOut)) => BounceOut, + _ when Compare(strValue, nameof(SpringIn)) => SpringIn, + _ when Compare(strValue, nameof(SpringOut)) => SpringOut, + _ => throw new InvalidOperationException($"Cannot convert \"{strValue}\" into {typeof(Easing)}") + }; + + static bool Compare(string left, string right) => + left.Equals(right, StringComparison.OrdinalIgnoreCase); } public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) @@ -71,30 +55,21 @@ public override object ConvertTo(ITypeDescriptorContext context, CultureInfo cul if (value is not Easing easing) throw new NotSupportedException(); - if (easing == Linear) - return nameof(Linear); - if (easing == SinIn) - return nameof(SinIn); - if (easing == SinOut) - return nameof(SinOut); - if (easing == SinInOut) - return nameof(SinInOut); - if (easing == CubicIn) - return nameof(CubicIn); - if (easing == CubicOut) - return nameof(CubicOut); - if (easing == CubicInOut) - return nameof(CubicInOut); - if (easing == BounceIn) - return nameof(BounceIn); - if (easing == BounceOut) - return nameof(BounceOut); - if (easing == SpringIn) - return nameof(SpringIn); - if (easing == SpringOut) - return nameof(SpringOut); - - throw new NotSupportedException(); + return easing switch + { + _ when easing.Equals(Linear) => nameof(Linear), + _ when easing.Equals(SinIn) => nameof(SinIn), + _ when easing.Equals(SinOut) => nameof(SinOut), + _ when easing.Equals(SinInOut) => nameof(SinInOut), + _ when easing.Equals(CubicIn) => nameof(CubicIn), + _ when easing.Equals(CubicOut) => nameof(CubicOut), + _ when easing.Equals(CubicInOut) => nameof(CubicInOut), + _ when easing.Equals(BounceIn) => nameof(BounceIn), + _ when easing.Equals(BounceOut) => nameof(BounceOut), + _ when easing.Equals(SpringIn) => nameof(SpringIn), + _ when easing.Equals(SpringOut) => nameof(SpringOut), + _ => throw new NotSupportedException(), + }; } public override bool GetStandardValuesSupported(ITypeDescriptorContext context) diff --git a/src/Controls/tests/Core.UnitTests/EasingTests.cs b/src/Core/tests/UnitTests/Animations/EasingTests.cs similarity index 70% rename from src/Controls/tests/Core.UnitTests/EasingTests.cs rename to src/Core/tests/UnitTests/Animations/EasingTests.cs index 23b63e7ff126..ecf9baaab278 100644 --- a/src/Controls/tests/Core.UnitTests/EasingTests.cs +++ b/src/Core/tests/UnitTests/Animations/EasingTests.cs @@ -2,12 +2,19 @@ using Microsoft.Maui.Converters; using Xunit; -namespace Microsoft.Maui.Controls.Core.UnitTests +namespace Microsoft.Maui.UnitTests { - - public class EasingTests : BaseTestFixture + [Category(TestCategory.Animations)] + public class EasingTests { - [Theory, MemberData(nameof(TestDataHelpers.Range), 0, 10, 1, MemberType = typeof(TestDataHelpers))] + [Theory] + [InlineData(0.0)] + [InlineData(1.0)] + [InlineData(2.0)] + [InlineData(5.0)] + [InlineData(8.0)] + [InlineData(9.0)] + [InlineData(10.0)] public void Linear(double input) { Assert.Equal(input, Easing.Linear.Ease(input)); @@ -19,6 +26,7 @@ public void Linear(double input) public void AllRunFromZeroToOne(double val) { const double epsilon = 0.001; + Assert.True(Math.Abs(val - Easing.Linear.Ease(val)) < epsilon); Assert.True(Math.Abs(val - Easing.BounceIn.Ease(val)) < epsilon); Assert.True(Math.Abs(val - Easing.BounceOut.Ease(val)) < epsilon); @@ -33,45 +41,97 @@ public void AllRunFromZeroToOne(double val) } [Fact] - public void TestEasingTypeConverter() + public void CanConvert() { var converter = new EasingTypeConverter(); + Assert.True(converter.CanConvertFrom(typeof(string))); - Assert.Null(converter.ConvertFromInvariantString(null)); - Assert.Null(converter.ConvertFromInvariantString(string.Empty)); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData(" ")] + public void NonTextEasingsAreNull(string input) + { + var converter = new EasingTypeConverter(); + + Assert.Null(converter.ConvertFromInvariantString(input)); + } + + [Fact] + public void CanConvertFromEasingNameToEasing() + { + var converter = new EasingTypeConverter(); + Assert.Equal(Easing.Linear, converter.ConvertFromInvariantString("Linear")); Assert.Equal(Easing.Linear, converter.ConvertFromInvariantString("linear")); Assert.Equal(Easing.Linear, converter.ConvertFromInvariantString("Easing.Linear")); + Assert.Equal(Easing.SinOut, converter.ConvertFromInvariantString("SinOut")); Assert.Equal(Easing.SinOut, converter.ConvertFromInvariantString("sinout")); Assert.Equal(Easing.SinOut, converter.ConvertFromInvariantString("Easing.SinOut")); + Assert.Equal(Easing.SinIn, converter.ConvertFromInvariantString("SinIn")); Assert.Equal(Easing.SinIn, converter.ConvertFromInvariantString("sinin")); Assert.Equal(Easing.SinIn, converter.ConvertFromInvariantString("Easing.SinIn")); + Assert.Equal(Easing.SinInOut, converter.ConvertFromInvariantString("SinInOut")); Assert.Equal(Easing.SinInOut, converter.ConvertFromInvariantString("sininout")); Assert.Equal(Easing.SinInOut, converter.ConvertFromInvariantString("Easing.SinInOut")); + Assert.Equal(Easing.CubicOut, converter.ConvertFromInvariantString("CubicOut")); Assert.Equal(Easing.CubicOut, converter.ConvertFromInvariantString("cubicout")); Assert.Equal(Easing.CubicOut, converter.ConvertFromInvariantString("Easing.CubicOut")); + Assert.Equal(Easing.CubicIn, converter.ConvertFromInvariantString("CubicIn")); Assert.Equal(Easing.CubicIn, converter.ConvertFromInvariantString("cubicin")); Assert.Equal(Easing.CubicIn, converter.ConvertFromInvariantString("Easing.CubicIn")); + Assert.Equal(Easing.CubicInOut, converter.ConvertFromInvariantString("CubicInOut")); Assert.Equal(Easing.CubicInOut, converter.ConvertFromInvariantString("cubicinout")); Assert.Equal(Easing.CubicInOut, converter.ConvertFromInvariantString("Easing.CubicInOut")); + Assert.Equal(Easing.BounceOut, converter.ConvertFromInvariantString("BounceOut")); Assert.Equal(Easing.BounceOut, converter.ConvertFromInvariantString("bounceout")); Assert.Equal(Easing.BounceOut, converter.ConvertFromInvariantString("Easing.BounceOut")); + Assert.Equal(Easing.BounceIn, converter.ConvertFromInvariantString("BounceIn")); Assert.Equal(Easing.BounceIn, converter.ConvertFromInvariantString("bouncein")); Assert.Equal(Easing.BounceIn, converter.ConvertFromInvariantString("Easing.BounceIn")); + Assert.Equal(Easing.SpringOut, converter.ConvertFromInvariantString("SpringOut")); Assert.Equal(Easing.SpringOut, converter.ConvertFromInvariantString("springout")); Assert.Equal(Easing.SpringOut, converter.ConvertFromInvariantString("Easing.SpringOut")); + Assert.Equal(Easing.SpringIn, converter.ConvertFromInvariantString("SpringIn")); Assert.Equal(Easing.SpringIn, converter.ConvertFromInvariantString("springin")); Assert.Equal(Easing.SpringIn, converter.ConvertFromInvariantString("Easing.SpringIn")); + } + + [Fact] + public void CanConvertFromEasingToEasingName() + { + var converter = new EasingTypeConverter(); + + Assert.Equal("Linear", converter.ConvertToInvariantString(Easing.Linear)); + Assert.Equal("SinOut", converter.ConvertToInvariantString(Easing.SinOut)); + Assert.Equal("SinIn", converter.ConvertToInvariantString(Easing.SinIn)); + Assert.Equal("SinInOut", converter.ConvertToInvariantString(Easing.SinInOut)); + Assert.Equal("CubicOut", converter.ConvertToInvariantString(Easing.CubicOut)); + Assert.Equal("CubicIn", converter.ConvertToInvariantString(Easing.CubicIn)); + Assert.Equal("CubicInOut", converter.ConvertToInvariantString(Easing.CubicInOut)); + Assert.Equal("BounceOut", converter.ConvertToInvariantString(Easing.BounceOut)); + Assert.Equal("BounceIn", converter.ConvertToInvariantString(Easing.BounceIn)); + Assert.Equal("SpringOut", converter.ConvertToInvariantString(Easing.SpringOut)); + Assert.Equal("SpringIn", converter.ConvertToInvariantString(Easing.SpringIn)); + } + + [Fact] + public void InvalidEasingNamesThrow() + { + var converter = new EasingTypeConverter(); + Assert.Throws(() => converter.ConvertFromInvariantString("WrongEasingName")); Assert.Throws(() => converter.ConvertFromInvariantString("Easing.Linear.SinInOut")); } diff --git a/src/Core/tests/UnitTests/TestCategory.cs b/src/Core/tests/UnitTests/TestCategory.cs index e0c37af7ec0d..b774f9a5b593 100644 --- a/src/Core/tests/UnitTests/TestCategory.cs +++ b/src/Core/tests/UnitTests/TestCategory.cs @@ -2,6 +2,7 @@ namespace Microsoft.Maui.UnitTests { public static class TestCategory { + public const string Animations = "Animations"; public const string Core = "Core"; public const string CommandMapping = "CommandMapping"; public const string Layout = "Layout";