#nullable enable
namespace UnitTests.ApplicationTests;
///
/// Tests for TimedEvents class, focusing on high-resolution timing with Stopwatch.
///
public class TimedEventsTests
{
[Fact]
public void HighFrequency_Concurrent_Invocations_No_Lost_Timeouts ()
{
var timedEvents = new TimedEvents ();
var counter = 0;
var expected = 1000;
var completed = new ManualResetEventSlim (false);
// Add many timeouts with TimeSpan.Zero concurrently
Parallel.For (
0,
expected,
i =>
{
timedEvents.Add (
TimeSpan.Zero,
() =>
{
int current = Interlocked.Increment (ref counter);
if (current == expected)
{
completed.Set ();
}
return false; // One-shot
});
});
// Run timers multiple times to ensure all are processed
for (var i = 0; i < 10; i++)
{
timedEvents.RunTimers ();
if (completed.IsSet)
{
break;
}
Thread.Sleep (10);
}
Assert.Equal (expected, counter);
}
[Fact]
public void GetTimestampTicks_Provides_High_Resolution ()
{
var timedEvents = new TimedEvents ();
// Add multiple timeouts with TimeSpan.Zero rapidly
List timestamps = new ();
// Single event handler to capture all timestamps
EventHandler? handler = null;
handler = (s, e) => { timestamps.Add (e.Ticks); };
timedEvents.Added += handler;
for (var i = 0; i < 100; i++)
{
timedEvents.Add (TimeSpan.Zero, () => false);
}
timedEvents.Added -= handler;
// Verify that we got timestamps
Assert.True (timestamps.Count > 0, $"Should have captured timestamps. Got {timestamps.Count}");
// Verify that we got unique timestamps (or very close)
// With Stopwatch, we should have much better resolution than DateTime.UtcNow
int uniqueTimestamps = timestamps.Distinct ().Count ();
// We should have mostly unique timestamps
// Allow some duplicates due to extreme speed, but should be > 50% unique
Assert.True (
uniqueTimestamps > timestamps.Count / 2,
$"Expected more unique timestamps. Got {uniqueTimestamps} unique out of {timestamps.Count} total");
}
[Fact]
public void TimeSpan_Zero_Executes_Immediately ()
{
var timedEvents = new TimedEvents ();
var executed = false;
timedEvents.Add (
TimeSpan.Zero,
() =>
{
executed = true;
return false;
});
Assert.True (timedEvents.Timeouts.Keys [0] > 0);
// Should execute on first RunTimers call
timedEvents.RunTimers ();
Assert.Empty (timedEvents.Timeouts);
Assert.True (executed);
}
[Fact]
public void Multiple_TimeSpan_Zero_Timeouts_All_Execute ()
{
var timedEvents = new TimedEvents ();
var executeCount = 0;
var expected = 100;
for (var i = 0; i < expected; i++)
{
timedEvents.Add (
TimeSpan.Zero,
() =>
{
Interlocked.Increment (ref executeCount);
return false;
});
}
// Run timers once
timedEvents.RunTimers ();
Assert.Equal (expected, executeCount);
}
}