namespace 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 UnShiftedChars => new List { 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); } }