From d0056743168691436ecd975039f270df51091aea Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Fri, 4 Oct 2024 01:40:02 +0200 Subject: [PATCH 1/5] Fix the argument passed to InvalidEnumArgumentException --- .../System/Windows/Input/Command/MouseActionConverter.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/MouseActionConverter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/MouseActionConverter.cs index 62f3ad65dc8..59fa1f97d6b 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/MouseActionConverter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/MouseActionConverter.cs @@ -87,7 +87,8 @@ public override object ConvertTo(ITypeDescriptorContext context, CultureInfo cul if (value is null || destinationType != typeof(string)) throw GetConvertToException(value, destinationType); - return (MouseAction)value switch + MouseAction mouseAction = (MouseAction)value; + return mouseAction switch { MouseAction.None => string.Empty, MouseAction.LeftClick => "LeftClick", @@ -97,7 +98,7 @@ public override object ConvertTo(ITypeDescriptorContext context, CultureInfo cul MouseAction.LeftDoubleClick => "LeftDoubleClick", MouseAction.RightDoubleClick => "RightDoubleClick", MouseAction.MiddleDoubleClick => "MiddleDoubleClick", - _ => throw new InvalidEnumArgumentException(nameof(value), (int)value, typeof(MouseAction)) + _ => throw new InvalidEnumArgumentException(nameof(value), (int)mouseAction, typeof(MouseAction)) }; } From 19c3547153a09d344042cea44177c11da3e78588 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Fri, 4 Oct 2024 01:40:18 +0200 Subject: [PATCH 2/5] Add test coverage for public surface of MouseActionConverter --- .../Input/MouseActionConverter.Tests.cs | 165 ++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/System/Windows/Input/MouseActionConverter.Tests.cs diff --git a/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/System/Windows/Input/MouseActionConverter.Tests.cs b/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/System/Windows/Input/MouseActionConverter.Tests.cs new file mode 100644 index 00000000000..a2b49e73129 --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/System/Windows/Input/MouseActionConverter.Tests.cs @@ -0,0 +1,165 @@ +using System.Collections; +using System.Windows.Input; +using System.ComponentModel; +using System.ComponentModel.Design.Serialization; + +namespace PresentationCore.Tests.System.Windows.Input +{ + public class MouseActionConverterTests + { + [Fact] + public void ConvertTo_ConvertFrom_ReturnsExpected() + { + MouseActionConverter converter = new(); + + // Test MouseAction.None (Special case) + string emptyToNone = string.Empty; + MouseAction constantName = MouseAction.None; + + MouseAction lowerCase = (MouseAction)converter.ConvertFrom(null, null, emptyToNone.ToLowerInvariant()); + MouseAction upperCase = (MouseAction)converter.ConvertFrom(null, null, emptyToNone.ToUpperInvariant()); + MouseAction normalColor = (MouseAction)converter.ConvertFrom(null, null, emptyToNone); + + Assert.Equal(constantName, lowerCase); + Assert.Equal(constantName, upperCase); + Assert.Equal(constantName, normalColor); + + // Test the rest of the enum + foreach (string action in Enum.GetNames()) + { + constantName = Enum.Parse(action); + + lowerCase = (MouseAction)converter.ConvertFrom(null, null, action.ToLowerInvariant()); + upperCase = (MouseAction)converter.ConvertFrom(null, null, action.ToUpperInvariant()); + normalColor = (MouseAction)converter.ConvertFrom(null, null, action); + + Assert.Equal(constantName, lowerCase); + Assert.Equal(constantName, upperCase); + Assert.Equal(constantName, normalColor); + + // Back to the original values + string result = (string)converter.ConvertTo(null, null, constantName, typeof(string)); + result = result == string.Empty ? "None" : result; // Test for MouseAction.None (Special case) + Assert.Equal(action, result); + } + } + + [Fact] + public void ConvertTo_ThrowsArgumentNullException() + { + MouseActionConverter converter = new(); + + Assert.Throws(() => converter.ConvertTo(MouseAction.None, destinationType: null!)); + } + + [Theory] + [InlineData(null, typeof(string))] // Unsupported value + [InlineData(MouseAction.None, typeof(int))] // Unsupported destinationType + public void ConvertTo_ThrowsNotSupportedException(object? value, Type destinationType) + { + MouseActionConverter converter = new(); + + Assert.Throws(() => converter.ConvertTo(value, destinationType)); + } + + [Fact] + public void ConvertTo_ThrowsInvalidCastException() + { + MouseActionConverter converter = new(); + + Assert.Throws(() => converter.ConvertTo(null, null, (int)(MouseAction.MiddleDoubleClick), typeof(string))); + } + + [Fact] + public void ConvertTo_ThrowsInvalidEnumArgumentException() + { + MouseActionConverter converter = new(); + + Assert.Throws(() => converter.ConvertTo(null, null, (MouseAction)(MouseAction.MiddleDoubleClick + 1), typeof(string))); + } + + [Theory] + // Unsupported values (data type) + [InlineData(null)] + [InlineData(MouseAction.None)] + // Unsupported value (bad string) + [InlineData("BadString")] + public void ConvertFrom_ThrowsNotSupportedException(object? value) + { + MouseActionConverter converter = new(); + + Assert.Throws(() => converter.ConvertFrom(null, null, value)); + } + + [Theory] + // Supported type + [InlineData(true, typeof(string))] + // Unsupported types + [InlineData(false, typeof(InstanceDescriptor))] + [InlineData(false, typeof(MouseAction))] + public void CanConvertFrom_ReturnsExpected(bool expected, Type sourceType) + { + MouseActionConverter converter = new(); + + Assert.Equal(expected, converter.CanConvertFrom(null, sourceType)); + } + + [Fact] + public void CanConvertTo_ThrowsInvalidCastException() + { + MouseActionConverter converter = new(); + StandardContextImpl context = new(); + context.Instance = 10; + + // NOTE: CanConvert* methods should not throw but the implementation is faulty + Assert.Throws(() => converter.CanConvertTo(context, typeof(string))); + } + + [Theory] + [MemberData(nameof(CanConvertTo_TestData))] + public void CanConvertTo_ReturnsExpected(bool expected, bool passContext, object? value, Type? destinationType) + { + MouseActionConverter converter = new(); + StandardContextImpl context = new(); + context.Instance = value; + + Assert.Equal(expected, converter.CanConvertTo(passContext ? context : null, destinationType)); + } + + public static IEnumerable CanConvertTo_TestData + { + get + { + // Supported cases + yield return new object[] { true, true, MouseAction.None, typeof(string) }; + yield return new object[] { true, true, MouseAction.MiddleDoubleClick, typeof(string) }; + + // Unsupported case (Value is above MouseAction range) + yield return new object[] { false, true, MouseAction.MiddleDoubleClick + 1, typeof(string) }; + + // Unsupported cases + yield return new object[] { false, false, MouseAction.None, typeof(string) }; + yield return new object[] { false, false, MouseAction.MiddleDoubleClick, typeof(string) }; + yield return new object?[] { false, true, null, typeof(MouseAction) }; + yield return new object?[] { false, true, null, typeof(string) }; + yield return new object?[] { false, false, MouseAction.MiddleDoubleClick, typeof(string) }; + yield return new object?[] { false, false, null, typeof(string) }; + yield return new object?[] { false, false, null, null }; + + yield return new object[] { false, true, MouseAction.MiddleDoubleClick + 1, typeof(string) }; + } + } + + public sealed class StandardContextImpl : ITypeDescriptorContext + { + public IContainer? Container => throw new NotImplementedException(); + + public object? Instance { get; set; } + + public PropertyDescriptor? PropertyDescriptor => throw new NotImplementedException(); + public object? GetService(Type serviceType) => throw new NotImplementedException(); + public void OnComponentChanged() => throw new NotImplementedException(); + public bool OnComponentChanging() => throw new NotImplementedException(); + } + } +} From 8de2f6430879d4545b940542d2356b6455bef14b Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Fri, 4 Oct 2024 02:01:12 +0200 Subject: [PATCH 3/5] Adjust namespace, add license --- .../System/Windows/Input/MouseActionConverter.Tests.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/System/Windows/Input/MouseActionConverter.Tests.cs b/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/System/Windows/Input/MouseActionConverter.Tests.cs index a2b49e73129..cb0ba53a2b4 100644 --- a/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/System/Windows/Input/MouseActionConverter.Tests.cs +++ b/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/System/Windows/Input/MouseActionConverter.Tests.cs @@ -1,9 +1,10 @@ -using System.Collections; -using System.Windows.Input; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + using System.ComponentModel; using System.ComponentModel.Design.Serialization; -namespace PresentationCore.Tests.System.Windows.Input +namespace System.Windows.Input { public class MouseActionConverterTests { From 45b8830f9f4d2789fea6cfb464a339e0d65acd7a Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Wed, 22 Jan 2025 11:04:53 +0100 Subject: [PATCH 4/5] Convert to file-scoped namespace --- .../Input/MouseActionConverter.Tests.cs | 251 +++++++++--------- 1 file changed, 126 insertions(+), 125 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/System/Windows/Input/MouseActionConverter.Tests.cs b/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/System/Windows/Input/MouseActionConverter.Tests.cs index cb0ba53a2b4..f0f86d5d09c 100644 --- a/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/System/Windows/Input/MouseActionConverter.Tests.cs +++ b/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/System/Windows/Input/MouseActionConverter.Tests.cs @@ -4,163 +4,164 @@ using System.ComponentModel; using System.ComponentModel.Design.Serialization; -namespace System.Windows.Input +namespace System.Windows.Input; + +public class MouseActionConverterTests { - public class MouseActionConverterTests + [Fact] + public void ConvertTo_ConvertFrom_ReturnsExpected() { - [Fact] - public void ConvertTo_ConvertFrom_ReturnsExpected() - { - MouseActionConverter converter = new(); + MouseActionConverter converter = new(); - // Test MouseAction.None (Special case) - string emptyToNone = string.Empty; - MouseAction constantName = MouseAction.None; + // Test MouseAction.None (Special case) + string emptyToNone = string.Empty; + MouseAction constantName = MouseAction.None; - MouseAction lowerCase = (MouseAction)converter.ConvertFrom(null, null, emptyToNone.ToLowerInvariant()); - MouseAction upperCase = (MouseAction)converter.ConvertFrom(null, null, emptyToNone.ToUpperInvariant()); - MouseAction normalColor = (MouseAction)converter.ConvertFrom(null, null, emptyToNone); + MouseAction lowerCase = (MouseAction)converter.ConvertFrom(null, null, emptyToNone.ToLowerInvariant()); + MouseAction upperCase = (MouseAction)converter.ConvertFrom(null, null, emptyToNone.ToUpperInvariant()); + MouseAction normalColor = (MouseAction)converter.ConvertFrom(null, null, emptyToNone); - Assert.Equal(constantName, lowerCase); - Assert.Equal(constantName, upperCase); - Assert.Equal(constantName, normalColor); + Assert.Equal(constantName, lowerCase); + Assert.Equal(constantName, upperCase); + Assert.Equal(constantName, normalColor); - // Test the rest of the enum - foreach (string action in Enum.GetNames()) - { - constantName = Enum.Parse(action); + // Test the rest of the enum + foreach (string action in Enum.GetNames()) + { + constantName = Enum.Parse(action); - lowerCase = (MouseAction)converter.ConvertFrom(null, null, action.ToLowerInvariant()); - upperCase = (MouseAction)converter.ConvertFrom(null, null, action.ToUpperInvariant()); - normalColor = (MouseAction)converter.ConvertFrom(null, null, action); + lowerCase = (MouseAction)converter.ConvertFrom(null, null, action.ToLowerInvariant()); + upperCase = (MouseAction)converter.ConvertFrom(null, null, action.ToUpperInvariant()); + normalColor = (MouseAction)converter.ConvertFrom(null, null, action); - Assert.Equal(constantName, lowerCase); - Assert.Equal(constantName, upperCase); - Assert.Equal(constantName, normalColor); + Assert.Equal(constantName, lowerCase); + Assert.Equal(constantName, upperCase); + Assert.Equal(constantName, normalColor); - // Back to the original values - string result = (string)converter.ConvertTo(null, null, constantName, typeof(string)); - result = result == string.Empty ? "None" : result; // Test for MouseAction.None (Special case) - Assert.Equal(action, result); - } + // Back to the original values + string result = (string)converter.ConvertTo(null, null, constantName, typeof(string)); + result = result == string.Empty ? "None" : result; // Test for MouseAction.None (Special case) + Assert.Equal(action, result); } + } - [Fact] - public void ConvertTo_ThrowsArgumentNullException() - { - MouseActionConverter converter = new(); + [Fact] + public void ConvertTo_ThrowsArgumentNullException() + { + MouseActionConverter converter = new(); - Assert.Throws(() => converter.ConvertTo(MouseAction.None, destinationType: null!)); - } + Assert.Throws(() => converter.ConvertTo(MouseAction.None, destinationType: null!)); + } - [Theory] - [InlineData(null, typeof(string))] // Unsupported value - [InlineData(MouseAction.None, typeof(int))] // Unsupported destinationType - public void ConvertTo_ThrowsNotSupportedException(object? value, Type destinationType) - { - MouseActionConverter converter = new(); + [Theory] + [InlineData(null, typeof(string))] // Unsupported value + [InlineData(MouseAction.None, typeof(int))] // Unsupported destinationType + public void ConvertTo_ThrowsNotSupportedException(object? value, Type destinationType) + { + MouseActionConverter converter = new(); - Assert.Throws(() => converter.ConvertTo(value, destinationType)); - } + Assert.Throws(() => converter.ConvertTo(value, destinationType)); + } - [Fact] - public void ConvertTo_ThrowsInvalidCastException() - { - MouseActionConverter converter = new(); + [Fact] + public void ConvertTo_ThrowsInvalidCastException() + { + MouseActionConverter converter = new(); - Assert.Throws(() => converter.ConvertTo(null, null, (int)(MouseAction.MiddleDoubleClick), typeof(string))); - } + Assert.Throws(() => converter.ConvertTo(null, null, (int)(MouseAction.MiddleDoubleClick), typeof(string))); + } - [Fact] - public void ConvertTo_ThrowsInvalidEnumArgumentException() - { - MouseActionConverter converter = new(); + [Fact] + public void ConvertTo_ThrowsInvalidEnumArgumentException() + { + MouseActionConverter converter = new(); - Assert.Throws(() => converter.ConvertTo(null, null, (MouseAction)(MouseAction.MiddleDoubleClick + 1), typeof(string))); - } + Assert.Throws(() => converter.ConvertTo(null, null, (MouseAction)(MouseAction.MiddleDoubleClick + 1), typeof(string))); + } - [Theory] - // Unsupported values (data type) - [InlineData(null)] - [InlineData(MouseAction.None)] - // Unsupported value (bad string) - [InlineData("BadString")] - public void ConvertFrom_ThrowsNotSupportedException(object? value) - { - MouseActionConverter converter = new(); + [Theory] + // Unsupported values (data type) + [InlineData(null)] + [InlineData(MouseAction.None)] + // Unsupported value (bad string) + [InlineData("BadString")] + public void ConvertFrom_ThrowsNotSupportedException(object? value) + { + MouseActionConverter converter = new(); - Assert.Throws(() => converter.ConvertFrom(null, null, value)); - } + Assert.Throws(() => converter.ConvertFrom(null, null, value)); + } - [Theory] - // Supported type - [InlineData(true, typeof(string))] - // Unsupported types - [InlineData(false, typeof(InstanceDescriptor))] - [InlineData(false, typeof(MouseAction))] - public void CanConvertFrom_ReturnsExpected(bool expected, Type sourceType) - { - MouseActionConverter converter = new(); + [Theory] + // Supported type + [InlineData(true, typeof(string))] + // Unsupported types + [InlineData(false, typeof(InstanceDescriptor))] + [InlineData(false, typeof(MouseAction))] + public void CanConvertFrom_ReturnsExpected(bool expected, Type sourceType) + { + MouseActionConverter converter = new(); - Assert.Equal(expected, converter.CanConvertFrom(null, sourceType)); - } + Assert.Equal(expected, converter.CanConvertFrom(null, sourceType)); + } - [Fact] - public void CanConvertTo_ThrowsInvalidCastException() - { - MouseActionConverter converter = new(); - StandardContextImpl context = new(); - context.Instance = 10; + [Fact] + public void CanConvertTo_ThrowsInvalidCastException() + { + MouseActionConverter converter = new(); + StandardContextImpl context = new(); + context.Instance = 10; - // NOTE: CanConvert* methods should not throw but the implementation is faulty - Assert.Throws(() => converter.CanConvertTo(context, typeof(string))); - } + // NOTE: CanConvert* methods should not throw but the implementation is faulty + Assert.Throws(() => converter.CanConvertTo(context, typeof(string))); + } - [Theory] - [MemberData(nameof(CanConvertTo_TestData))] - public void CanConvertTo_ReturnsExpected(bool expected, bool passContext, object? value, Type? destinationType) + [Theory] + [MemberData(nameof(CanConvertTo_Data))] + public void CanConvertTo_ReturnsExpected(bool expected, bool passContext, object? value, Type? destinationType) + { + MouseActionConverter converter = new(); + StandardContextImpl context = new() { - MouseActionConverter converter = new(); - StandardContextImpl context = new(); - context.Instance = value; + Instance = value + }; - Assert.Equal(expected, converter.CanConvertTo(passContext ? context : null, destinationType)); - } + Assert.Equal(expected, converter.CanConvertTo(passContext ? context : null, destinationType)); + } - public static IEnumerable CanConvertTo_TestData + public static IEnumerable CanConvertTo_Data + { + get { - get - { - // Supported cases - yield return new object[] { true, true, MouseAction.None, typeof(string) }; - yield return new object[] { true, true, MouseAction.MiddleDoubleClick, typeof(string) }; - - // Unsupported case (Value is above MouseAction range) - yield return new object[] { false, true, MouseAction.MiddleDoubleClick + 1, typeof(string) }; - - // Unsupported cases - yield return new object[] { false, false, MouseAction.None, typeof(string) }; - yield return new object[] { false, false, MouseAction.MiddleDoubleClick, typeof(string) }; - yield return new object?[] { false, true, null, typeof(MouseAction) }; - yield return new object?[] { false, true, null, typeof(string) }; - yield return new object?[] { false, false, MouseAction.MiddleDoubleClick, typeof(string) }; - yield return new object?[] { false, false, null, typeof(string) }; - yield return new object?[] { false, false, null, null }; - - yield return new object[] { false, true, MouseAction.MiddleDoubleClick + 1, typeof(string) }; - } + // Supported cases + yield return new object[] { true, true, MouseAction.None, typeof(string) }; + yield return new object[] { true, true, MouseAction.MiddleDoubleClick, typeof(string) }; + + // Unsupported case (Value is above MouseAction range) + yield return new object[] { false, true, MouseAction.MiddleDoubleClick + 1, typeof(string) }; + + // Unsupported cases + yield return new object[] { false, false, MouseAction.None, typeof(string) }; + yield return new object[] { false, false, MouseAction.MiddleDoubleClick, typeof(string) }; + yield return new object?[] { false, true, null, typeof(MouseAction) }; + yield return new object?[] { false, true, null, typeof(string) }; + yield return new object?[] { false, false, MouseAction.MiddleDoubleClick, typeof(string) }; + yield return new object?[] { false, false, null, typeof(string) }; + yield return new object?[] { false, false, null, null }; + + yield return new object[] { false, true, MouseAction.MiddleDoubleClick + 1, typeof(string) }; } + } - public sealed class StandardContextImpl : ITypeDescriptorContext - { - public IContainer? Container => throw new NotImplementedException(); + public sealed class StandardContextImpl : ITypeDescriptorContext + { + public IContainer? Container => throw new NotImplementedException(); - public object? Instance { get; set; } + public object? Instance { get; set; } - public PropertyDescriptor? PropertyDescriptor => throw new NotImplementedException(); - public object? GetService(Type serviceType) => throw new NotImplementedException(); - public void OnComponentChanged() => throw new NotImplementedException(); - public bool OnComponentChanging() => throw new NotImplementedException(); - } + public PropertyDescriptor? PropertyDescriptor => throw new NotImplementedException(); + public object? GetService(Type serviceType) => throw new NotImplementedException(); + public void OnComponentChanged() => throw new NotImplementedException(); + public bool OnComponentChanging() => throw new NotImplementedException(); } } From 085c76ea3226ea008a10fe0766960385130e415f Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Wed, 22 Jan 2025 11:37:57 +0100 Subject: [PATCH 5/5] Add more tests, theorize ConvertTo/ConvertFrom (ReturnsExpected) --- .../Input/MouseActionConverter.Tests.cs | 194 ++++++++++-------- 1 file changed, 112 insertions(+), 82 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/System/Windows/Input/MouseActionConverter.Tests.cs b/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/System/Windows/Input/MouseActionConverter.Tests.cs index f0f86d5d09c..5fa2efad7d3 100644 --- a/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/System/Windows/Input/MouseActionConverter.Tests.cs +++ b/src/Microsoft.DotNet.Wpf/tests/UnitTests/PresentationCore.Tests/System/Windows/Input/MouseActionConverter.Tests.cs @@ -1,90 +1,117 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.ComponentModel; using System.ComponentModel.Design.Serialization; +using System.ComponentModel; +using System.Globalization; namespace System.Windows.Input; -public class MouseActionConverterTests +public sealed class MouseActionConverterTests { - [Fact] - public void ConvertTo_ConvertFrom_ReturnsExpected() + [Theory] + // Supported type + [InlineData(true, typeof(string))] + // Unsupported types + [InlineData(false, typeof(InstanceDescriptor))] + [InlineData(false, typeof(MouseAction))] + public void CanConvertFrom_ReturnsExpected(bool expected, Type sourceType) { MouseActionConverter converter = new(); - // Test MouseAction.None (Special case) - string emptyToNone = string.Empty; - MouseAction constantName = MouseAction.None; + Assert.Equal(expected, converter.CanConvertFrom(null, sourceType)); + } - MouseAction lowerCase = (MouseAction)converter.ConvertFrom(null, null, emptyToNone.ToLowerInvariant()); - MouseAction upperCase = (MouseAction)converter.ConvertFrom(null, null, emptyToNone.ToUpperInvariant()); - MouseAction normalColor = (MouseAction)converter.ConvertFrom(null, null, emptyToNone); + [Theory] + [MemberData(nameof(CanConvertTo_Data))] + public void CanConvertTo_ReturnsExpected(bool expected, bool passContext, object? value, Type? destinationType) + { + MouseActionConverter converter = new(); + StandardContextImpl context = new() { Instance = value }; - Assert.Equal(constantName, lowerCase); - Assert.Equal(constantName, upperCase); - Assert.Equal(constantName, normalColor); + Assert.Equal(expected, converter.CanConvertTo(passContext ? context : null, destinationType)); + } - // Test the rest of the enum - foreach (string action in Enum.GetNames()) + public static IEnumerable CanConvertTo_Data + { + get { - constantName = Enum.Parse(action); + // Supported cases + yield return new object[] { true, true, MouseAction.None, typeof(string) }; + yield return new object[] { true, true, MouseAction.MiddleDoubleClick, typeof(string) }; - lowerCase = (MouseAction)converter.ConvertFrom(null, null, action.ToLowerInvariant()); - upperCase = (MouseAction)converter.ConvertFrom(null, null, action.ToUpperInvariant()); - normalColor = (MouseAction)converter.ConvertFrom(null, null, action); + // Unsupported case (Value is above MouseAction range) + yield return new object[] { false, true, MouseAction.MiddleDoubleClick + 1, typeof(string) }; - Assert.Equal(constantName, lowerCase); - Assert.Equal(constantName, upperCase); - Assert.Equal(constantName, normalColor); + // Unsupported cases + yield return new object[] { false, false, MouseAction.None, typeof(string) }; + yield return new object[] { false, false, MouseAction.MiddleDoubleClick, typeof(string) }; + yield return new object?[] { false, true, null, typeof(MouseAction) }; + yield return new object?[] { false, true, null, typeof(string) }; + yield return new object?[] { false, false, MouseAction.MiddleDoubleClick, typeof(string) }; + yield return new object?[] { false, false, null, typeof(string) }; + yield return new object?[] { false, false, null, null }; - // Back to the original values - string result = (string)converter.ConvertTo(null, null, constantName, typeof(string)); - result = result == string.Empty ? "None" : result; // Test for MouseAction.None (Special case) - Assert.Equal(action, result); + yield return new object[] { false, true, MouseAction.MiddleDoubleClick + 1, typeof(string) }; } } [Fact] - public void ConvertTo_ThrowsArgumentNullException() + public void CanConvertTo_ThrowsInvalidCastException() { MouseActionConverter converter = new(); + StandardContextImpl context = new() { Instance = 10 }; - Assert.Throws(() => converter.ConvertTo(MouseAction.None, destinationType: null!)); + // TODO: CanConvert* methods should not throw but the implementation is faulty + Assert.Throws(() => converter.CanConvertTo(context, typeof(string))); } [Theory] - [InlineData(null, typeof(string))] // Unsupported value - [InlineData(MouseAction.None, typeof(int))] // Unsupported destinationType - public void ConvertTo_ThrowsNotSupportedException(object? value, Type destinationType) + [MemberData(nameof(ConvertFrom_ReturnsExpected_Data))] + public void ConvertFrom_ReturnsExpected(MouseAction expected, ITypeDescriptorContext context, CultureInfo? cultureInfo, string value) { MouseActionConverter converter = new(); - Assert.Throws(() => converter.ConvertTo(value, destinationType)); + Assert.Equal(expected, (MouseAction)converter.ConvertFrom(context, cultureInfo, value)); } - [Fact] - public void ConvertTo_ThrowsInvalidCastException() + public static IEnumerable ConvertFrom_ReturnsExpected_Data { - MouseActionConverter converter = new(); - - Assert.Throws(() => converter.ConvertTo(null, null, (int)(MouseAction.MiddleDoubleClick), typeof(string))); - } - - [Fact] - public void ConvertTo_ThrowsInvalidEnumArgumentException() - { - MouseActionConverter converter = new(); - - Assert.Throws(() => converter.ConvertTo(null, null, (MouseAction)(MouseAction.MiddleDoubleClick + 1), typeof(string))); + get + { + // ConvertTo handles two different inputs as MouseAction.None + yield return new object?[] { MouseAction.None, null, CultureInfo.InvariantCulture, string.Empty }; + yield return new object?[] { MouseAction.None, null, CultureInfo.InvariantCulture, "None" }; + + // Supported cases (Culture must stay irrelevant) + yield return new object?[] { MouseAction.None, null, CultureInfo.InvariantCulture, string.Empty }; + yield return new object?[] { MouseAction.LeftClick, null, new CultureInfo("ru-RU"), "LeftClick" }; + yield return new object?[] { MouseAction.RightClick, null, CultureInfo.InvariantCulture, "RightClick" }; + yield return new object?[] { MouseAction.MiddleClick, null, CultureInfo.InvariantCulture, "MiddleClick" }; + yield return new object?[] { MouseAction.WheelClick, null, new CultureInfo("no-NO"), "WheelClick" }; + yield return new object?[] { MouseAction.LeftDoubleClick, null, CultureInfo.InvariantCulture, "LeftDoubleClick" }; + yield return new object?[] { MouseAction.RightDoubleClick, null, CultureInfo.InvariantCulture, "RightDoubleClick" }; + yield return new object?[] { MouseAction.MiddleDoubleClick, null, CultureInfo.InvariantCulture, "MiddleDoubleClick" }; + + // Supported cases (fuzzed via whitespace and casing) + yield return new object?[] { MouseAction.None, null, CultureInfo.InvariantCulture, " " }; + yield return new object?[] { MouseAction.None, null, new CultureInfo("ru-RU"), " NoNE " }; + yield return new object?[] { MouseAction.LeftClick, null, CultureInfo.InvariantCulture, " LeFTCliCK " }; + yield return new object?[] { MouseAction.WheelClick, null, CultureInfo.InvariantCulture, " WHEELCLICK" }; + yield return new object?[] { MouseAction.MiddleClick, null, new CultureInfo("no-NO"), " MiDDLeCliCK " }; + yield return new object?[] { MouseAction.LeftDoubleClick, null, CultureInfo.InvariantCulture, " leftdoubleclick " }; + yield return new object?[] { MouseAction.RightClick, null, CultureInfo.InvariantCulture, " rightclick" }; + } } [Theory] // Unsupported values (data type) [InlineData(null)] + [InlineData(Key.VolumeDown)] [InlineData(MouseAction.None)] // Unsupported value (bad string) [InlineData("BadString")] + [InlineData("MouseZClick")] public void ConvertFrom_ThrowsNotSupportedException(object? value) { MouseActionConverter converter = new(); @@ -93,64 +120,67 @@ public void ConvertFrom_ThrowsNotSupportedException(object? value) } [Theory] - // Supported type - [InlineData(true, typeof(string))] - // Unsupported types - [InlineData(false, typeof(InstanceDescriptor))] - [InlineData(false, typeof(MouseAction))] - public void CanConvertFrom_ReturnsExpected(bool expected, Type sourceType) + [MemberData(nameof(ConvertTo_ReturnsExpected_Data))] + public void ConvertTo_ReturnsExpected(string expected, ITypeDescriptorContext context, CultureInfo? cultureInfo, object? value) { MouseActionConverter converter = new(); - Assert.Equal(expected, converter.CanConvertFrom(null, sourceType)); + // Culture and context must not have any meaning + Assert.Equal(expected, (string)converter.ConvertTo(context, cultureInfo, value, typeof(string))); + } + + public static IEnumerable ConvertTo_ReturnsExpected_Data + { + get + { + // Supported cases (Culture must stay irrelevant) + yield return new object?[] { string.Empty, null, CultureInfo.InvariantCulture, MouseAction.None }; + yield return new object?[] { "LeftClick", null, CultureInfo.InvariantCulture, MouseAction.LeftClick }; + yield return new object?[] { "RightClick", null, new CultureInfo("ru-RU"), MouseAction.RightClick }; + yield return new object?[] { "MiddleClick", null, CultureInfo.InvariantCulture, MouseAction.MiddleClick }; + yield return new object?[] { "WheelClick", null, new CultureInfo("no-NO"), MouseAction.WheelClick }; + yield return new object?[] { "LeftDoubleClick", null, CultureInfo.InvariantCulture, MouseAction.LeftDoubleClick }; + yield return new object?[] { "RightDoubleClick", null, null, MouseAction.RightDoubleClick }; + yield return new object?[] { "MiddleDoubleClick", null, null, MouseAction.MiddleDoubleClick }; + } } [Fact] - public void CanConvertTo_ThrowsInvalidCastException() + public void ConvertTo_ThrowsArgumentNullException() { MouseActionConverter converter = new(); - StandardContextImpl context = new(); - context.Instance = 10; - // NOTE: CanConvert* methods should not throw but the implementation is faulty - Assert.Throws(() => converter.CanConvertTo(context, typeof(string))); + Assert.Throws(() => converter.ConvertTo(MouseAction.None, destinationType: null!)); } [Theory] - [MemberData(nameof(CanConvertTo_Data))] - public void CanConvertTo_ReturnsExpected(bool expected, bool passContext, object? value, Type? destinationType) + // Unsupported value + [InlineData(null, typeof(string))] + // Unsupported destinationType + [InlineData(MouseAction.None, typeof(int))] + [InlineData(MouseAction.LeftClick, typeof(byte))] + public void ConvertTo_ThrowsNotSupportedException(object? value, Type destinationType) { MouseActionConverter converter = new(); - StandardContextImpl context = new() - { - Instance = value - }; - Assert.Equal(expected, converter.CanConvertTo(passContext ? context : null, destinationType)); + Assert.Throws(() => converter.ConvertTo(value, destinationType)); } - public static IEnumerable CanConvertTo_Data + [Fact] + public void ConvertTo_ThrowsInvalidCastException() { - get - { - // Supported cases - yield return new object[] { true, true, MouseAction.None, typeof(string) }; - yield return new object[] { true, true, MouseAction.MiddleDoubleClick, typeof(string) }; + MouseActionConverter converter = new(); - // Unsupported case (Value is above MouseAction range) - yield return new object[] { false, true, MouseAction.MiddleDoubleClick + 1, typeof(string) }; + // TODO: This should not throw InvalidCastException but NotSupportedException + Assert.Throws(() => converter.ConvertTo(null, null, (int)(MouseAction.MiddleDoubleClick), typeof(string))); + } - // Unsupported cases - yield return new object[] { false, false, MouseAction.None, typeof(string) }; - yield return new object[] { false, false, MouseAction.MiddleDoubleClick, typeof(string) }; - yield return new object?[] { false, true, null, typeof(MouseAction) }; - yield return new object?[] { false, true, null, typeof(string) }; - yield return new object?[] { false, false, MouseAction.MiddleDoubleClick, typeof(string) }; - yield return new object?[] { false, false, null, typeof(string) }; - yield return new object?[] { false, false, null, null }; + [Fact] + public void ConvertTo_ThrowsInvalidEnumArgumentException() + { + MouseActionConverter converter = new(); - yield return new object[] { false, true, MouseAction.MiddleDoubleClick + 1, typeof(string) }; - } + Assert.Throws(() => converter.ConvertTo(null, null, (MouseAction)(MouseAction.MiddleDoubleClick + 1), typeof(string))); } public sealed class StandardContextImpl : ITypeDescriptorContext