TimedEventsTests.cs 4.1 KB

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