ConsoleKeyMappingTests.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. namespace DriverTests;
  2. public class ConsoleKeyMappingTests
  3. {
  4. [Theory]
  5. [InlineData ('a', ConsoleKey.A, false, false, false, (KeyCode)'a')]
  6. [InlineData ('A', ConsoleKey.A, true, false, false, KeyCode.A | KeyCode.ShiftMask)]
  7. [InlineData ('á', ConsoleKey.A, false, false, false, (KeyCode)'á')]
  8. [InlineData ('Á', ConsoleKey.A, true, false, false, (KeyCode)'Á' | KeyCode.ShiftMask)]
  9. [InlineData ('à', ConsoleKey.A, false, false, false, (KeyCode)'à')]
  10. [InlineData ('À', ConsoleKey.A, true, false, false, (KeyCode)'À' | KeyCode.ShiftMask)]
  11. [InlineData ('5', ConsoleKey.D5, false, false, false, KeyCode.D5)]
  12. [InlineData ('%', ConsoleKey.D5, true, false, false, (KeyCode)'%' | KeyCode.ShiftMask)]
  13. [InlineData ('€', ConsoleKey.D5, false, true, true, (KeyCode)'€' | KeyCode.AltMask | KeyCode.CtrlMask)]
  14. [InlineData ('?', ConsoleKey.Oem4, true, false, false, (KeyCode)'?' | KeyCode.ShiftMask)]
  15. [InlineData ('\'', ConsoleKey.Oem4, false, false, false, (KeyCode)'\'')]
  16. [InlineData ('q', ConsoleKey.Q, false, false, false, (KeyCode)'q')]
  17. [InlineData ('\0', ConsoleKey.F2, false, false, false, KeyCode.F2)]
  18. [InlineData ('英', ConsoleKey.None, false, false, false, (KeyCode)'英')]
  19. [InlineData ('\r', ConsoleKey.Enter, false, false, false, KeyCode.Enter)]
  20. public void MapConsoleKeyInfoToKeyCode_Also_Return_Modifiers (
  21. char keyChar,
  22. ConsoleKey consoleKey,
  23. bool shift,
  24. bool alt,
  25. bool control,
  26. KeyCode expectedKeyCode
  27. )
  28. {
  29. var consoleKeyInfo = new ConsoleKeyInfo (keyChar, consoleKey, shift, alt, control);
  30. KeyCode keyCode = ConsoleKeyMapping.MapConsoleKeyInfoToKeyCode (consoleKeyInfo);
  31. Assert.Equal (keyCode, expectedKeyCode);
  32. }
  33. [Theory]
  34. [InlineData ('a', false, false, false, (KeyCode)'a')]
  35. [InlineData ('A', true, false, false, KeyCode.A | KeyCode.ShiftMask)]
  36. [InlineData ('á', false, false, false, (KeyCode)'á')]
  37. [InlineData ('Á', true, false, false, (KeyCode)'Á' | KeyCode.ShiftMask)]
  38. [InlineData ('à', false, false, false, (KeyCode)'à')]
  39. [InlineData ('À', true, false, false, (KeyCode)'À' | KeyCode.ShiftMask)]
  40. [InlineData ('5', false, false, false, KeyCode.D5)]
  41. [InlineData ('%', true, false, false, (KeyCode)'%' | KeyCode.ShiftMask)]
  42. [InlineData ('€', false, true, true, (KeyCode)'€' | KeyCode.AltMask | KeyCode.CtrlMask)]
  43. [InlineData ('?', true, false, false, (KeyCode)'?' | KeyCode.ShiftMask)]
  44. [InlineData ('\'', false, false, false, (KeyCode)'\'')]
  45. [InlineData ('q', false, false, false, (KeyCode)'q')]
  46. [InlineData ((uint)KeyCode.F2, false, false, false, KeyCode.F2)]
  47. [InlineData ('英', false, false, false, (KeyCode)'英')]
  48. [InlineData ('\r', false, false, false, KeyCode.Enter)]
  49. [InlineData ('\n', false, false, false, (KeyCode)'\n')]
  50. public void MapToKeyCodeModifiers_Tests (
  51. uint keyChar,
  52. bool shift,
  53. bool alt,
  54. bool control,
  55. KeyCode expectedKeyCode
  56. )
  57. {
  58. ConsoleModifiers modifiers = ConsoleKeyMapping.GetModifiers (shift, alt, control);
  59. var keyCode = (KeyCode)keyChar;
  60. keyCode = ConsoleKeyMapping.MapToKeyCodeModifiers (modifiers, keyCode);
  61. Assert.Equal (keyCode, expectedKeyCode);
  62. }
  63. public static IEnumerable<object []> UnShiftedChars =>
  64. new List<object []>
  65. {
  66. new object [] { 'a', 'A', KeyCode.A | KeyCode.ShiftMask },
  67. new object [] { 'z', 'Z', KeyCode.Z | KeyCode.ShiftMask },
  68. new object [] { 'á', 'Á', (KeyCode)'Á' | KeyCode.ShiftMask },
  69. new object [] { 'à', 'À', (KeyCode)'À' | KeyCode.ShiftMask },
  70. new object [] { 'ý', 'Ý', (KeyCode)'Ý' | KeyCode.ShiftMask },
  71. new object [] { '1', '!', (KeyCode)'!' | KeyCode.ShiftMask },
  72. new object [] { '2', '"', (KeyCode)'"' | KeyCode.ShiftMask },
  73. new object [] { '3', '#', (KeyCode)'#' | KeyCode.ShiftMask },
  74. new object [] { '4', '$', (KeyCode)'$' | KeyCode.ShiftMask },
  75. new object [] { '5', '%', (KeyCode)'%' | KeyCode.ShiftMask },
  76. new object [] { '6', '&', (KeyCode)'&' | KeyCode.ShiftMask },
  77. new object [] { '7', '/', (KeyCode)'/' | KeyCode.ShiftMask },
  78. new object [] { '8', '(', (KeyCode)'(' | KeyCode.ShiftMask },
  79. new object [] { '9', ')', (KeyCode)')' | KeyCode.ShiftMask },
  80. new object [] { '0', '=', (KeyCode)'=' | KeyCode.ShiftMask },
  81. new object [] { '\\', '|', (KeyCode)'|' | KeyCode.ShiftMask },
  82. new object [] { '\'', '?', (KeyCode)'?' | KeyCode.ShiftMask },
  83. new object [] { '«', '»', (KeyCode)'»' | KeyCode.ShiftMask },
  84. new object [] { '+', '*', (KeyCode)'*' | KeyCode.ShiftMask },
  85. new object [] { '´', '`', (KeyCode)'`' | KeyCode.ShiftMask },
  86. new object [] { 'º', 'ª', (KeyCode)'ª' | KeyCode.ShiftMask },
  87. new object [] { '~', '^', (KeyCode)'^' | KeyCode.ShiftMask },
  88. new object [] { '<', '>', (KeyCode)'>' | KeyCode.ShiftMask },
  89. new object [] { ',', ';', (KeyCode)';' | KeyCode.ShiftMask },
  90. new object [] { '.', ':', (KeyCode)':' | KeyCode.ShiftMask },
  91. new object [] { '-', '_', (KeyCode)'_' | KeyCode.ShiftMask }
  92. };
  93. [Theory]
  94. [InlineData (KeyCode.A, false, false, false)] // Unshifted A (lowercase)
  95. [InlineData (KeyCode.B, false, false, false)]
  96. [InlineData (KeyCode.Z, false, false, false)]
  97. [InlineData (KeyCode.A | KeyCode.ShiftMask, true, false, false)] // Shifted A (uppercase)
  98. [InlineData (KeyCode.Z | KeyCode.ShiftMask, true, false, false)]
  99. [InlineData (KeyCode.A | KeyCode.CtrlMask, false, false, true)] // Ctrl+A
  100. [InlineData (KeyCode.A | KeyCode.AltMask, false, true, false)] // Alt+A
  101. [InlineData (KeyCode.A | KeyCode.ShiftMask | KeyCode.CtrlMask, true, false, true)] // Ctrl+Shift+A
  102. [InlineData (KeyCode.A | KeyCode.ShiftMask | KeyCode.AltMask, true, true, false)] // Alt+Shift+A
  103. [InlineData (KeyCode.A | KeyCode.CtrlMask | KeyCode.AltMask, false, true, true)] // Ctrl+Alt+A
  104. [InlineData (KeyCode.A | KeyCode.ShiftMask | KeyCode.CtrlMask | KeyCode.AltMask, true, true, true)] // All modifiers
  105. public void MapToConsoleModifiers_LetterKeys_ReturnsCorrectModifiers (
  106. KeyCode key,
  107. bool expectedShift,
  108. bool expectedAlt,
  109. bool expectedControl
  110. )
  111. {
  112. // Act
  113. ConsoleModifiers result = ConsoleKeyMapping.MapToConsoleModifiers (key);
  114. // Assert
  115. Assert.Equal (expectedShift, result.HasFlag (ConsoleModifiers.Shift));
  116. Assert.Equal (expectedAlt, result.HasFlag (ConsoleModifiers.Alt));
  117. Assert.Equal (expectedControl, result.HasFlag (ConsoleModifiers.Control));
  118. }
  119. [Theory]
  120. [InlineData (KeyCode.A)] // 65 = 'A' in ASCII, but represents unshifted key
  121. [InlineData (KeyCode.B)]
  122. [InlineData (KeyCode.M)]
  123. [InlineData (KeyCode.Z)]
  124. public void MapToConsoleModifiers_UnshiftedLetterKeys_DoesNotSetShiftFlag (KeyCode key)
  125. {
  126. // This test verifies the BUGFIX: KeyCode.A-Z (65-90) represent UNSHIFTED keys,
  127. // even though their numeric values match uppercase ASCII characters.
  128. // The old code incorrectly checked char.IsUpper((char)key) which would fail this test.
  129. // Act
  130. ConsoleModifiers result = ConsoleKeyMapping.MapToConsoleModifiers (key);
  131. // Assert - Shift should NOT be set for unshifted letter keys
  132. Assert.False (result.HasFlag (ConsoleModifiers.Shift),
  133. $"Shift should not be set for unshifted {key}. The KeyCode value {(int)key} represents a lowercase, unshifted key.");
  134. }
  135. [Theory]
  136. [InlineData (KeyCode.D1, false)] // Unshifted number keys
  137. [InlineData (KeyCode.D5, false)]
  138. [InlineData (KeyCode.Space, false)]
  139. [InlineData (KeyCode.Enter, false)]
  140. [InlineData (KeyCode.Tab, false)]
  141. [InlineData (KeyCode.D1 | KeyCode.ShiftMask, true)] // Shifted number keys
  142. public void MapToConsoleModifiers_NonLetterKeys_ReturnsCorrectShiftState (KeyCode key, bool expectedShift)
  143. {
  144. // Act
  145. ConsoleModifiers result = ConsoleKeyMapping.MapToConsoleModifiers (key);
  146. // Assert
  147. Assert.Equal (expectedShift, result.HasFlag (ConsoleModifiers.Shift));
  148. }
  149. [Theory]
  150. [InlineData (KeyCode.A, 'a', ConsoleKey.A, false)] // Unshifted A = lowercase 'a'
  151. [InlineData (KeyCode.B, 'b', ConsoleKey.B, false)]
  152. [InlineData (KeyCode.M, 'm', ConsoleKey.M, false)]
  153. [InlineData (KeyCode.Z, 'z', ConsoleKey.Z, false)]
  154. public void GetConsoleKeyInfoFromKeyCode_UnshiftedLetterKeys_ReturnsLowercaseChar (
  155. KeyCode key,
  156. char expectedChar,
  157. ConsoleKey expectedKey,
  158. bool expectedShift
  159. )
  160. {
  161. // This test verifies the BUGFIX: Key.A through Key.Z should produce lowercase characters
  162. // when no ShiftMask is set. The old code would incorrectly return uppercase 'A'.
  163. // Act
  164. ConsoleKeyInfo result = ConsoleKeyMapping.GetConsoleKeyInfoFromKeyCode (key);
  165. // Assert
  166. Assert.Equal (expectedChar, result.KeyChar);
  167. Assert.Equal (expectedKey, result.Key);
  168. Assert.Equal (expectedShift, (result.Modifiers & ConsoleModifiers.Shift) != 0);
  169. }
  170. [Theory]
  171. [InlineData (KeyCode.A | KeyCode.ShiftMask, 'A', ConsoleKey.A, true)] // Shifted A = uppercase 'A'
  172. [InlineData (KeyCode.B | KeyCode.ShiftMask, 'B', ConsoleKey.B, true)]
  173. [InlineData (KeyCode.M | KeyCode.ShiftMask, 'M', ConsoleKey.M, true)]
  174. [InlineData (KeyCode.Z | KeyCode.ShiftMask, 'Z', ConsoleKey.Z, true)]
  175. public void GetConsoleKeyInfoFromKeyCode_ShiftedLetterKeys_ReturnsUppercaseChar (
  176. KeyCode key,
  177. char expectedChar,
  178. ConsoleKey expectedKey,
  179. bool expectedShift
  180. )
  181. {
  182. // Act
  183. ConsoleKeyInfo result = ConsoleKeyMapping.GetConsoleKeyInfoFromKeyCode (key);
  184. // Assert
  185. Assert.Equal (expectedChar, result.KeyChar);
  186. Assert.Equal (expectedKey, result.Key);
  187. Assert.Equal (expectedShift, (result.Modifiers & ConsoleModifiers.Shift) != 0);
  188. }
  189. [Theory]
  190. [InlineData (KeyCode.A | KeyCode.CtrlMask, ConsoleKey.A, false, false, true)]
  191. [InlineData (KeyCode.A | KeyCode.AltMask, ConsoleKey.A, false, true, false)]
  192. [InlineData (KeyCode.A | KeyCode.ShiftMask | KeyCode.CtrlMask, ConsoleKey.A, true, false, true)]
  193. [InlineData (KeyCode.Z | KeyCode.CtrlMask, ConsoleKey.Z, false, false, true)]
  194. public void GetConsoleKeyInfoFromKeyCode_LetterKeysWithModifiers_ReturnsCorrectModifiers (
  195. KeyCode key,
  196. ConsoleKey expectedConsoleKey,
  197. bool expectedShift,
  198. bool expectedAlt,
  199. bool expectedControl
  200. )
  201. {
  202. // Act
  203. ConsoleKeyInfo result = ConsoleKeyMapping.GetConsoleKeyInfoFromKeyCode (key);
  204. // Assert
  205. Assert.Equal (expectedConsoleKey, result.Key);
  206. Assert.Equal (expectedShift, (result.Modifiers & ConsoleModifiers.Shift) != 0);
  207. Assert.Equal (expectedAlt, (result.Modifiers & ConsoleModifiers.Alt) != 0);
  208. Assert.Equal (expectedControl, (result.Modifiers & ConsoleModifiers.Control) != 0);
  209. }
  210. [Theory]
  211. [InlineData (KeyCode.Enter, '\r', ConsoleKey.Enter)]
  212. [InlineData (KeyCode.Tab, '\t', ConsoleKey.Tab)]
  213. [InlineData (KeyCode.Esc, '\u001B', ConsoleKey.Escape)]
  214. [InlineData (KeyCode.Backspace, '\b', ConsoleKey.Backspace)]
  215. [InlineData (KeyCode.Space, ' ', ConsoleKey.Spacebar)]
  216. public void GetConsoleKeyInfoFromKeyCode_SpecialKeys_ReturnsCorrectKeyChar (
  217. KeyCode key,
  218. char expectedChar,
  219. ConsoleKey expectedKey
  220. )
  221. {
  222. // Act
  223. ConsoleKeyInfo result = ConsoleKeyMapping.GetConsoleKeyInfoFromKeyCode (key);
  224. // Assert
  225. Assert.Equal (expectedChar, result.KeyChar);
  226. Assert.Equal (expectedKey, result.Key);
  227. }
  228. [Theory]
  229. [InlineData (KeyCode.F1, ConsoleKey.F1)]
  230. [InlineData (KeyCode.F5, ConsoleKey.F5)]
  231. [InlineData (KeyCode.F12, ConsoleKey.F12)]
  232. [InlineData (KeyCode.CursorUp, ConsoleKey.UpArrow)]
  233. [InlineData (KeyCode.CursorDown, ConsoleKey.DownArrow)]
  234. [InlineData (KeyCode.CursorLeft, ConsoleKey.LeftArrow)]
  235. [InlineData (KeyCode.CursorRight, ConsoleKey.RightArrow)]
  236. [InlineData (KeyCode.Home, ConsoleKey.Home)]
  237. [InlineData (KeyCode.End, ConsoleKey.End)]
  238. [InlineData (KeyCode.PageUp, ConsoleKey.PageUp)]
  239. [InlineData (KeyCode.PageDown, ConsoleKey.PageDown)]
  240. [InlineData (KeyCode.Delete, ConsoleKey.Delete)]
  241. [InlineData (KeyCode.Insert, ConsoleKey.Insert)]
  242. public void GetConsoleKeyInfoFromKeyCode_NavigationAndFunctionKeys_ReturnsCorrectConsoleKey (
  243. KeyCode key,
  244. ConsoleKey expectedKey
  245. )
  246. {
  247. // Act
  248. ConsoleKeyInfo result = ConsoleKeyMapping.GetConsoleKeyInfoFromKeyCode (key);
  249. // Assert
  250. Assert.Equal (expectedKey, result.Key);
  251. }
  252. [Theory]
  253. [InlineData (KeyCode.D0, '0', ConsoleKey.D0)]
  254. [InlineData (KeyCode.D1, '1', ConsoleKey.D1)]
  255. [InlineData (KeyCode.D5, '5', ConsoleKey.D5)]
  256. [InlineData (KeyCode.D9, '9', ConsoleKey.D9)]
  257. public void GetConsoleKeyInfoFromKeyCode_NumberKeys_ReturnsCorrectKeyChar (
  258. KeyCode key,
  259. char expectedChar,
  260. ConsoleKey expectedKey
  261. )
  262. {
  263. // Act
  264. ConsoleKeyInfo result = ConsoleKeyMapping.GetConsoleKeyInfoFromKeyCode (key);
  265. // Assert
  266. Assert.Equal (expectedChar, result.KeyChar);
  267. Assert.Equal (expectedKey, result.Key);
  268. }
  269. [Fact]
  270. public void MapToConsoleModifiers_AllLetterKeys_UnshiftedDoesNotSetShift ()
  271. {
  272. // This comprehensive test ensures ALL letter keys A-Z without ShiftMask
  273. // do not have Shift set in the returned modifiers.
  274. // This will fail if the old "char.IsUpper" check is present.
  275. for (KeyCode key = KeyCode.A; key <= KeyCode.Z; key++)
  276. {
  277. // Act
  278. ConsoleModifiers result = ConsoleKeyMapping.MapToConsoleModifiers (key);
  279. // Assert
  280. Assert.False (
  281. result.HasFlag (ConsoleModifiers.Shift),
  282. $"Shift should not be set for unshifted {key} (value {(int)key}). " +
  283. $"KeyCode.{key} represents a lowercase, unshifted key even though its numeric value is {(int)key}."
  284. );
  285. }
  286. }
  287. [Fact]
  288. public void MapToConsoleModifiers_AllLetterKeysShifted_SetsShift ()
  289. {
  290. // Verify that WITH ShiftMask, all letter keys DO set Shift
  291. for (KeyCode key = KeyCode.A; key <= KeyCode.Z; key++)
  292. {
  293. KeyCode shiftedKey = key | KeyCode.ShiftMask;
  294. // Act
  295. ConsoleModifiers result = ConsoleKeyMapping.MapToConsoleModifiers (shiftedKey);
  296. // Assert
  297. Assert.True (
  298. result.HasFlag (ConsoleModifiers.Shift),
  299. $"Shift should be set for {shiftedKey}"
  300. );
  301. }
  302. }
  303. [Fact]
  304. public void GetConsoleKeyInfoFromKeyCode_AllUnshiftedLetterKeys_ReturnLowercaseChars ()
  305. {
  306. // This comprehensive test verifies ALL letter keys A-Z produce lowercase characters
  307. // when no ShiftMask is set. This is the KEY test that will fail with the old code.
  308. for (KeyCode key = KeyCode.A; key <= KeyCode.Z; key++)
  309. {
  310. // Act
  311. ConsoleKeyInfo result = ConsoleKeyMapping.GetConsoleKeyInfoFromKeyCode (key);
  312. // Calculate expected lowercase character
  313. char expectedChar = (char)('a' + (key - KeyCode.A));
  314. // Assert
  315. Assert.True (
  316. char.IsLower (result.KeyChar),
  317. $"KeyChar for unshifted {key} should be lowercase, but got '{result.KeyChar}' (0x{(int)result.KeyChar:X2}). " +
  318. $"Expected lowercase '{expectedChar}' (0x{(int)expectedChar:X2})."
  319. );
  320. Assert.Equal (
  321. expectedChar,
  322. result.KeyChar
  323. );
  324. Assert.False (
  325. (result.Modifiers & ConsoleModifiers.Shift) != 0,
  326. $"Shift modifier should not be set for unshifted {key}"
  327. );
  328. }
  329. }
  330. [Fact]
  331. public void GetConsoleKeyInfoFromKeyCode_AllShiftedLetterKeys_ReturnUppercaseChars ()
  332. {
  333. // Verify that WITH ShiftMask, all letter keys produce uppercase characters
  334. for (KeyCode key = KeyCode.A; key <= KeyCode.Z; key++)
  335. {
  336. KeyCode shiftedKey = key | KeyCode.ShiftMask;
  337. // Act
  338. ConsoleKeyInfo result = ConsoleKeyMapping.GetConsoleKeyInfoFromKeyCode (shiftedKey);
  339. // Calculate expected uppercase character
  340. char expectedChar = (char)('A' + (key - KeyCode.A));
  341. // Assert
  342. Assert.True (
  343. char.IsUpper (result.KeyChar),
  344. $"KeyChar for shifted {shiftedKey} should be uppercase, but got '{result.KeyChar}'"
  345. );
  346. Assert.Equal (expectedChar, result.KeyChar);
  347. Assert.True (
  348. (result.Modifiers & ConsoleModifiers.Shift) != 0,
  349. $"Shift modifier should be set for {shiftedKey}"
  350. );
  351. }
  352. }
  353. [Theory]
  354. [InlineData (KeyCode.A, KeyCode.A | KeyCode.ShiftMask, 'a', 'A')] // Without vs With Shift
  355. [InlineData (KeyCode.M, KeyCode.M | KeyCode.ShiftMask, 'm', 'M')]
  356. [InlineData (KeyCode.Z, KeyCode.Z | KeyCode.ShiftMask, 'z', 'Z')]
  357. public void GetConsoleKeyInfoFromKeyCode_ShiftMaskChangesCase (
  358. KeyCode unshifted,
  359. KeyCode shifted,
  360. char expectedUnshiftedChar,
  361. char expectedShiftedChar
  362. )
  363. {
  364. // Act
  365. ConsoleKeyInfo unshiftedResult = ConsoleKeyMapping.GetConsoleKeyInfoFromKeyCode (unshifted);
  366. ConsoleKeyInfo shiftedResult = ConsoleKeyMapping.GetConsoleKeyInfoFromKeyCode (shifted);
  367. // Assert - Unshifted should be lowercase
  368. Assert.Equal (expectedUnshiftedChar, unshiftedResult.KeyChar);
  369. Assert.False ((unshiftedResult.Modifiers & ConsoleModifiers.Shift) != 0);
  370. // Assert - Shifted should be uppercase
  371. Assert.Equal (expectedShiftedChar, shiftedResult.KeyChar);
  372. Assert.True ((shiftedResult.Modifiers & ConsoleModifiers.Shift) != 0);
  373. }
  374. }