Skip to content

Commit

Permalink
feat: added StartTime to ManualTimeProvider
Browse files Browse the repository at this point in the history
  • Loading branch information
egil committed May 24, 2023
1 parent a56cb14 commit 78f50a2
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 14 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ All notable changes to TimeScheduler will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.0.0-preview.4]

- Added 'StartTime' to `ManualTestProvider`, which represents the initial date/time when the `ManualtTimeProvider` was initialized.

## [1.0.0-preview.3]

- Changed `ManualTestProvider` sets the local time zone to UTC by default, provides method for overriding during testing.
Expand Down
20 changes: 9 additions & 11 deletions src/TimeProviderExtensions/ManualTimeProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ public class ManualTimeProvider : TimeProvider
private DateTimeOffset utcNow;
private TimeZoneInfo localTimeZone = TimeZoneInfo.Utc;

/// <summary>
/// Gets the date and time which was set when this <see cref="TimeProvider"/> was initialized.
/// </summary>
public DateTimeOffset StartTime { get; }

/// <summary>
/// Gets the frequency of <see cref="GetTimestamp"/> of high-frequency value per second.
/// </summary>
Expand All @@ -44,9 +49,8 @@ public class ManualTimeProvider : TimeProvider
/// </summary>
/// <param name="localTimeZone">Optional local time zone to use during testing. Defaults to <see cref="TimeZoneInfo.Utc"/>.</param>
public ManualTimeProvider(TimeZoneInfo? localTimeZone = null)
: this(Epoch)
{
this.localTimeZone = localTimeZone ?? TimeZoneInfo.Utc;
: this(Epoch, localTimeZone)
{
}

/// <summary>
Expand All @@ -55,8 +59,8 @@ public ManualTimeProvider(TimeZoneInfo? localTimeZone = null)
/// </summary>
/// <param name="startDateTime">The initial date and time <see cref="GetUtcNow()"/> will return.</param>
public ManualTimeProvider(DateTimeOffset startDateTime)
: this(startDateTime, TimeZoneInfo.Utc)
{
utcNow = startDateTime;
}

/// <summary>
Expand All @@ -68,6 +72,7 @@ public ManualTimeProvider(DateTimeOffset startDateTime)
public ManualTimeProvider(DateTimeOffset startDateTime, TimeZoneInfo? localTimeZone = null)
{
utcNow = startDateTime;
StartTime = startDateTime;
this.localTimeZone = localTimeZone ?? TimeZoneInfo.Utc;
}

Expand Down Expand Up @@ -165,13 +170,6 @@ public void Advance(TimeSpan delta)
SetUtcNow(utcNow + delta);
}

/// <summary>
/// Advance the date and time represented by <see cref="GetUtcNow()"/>
/// by one millisecond, and triggers any scheduled items that are waiting for time to be forwarded.
/// </summary>
public void Advance()
=> Advance(TimeSpan.FromMilliseconds(1));

/// <summary>
/// Sets the date and time returned by <see cref="GetUtcNow()"/> to <paramref name="newUtcNew"/> and triggers any
/// scheduled items that are waiting for time to be forwarded.
Expand Down
11 changes: 11 additions & 0 deletions test/TimeProviderExtensions.Tests/ManualTimeProviderExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace TimeProviderExtensions;

public static class ManualTimeProviderExtensions
{
/// <summary>
/// Advance the date and time represented by <see cref="GetUtcNow()"/>
/// by one millisecond, and triggers any scheduled items that are waiting for time to be forwarded.
/// </summary>
public static void Advance(this ManualTimeProvider timeProvider)
=> timeProvider.Advance(TimeSpan.FromMilliseconds(1));
}
33 changes: 33 additions & 0 deletions test/TimeProviderExtensions.Tests/ManualTimeProviderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,37 @@ await Task.Delay(TimeSpan.FromDays(1))
}
}
#endif

#if NET8_0_OR_GREATER
[Fact]
public async Task Callbacks_happens_in_schedule_order()
{
var sut = new SutTimeProvider();
var periodicTimer = sut.CreatePeriodicTimer(TimeSpan.FromSeconds(10));
var startTime = sut.GetUtcNow();
var callbacks = new List<DateTimeOffset>();
var callbacksTask = AsyncCallbacks(periodicTimer);

sut.Advance(TimeSpan.FromSeconds(23));

callbacks.Should().ContainInOrder(
startTime + TimeSpan.FromSeconds(10),
startTime + TimeSpan.FromSeconds(13),
startTime + TimeSpan.FromSeconds(20),
startTime + TimeSpan.FromSeconds(23));

periodicTimer.Dispose();
await callbacksTask;

async Task AsyncCallbacks(PeriodicTimer periodicTimer)
{
while (await periodicTimer.WaitForNextTickAsync().ConfigureAwait(false))
{
callbacks.Add(sut.GetUtcNow());
await sut.Delay(TimeSpan.FromSeconds(3));
callbacks.Add(sut.GetUtcNow());
}
}
}
#endif
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,10 @@ public void Change_timer()
}

[Fact]
public void Timer_callback_invoked_multiple_times_single_forward()
public void Timer_callback_invoked_multiple_times_single_advance()
{
var callbackCount = 0;
var sut = new SutTimeProvider();
var callbackCount = 0;
var dueTime = TimeSpan.FromSeconds(3);
var period = TimeSpan.FromSeconds(5);
using var timer = sut.CreateTimer(_ => callbackCount++, null, dueTime, period);
Expand All @@ -131,7 +131,7 @@ public void GetUtcNow_matches_time_at_callback_time()

sut.Advance(interval + interval + interval);

callbackTimes.Should().Equal(
callbackTimes.Should().ContainInOrder(
startTime + interval,
startTime + interval + interval,
startTime + interval + interval + interval);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using SutTimeProvider = Microsoft.Extensions.Time.Testing.FakeTimeProvider;
#else
using SutTimeProvider = TimeProviderExtensions.ManualTimeProvider;
using TimeProviderExtensions;
#endif

// Licensed to the .NET Foundation under one or more agreements.
Expand Down

0 comments on commit 78f50a2

Please sign in to comment.