File: System\Windows\Input\ModifierKeysConverterTests.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\tests\UnitTests\WindowsBase.Tests\WindowsBase.Tests.csproj (WindowsBase.Tests)
// 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.Globalization;
 
namespace System.Windows.Input.Tests;
 
public class ModifierKeysConverterTests
{
    public static IEnumerable<object?[]> CanConvertTo_TestData()
    {
        yield return new object?[] { null, null, false };
        yield return new object?[] { null, typeof(object), false };
        yield return new object?[] { null, typeof(string), false };
        yield return new object?[] { null, typeof(InstanceDescriptor), false };
        yield return new object?[] { null, typeof(Key), false };
        yield return new object?[] { null, typeof(ModifierKeys), false };
        yield return new object?[] { new CustomTypeDescriptorContext(), null, false };
        yield return new object?[] { new CustomTypeDescriptorContext(), typeof(object), false };
        yield return new object?[] { new CustomTypeDescriptorContext(), typeof(string), false };
        yield return new object?[] { new CustomTypeDescriptorContext(), typeof(InstanceDescriptor), false };
        yield return new object?[] { new CustomTypeDescriptorContext(), typeof(Key), false };
        yield return new object?[] { new CustomTypeDescriptorContext(), typeof(ModifierKeys), false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = new object() }, null, false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = new object() }, typeof(object), false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = new object() }, typeof(string), false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = new object() }, typeof(InstanceDescriptor), false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = new object() }, typeof(Key), false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = new object() }, typeof(ModifierKeys), false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = ModifierKeys.None }, null, false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = ModifierKeys.None }, typeof(object), false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = ModifierKeys.None }, typeof(string), true };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = ModifierKeys.None }, typeof(InstanceDescriptor), false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = ModifierKeys.None }, typeof(Key), false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = ModifierKeys.None }, typeof(ModifierKeys), false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = ModifierKeys.Control }, null, false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = ModifierKeys.Control }, typeof(object), false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = ModifierKeys.Control }, typeof(string), true };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = ModifierKeys.Control }, typeof(InstanceDescriptor), false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = ModifierKeys.Control }, typeof(Key), false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = ModifierKeys.Control }, typeof(ModifierKeys), false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Windows | ModifierKeys.Shift }, null, false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Windows | ModifierKeys.Shift }, typeof(object), false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Windows | ModifierKeys.Shift }, typeof(string), true };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Windows | ModifierKeys.Shift }, typeof(InstanceDescriptor), false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Windows | ModifierKeys.Shift }, typeof(Key), false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Windows | ModifierKeys.Shift }, typeof(ModifierKeys), false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = (ModifierKeys)(-1) }, null, false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = (ModifierKeys)(-1) }, typeof(object), false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = (ModifierKeys)(-1) }, typeof(string), false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = (ModifierKeys)(-1) }, typeof(InstanceDescriptor), false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = (ModifierKeys)(-1) }, typeof(Key), false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = (ModifierKeys)(-1) }, typeof(ModifierKeys), false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = (ModifierKeys)0x10 }, null, false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = (ModifierKeys)0x10 }, typeof(object), false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = (ModifierKeys)0x10 }, typeof(string), false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = (ModifierKeys)0x10 }, typeof(InstanceDescriptor), false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = (ModifierKeys)0x10 }, typeof(Key), false };
        yield return new object?[] { new CustomTypeDescriptorContext { Instance = (ModifierKeys)0x10 }, typeof(ModifierKeys), false };
    }
 
    [Theory]
    [MemberData(nameof(CanConvertTo_TestData))]
    public void CanConvertTo_Invoke_ReturnsExpected(ITypeDescriptorContext context, Type destinationType, bool expected)
    {
        var converter = new ModifierKeysConverter();
        Assert.Equal(expected, converter.CanConvertTo(context, destinationType));
    }
 
    public static IEnumerable<object[]> ConvertTo_ModifierKeysToString_TestData()
    {
        yield return new object[] { ModifierKeys.None, "" };
        yield return new object[] { ModifierKeys.Alt, "Alt" };
        yield return new object[] { ModifierKeys.Control, "Ctrl" };
        yield return new object[] { ModifierKeys.Shift, "Shift" };
        yield return new object[] { ModifierKeys.Windows, "Windows" };
        
        yield return new object[] { ModifierKeys.Control | ModifierKeys.Alt, "Ctrl+Alt" };
        yield return new object[] { ModifierKeys.Control | ModifierKeys.Windows, "Ctrl+Windows" };
        yield return new object[] { ModifierKeys.Control | ModifierKeys.Shift, "Ctrl+Shift" };
        yield return new object[] { ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Windows, "Ctrl+Alt+Windows" };
        yield return new object[] { ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Windows | ModifierKeys.Shift, "Ctrl+Alt+Windows+Shift" };
        yield return new object[] { ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Shift, "Ctrl+Alt+Shift" };
        yield return new object[] { ModifierKeys.Control | ModifierKeys.Windows | ModifierKeys.Shift, "Ctrl+Windows+Shift" };
        yield return new object[] { ModifierKeys.Alt | ModifierKeys.Windows, "Alt+Windows" };
        yield return new object[] { ModifierKeys.Alt | ModifierKeys.Windows | ModifierKeys.Shift, "Alt+Windows+Shift" };
        yield return new object[] { ModifierKeys.Alt | ModifierKeys.Shift, "Alt+Shift" };
        yield return new object[] { ModifierKeys.Windows | ModifierKeys.Shift, "Windows+Shift" };
    }
 
    [Theory]
    [MemberData(nameof(ConvertTo_ModifierKeysToString_TestData))]
    public void ConvertTo_InvokeModifierKeysToString_ReturnsExpected(ModifierKeys value, string expected)
    {
        var converter = new ModifierKeysConverter();
        Assert.Equal(expected, converter.ConvertTo(value, typeof(string)));
        Assert.Equal(expected, converter.ConvertTo(new CustomTypeDescriptorContext(), null, value, typeof(string)));
        Assert.Equal(expected, converter.ConvertTo(new CustomTypeDescriptorContext(), CultureInfo.InvariantCulture, value, typeof(string)));
    }
 
    public static IEnumerable<object[]> ConvertTo_InvalidModifierKeys_TestData()
    {
        yield return new object[] { (ModifierKeys)int.MinValue };
        yield return new object[] { (ModifierKeys)(-1) };
        yield return new object[] { (ModifierKeys)32 };
        yield return new object[] { (ModifierKeys)int.MaxValue };
    }
 
    [Theory]
    [MemberData(nameof(ConvertTo_InvalidModifierKeys_TestData))]
    public void ConvertTo_InvalidModifierKeys_ThrowsInvalidEnumArgumentException(ModifierKeys value)
    {
        var converter = new ModifierKeysConverter();
        Assert.Throws<InvalidEnumArgumentException>("value", () => converter.ConvertTo(value, typeof(string)));
        Assert.Throws<InvalidEnumArgumentException>("value", () => converter.ConvertTo(new CustomTypeDescriptorContext(), null, value, typeof(string)));
        Assert.Throws<InvalidEnumArgumentException>("value", () => converter.ConvertTo(new CustomTypeDescriptorContext(), CultureInfo.InvariantCulture, value, typeof(string)));
    }
 
    [Theory]
    [InlineData(null)]
    // TODO: this should not throw InvalidCastException.
    //[InlineData("", "")]
    //[InlineData("value", "value")]
    public void ConvertTo_InvokeNotModifierKeysToStringNull_ThrowsNotSupportedException(object? value)
    {
        var converter = new ModifierKeysConverter();
        // TODO: should not throw NullReferenceException
        //Assert.Throws<NotSupportedException>(() => converter.ConvertTo(value, typeof(string)));
        //Assert.Throws<NotSupportedException>(() => converter.ConvertTo(new CustomTypeDescriptorContext(), null, value, typeof(string)));
        //Assert.Throws<NotSupportedException>(() => converter.ConvertTo(new CustomTypeDescriptorContext(), CultureInfo.InvariantCulture, value, typeof(string)));
        Assert.Throws<NullReferenceException>(() => converter.ConvertTo(value, typeof(string)));
        Assert.Throws<NullReferenceException>(() => converter.ConvertTo(new CustomTypeDescriptorContext(), null, value, typeof(string)));
        Assert.Throws<NullReferenceException>(() => converter.ConvertTo(new CustomTypeDescriptorContext(), CultureInfo.InvariantCulture, value, typeof(string)));
    }
 
    [Theory]
    [InlineData("")]
    [InlineData("value")]
    public void ConvertTo_InvokeNotModifierKeysToStringNotNull_ThrowsInvalidCastException(object value)
    {
        // TODO: this should not throw InvalidCastException.
        var converter = new ModifierKeysConverter();
        Assert.Throws<InvalidCastException>(() => converter.ConvertTo(value, typeof(string)));
        Assert.Throws<InvalidCastException>(() => converter.ConvertTo(new CustomTypeDescriptorContext(), null, value, typeof(string)));
        Assert.Throws<InvalidCastException>(() => converter.ConvertTo(new CustomTypeDescriptorContext(), CultureInfo.InvariantCulture, value, typeof(string)));
    }
 
    public static IEnumerable<object?[]> ConvertTo_CantConvert_TestData()
    {
        yield return new object?[] { null, typeof(object) };
        yield return new object?[] { string.Empty, typeof(object) };
        yield return new object?[] { "value", typeof(object) };
        yield return new object?[] { new object(), typeof(object) };
        yield return new object?[] { Key.None, typeof(object) };
        
        yield return new object?[] { null, typeof(Key) };
        yield return new object?[] { string.Empty, typeof(Key) };
        yield return new object?[] { "value", typeof(Key) };
        yield return new object?[] { new object(), typeof(Key) };
        yield return new object?[] { Key.None, typeof(Key) };
    }
 
    [Theory]
    [MemberData(nameof(ConvertTo_CantConvert_TestData))]
    public void ConvertTo_CantConvert_ThrowsNotSupportedException(object value, Type destinationType)
    {
        var converter = new ModifierKeysConverter();
        Assert.Throws<NotSupportedException>(() => converter.ConvertTo(value, destinationType));
        Assert.Throws<NotSupportedException>(() => converter.ConvertTo(null, null, value, destinationType));
        Assert.Throws<NotSupportedException>(() => converter.ConvertTo(new CustomTypeDescriptorContext(), CultureInfo.InvariantCulture, value, destinationType));
    }
 
    public static IEnumerable<object?[]> ConvertTo_NullDestinationType_TestData()
    {
        yield return new object?[] { null };
        yield return new object?[] { string.Empty };
        yield return new object?[] { "value" };
        yield return new object?[] { new object() };
        yield return new object?[] { ModifierKeys.None };
    }
 
    [Theory]
    [MemberData(nameof(ConvertTo_NullDestinationType_TestData))]
    public void ConvertTo_NullDestinationType_ThrowsArgumentNullException(object value)
    {
        var converter = new ModifierKeysConverter();
        Assert.Throws<ArgumentNullException>("destinationType", () => converter.ConvertTo(value, null!));
        Assert.Throws<ArgumentNullException>("destinationType", () => converter.ConvertTo(null, null, Key.None, null!));
        Assert.Throws<ArgumentNullException>("destinationType", () => converter.ConvertTo(new CustomTypeDescriptorContext(), CultureInfo.InvariantCulture, Key.None, null!));
    }
 
    [Theory]
    [InlineData(null, false)]
    [InlineData(typeof(object), false)]
    [InlineData(typeof(string), true)]
    [InlineData(typeof(InstanceDescriptor), false)]
    [InlineData(typeof(Key), false)]
    public void CanConvertFrom_Invoke_ReturnsExpected(Type? sourceType, bool expected)
    {
        var converter = new ModifierKeysConverter();
        Assert.Equal(expected, converter.CanConvertFrom(sourceType!));
        Assert.Equal(expected, converter.CanConvertFrom(null, sourceType));
        Assert.Equal(expected, converter.CanConvertFrom(new CustomTypeDescriptorContext(), sourceType));
    }
 
    public static IEnumerable<object[]> ConvertFrom_TestData()
    {
        yield return new object[] { "", ModifierKeys.None };
        yield return new object[] { "  ", ModifierKeys.None };
        yield return new object[] { "+", ModifierKeys.None };
        yield return new object[] { " + ", ModifierKeys.None };
        yield return new object[] { "Alt", ModifierKeys.Alt };
        yield return new object[] { "ALT", ModifierKeys.Alt };
        yield return new object[] { " Alt ", ModifierKeys.Alt };
        yield return new object[] { "Ctrl", ModifierKeys.Control };
        yield return new object[] { "Control", ModifierKeys.Control };
        yield return new object[] { "Shift", ModifierKeys.Shift };
        yield return new object[] { "Windows", ModifierKeys.Windows };
        yield return new object[] { "Win", ModifierKeys.Windows };
        
        yield return new object[] { "Ctrl+Alt", ModifierKeys.Control | ModifierKeys.Alt };
        yield return new object[] { "  Ctrl  +  Alt  ", ModifierKeys.Control | ModifierKeys.Alt };
        yield return new object[] { "Ctrl+Windows", ModifierKeys.Control | ModifierKeys.Windows };
        yield return new object[] { "Control+Win", ModifierKeys.Control | ModifierKeys.Windows };
        yield return new object[] { "Ctrl+Shift", ModifierKeys.Control | ModifierKeys.Shift };
        yield return new object[] { "Ctrl+Alt+Windows", ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Windows };
        yield return new object[] { "Ctrl+Alt+Windows+Shift", ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Windows | ModifierKeys.Shift };
        yield return new object[] { "Ctrl+Alt+Shift", ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Shift };
        yield return new object[] { "Ctrl+Windows+Shift", ModifierKeys.Control | ModifierKeys.Windows | ModifierKeys.Shift };
        yield return new object[] { "Alt+Windows", ModifierKeys.Alt | ModifierKeys.Windows };
        yield return new object[] { "Alt+Windows+Shift", ModifierKeys.Alt | ModifierKeys.Windows | ModifierKeys.Shift };
        yield return new object[] { "Alt+Shift", ModifierKeys.Alt | ModifierKeys.Shift };
        yield return new object[] { "Windows+Shift", ModifierKeys.Windows | ModifierKeys.Shift };
 
        yield return new object[] { "Ctrl+", ModifierKeys.Control };
        yield return new object[] { "Ctrl+Windows+", ModifierKeys.Control | ModifierKeys.Windows };
        yield return new object[] { "Ctrl+Ctrl+Ctrl", ModifierKeys.Control };
    }
 
    [Theory]
    [MemberData(nameof(ConvertFrom_TestData))]
    public void ConvertFrom_InvokeStringValue_ReturnsExpected(string value, ModifierKeys expected)
    {
        var converter = new ModifierKeysConverter();
        Assert.Equal(expected, converter.ConvertFrom(value));
        Assert.Equal(expected, converter.ConvertFrom(null, null, value));
        Assert.Equal(expected, converter.ConvertFrom(new CustomTypeDescriptorContext(), CultureInfo.InvariantCulture, value));
    }
 
    [Fact]
    public void ConvertFrom_NullValue_ThrowsNotSupportedException()
    {
        var converter = new ModifierKeysConverter();
        Assert.Throws<NotSupportedException>(() => converter.ConvertFrom(null!));
        Assert.Throws<NotSupportedException>(() => converter.ConvertFrom(null, null, null));
        Assert.Throws<NotSupportedException>(() => converter.ConvertFrom(new CustomTypeDescriptorContext(), CultureInfo.InvariantCulture, null));
    }
 
    public static IEnumerable<object[]> ConvertFrom_InvalidValue_TestData()
    {
        yield return new object[] { "_" };
        yield return new object[] { " _ " };
        yield return new object[] { "NOSUCHKEY" };
        yield return new object[] { " NOSUCHKEY " };
        yield return new object[] { "Control+NOSUCHKEY" };
    }
 
    [Theory]
    [MemberData(nameof(ConvertFrom_InvalidValue_TestData))]
    public void ConvertFrom_InvokeInvalidValue_ThrowsNotSupportedException(string value)
    {
        var converter = new ModifierKeysConverter();
        Assert.Throws<NotSupportedException>(() => converter.ConvertFrom(value));
        Assert.Throws<NotSupportedException>(() => converter.ConvertFrom(null, null, value));
        Assert.Throws<NotSupportedException>(() => converter.ConvertFrom(new CustomTypeDescriptorContext(), CultureInfo.InvariantCulture, value));
    }
 
    public static IEnumerable<object[]> ConvertFrom_CantConvert_TestData()
    {
        yield return new object[] { new object() };
        yield return new object[] { Key.A };
        yield return new object[] { ModifierKeys.None };
    }
    
    [Theory]
    [MemberData(nameof(ConvertFrom_CantConvert_TestData))]
    public void ConvertFrom_CantConvert_ThrowsNotSupportedException(object value)
    {
        var converter = new ModifierKeysConverter();
        Assert.Throws<NotSupportedException>(() => converter.ConvertFrom(value));
        Assert.Throws<NotSupportedException>(() => converter.ConvertFrom(null, null, value));
        Assert.Throws<NotSupportedException>(() => converter.ConvertFrom(new CustomTypeDescriptorContext(), CultureInfo.InvariantCulture, value));
    }
 
    private class CustomTypeDescriptorContext : ITypeDescriptorContext
    {
        public IContainer Container => throw new NotImplementedException();
 
        private object? _instance;
 
        public object Instance
        {
            get => _instance!;
            set => _instance = value;
        }
 
        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();
    }
}