Files
Terminal.Gui/Tests/UnitTestsParallelizable/Drivers/ConsoleKeyMappingTests.cs

423 lines
18 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

namespace UnitTests_Parallelizable.DriverTests;
public class ConsoleKeyMappingTests
{
[Theory]
[InlineData ('a', ConsoleKey.A, false, false, false, (KeyCode)'a')]
[InlineData ('A', ConsoleKey.A, true, false, false, KeyCode.A | KeyCode.ShiftMask)]
[InlineData ('á', ConsoleKey.A, false, false, false, (KeyCode)'á')]
[InlineData ('Á', ConsoleKey.A, true, false, false, (KeyCode)'Á' | KeyCode.ShiftMask)]
[InlineData ('à', ConsoleKey.A, false, false, false, (KeyCode)'à')]
[InlineData ('À', ConsoleKey.A, true, false, false, (KeyCode)'À' | KeyCode.ShiftMask)]
[InlineData ('5', ConsoleKey.D5, false, false, false, KeyCode.D5)]
[InlineData ('%', ConsoleKey.D5, true, false, false, (KeyCode)'%' | KeyCode.ShiftMask)]
[InlineData ('€', ConsoleKey.D5, false, true, true, (KeyCode)'€' | KeyCode.AltMask | KeyCode.CtrlMask)]
[InlineData ('?', ConsoleKey.Oem4, true, false, false, (KeyCode)'?' | KeyCode.ShiftMask)]
[InlineData ('\'', ConsoleKey.Oem4, false, false, false, (KeyCode)'\'')]
[InlineData ('q', ConsoleKey.Q, false, false, false, (KeyCode)'q')]
[InlineData ('\0', ConsoleKey.F2, false, false, false, KeyCode.F2)]
[InlineData ('英', ConsoleKey.None, false, false, false, (KeyCode)'英')]
[InlineData ('\r', ConsoleKey.Enter, false, false, false, KeyCode.Enter)]
public void MapConsoleKeyInfoToKeyCode_Also_Return_Modifiers (
char keyChar,
ConsoleKey consoleKey,
bool shift,
bool alt,
bool control,
KeyCode expectedKeyCode
)
{
var consoleKeyInfo = new ConsoleKeyInfo (keyChar, consoleKey, shift, alt, control);
KeyCode keyCode = ConsoleKeyMapping.MapConsoleKeyInfoToKeyCode (consoleKeyInfo);
Assert.Equal (keyCode, expectedKeyCode);
}
[Theory]
[InlineData ('a', false, false, false, (KeyCode)'a')]
[InlineData ('A', true, false, false, KeyCode.A | KeyCode.ShiftMask)]
[InlineData ('á', false, false, false, (KeyCode)'á')]
[InlineData ('Á', true, false, false, (KeyCode)'Á' | KeyCode.ShiftMask)]
[InlineData ('à', false, false, false, (KeyCode)'à')]
[InlineData ('À', true, false, false, (KeyCode)'À' | KeyCode.ShiftMask)]
[InlineData ('5', false, false, false, KeyCode.D5)]
[InlineData ('%', true, false, false, (KeyCode)'%' | KeyCode.ShiftMask)]
[InlineData ('€', false, true, true, (KeyCode)'€' | KeyCode.AltMask | KeyCode.CtrlMask)]
[InlineData ('?', true, false, false, (KeyCode)'?' | KeyCode.ShiftMask)]
[InlineData ('\'', false, false, false, (KeyCode)'\'')]
[InlineData ('q', false, false, false, (KeyCode)'q')]
[InlineData ((uint)KeyCode.F2, false, false, false, KeyCode.F2)]
[InlineData ('英', false, false, false, (KeyCode)'英')]
[InlineData ('\r', false, false, false, KeyCode.Enter)]
[InlineData ('\n', false, false, false, (KeyCode)'\n')]
public void MapToKeyCodeModifiers_Tests (
uint keyChar,
bool shift,
bool alt,
bool control,
KeyCode expectedKeyCode
)
{
ConsoleModifiers modifiers = ConsoleKeyMapping.GetModifiers (shift, alt, control);
var keyCode = (KeyCode)keyChar;
keyCode = ConsoleKeyMapping.MapToKeyCodeModifiers (modifiers, keyCode);
Assert.Equal (keyCode, expectedKeyCode);
}
public static IEnumerable<object []> UnShiftedChars =>
new List<object []>
{
new object [] { 'a', 'A', KeyCode.A | KeyCode.ShiftMask },
new object [] { 'z', 'Z', KeyCode.Z | KeyCode.ShiftMask },
new object [] { 'á', 'Á', (KeyCode)'Á' | KeyCode.ShiftMask },
new object [] { 'à', 'À', (KeyCode)'À' | KeyCode.ShiftMask },
new object [] { 'ý', 'Ý', (KeyCode)'Ý' | KeyCode.ShiftMask },
new object [] { '1', '!', (KeyCode)'!' | KeyCode.ShiftMask },
new object [] { '2', '"', (KeyCode)'"' | KeyCode.ShiftMask },
new object [] { '3', '#', (KeyCode)'#' | KeyCode.ShiftMask },
new object [] { '4', '$', (KeyCode)'$' | KeyCode.ShiftMask },
new object [] { '5', '%', (KeyCode)'%' | KeyCode.ShiftMask },
new object [] { '6', '&', (KeyCode)'&' | KeyCode.ShiftMask },
new object [] { '7', '/', (KeyCode)'/' | KeyCode.ShiftMask },
new object [] { '8', '(', (KeyCode)'(' | KeyCode.ShiftMask },
new object [] { '9', ')', (KeyCode)')' | KeyCode.ShiftMask },
new object [] { '0', '=', (KeyCode)'=' | KeyCode.ShiftMask },
new object [] { '\\', '|', (KeyCode)'|' | KeyCode.ShiftMask },
new object [] { '\'', '?', (KeyCode)'?' | KeyCode.ShiftMask },
new object [] { '«', '»', (KeyCode)'»' | KeyCode.ShiftMask },
new object [] { '+', '*', (KeyCode)'*' | KeyCode.ShiftMask },
new object [] { '´', '`', (KeyCode)'`' | KeyCode.ShiftMask },
new object [] { 'º', 'ª', (KeyCode)'ª' | KeyCode.ShiftMask },
new object [] { '~', '^', (KeyCode)'^' | KeyCode.ShiftMask },
new object [] { '<', '>', (KeyCode)'>' | KeyCode.ShiftMask },
new object [] { ',', ';', (KeyCode)';' | KeyCode.ShiftMask },
new object [] { '.', ':', (KeyCode)':' | KeyCode.ShiftMask },
new object [] { '-', '_', (KeyCode)'_' | KeyCode.ShiftMask }
};
[Theory]
[InlineData (KeyCode.A, false, false, false)] // Unshifted A (lowercase)
[InlineData (KeyCode.B, false, false, false)]
[InlineData (KeyCode.Z, false, false, false)]
[InlineData (KeyCode.A | KeyCode.ShiftMask, true, false, false)] // Shifted A (uppercase)
[InlineData (KeyCode.Z | KeyCode.ShiftMask, true, false, false)]
[InlineData (KeyCode.A | KeyCode.CtrlMask, false, false, true)] // Ctrl+A
[InlineData (KeyCode.A | KeyCode.AltMask, false, true, false)] // Alt+A
[InlineData (KeyCode.A | KeyCode.ShiftMask | KeyCode.CtrlMask, true, false, true)] // Ctrl+Shift+A
[InlineData (KeyCode.A | KeyCode.ShiftMask | KeyCode.AltMask, true, true, false)] // Alt+Shift+A
[InlineData (KeyCode.A | KeyCode.CtrlMask | KeyCode.AltMask, false, true, true)] // Ctrl+Alt+A
[InlineData (KeyCode.A | KeyCode.ShiftMask | KeyCode.CtrlMask | KeyCode.AltMask, true, true, true)] // All modifiers
public void MapToConsoleModifiers_LetterKeys_ReturnsCorrectModifiers (
KeyCode key,
bool expectedShift,
bool expectedAlt,
bool expectedControl
)
{
// Act
ConsoleModifiers result = ConsoleKeyMapping.MapToConsoleModifiers (key);
// Assert
Assert.Equal (expectedShift, result.HasFlag (ConsoleModifiers.Shift));
Assert.Equal (expectedAlt, result.HasFlag (ConsoleModifiers.Alt));
Assert.Equal (expectedControl, result.HasFlag (ConsoleModifiers.Control));
}
[Theory]
[InlineData (KeyCode.A)] // 65 = 'A' in ASCII, but represents unshifted key
[InlineData (KeyCode.B)]
[InlineData (KeyCode.M)]
[InlineData (KeyCode.Z)]
public void MapToConsoleModifiers_UnshiftedLetterKeys_DoesNotSetShiftFlag (KeyCode key)
{
// This test verifies the BUGFIX: KeyCode.A-Z (65-90) represent UNSHIFTED keys,
// even though their numeric values match uppercase ASCII characters.
// The old code incorrectly checked char.IsUpper((char)key) which would fail this test.
// Act
ConsoleModifiers result = ConsoleKeyMapping.MapToConsoleModifiers (key);
// Assert - Shift should NOT be set for unshifted letter keys
Assert.False (result.HasFlag (ConsoleModifiers.Shift),
$"Shift should not be set for unshifted {key}. The KeyCode value {(int)key} represents a lowercase, unshifted key.");
}
[Theory]
[InlineData (KeyCode.D1, false)] // Unshifted number keys
[InlineData (KeyCode.D5, false)]
[InlineData (KeyCode.Space, false)]
[InlineData (KeyCode.Enter, false)]
[InlineData (KeyCode.Tab, false)]
[InlineData (KeyCode.D1 | KeyCode.ShiftMask, true)] // Shifted number keys
public void MapToConsoleModifiers_NonLetterKeys_ReturnsCorrectShiftState (KeyCode key, bool expectedShift)
{
// Act
ConsoleModifiers result = ConsoleKeyMapping.MapToConsoleModifiers (key);
// Assert
Assert.Equal (expectedShift, result.HasFlag (ConsoleModifiers.Shift));
}
[Theory]
[InlineData (KeyCode.A, 'a', ConsoleKey.A, false)] // Unshifted A = lowercase 'a'
[InlineData (KeyCode.B, 'b', ConsoleKey.B, false)]
[InlineData (KeyCode.M, 'm', ConsoleKey.M, false)]
[InlineData (KeyCode.Z, 'z', ConsoleKey.Z, false)]
public void GetConsoleKeyInfoFromKeyCode_UnshiftedLetterKeys_ReturnsLowercaseChar (
KeyCode key,
char expectedChar,
ConsoleKey expectedKey,
bool expectedShift
)
{
// This test verifies the BUGFIX: Key.A through Key.Z should produce lowercase characters
// when no ShiftMask is set. The old code would incorrectly return uppercase 'A'.
// Act
ConsoleKeyInfo result = ConsoleKeyMapping.GetConsoleKeyInfoFromKeyCode (key);
// Assert
Assert.Equal (expectedChar, result.KeyChar);
Assert.Equal (expectedKey, result.Key);
Assert.Equal (expectedShift, (result.Modifiers & ConsoleModifiers.Shift) != 0);
}
[Theory]
[InlineData (KeyCode.A | KeyCode.ShiftMask, 'A', ConsoleKey.A, true)] // Shifted A = uppercase 'A'
[InlineData (KeyCode.B | KeyCode.ShiftMask, 'B', ConsoleKey.B, true)]
[InlineData (KeyCode.M | KeyCode.ShiftMask, 'M', ConsoleKey.M, true)]
[InlineData (KeyCode.Z | KeyCode.ShiftMask, 'Z', ConsoleKey.Z, true)]
public void GetConsoleKeyInfoFromKeyCode_ShiftedLetterKeys_ReturnsUppercaseChar (
KeyCode key,
char expectedChar,
ConsoleKey expectedKey,
bool expectedShift
)
{
// Act
ConsoleKeyInfo result = ConsoleKeyMapping.GetConsoleKeyInfoFromKeyCode (key);
// Assert
Assert.Equal (expectedChar, result.KeyChar);
Assert.Equal (expectedKey, result.Key);
Assert.Equal (expectedShift, (result.Modifiers & ConsoleModifiers.Shift) != 0);
}
[Theory]
[InlineData (KeyCode.A | KeyCode.CtrlMask, ConsoleKey.A, false, false, true)]
[InlineData (KeyCode.A | KeyCode.AltMask, ConsoleKey.A, false, true, false)]
[InlineData (KeyCode.A | KeyCode.ShiftMask | KeyCode.CtrlMask, ConsoleKey.A, true, false, true)]
[InlineData (KeyCode.Z | KeyCode.CtrlMask, ConsoleKey.Z, false, false, true)]
public void GetConsoleKeyInfoFromKeyCode_LetterKeysWithModifiers_ReturnsCorrectModifiers (
KeyCode key,
ConsoleKey expectedConsoleKey,
bool expectedShift,
bool expectedAlt,
bool expectedControl
)
{
// Act
ConsoleKeyInfo result = ConsoleKeyMapping.GetConsoleKeyInfoFromKeyCode (key);
// Assert
Assert.Equal (expectedConsoleKey, result.Key);
Assert.Equal (expectedShift, (result.Modifiers & ConsoleModifiers.Shift) != 0);
Assert.Equal (expectedAlt, (result.Modifiers & ConsoleModifiers.Alt) != 0);
Assert.Equal (expectedControl, (result.Modifiers & ConsoleModifiers.Control) != 0);
}
[Theory]
[InlineData (KeyCode.Enter, '\r', ConsoleKey.Enter)]
[InlineData (KeyCode.Tab, '\t', ConsoleKey.Tab)]
[InlineData (KeyCode.Esc, '\u001B', ConsoleKey.Escape)]
[InlineData (KeyCode.Backspace, '\b', ConsoleKey.Backspace)]
[InlineData (KeyCode.Space, ' ', ConsoleKey.Spacebar)]
public void GetConsoleKeyInfoFromKeyCode_SpecialKeys_ReturnsCorrectKeyChar (
KeyCode key,
char expectedChar,
ConsoleKey expectedKey
)
{
// Act
ConsoleKeyInfo result = ConsoleKeyMapping.GetConsoleKeyInfoFromKeyCode (key);
// Assert
Assert.Equal (expectedChar, result.KeyChar);
Assert.Equal (expectedKey, result.Key);
}
[Theory]
[InlineData (KeyCode.F1, ConsoleKey.F1)]
[InlineData (KeyCode.F5, ConsoleKey.F5)]
[InlineData (KeyCode.F12, ConsoleKey.F12)]
[InlineData (KeyCode.CursorUp, ConsoleKey.UpArrow)]
[InlineData (KeyCode.CursorDown, ConsoleKey.DownArrow)]
[InlineData (KeyCode.CursorLeft, ConsoleKey.LeftArrow)]
[InlineData (KeyCode.CursorRight, ConsoleKey.RightArrow)]
[InlineData (KeyCode.Home, ConsoleKey.Home)]
[InlineData (KeyCode.End, ConsoleKey.End)]
[InlineData (KeyCode.PageUp, ConsoleKey.PageUp)]
[InlineData (KeyCode.PageDown, ConsoleKey.PageDown)]
[InlineData (KeyCode.Delete, ConsoleKey.Delete)]
[InlineData (KeyCode.Insert, ConsoleKey.Insert)]
public void GetConsoleKeyInfoFromKeyCode_NavigationAndFunctionKeys_ReturnsCorrectConsoleKey (
KeyCode key,
ConsoleKey expectedKey
)
{
// Act
ConsoleKeyInfo result = ConsoleKeyMapping.GetConsoleKeyInfoFromKeyCode (key);
// Assert
Assert.Equal (expectedKey, result.Key);
}
[Theory]
[InlineData (KeyCode.D0, '0', ConsoleKey.D0)]
[InlineData (KeyCode.D1, '1', ConsoleKey.D1)]
[InlineData (KeyCode.D5, '5', ConsoleKey.D5)]
[InlineData (KeyCode.D9, '9', ConsoleKey.D9)]
public void GetConsoleKeyInfoFromKeyCode_NumberKeys_ReturnsCorrectKeyChar (
KeyCode key,
char expectedChar,
ConsoleKey expectedKey
)
{
// Act
ConsoleKeyInfo result = ConsoleKeyMapping.GetConsoleKeyInfoFromKeyCode (key);
// Assert
Assert.Equal (expectedChar, result.KeyChar);
Assert.Equal (expectedKey, result.Key);
}
[Fact]
public void MapToConsoleModifiers_AllLetterKeys_UnshiftedDoesNotSetShift ()
{
// This comprehensive test ensures ALL letter keys A-Z without ShiftMask
// do not have Shift set in the returned modifiers.
// This will fail if the old "char.IsUpper" check is present.
for (KeyCode key = KeyCode.A; key <= KeyCode.Z; key++)
{
// Act
ConsoleModifiers result = ConsoleKeyMapping.MapToConsoleModifiers (key);
// Assert
Assert.False (
result.HasFlag (ConsoleModifiers.Shift),
$"Shift should not be set for unshifted {key} (value {(int)key}). " +
$"KeyCode.{key} represents a lowercase, unshifted key even though its numeric value is {(int)key}."
);
}
}
[Fact]
public void MapToConsoleModifiers_AllLetterKeysShifted_SetsShift ()
{
// Verify that WITH ShiftMask, all letter keys DO set Shift
for (KeyCode key = KeyCode.A; key <= KeyCode.Z; key++)
{
KeyCode shiftedKey = key | KeyCode.ShiftMask;
// Act
ConsoleModifiers result = ConsoleKeyMapping.MapToConsoleModifiers (shiftedKey);
// Assert
Assert.True (
result.HasFlag (ConsoleModifiers.Shift),
$"Shift should be set for {shiftedKey}"
);
}
}
[Fact]
public void GetConsoleKeyInfoFromKeyCode_AllUnshiftedLetterKeys_ReturnLowercaseChars ()
{
// This comprehensive test verifies ALL letter keys A-Z produce lowercase characters
// when no ShiftMask is set. This is the KEY test that will fail with the old code.
for (KeyCode key = KeyCode.A; key <= KeyCode.Z; key++)
{
// Act
ConsoleKeyInfo result = ConsoleKeyMapping.GetConsoleKeyInfoFromKeyCode (key);
// Calculate expected lowercase character
char expectedChar = (char)('a' + (key - KeyCode.A));
// Assert
Assert.True (
char.IsLower (result.KeyChar),
$"KeyChar for unshifted {key} should be lowercase, but got '{result.KeyChar}' (0x{(int)result.KeyChar:X2}). " +
$"Expected lowercase '{expectedChar}' (0x{(int)expectedChar:X2})."
);
Assert.Equal (
expectedChar,
result.KeyChar
);
Assert.False (
(result.Modifiers & ConsoleModifiers.Shift) != 0,
$"Shift modifier should not be set for unshifted {key}"
);
}
}
[Fact]
public void GetConsoleKeyInfoFromKeyCode_AllShiftedLetterKeys_ReturnUppercaseChars ()
{
// Verify that WITH ShiftMask, all letter keys produce uppercase characters
for (KeyCode key = KeyCode.A; key <= KeyCode.Z; key++)
{
KeyCode shiftedKey = key | KeyCode.ShiftMask;
// Act
ConsoleKeyInfo result = ConsoleKeyMapping.GetConsoleKeyInfoFromKeyCode (shiftedKey);
// Calculate expected uppercase character
char expectedChar = (char)('A' + (key - KeyCode.A));
// Assert
Assert.True (
char.IsUpper (result.KeyChar),
$"KeyChar for shifted {shiftedKey} should be uppercase, but got '{result.KeyChar}'"
);
Assert.Equal (expectedChar, result.KeyChar);
Assert.True (
(result.Modifiers & ConsoleModifiers.Shift) != 0,
$"Shift modifier should be set for {shiftedKey}"
);
}
}
[Theory]
[InlineData (KeyCode.A, KeyCode.A | KeyCode.ShiftMask, 'a', 'A')] // Without vs With Shift
[InlineData (KeyCode.M, KeyCode.M | KeyCode.ShiftMask, 'm', 'M')]
[InlineData (KeyCode.Z, KeyCode.Z | KeyCode.ShiftMask, 'z', 'Z')]
public void GetConsoleKeyInfoFromKeyCode_ShiftMaskChangesCase (
KeyCode unshifted,
KeyCode shifted,
char expectedUnshiftedChar,
char expectedShiftedChar
)
{
// Act
ConsoleKeyInfo unshiftedResult = ConsoleKeyMapping.GetConsoleKeyInfoFromKeyCode (unshifted);
ConsoleKeyInfo shiftedResult = ConsoleKeyMapping.GetConsoleKeyInfoFromKeyCode (shifted);
// Assert - Unshifted should be lowercase
Assert.Equal (expectedUnshiftedChar, unshiftedResult.KeyChar);
Assert.False ((unshiftedResult.Modifiers & ConsoleModifiers.Shift) != 0);
// Assert - Shifted should be uppercase
Assert.Equal (expectedShiftedChar, shiftedResult.KeyChar);
Assert.True ((shiftedResult.Modifiers & ConsoleModifiers.Shift) != 0);
}
}