MouseInterfaceTests.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. using Xunit.Abstractions;
  2. namespace ApplicationTests.Mouse;
  3. /// <summary>
  4. /// Parallelizable tests for IMouse interface.
  5. /// Tests the decoupled mouse handling without Application.Init or global state.
  6. /// </summary>
  7. [Trait ("Category", "Input")]
  8. public class MouseInterfaceTests (ITestOutputHelper output)
  9. {
  10. private readonly ITestOutputHelper _output = output;
  11. #region IMouse Basic Properties
  12. [Fact]
  13. public void Mouse_LastMousePosition_InitiallyNull ()
  14. {
  15. // Arrange
  16. MouseImpl mouse = new ();
  17. // Act & Assert
  18. Assert.Null (mouse.LastMousePosition);
  19. }
  20. [Theory]
  21. [InlineData (0, 0)]
  22. [InlineData (10, 20)]
  23. [InlineData (-5, -10)]
  24. [InlineData (100, 200)]
  25. public void Mouse_LastMousePosition_CanBeSetAndRetrieved (int x, int y)
  26. {
  27. // Arrange
  28. MouseImpl mouse = new ();
  29. Point testPosition = new (x, y);
  30. // Act
  31. mouse.LastMousePosition = testPosition;
  32. // Assert
  33. Assert.Equal (testPosition, mouse.LastMousePosition);
  34. Assert.Equal (testPosition, mouse.LastMousePosition);
  35. }
  36. [Fact]
  37. public void Mouse_IsMouseDisabled_DefaultsFalse ()
  38. {
  39. // Arrange
  40. MouseImpl mouse = new ();
  41. // Act & Assert
  42. Assert.False (mouse.IsMouseDisabled);
  43. }
  44. [Theory]
  45. [InlineData (true)]
  46. [InlineData (false)]
  47. public void Mouse_IsMouseDisabled_CanBeSetAndRetrieved (bool disabled)
  48. {
  49. // Arrange
  50. MouseImpl mouse = new ();
  51. // Act
  52. mouse.IsMouseDisabled = disabled;
  53. // Assert
  54. Assert.Equal (disabled, mouse.IsMouseDisabled);
  55. }
  56. [Fact]
  57. public void Mouse_CachedViewsUnderMouse_InitiallyEmpty ()
  58. {
  59. // Arrange
  60. MouseImpl mouse = new ();
  61. // Act & Assert
  62. Assert.NotNull (mouse.CachedViewsUnderMouse);
  63. Assert.Empty (mouse.CachedViewsUnderMouse);
  64. }
  65. #endregion
  66. #region IMouse Event Handling
  67. [Fact]
  68. public void Mouse_MouseEvent_CanSubscribeAndFire ()
  69. {
  70. // Arrange
  71. MouseImpl mouse = new ();
  72. var eventFired = false;
  73. MouseEventArgs? capturedArgs = null;
  74. mouse.MouseEvent += (sender, args) =>
  75. {
  76. eventFired = true;
  77. capturedArgs = args;
  78. };
  79. MouseEventArgs testEvent = new ()
  80. {
  81. ScreenPosition = new (5, 10),
  82. Flags = MouseFlags.Button1Pressed
  83. };
  84. // Act
  85. mouse.RaiseMouseEvent (testEvent);
  86. // Assert
  87. Assert.True (eventFired);
  88. Assert.NotNull (capturedArgs);
  89. Assert.Equal (testEvent.ScreenPosition, capturedArgs.ScreenPosition);
  90. Assert.Equal (testEvent.Flags, capturedArgs.Flags);
  91. }
  92. [Fact]
  93. public void Mouse_MouseEvent_CanUnsubscribe ()
  94. {
  95. // Arrange
  96. MouseImpl mouse = new ();
  97. var eventCount = 0;
  98. void Handler (object? sender, MouseEventArgs args) { eventCount++; }
  99. mouse.MouseEvent += Handler;
  100. MouseEventArgs testEvent = new ()
  101. {
  102. ScreenPosition = new (0, 0),
  103. Flags = MouseFlags.Button1Pressed
  104. };
  105. // Act - Fire once
  106. mouse.RaiseMouseEvent (testEvent);
  107. Assert.Equal (1, eventCount);
  108. // Unsubscribe
  109. mouse.MouseEvent -= Handler;
  110. // Fire again
  111. mouse.RaiseMouseEvent (testEvent);
  112. // Assert - Count should not increase
  113. Assert.Equal (1, eventCount);
  114. }
  115. [Fact]
  116. public void Mouse_RaiseMouseEvent_WithDisabledMouse_DoesNotFireEvent ()
  117. {
  118. // Arrange
  119. MouseImpl mouse = new ();
  120. var eventFired = false;
  121. mouse.MouseEvent += (sender, args) => { eventFired = true; };
  122. mouse.IsMouseDisabled = true;
  123. MouseEventArgs testEvent = new ()
  124. {
  125. ScreenPosition = new (0, 0),
  126. Flags = MouseFlags.Button1Pressed
  127. };
  128. // Act
  129. mouse.RaiseMouseEvent (testEvent);
  130. // Assert
  131. Assert.False (eventFired);
  132. }
  133. [Theory]
  134. [InlineData (MouseFlags.Button1Pressed)]
  135. [InlineData (MouseFlags.Button1Released)]
  136. [InlineData (MouseFlags.Button1Clicked)]
  137. [InlineData (MouseFlags.Button2Pressed)]
  138. [InlineData (MouseFlags.WheeledUp)]
  139. [InlineData (MouseFlags.ReportMousePosition)]
  140. public void Mouse_RaiseMouseEvent_CorrectlyPassesFlags (MouseFlags flags)
  141. {
  142. // Arrange
  143. MouseImpl mouse = new ();
  144. MouseFlags? capturedFlags = null;
  145. mouse.MouseEvent += (sender, args) => { capturedFlags = args.Flags; };
  146. MouseEventArgs testEvent = new ()
  147. {
  148. ScreenPosition = new (5, 5),
  149. Flags = flags
  150. };
  151. // Act
  152. mouse.RaiseMouseEvent (testEvent);
  153. // Assert
  154. Assert.NotNull (capturedFlags);
  155. Assert.Equal (flags, capturedFlags.Value);
  156. }
  157. #endregion
  158. #region IMouse ResetState
  159. [Fact]
  160. public void Mouse_ResetState_ClearsCachedViews ()
  161. {
  162. // Arrange
  163. MouseImpl mouse = new ();
  164. View testView = new () { Width = 10, Height = 10 };
  165. mouse.CachedViewsUnderMouse.Add (testView);
  166. Assert.Single (mouse.CachedViewsUnderMouse);
  167. // Act
  168. mouse.ResetState ();
  169. // Assert
  170. Assert.Empty (mouse.CachedViewsUnderMouse);
  171. testView.Dispose ();
  172. }
  173. [Fact]
  174. public void Mouse_ResetState_ClearsEventHandlers ()
  175. {
  176. // Arrange
  177. MouseImpl mouse = new ();
  178. var eventCount = 0;
  179. mouse.MouseEvent += (sender, args) => eventCount++;
  180. MouseEventArgs testEvent = new ()
  181. {
  182. ScreenPosition = new (0, 0),
  183. Flags = MouseFlags.Button1Pressed
  184. };
  185. // Verify event fires before reset
  186. mouse.RaiseMouseEvent (testEvent);
  187. Assert.Equal (1, eventCount);
  188. // Act
  189. mouse.ResetState ();
  190. // Raise event again
  191. mouse.RaiseMouseEvent (testEvent);
  192. // Assert - Event count should not increase after reset
  193. Assert.Equal (1, eventCount);
  194. }
  195. [Fact]
  196. public void Mouse_ResetState_DoesNotClearLastMousePosition ()
  197. {
  198. // Arrange
  199. MouseImpl mouse = new ();
  200. Point testPosition = new (42, 84);
  201. mouse.LastMousePosition = testPosition;
  202. // Act
  203. mouse.ResetState ();
  204. // Assert - LastMousePosition should NOT be cleared (per design)
  205. Assert.Equal (testPosition, mouse.LastMousePosition);
  206. }
  207. #endregion
  208. #region IMouse Isolation
  209. [Fact]
  210. public void Mouse_Instances_AreIndependent ()
  211. {
  212. // Arrange
  213. MouseImpl mouse1 = new ();
  214. MouseImpl mouse2 = new ();
  215. // Act
  216. mouse1.IsMouseDisabled = true;
  217. mouse1.LastMousePosition = new Point (10, 10);
  218. // Assert - mouse2 should be unaffected
  219. Assert.False (mouse2.IsMouseDisabled);
  220. Assert.Null (mouse2.LastMousePosition);
  221. }
  222. [Fact]
  223. public void Mouse_Events_AreIndependent ()
  224. {
  225. // Arrange
  226. MouseImpl mouse1 = new ();
  227. var mouse1EventCount = 0;
  228. MouseImpl mouse2 = new ();
  229. var mouse2EventCount = 0;
  230. mouse1.MouseEvent += (sender, args) => mouse1EventCount++;
  231. mouse2.MouseEvent += (sender, args) => mouse2EventCount++;
  232. MouseEventArgs testEvent = new ()
  233. {
  234. ScreenPosition = new (0, 0),
  235. Flags = MouseFlags.Button1Pressed
  236. };
  237. // Act
  238. mouse1.RaiseMouseEvent (testEvent);
  239. // Assert
  240. Assert.Equal (1, mouse1EventCount);
  241. Assert.Equal (0, mouse2EventCount);
  242. }
  243. [Fact]
  244. public void Mouse_CachedViews_AreIndependent ()
  245. {
  246. // Arrange
  247. MouseImpl mouse1 = new ();
  248. MouseImpl mouse2 = new ();
  249. View view1 = new ();
  250. View view2 = new ();
  251. // Act
  252. mouse1.CachedViewsUnderMouse.Add (view1);
  253. mouse2.CachedViewsUnderMouse.Add (view2);
  254. // Assert
  255. Assert.Single (mouse1.CachedViewsUnderMouse);
  256. Assert.Single (mouse2.CachedViewsUnderMouse);
  257. Assert.Contains (view1, mouse1.CachedViewsUnderMouse);
  258. Assert.Contains (view2, mouse2.CachedViewsUnderMouse);
  259. Assert.DoesNotContain (view2, mouse1.CachedViewsUnderMouse);
  260. Assert.DoesNotContain (view1, mouse2.CachedViewsUnderMouse);
  261. view1.Dispose ();
  262. view2.Dispose ();
  263. }
  264. #endregion
  265. #region Mouse Grab Tests
  266. [Fact]
  267. public void Mouse_GrabMouse_SetsMouseGrabView ()
  268. {
  269. // Arrange
  270. MouseImpl mouse = new ();
  271. View testView = new ();
  272. // Act
  273. mouse.GrabMouse (testView);
  274. // Assert
  275. Assert.Equal (testView, mouse.MouseGrabView);
  276. }
  277. [Fact]
  278. public void Mouse_UngrabMouse_ClearsMouseGrabView ()
  279. {
  280. // Arrange
  281. MouseImpl mouse = new ();
  282. View testView = new ();
  283. mouse.GrabMouse (testView);
  284. // Act
  285. mouse.UngrabMouse ();
  286. // Assert
  287. Assert.Null (mouse.MouseGrabView);
  288. }
  289. [Fact]
  290. public void Mouse_GrabbingMouse_CanBeCanceled ()
  291. {
  292. // Arrange
  293. MouseImpl mouse = new ();
  294. View testView = new ();
  295. var eventFired = false;
  296. mouse.GrabbingMouse += (sender, args) =>
  297. {
  298. eventFired = true;
  299. args.Cancel = true;
  300. };
  301. // Act
  302. mouse.GrabMouse (testView);
  303. // Assert
  304. Assert.True (eventFired);
  305. Assert.Null (mouse.MouseGrabView); // Should not be set because it was cancelled
  306. }
  307. [Fact]
  308. public void Mouse_GrabbedMouse_EventFired ()
  309. {
  310. // Arrange
  311. MouseImpl mouse = new ();
  312. View testView = new ();
  313. var eventFired = false;
  314. View? eventView = null;
  315. mouse.GrabbedMouse += (sender, args) =>
  316. {
  317. eventFired = true;
  318. eventView = args.View;
  319. };
  320. // Act
  321. mouse.GrabMouse (testView);
  322. // Assert
  323. Assert.True (eventFired);
  324. Assert.Equal (testView, eventView);
  325. }
  326. [Fact]
  327. public void Mouse_UnGrabbedMouse_EventFired ()
  328. {
  329. // Arrange
  330. MouseImpl mouse = new ();
  331. View testView = new ();
  332. mouse.GrabMouse (testView);
  333. var eventFired = false;
  334. View? eventView = null;
  335. mouse.UnGrabbedMouse += (sender, args) =>
  336. {
  337. eventFired = true;
  338. eventView = args.View;
  339. };
  340. // Act
  341. mouse.UngrabMouse ();
  342. // Assert
  343. Assert.True (eventFired);
  344. Assert.Equal (testView, eventView);
  345. }
  346. #endregion
  347. }