TimedEventsTests.cs 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. using System.Diagnostics;
  2. namespace UnitTests.ApplicationTests;
  3. /// <summary>
  4. /// Tests for TimedEvents class, focusing on high-resolution timing with Stopwatch.
  5. /// </summary>
  6. public class TimedEventsTests
  7. {
  8. [Fact]
  9. public void HighFrequency_Concurrent_Invocations_No_Lost_Timeouts ()
  10. {
  11. var timedEvents = new Terminal.Gui.App.TimedEvents ();
  12. var counter = 0;
  13. var expected = 1000;
  14. var completed = new ManualResetEventSlim (false);
  15. // Add many timeouts with TimeSpan.Zero concurrently
  16. Parallel.For (0, expected, i =>
  17. {
  18. timedEvents.Add (TimeSpan.Zero, () =>
  19. {
  20. var current = Interlocked.Increment (ref counter);
  21. if (current == expected)
  22. {
  23. completed.Set ();
  24. }
  25. return false; // One-shot
  26. });
  27. });
  28. // Run timers multiple times to ensure all are processed
  29. for (int i = 0; i < 10; i++)
  30. {
  31. timedEvents.RunTimers ();
  32. if (completed.IsSet)
  33. {
  34. break;
  35. }
  36. Thread.Sleep (10);
  37. }
  38. Assert.Equal (expected, counter);
  39. }
  40. [Fact]
  41. public void GetTimestampTicks_Provides_High_Resolution ()
  42. {
  43. var timedEvents = new Terminal.Gui.App.TimedEvents ();
  44. // Add multiple timeouts with TimeSpan.Zero rapidly
  45. var timestamps = new List<long> ();
  46. // Single event handler to capture all timestamps
  47. EventHandler<Terminal.Gui.App.TimeoutEventArgs>? handler = null;
  48. handler = (s, e) =>
  49. {
  50. timestamps.Add (e.Ticks);
  51. };
  52. timedEvents.Added += handler;
  53. for (int i = 0; i < 100; i++)
  54. {
  55. timedEvents.Add (TimeSpan.Zero, () => false);
  56. }
  57. timedEvents.Added -= handler;
  58. // Verify that we got timestamps
  59. Assert.True (timestamps.Count > 0, $"Should have captured timestamps. Got {timestamps.Count}");
  60. // Verify that we got unique timestamps (or very close)
  61. // With Stopwatch, we should have much better resolution than DateTime.UtcNow
  62. var uniqueTimestamps = timestamps.Distinct ().Count ();
  63. // We should have mostly unique timestamps
  64. // Allow some duplicates due to extreme speed, but should be > 50% unique
  65. Assert.True (uniqueTimestamps > timestamps.Count / 2,
  66. $"Expected more unique timestamps. Got {uniqueTimestamps} unique out of {timestamps.Count} total");
  67. }
  68. [Fact]
  69. public void TimeSpan_Zero_Executes_Immediately ()
  70. {
  71. var timedEvents = new Terminal.Gui.App.TimedEvents ();
  72. var executed = false;
  73. timedEvents.Add (TimeSpan.Zero, () =>
  74. {
  75. executed = true;
  76. return false;
  77. });
  78. Assert.True (timedEvents.Timeouts.Keys [0] > 0);
  79. // Should execute on first RunTimers call
  80. timedEvents.RunTimers ();
  81. Assert.Empty (timedEvents.Timeouts);
  82. Assert.True (executed);
  83. }
  84. [Fact]
  85. public void Multiple_TimeSpan_Zero_Timeouts_All_Execute ()
  86. {
  87. var timedEvents = new Terminal.Gui.App.TimedEvents ();
  88. var executeCount = 0;
  89. var expected = 100;
  90. for (int i = 0; i < expected; i++)
  91. {
  92. timedEvents.Add (TimeSpan.Zero, () =>
  93. {
  94. Interlocked.Increment (ref executeCount);
  95. return false;
  96. });
  97. }
  98. // Run timers once
  99. timedEvents.RunTimers ();
  100. Assert.Equal (expected, executeCount);
  101. }
  102. }