From b9fcbcd3ed1b6a7c0373da2e16d6432bdc8e645a Mon Sep 17 00:00:00 2001 From: Egil Hansen Date: Mon, 2 Oct 2023 16:26:37 +0000 Subject: [PATCH] refactor: code clean up and mistakes in code docs --- ...meProviderExtensions.ManualTimeProvider.md | 22 +- .../ManualTimeProvider.cs | 39 ++- .../CallerArgumentExpressionAttribute.cs | 3 + .../IsExternalInit.cs | 6 +- .../System.Threading/PeriodicTimerPort.cs | 1 + .../System.Threading/PeriodicTimerWrapper.cs | 2 + .../ManualTimeProviderTests.cs | 10 +- .../ManualTimeProviderTimerTests.cs | 238 ------------------ .../ManualTimeProviderWaitAsyncTests.cs | 6 +- .../ManualTimerTests.cs | 234 +++++++++++++++++ 10 files changed, 281 insertions(+), 280 deletions(-) delete mode 100644 test/TimeProviderExtensions.Tests/ManualTimeProviderTimerTests.cs diff --git a/docs/TimeProviderExtensions.ManualTimeProvider.md b/docs/TimeProviderExtensions.ManualTimeProvider.md index 9f66d85..1185759 100644 --- a/docs/TimeProviderExtensions.ManualTimeProvider.md +++ b/docs/TimeProviderExtensions.ManualTimeProvider.md @@ -12,7 +12,7 @@ public class ManualTimeProvider : System.TimeProvider Inheritance [System.Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object 'System.Object') 🡒 [System.TimeProvider](https://docs.microsoft.com/en-us/dotnet/api/System.TimeProvider 'System.TimeProvider') 🡒 ManualTimeProvider ### Remarks -Learn more at [https://github.com/egil/TimeProviderExtensions](https://github.com/egil/TimeProviderExtensions 'https://github.com/egil/TimeProviderExtensions'). +Learn more at TimeProviderExtensions on GitHub. ### Constructors @@ -193,7 +193,7 @@ var timer = manualTimeProvider.CreateTimer( dueTime: Span.FromSecond(1), period: TimeSpan.FromSecond(1)); -manualtTimeProvider.Advance(TimeSpan.FromSecond(3)); +manualTimeProvider.Advance(TimeSpan.FromSecond(3)); ``` The call to `Advance(TimeSpan.FromSecond(3))` causes the `timer`s callback to be invoked three times, and the result of the `manualTimeProvider.GetElapsedTime(start)` in the callback call will be 1 second, 2 seconds, @@ -204,7 +204,7 @@ If the desired result is to jump time by [delta](TimeProviderExtensions.ManualTi the expected number of times, i.e. such that the result of `manualTimeProvider.GetElapsedTime(start)` in the callback is 3 seconds, 3 seconds, and 3 seconds, use [Jump(DateTimeOffset)](TimeProviderExtensions.ManualTimeProvider.md#TimeProviderExtensions.ManualTimeProvider.Jump(System.DateTimeOffset) 'TimeProviderExtensions.ManualTimeProvider.Jump(System.DateTimeOffset)') or [Jump(TimeSpan)](TimeProviderExtensions.ManualTimeProvider.md#TimeProviderExtensions.ManualTimeProvider.Jump(System.TimeSpan) 'TimeProviderExtensions.ManualTimeProvider.Jump(System.TimeSpan)') instead. -Learn more about this behavior at [https://github.com/egil/TimeProviderExtensions/#difference-between-manualtimeprovider-and-faketimeprovider](https://github.com/egil/TimeProviderExtensions/#difference-between-manualtimeprovider-and-faketimeprovider 'https://github.com/egil/TimeProviderExtensions/#difference-between-manualtimeprovider-and-faketimeprovider'). +Learn more about this behavior at in the documentation. @@ -397,9 +397,9 @@ var timer = manualTimeProvider.CreateTimer( dueTime: Span.FromSecond(1), period: TimeSpan.FromSecond(1)); -manualtTimeProvider.Jump(manualtTimeProvider.Start + TimeSpan.FromSecond(3)); +manualTimeProvider.Jump(manualTimeProvider.Start + TimeSpan.FromSecond(3)); ``` -The call to `Jump(manualtTimeProvider.Start + TimeSpan.FromSecond(3))` causes the `timer`s callback to be invoked three times, +The call to `Jump(manualTimeProvider.Start + TimeSpan.FromSecond(3))` causes the `timer`s callback to be invoked three times, and the result of the `manualTimeProvider.GetElapsedTime(start)` in the callback call will be 3 seconds during all three invocations. @@ -407,7 +407,7 @@ If the desired result is that timer callbacks happens exactly at their scheduled of `manualTimeProvider.GetElapsedTime(start)` in the callback will be 1 second, 2 seconds, and 3 seconds, use [Advance(TimeSpan)](TimeProviderExtensions.ManualTimeProvider.md#TimeProviderExtensions.ManualTimeProvider.Advance(System.TimeSpan) 'TimeProviderExtensions.ManualTimeProvider.Advance(System.TimeSpan)') or [SetUtcNow(DateTimeOffset)](TimeProviderExtensions.ManualTimeProvider.md#TimeProviderExtensions.ManualTimeProvider.SetUtcNow(System.DateTimeOffset) 'TimeProviderExtensions.ManualTimeProvider.SetUtcNow(System.DateTimeOffset)') instead. -Learn more about this behavior at [https://github.com/egil/TimeProviderExtensions/#difference-between-manualtimeprovider-and-faketimeprovider](https://github.com/egil/TimeProviderExtensions/#difference-between-manualtimeprovider-and-faketimeprovider 'https://github.com/egil/TimeProviderExtensions/#difference-between-manualtimeprovider-and-faketimeprovider'). +Learn more about this behavior at in the documentation. /// @@ -452,7 +452,7 @@ var timer = manualTimeProvider.CreateTimer( dueTime: Span.FromSecond(1), period: TimeSpan.FromSecond(1)); -manualtTimeProvider.Jump(TimeSpan.FromSecond(3)); +manualTimeProvider.Jump(TimeSpan.FromSecond(3)); ``` The call to `Jump(TimeSpan.FromSecond(3))` causes the `timer`s callback to be invoked three times, and the result of the `manualTimeProvider.GetElapsedTime(start)` in the callback call will be 3 seconds @@ -462,7 +462,7 @@ If the desired result is that timer callbacks happens exactly at their scheduled of `manualTimeProvider.GetElapsedTime(start)` in the callback will be 1 second, 2 seconds, and 3 seconds, use [Advance(TimeSpan)](TimeProviderExtensions.ManualTimeProvider.md#TimeProviderExtensions.ManualTimeProvider.Advance(System.TimeSpan) 'TimeProviderExtensions.ManualTimeProvider.Advance(System.TimeSpan)') or [SetUtcNow(DateTimeOffset)](TimeProviderExtensions.ManualTimeProvider.md#TimeProviderExtensions.ManualTimeProvider.SetUtcNow(System.DateTimeOffset) 'TimeProviderExtensions.ManualTimeProvider.SetUtcNow(System.DateTimeOffset)') instead. -Learn more about this behavior at [https://github.com/egil/TimeProviderExtensions/#difference-between-manualtimeprovider-and-faketimeprovider](https://github.com/egil/TimeProviderExtensions/#difference-between-manualtimeprovider-and-faketimeprovider 'https://github.com/egil/TimeProviderExtensions/#difference-between-manualtimeprovider-and-faketimeprovider'). +Learn more about this behavior at in the documentation. /// @@ -525,9 +525,9 @@ var timer = manualTimeProvider.CreateTimer( dueTime: Span.FromSecond(1), period: TimeSpan.FromSecond(1)); -manualtTimeProvider.SetUtcNow(manualtTimeProvider.Start + TimeSpan.FromSecond(3)); +manualTimeProvider.SetUtcNow(manualTimeProvider.Start + TimeSpan.FromSecond(3)); ``` -The call to `SetUtcNow(manualtTimeProvider.Start + TimeSpan.FromSecond(3))` causes the `timer`s callback to be invoked three times, +The call to `SetUtcNow(manualTimeProvider.Start + TimeSpan.FromSecond(3))` causes the `timer`s callback to be invoked three times, and the result of the `manualTimeProvider.GetElapsedTime(start)` in the callback call will be 1 second, 2 seconds, and 3 seconds. In other words, the time of the provider is set before the time callback is invoked to the time that the callback is scheduled to be invoked at. @@ -536,7 +536,7 @@ If the desired result is to jump to the time specified in [value](TimeProviderEx the expected number of times, i.e. such that the result of `manualTimeProvider.GetElapsedTime(start)` in the callback is 3 seconds, 3 seconds, and 3 seconds, use [Jump(DateTimeOffset)](TimeProviderExtensions.ManualTimeProvider.md#TimeProviderExtensions.ManualTimeProvider.Jump(System.DateTimeOffset) 'TimeProviderExtensions.ManualTimeProvider.Jump(System.DateTimeOffset)') or [Jump(TimeSpan)](TimeProviderExtensions.ManualTimeProvider.md#TimeProviderExtensions.ManualTimeProvider.Jump(System.TimeSpan) 'TimeProviderExtensions.ManualTimeProvider.Jump(System.TimeSpan)') instead. -Learn more about this behavior at [https://github.com/egil/TimeProviderExtensions/#difference-between-manualtimeprovider-and-faketimeprovider](https://github.com/egil/TimeProviderExtensions/#difference-between-manualtimeprovider-and-faketimeprovider 'https://github.com/egil/TimeProviderExtensions/#difference-between-manualtimeprovider-and-faketimeprovider'). +Learn more about this behavior at in the documentation. /// diff --git a/src/TimeProviderExtensions/ManualTimeProvider.cs b/src/TimeProviderExtensions/ManualTimeProvider.cs index 5a77ffb..9f5458e 100644 --- a/src/TimeProviderExtensions/ManualTimeProvider.cs +++ b/src/TimeProviderExtensions/ManualTimeProvider.cs @@ -8,7 +8,7 @@ namespace TimeProviderExtensions; /// Represents a synthetic time provider that can be used to enable deterministic behavior in tests. /// /// -/// Learn more at . +/// Learn more at TimeProviderExtensions on GitHub. /// [DebuggerDisplay("UtcNow: {ToString(),nq}. Active timers: {ActiveTimers}. {AutoAdvanceBehavior,nq}.")] public class ManualTimeProvider : TimeProvider @@ -232,14 +232,14 @@ public void SetLocalTimeZone(TimeZoneInfo localTimeZone) /// For example: /// /// var start = sut.GetTimestamp(); - /// + /// /// var timer = manualTimeProvider.CreateTimer( /// callback: _ => manualTimeProvider.GetElapsedTime(start), /// state: null, /// dueTime: Span.FromSecond(1), /// period: TimeSpan.FromSecond(1)); - /// - /// manualtTimeProvider.Advance(TimeSpan.FromSecond(3)); + /// + /// manualTimeProvider.Advance(TimeSpan.FromSecond(3)); /// /// The call to Advance(TimeSpan.FromSecond(3)) causes the timers callback to be invoked three times, /// and the result of the manualTimeProvider.GetElapsedTime(start) in the callback call will be 1 second, 2 seconds, @@ -252,7 +252,7 @@ public void SetLocalTimeZone(TimeZoneInfo localTimeZone) /// 3 seconds, 3 seconds, and 3 seconds, use or instead. /// /// - /// Learn more about this behavior at . + /// Learn more about this behavior at in the documentation. /// /// /// Thrown if is negative. Going back in time is not supported. @@ -290,16 +290,16 @@ public void Advance(TimeSpan delta) /// For example: /// /// var start = sut.GetTimestamp(); - /// + /// /// var timer = manualTimeProvider.CreateTimer( /// callback: _ => manualTimeProvider.GetElapsedTime(start), /// state: null, /// dueTime: Span.FromSecond(1), /// period: TimeSpan.FromSecond(1)); - /// - /// manualtTimeProvider.SetUtcNow(manualtTimeProvider.Start + TimeSpan.FromSecond(3)); + /// + /// manualTimeProvider.SetUtcNow(manualTimeProvider.Start + TimeSpan.FromSecond(3)); /// - /// The call to SetUtcNow(manualtTimeProvider.Start + TimeSpan.FromSecond(3)) causes the timers callback to be invoked three times, + /// The call to SetUtcNow(manualTimeProvider.Start + TimeSpan.FromSecond(3)) causes the timers callback to be invoked three times, /// and the result of the manualTimeProvider.GetElapsedTime(start) in the callback call will be 1 second, 2 seconds, /// and 3 seconds. In other words, the time of the provider is set before the time callback is invoked /// to the time that the callback is scheduled to be invoked at. @@ -310,8 +310,7 @@ public void Advance(TimeSpan delta) /// 3 seconds, 3 seconds, and 3 seconds, use or instead. /// /// - /// Learn more about this behavior at . - /// + /// Learn more about this behavior at in the documentation. /// /// /// Thrown if is less than the value returned by . Going back in time is not supported. public void SetUtcNow(DateTimeOffset value) @@ -376,14 +375,14 @@ public void SetUtcNow(DateTimeOffset value) /// For example: /// /// var start = sut.GetTimestamp(); - /// + /// /// var timer = manualTimeProvider.CreateTimer( /// callback: _ => manualTimeProvider.GetElapsedTime(start), /// state: null, /// dueTime: Span.FromSecond(1), /// period: TimeSpan.FromSecond(1)); - /// - /// manualtTimeProvider.Jump(TimeSpan.FromSecond(3)); + /// + /// manualTimeProvider.Jump(TimeSpan.FromSecond(3)); /// /// The call to Jump(TimeSpan.FromSecond(3)) causes the timers callback to be invoked three times, /// and the result of the manualTimeProvider.GetElapsedTime(start) in the callback call will be 3 seconds @@ -395,8 +394,7 @@ public void SetUtcNow(DateTimeOffset value) /// use or instead. /// /// - /// Learn more about this behavior at . - /// + /// Learn more about this behavior at in the documentation. /// /// /// Thrown if is negative. Going back in time is not supported. public void Jump(TimeSpan delta) @@ -433,16 +431,16 @@ public void Jump(TimeSpan delta) /// For example: /// /// var start = sut.GetTimestamp(); - /// + /// /// var timer = manualTimeProvider.CreateTimer( /// callback: _ => manualTimeProvider.GetElapsedTime(start), /// state: null, /// dueTime: Span.FromSecond(1), /// period: TimeSpan.FromSecond(1)); /// - /// manualtTimeProvider.Jump(manualtTimeProvider.Start + TimeSpan.FromSecond(3)); + /// manualTimeProvider.Jump(manualTimeProvider.Start + TimeSpan.FromSecond(3)); /// - /// The call to Jump(manualtTimeProvider.Start + TimeSpan.FromSecond(3)) causes the timers callback to be invoked three times, + /// The call to Jump(manualTimeProvider.Start + TimeSpan.FromSecond(3)) causes the timers callback to be invoked three times, /// and the result of the manualTimeProvider.GetElapsedTime(start) in the callback call will be 3 seconds /// during all three invocations. /// @@ -452,8 +450,7 @@ public void Jump(TimeSpan delta) /// use or instead. /// /// - /// Learn more about this behavior at . - /// + /// Learn more about this behavior at in the documentation. /// /// /// Thrown if is less than the value returned by . Going back in time is not supported. public void Jump(DateTimeOffset value) diff --git a/src/TimeProviderExtensions/System.Runtime.CompilerServices/CallerArgumentExpressionAttribute.cs b/src/TimeProviderExtensions/System.Runtime.CompilerServices/CallerArgumentExpressionAttribute.cs index 192e1d5..e74b875 100644 --- a/src/TimeProviderExtensions/System.Runtime.CompilerServices/CallerArgumentExpressionAttribute.cs +++ b/src/TimeProviderExtensions/System.Runtime.CompilerServices/CallerArgumentExpressionAttribute.cs @@ -1,6 +1,9 @@ #if !NET6_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; + namespace System.Runtime.CompilerServices; +[ExcludeFromCodeCoverage] [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] internal sealed class CallerArgumentExpressionAttribute : Attribute { diff --git a/src/TimeProviderExtensions/System.Runtime.CompilerServices/IsExternalInit.cs b/src/TimeProviderExtensions/System.Runtime.CompilerServices/IsExternalInit.cs index c3e1e82..ea5d893 100644 --- a/src/TimeProviderExtensions/System.Runtime.CompilerServices/IsExternalInit.cs +++ b/src/TimeProviderExtensions/System.Runtime.CompilerServices/IsExternalInit.cs @@ -1,9 +1,10 @@ #if NETSTANDARD2_0 // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. - + using System.ComponentModel; - +using System.Diagnostics.CodeAnalysis; + namespace System.Runtime.CompilerServices { /// @@ -11,6 +12,7 @@ namespace System.Runtime.CompilerServices /// This class should not be used by developers in source code. /// This dummy class is required to compile records when targeting .NET Standard /// + [ExcludeFromCodeCoverage] [EditorBrowsable(EditorBrowsableState.Never)] internal static class IsExternalInit { diff --git a/src/TimeProviderExtensions/System.Threading/PeriodicTimerPort.cs b/src/TimeProviderExtensions/System.Threading/PeriodicTimerPort.cs index 1a9abf6..4fdeeb7 100644 --- a/src/TimeProviderExtensions/System.Threading/PeriodicTimerPort.cs +++ b/src/TimeProviderExtensions/System.Threading/PeriodicTimerPort.cs @@ -18,6 +18,7 @@ namespace TimeProviderExtensions; /// may be in flight at any given moment. may be used concurrently with an active /// to interrupt it and cause it to return false. /// +[ExcludeFromCodeCoverage] internal sealed class PeriodicTimerPort : IDisposable { internal const uint MaxSupportedTimeout = 0xfffffffe; diff --git a/src/TimeProviderExtensions/System.Threading/PeriodicTimerWrapper.cs b/src/TimeProviderExtensions/System.Threading/PeriodicTimerWrapper.cs index e9b5dbb..5c562ed 100644 --- a/src/TimeProviderExtensions/System.Threading/PeriodicTimerWrapper.cs +++ b/src/TimeProviderExtensions/System.Threading/PeriodicTimerWrapper.cs @@ -1,4 +1,5 @@ #if NET6_0_OR_GREATER && !NET8_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; using TimeProviderExtensions; namespace System.Threading; @@ -14,6 +15,7 @@ namespace System.Threading; /// to interrupt it and cause it to return false. /// /// +[ExcludeFromCodeCoverage] public abstract class PeriodicTimerWrapper : IDisposable { /// Wait for the next tick of the timer, or for the timer to be stopped. diff --git a/test/TimeProviderExtensions.Tests/ManualTimeProviderTests.cs b/test/TimeProviderExtensions.Tests/ManualTimeProviderTests.cs index 42e0b2f..ae36146 100644 --- a/test/TimeProviderExtensions.Tests/ManualTimeProviderTests.cs +++ b/test/TimeProviderExtensions.Tests/ManualTimeProviderTests.cs @@ -134,9 +134,9 @@ public async Task Callbacks_happens_in_schedule_order() periodicTimer.Dispose(); await callbacksTask; - async Task AsyncCallbacks(PeriodicTimer periodicTimer) + async Task AsyncCallbacks(PeriodicTimer timer) { - while (await periodicTimer.WaitForNextTickAsync().ConfigureAwait(false)) + while (await timer.WaitForNextTickAsync().ConfigureAwait(false)) { callbacks.Add(sut.GetUtcNow()); await sut.Delay(TimeSpan.FromSeconds(3)); @@ -292,7 +292,7 @@ public void ActiveTimers_with_active_timers() { var sut = new ManualTimeProvider(); - var timer = sut.CreateTimer(_ => { }, null, 1.Seconds(), Timeout.InfiniteTimeSpan); + using var timer = sut.CreateTimer(_ => { }, null, 1.Seconds(), Timeout.InfiniteTimeSpan); sut.ActiveTimers.Should().Be(1); } @@ -302,7 +302,7 @@ public void ActiveTimers_with_inactive_timers() { var sut = new ManualTimeProvider(); - var timer = sut.CreateTimer(_ => { }, null, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan); + using var timer = sut.CreateTimer(_ => { }, null, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan); sut.ActiveTimers.Should().Be(0); } @@ -312,7 +312,7 @@ public void ActiveTimers_with_after_timer_state_change() { var sut = new ManualTimeProvider(); - var timer = sut.CreateTimer(_ => { }, null, 1.Seconds(), Timeout.InfiniteTimeSpan); + using var timer = sut.CreateTimer(_ => { }, null, 1.Seconds(), Timeout.InfiniteTimeSpan); sut.Advance(1.Seconds()); sut.ActiveTimers.Should().Be(0); diff --git a/test/TimeProviderExtensions.Tests/ManualTimeProviderTimerTests.cs b/test/TimeProviderExtensions.Tests/ManualTimeProviderTimerTests.cs deleted file mode 100644 index 7165f86..0000000 --- a/test/TimeProviderExtensions.Tests/ManualTimeProviderTimerTests.cs +++ /dev/null @@ -1,238 +0,0 @@ -namespace TimeProviderExtensions; - -public class ManualTimeProviderTimerTests -{ - [Fact] - public void CreateTimer_with_positive_DueTime_and_infinite_Period() - { - var callbackCount = 0; - var dueTime = TimeSpan.FromSeconds(1); - var period = Timeout.InfiniteTimeSpan; - var sut = new ManualTimeProvider(); - using var timer = sut.CreateTimer(_ => callbackCount++, null, dueTime, period); - - sut.Advance(dueTime); - callbackCount.Should().Be(1); - - sut.Advance(dueTime); - callbackCount.Should().Be(1); - } - - [Fact] - public void CreateTimer_with_positive_DueTime_and_Period() - { - var callbackCount = 0; - var dueTime = TimeSpan.FromSeconds(1); - var period = TimeSpan.FromSeconds(2); - var sut = new ManualTimeProvider(); - using var timer = sut.CreateTimer(_ => callbackCount++, null, dueTime, period); - - sut.Advance(dueTime); - callbackCount.Should().Be(1); - - sut.Advance(period); - callbackCount.Should().Be(2); - - sut.Advance(period); - callbackCount.Should().Be(3); - } - - [Fact] - public void CreateTimer_with_infinite_DueTime_and_Period() - { - var callbackCount = 0; - var dueTime = Timeout.InfiniteTimeSpan; - var period = Timeout.InfiniteTimeSpan; - var sut = new ManualTimeProvider(); - using var timer = sut.CreateTimer(_ => callbackCount++, null, dueTime, period); - - sut.Advance(TimeSpan.FromSeconds(1)); - - callbackCount.Should().Be(0); - } - - [Fact] - public void Change_timer_from_stopped_to_started() - { - // Arrange - var callbackCount = 0; - var sut = new ManualTimeProvider(); - using var timer = sut.CreateTimer(_ => callbackCount++, null, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan); - var dueTime = TimeSpan.FromSeconds(1); - var period = TimeSpan.FromSeconds(2); - - // Act - timer.Change(dueTime, period); - - // Assert - sut.Advance(dueTime); - callbackCount.Should().Be(1); - - sut.Advance(period); - callbackCount.Should().Be(2); - - sut.Advance(period); - callbackCount.Should().Be(3); - } - - [Fact] - public void Change_timer() - { - // Arrange - var callbackCount = 0; - var sut = new ManualTimeProvider(); - var originalDueTime = TimeSpan.FromSeconds(3); - var period = TimeSpan.FromSeconds(5); - using var timer = sut.CreateTimer(_ => callbackCount++, null, originalDueTime, period); - var dueTime = TimeSpan.FromSeconds(4); - - // Change to a larger value - timer.Change(dueTime, period); - - // Assert that previous dueTime is ignored - sut.Advance(originalDueTime); - callbackCount.Should().Be(0); - - sut.Advance(dueTime); - callbackCount.Should().Be(1); - - sut.Advance(period); - callbackCount.Should().Be(2); - } - - [Fact] - public void Timer_callback_invoked_multiple_times_single_advance() - { - var sut = new ManualTimeProvider(); - var callbackCount = 0; - var dueTime = TimeSpan.FromSeconds(3); - var period = TimeSpan.FromSeconds(5); - using var timer = sut.CreateTimer(_ => callbackCount++, null, dueTime, period); - - sut.Advance(TimeSpan.FromSeconds(13)); - - callbackCount.Should().Be(3); - } - - [Fact] - public void Advancing_GetUtcNow_matches_time_at_callback_time() - { - var sut = new ManualTimeProvider(); - var startTime = sut.GetUtcNow(); - var callbackTimes = new List(); - var interval = TimeSpan.FromSeconds(3); - using var timer = sut.CreateTimer(_ => callbackTimes.Add(sut.GetUtcNow()), null, interval, interval); - - sut.Advance(interval + interval + interval); - - callbackTimes.Should().ContainInOrder( - startTime + interval, - startTime + interval + interval, - startTime + interval + interval + interval); - } - - [Fact] - public void Disposing_timer_in_callback() - { - var interval = TimeSpan.FromSeconds(3); - var sut = new ManualTimeProvider(); - ITimer timer = default!; - timer = sut.CreateTimer(_ => timer!.Dispose(), null, interval, interval); - - sut.Advance(interval); - } - - [Fact] - public void Advancing_causes_multiple_timers_invokes_callback_in_order() - { - var oneSec = TimeSpan.FromSeconds(1); - var callbacks = new List<(int TimerNumber, TimeSpan CallbackTime)>(); - var sut = new ManualTimeProvider(); - var startTime = sut.GetTimestamp(); - using var timer1 = sut.CreateTimer(_ => callbacks.Add((1, sut.GetElapsedTime(startTime))), null, TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(2)); - using var timer2 = sut.CreateTimer(_ => callbacks.Add((2, sut.GetElapsedTime(startTime))), null, TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(3)); - - sut.Advance(TimeSpan.FromSeconds(11)); - - callbacks.Should().Equal( - (1, TimeSpan.FromSeconds(2)), - (2, TimeSpan.FromSeconds(3)), - (1, TimeSpan.FromSeconds(4)), - (2, TimeSpan.FromSeconds(6)), - (1, TimeSpan.FromSeconds(6)), - (1, TimeSpan.FromSeconds(8)), - (2, TimeSpan.FromSeconds(9)), - (1, TimeSpan.FromSeconds(10))); - } - - [Fact] - public void Jumping_causes_multiple_timers_invokes_callback_in_order() - { - var oneSec = TimeSpan.FromSeconds(1); - var callbacks = new List(); - var sut = new ManualTimeProvider(); - var startTime = sut.GetTimestamp(); - using var timer1 = sut.CreateTimer(_ => callbacks.Add(1), null, TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(2)); - using var timer2 = sut.CreateTimer(_ => callbacks.Add(2), null, TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(3)); - - sut.Advance(TimeSpan.FromSeconds(11)); - - callbacks.Should().Equal(1, 2, 1, 2, 1, 1, 2, 1); - } - - [Fact] - public void Jumping_GetUtcNow_matches_jump_target() - { - var sut = new ManualTimeProvider(); - var startTime = sut.GetUtcNow(); - var callbackTimes = new List(); - var interval = TimeSpan.FromSeconds(3); - var target = interval + interval + interval; - using var timer = sut.CreateTimer(_ => callbackTimes.Add(sut.GetUtcNow()), null, interval, interval); - - sut.Jump(target); - - callbackTimes.Should().Equal(startTime + target, startTime + target, startTime + target); - } - - [Fact] - public void Jumping_past_longer_than_recurrence() - { - var sut = new ManualTimeProvider(); - var startTime = sut.GetUtcNow(); - var callbackTimes = new List(); - var interval = TimeSpan.FromSeconds(3); - var target = TimeSpan.FromSeconds(4); - using var timer = sut.CreateTimer(_ => callbackTimes.Add(sut.GetUtcNow()), null, interval, interval); - - sut.Jump(target); - - callbackTimes.Should().Equal(startTime + target); - } - - [Fact] - public void jumping_causes_multiple_timers_invokes_callback_in_order() - { - var sut = new ManualTimeProvider(); - var callbacks = new List<(int timerId, TimeSpan callbackTime)>(); - var startTime = sut.GetTimestamp(); - using var timer1 = sut.CreateTimer(_ => callbacks.Add((1, sut.GetElapsedTime(startTime))), null, TimeSpan.FromMilliseconds(3), TimeSpan.FromMilliseconds(3)); - using var timer2 = sut.CreateTimer(_ => callbacks.Add((2, sut.GetElapsedTime(startTime))), null, TimeSpan.FromMilliseconds(3), TimeSpan.FromMilliseconds(3)); - using var timer3 = sut.CreateTimer(_ => callbacks.Add((3, sut.GetElapsedTime(startTime))), null, TimeSpan.FromMilliseconds(6), TimeSpan.FromMilliseconds(5)); - - sut.Jump(TimeSpan.FromMilliseconds(3)); - sut.Jump(TimeSpan.FromMilliseconds(3)); - sut.Jump(TimeSpan.FromMilliseconds(3)); - sut.Jump(TimeSpan.FromMilliseconds(2)); - - callbacks.Should().Equal( - (1, TimeSpan.FromMilliseconds(3)), - (2, TimeSpan.FromMilliseconds(3)), - (3, TimeSpan.FromMilliseconds(6)), - (1, TimeSpan.FromMilliseconds(6)), - (2, TimeSpan.FromMilliseconds(6)), - (1, TimeSpan.FromMilliseconds(9)), - (2, TimeSpan.FromMilliseconds(9)), - (3, TimeSpan.FromMilliseconds(11))); - } -} \ No newline at end of file diff --git a/test/TimeProviderExtensions.Tests/ManualTimeProviderWaitAsyncTests.cs b/test/TimeProviderExtensions.Tests/ManualTimeProviderWaitAsyncTests.cs index 01aa7bb..ab0335f 100644 --- a/test/TimeProviderExtensions.Tests/ManualTimeProviderWaitAsyncTests.cs +++ b/test/TimeProviderExtensions.Tests/ManualTimeProviderWaitAsyncTests.cs @@ -4,9 +4,9 @@ namespace TimeProviderExtensions; public class ManualTimeProviderWaitAsyncTests { - internal const uint MaxSupportedTimeout = 0xfffffffe; - private readonly static TimeSpan DelayedTaskDelay = TimeSpan.FromMilliseconds(2); - private readonly static string StringTaskResult = Guid.NewGuid().ToString(); + private const uint MaxSupportedTimeout = 0xfffffffe; + private static readonly TimeSpan DelayedTaskDelay = TimeSpan.FromMilliseconds(2); + private static readonly string StringTaskResult = Guid.NewGuid().ToString(); private static async Task DelayedTask(TimeProvider provider) => await provider.Delay(DelayedTaskDelay); diff --git a/test/TimeProviderExtensions.Tests/ManualTimerTests.cs b/test/TimeProviderExtensions.Tests/ManualTimerTests.cs index e531d9e..1651325 100644 --- a/test/TimeProviderExtensions.Tests/ManualTimerTests.cs +++ b/test/TimeProviderExtensions.Tests/ManualTimerTests.cs @@ -137,4 +137,238 @@ public void CallbackInvokeCount_with_disposed_timer() sut.CallbackInvokeCount.Should().Be(1); } + + [Fact] + public void CreateTimer_with_positive_DueTime_and_infinite_Period() + { + var callbackCount = 0; + var dueTime = TimeSpan.FromSeconds(1); + var period = Timeout.InfiniteTimeSpan; + var sut = new ManualTimeProvider(); + using var timer = sut.CreateTimer(_ => callbackCount++, null, dueTime, period); + + sut.Advance(dueTime); + callbackCount.Should().Be(1); + + sut.Advance(dueTime); + callbackCount.Should().Be(1); + } + + [Fact] + public void CreateTimer_with_positive_DueTime_and_Period() + { + var callbackCount = 0; + var dueTime = TimeSpan.FromSeconds(1); + var period = TimeSpan.FromSeconds(2); + var sut = new ManualTimeProvider(); + using var timer = sut.CreateTimer(_ => callbackCount++, null, dueTime, period); + + sut.Advance(dueTime); + callbackCount.Should().Be(1); + + sut.Advance(period); + callbackCount.Should().Be(2); + + sut.Advance(period); + callbackCount.Should().Be(3); + } + + [Fact] + public void CreateTimer_with_infinite_DueTime_and_Period() + { + var callbackCount = 0; + var dueTime = Timeout.InfiniteTimeSpan; + var period = Timeout.InfiniteTimeSpan; + var sut = new ManualTimeProvider(); + using var timer = sut.CreateTimer(_ => callbackCount++, null, dueTime, period); + + sut.Advance(TimeSpan.FromSeconds(1)); + + callbackCount.Should().Be(0); + } + + [Fact] + public void Change_timer_from_stopped_to_started() + { + // Arrange + var callbackCount = 0; + var sut = new ManualTimeProvider(); + using var timer = sut.CreateTimer(_ => callbackCount++, null, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan); + var dueTime = TimeSpan.FromSeconds(1); + var period = TimeSpan.FromSeconds(2); + + // Act + timer.Change(dueTime, period); + + // Assert + sut.Advance(dueTime); + callbackCount.Should().Be(1); + + sut.Advance(period); + callbackCount.Should().Be(2); + + sut.Advance(period); + callbackCount.Should().Be(3); + } + + [Fact] + public void Change_timer() + { + // Arrange + var callbackCount = 0; + var sut = new ManualTimeProvider(); + var originalDueTime = TimeSpan.FromSeconds(3); + var period = TimeSpan.FromSeconds(5); + using var timer = sut.CreateTimer(_ => callbackCount++, null, originalDueTime, period); + var dueTime = TimeSpan.FromSeconds(4); + + // Change to a larger value + timer.Change(dueTime, period); + + // Assert that previous dueTime is ignored + sut.Advance(originalDueTime); + callbackCount.Should().Be(0); + + sut.Advance(dueTime); + callbackCount.Should().Be(1); + + sut.Advance(period); + callbackCount.Should().Be(2); + } + + [Fact] + public void Timer_callback_invoked_multiple_times_single_advance() + { + var sut = new ManualTimeProvider(); + var callbackCount = 0; + var dueTime = TimeSpan.FromSeconds(3); + var period = TimeSpan.FromSeconds(5); + using var timer = sut.CreateTimer(_ => callbackCount++, null, dueTime, period); + + sut.Advance(TimeSpan.FromSeconds(13)); + + callbackCount.Should().Be(3); + } + + [Fact] + public void Advancing_GetUtcNow_matches_time_at_callback_time() + { + var sut = new ManualTimeProvider(); + var startTime = sut.GetUtcNow(); + var callbackTimes = new List(); + var interval = TimeSpan.FromSeconds(3); + using var timer = sut.CreateTimer(_ => callbackTimes.Add(sut.GetUtcNow()), null, interval, interval); + + sut.Advance(interval + interval + interval); + + callbackTimes.Should().ContainInOrder( + startTime + interval, + startTime + interval + interval, + startTime + interval + interval + interval); + } + + [Fact] + public void Disposing_timer_in_callback() + { + var interval = TimeSpan.FromSeconds(3); + var sut = new ManualTimeProvider(); + ITimer timer = default!; + timer = sut.CreateTimer(_ => timer!.Dispose(), null, interval, interval); + + sut.Advance(interval); + } + + [Fact] + public void Advancing_causes_multiple_timers_invokes_callback_in_order() + { + var oneSec = TimeSpan.FromSeconds(1); + var callbacks = new List<(int TimerNumber, TimeSpan CallbackTime)>(); + var sut = new ManualTimeProvider(); + var startTime = sut.GetTimestamp(); + using var timer1 = sut.CreateTimer(_ => callbacks.Add((1, sut.GetElapsedTime(startTime))), null, TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(2)); + using var timer2 = sut.CreateTimer(_ => callbacks.Add((2, sut.GetElapsedTime(startTime))), null, TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(3)); + + sut.Advance(TimeSpan.FromSeconds(11)); + + callbacks.Should().Equal( + (1, TimeSpan.FromSeconds(2)), + (2, TimeSpan.FromSeconds(3)), + (1, TimeSpan.FromSeconds(4)), + (2, TimeSpan.FromSeconds(6)), + (1, TimeSpan.FromSeconds(6)), + (1, TimeSpan.FromSeconds(8)), + (2, TimeSpan.FromSeconds(9)), + (1, TimeSpan.FromSeconds(10))); + } + + [Fact] + public void Jumping_causes_multiple_timers_invokes_callback_in_order() + { + var oneSec = TimeSpan.FromSeconds(1); + var callbacks = new List(); + var sut = new ManualTimeProvider(); + var startTime = sut.GetTimestamp(); + using var timer1 = sut.CreateTimer(_ => callbacks.Add(1), null, TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(2)); + using var timer2 = sut.CreateTimer(_ => callbacks.Add(2), null, TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(3)); + + sut.Advance(TimeSpan.FromSeconds(11)); + + callbacks.Should().Equal(1, 2, 1, 2, 1, 1, 2, 1); + } + + [Fact] + public void Jumping_GetUtcNow_matches_jump_target() + { + var sut = new ManualTimeProvider(); + var startTime = sut.GetUtcNow(); + var callbackTimes = new List(); + var interval = TimeSpan.FromSeconds(3); + var target = interval + interval + interval; + using var timer = sut.CreateTimer(_ => callbackTimes.Add(sut.GetUtcNow()), null, interval, interval); + + sut.Jump(target); + + callbackTimes.Should().Equal(startTime + target, startTime + target, startTime + target); + } + + [Fact] + public void Jumping_past_longer_than_recurrence() + { + var sut = new ManualTimeProvider(); + var startTime = sut.GetUtcNow(); + var callbackTimes = new List(); + var interval = TimeSpan.FromSeconds(3); + var target = TimeSpan.FromSeconds(4); + using var timer = sut.CreateTimer(_ => callbackTimes.Add(sut.GetUtcNow()), null, interval, interval); + + sut.Jump(target); + + callbackTimes.Should().Equal(startTime + target); + } + + [Fact] + public void jumping_causes_multiple_timers_invokes_callback_in_order() + { + var sut = new ManualTimeProvider(); + var callbacks = new List<(int timerId, TimeSpan callbackTime)>(); + var startTime = sut.GetTimestamp(); + using var timer1 = sut.CreateTimer(_ => callbacks.Add((1, sut.GetElapsedTime(startTime))), null, TimeSpan.FromMilliseconds(3), TimeSpan.FromMilliseconds(3)); + using var timer2 = sut.CreateTimer(_ => callbacks.Add((2, sut.GetElapsedTime(startTime))), null, TimeSpan.FromMilliseconds(3), TimeSpan.FromMilliseconds(3)); + using var timer3 = sut.CreateTimer(_ => callbacks.Add((3, sut.GetElapsedTime(startTime))), null, TimeSpan.FromMilliseconds(6), TimeSpan.FromMilliseconds(5)); + + sut.Jump(TimeSpan.FromMilliseconds(3)); + sut.Jump(TimeSpan.FromMilliseconds(3)); + sut.Jump(TimeSpan.FromMilliseconds(3)); + sut.Jump(TimeSpan.FromMilliseconds(2)); + + callbacks.Should().Equal( + (1, TimeSpan.FromMilliseconds(3)), + (2, TimeSpan.FromMilliseconds(3)), + (3, TimeSpan.FromMilliseconds(6)), + (1, TimeSpan.FromMilliseconds(6)), + (2, TimeSpan.FromMilliseconds(6)), + (1, TimeSpan.FromMilliseconds(9)), + (2, TimeSpan.FromMilliseconds(9)), + (3, TimeSpan.FromMilliseconds(11))); + } }