Skip to content
Merged
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
Expand Up @@ -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",
Expand All @@ -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))
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
// 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.Design.Serialization;
using System.ComponentModel;
using System.Globalization;

namespace System.Windows.Input;

public sealed class MouseActionConverterTests
{
[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));
}

[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(expected, converter.CanConvertTo(passContext ? context : null, destinationType));
}

public static IEnumerable<object?[]> CanConvertTo_Data
{
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) };
}
}

[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)
{
MouseActionConverter converter = new();

Assert.Equal(expected, (MouseAction)converter.ConvertFrom(context, cultureInfo, value));
}

public static IEnumerable<object?[]> ConvertFrom_ReturnsExpected_Data
{
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();

Assert.Throws<NotSupportedException>(() => converter.ConvertFrom(null, null, value));
}

[Theory]
[MemberData(nameof(ConvertTo_ReturnsExpected_Data))]
public void ConvertTo_ReturnsExpected(string expected, ITypeDescriptorContext context, CultureInfo? cultureInfo, object? value)
{
MouseActionConverter converter = new();

// Culture and context must not have any meaning
Assert.Equal(expected, (string)converter.ConvertTo(context, cultureInfo, value, typeof(string)));
}

public static IEnumerable<object?[]> 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 ConvertTo_ThrowsArgumentNullException()
{
MouseActionConverter converter = new();

Assert.Throws<ArgumentNullException>(() => converter.ConvertTo(MouseAction.None, destinationType: null!));
}

[Theory]
// 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();

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)));
}

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();
}
}