Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix InvalidCastException in Can/ConvertTo from MouseActionConverter #10381

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.ComponentModel;
using System.Globalization;

namespace System.Windows.Input
{
/// <summary>
/// Converter class for converting between a <see langword="string"/> and <see cref="MouseAction"/>.
/// Converter class for converting between a <see cref="string"/> and <see cref="MouseAction"/>.
/// </summary>
public class MouseActionConverter : TypeConverter
{
///<summary>
/// Used to check whether we can convert a <see langword="string"/> into a <see cref="MouseAction"/>.
/// Used to check whether we can convert a <see cref="string"/> into a <see cref="MouseAction"/>.
///</summary>
///<param name="context">ITypeDescriptorContext</param>
///<param name="sourceType">type to convert from</param>
Expand All @@ -25,32 +24,32 @@ public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceT
}

/// <summary>
/// Used to check whether we can convert specified value to <see langword="string"/>.
/// Used to check whether we can convert specified value to <see cref="string"/>.
/// </summary>
/// <param name="context">ITypeDescriptorContext</param>
/// <param name="destinationType">Type to convert to</param>
/// <returns><see langword="true"/> if conversion to <see langword="string"/> is possible, <see langword="false"/> otherwise.</returns>
/// <returns><see langword="true"/> if conversion to <see cref="string"/> is possible, <see langword="false"/> otherwise.</returns>
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
// We can convert to an InstanceDescriptor or to a string
if (destinationType != typeof(string))
return false;

// When invoked by the serialization engine we can convert to string only for known type
if (context is null || context.Instance is null)
if (context?.Instance is not MouseAction mouseAction)
return false;

// Make sure the value falls within defined set
return IsDefinedMouseAction((MouseAction)context.Instance);
return IsDefinedMouseAction(mouseAction);
}

/// <summary>
/// Converts <paramref name="source"/> of <see langword="string"/> type to its <see cref="MouseAction"/> represensation.
/// Converts <paramref name="source"/> of <see cref="string"/> type to its <see cref="MouseAction"/> representation.
/// </summary>
/// <param name="context">Parser Context</param>
/// <param name="culture">Culture Info</param>
/// <param name="source">MouseAction String</param>
/// <returns>A <see cref="MouseAction"/> representing the <see langword="string"/> specified by <paramref name="source"/>.</returns>
/// <returns>A <see cref="MouseAction"/> representing the <see cref="string"/> specified by <paramref name="source"/>.</returns>
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object source)
{
if (source is not string mouseAction)
Expand All @@ -73,21 +72,20 @@ _ when mouseActionToken.Equals("MiddleDoubleClick", StringComparison.OrdinalIgno
}

/// <summary>
/// Converts a <paramref name="value"/> of <see cref="MouseAction"/> to its <see langword="string"/> represensation.
/// Converts a <paramref name="value"/> of <see cref="MouseAction"/> to its <see cref="string"/> representation.
/// </summary>
/// <param name="context">Serialization Context</param>
/// <param name="culture">Culture Info</param>
/// <param name="value">MouseAction value </param>
/// <param name="destinationType">Type to Convert</param>
/// <returns>A <see langword="string"/> representing the <see cref="MouseAction"/> specified by <paramref name="value"/>.</returns>
/// <returns>A <see cref="string"/> representing the <see cref="MouseAction"/> specified by <paramref name="value"/>.</returns>
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
ArgumentNullException.ThrowIfNull(destinationType);

if (value is null || destinationType != typeof(string))
if (value is not MouseAction mouseAction || destinationType != typeof(string))
throw GetConvertToException(value, destinationType);

MouseAction mouseAction = (MouseAction)value;
return mouseAction switch
{
MouseAction.None => string.Empty,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public void CanConvertFrom_ReturnsExpected(bool expected, Type sourceType)
}

[Theory]
[MemberData(nameof(CanConvertTo_Data))]
[MemberData(nameof(CanConvertTo_ReturnsExpected_Data))]
public void CanConvertTo_ReturnsExpected(bool expected, bool passContext, object? value, Type? destinationType)
{
MouseActionConverter converter = new();
Expand All @@ -32,7 +32,7 @@ public void CanConvertTo_ReturnsExpected(bool expected, bool passContext, object
Assert.Equal(expected, converter.CanConvertTo(passContext ? context : null, destinationType));
}

public static IEnumerable<object?[]> CanConvertTo_Data
public static IEnumerable<object?[]> CanConvertTo_ReturnsExpected_Data
{
get
{
Expand All @@ -46,6 +46,8 @@ public static IEnumerable<object?[]> CanConvertTo_Data
// 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, (int)MouseAction.MiddleDoubleClick, typeof(string) };
yield return new object[] { false, true, (short)MouseAction.LeftDoubleClick, 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) };
Expand All @@ -56,16 +58,6 @@ public static IEnumerable<object?[]> CanConvertTo_Data
}
}

[Fact]
public void CanConvertTo_ThrowsInvalidCastException()
{
MouseActionConverter converter = new();
StandardContextImpl context = new() { Instance = 10 };

// TODO: CanConvert* methods should not throw but the implementation is faulty
Assert.Throws<InvalidCastException>(() => converter.CanConvertTo(context, typeof(string)));
}

[Theory]
[MemberData(nameof(ConvertFrom_ReturnsExpected_Data))]
public void ConvertFrom_ReturnsExpected(MouseAction expected, ITypeDescriptorContext context, CultureInfo? cultureInfo, string value)
Expand Down Expand Up @@ -156,6 +148,9 @@ public void ConvertTo_ThrowsArgumentNullException()
[Theory]
// Unsupported value
[InlineData(null, typeof(string))]
// Unsupported unboxing casts
[InlineData((int)MouseAction.RightClick, typeof(string))]
[InlineData((short)MouseAction.LeftDoubleClick, typeof(string))]
// Unsupported destinationType
[InlineData(MouseAction.None, typeof(int))]
[InlineData(MouseAction.LeftClick, typeof(byte))]
Expand All @@ -166,21 +161,12 @@ public void ConvertTo_ThrowsNotSupportedException(object? value, Type destinatio
Assert.Throws<NotSupportedException>(() => converter.ConvertTo(value, destinationType));
}

[Fact]
public void ConvertTo_ThrowsInvalidCastException()
{
MouseActionConverter converter = new();

// TODO: This should not throw InvalidCastException but NotSupportedException
Assert.Throws<InvalidCastException>(() => converter.ConvertTo(null, null, (int)(MouseAction.MiddleDoubleClick), typeof(string)));
}

[Fact]
public void ConvertTo_ThrowsInvalidEnumArgumentException()
{
MouseActionConverter converter = new();

Assert.Throws<InvalidEnumArgumentException>(() => converter.ConvertTo(null, null, (MouseAction)(MouseAction.MiddleDoubleClick + 1), typeof(string)));
Assert.Throws<InvalidEnumArgumentException>(() => converter.ConvertTo(null, null, MouseAction.MiddleDoubleClick + 1, typeof(string)));
}

public sealed class StandardContextImpl : ITypeDescriptorContext
Expand Down