Skip to content

Commit

Permalink
Introduce FakeTimeProvider.AdjustTime
Browse files Browse the repository at this point in the history
This addresses #5072 by allowing time to be
adjusted forwards and backwards in order to simulate
the system's clock being adjusted. Messing around
with the time in this way doesn't affect outstanding
timers.
  • Loading branch information
Martin Taillefer committed May 31, 2024
1 parent a06edbd commit d3f4338
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Threading;
using Microsoft.Shared.DiagnosticIds;
using Microsoft.Shared.Diagnostics;

namespace Microsoft.Extensions.Time.Testing;
Expand Down Expand Up @@ -86,10 +88,16 @@ public override DateTimeOffset GetUtcNow()
}

/// <summary>
/// Sets the date and time in the UTC time zone.
/// Advances the date and time in the UTC time zone.
/// </summary>
/// <param name="value">The date and time in the UTC time zone.</param>
/// <exception cref="ArgumentOutOfRangeException">if the supplied time value is before the current time.</exception>
/// <remarks>
/// This method simply advances time. If the time is set forward beyond the
/// trigger point of any outstanding timers, those timers will immediately trigger.
/// This is unlike the <see cref="AdjustTime" /> method below, which has no impact
/// on timers.
/// </remarks>
public void SetUtcNow(DateTimeOffset value)
{
lock (Waiters)
Expand Down Expand Up @@ -128,6 +136,31 @@ public void Advance(TimeSpan delta)
WakeWaiters();
}

/// <summary>
/// Advances the date and time in the UTC time zone.
/// </summary>
/// <param name="value">The date and time in the UTC time zone.</param>
/// <remarks>
/// This method updates the current time, and has no impact on outstanding
/// timers. This is similar to what happens in a real system when the system's
/// time is changed.
/// </remarks>
[Experimental(diagnosticId: DiagnosticIds.Experiments.TimeProvider, UrlFormat = DiagnosticIds.UrlFormat)]
public void AdjustTime(DateTimeOffset value)
{
lock (Waiters)
{
var delta = value - _now;
_now = value;

// adjust the wake times so they're relative to the new time value
foreach (var w in Waiters)
{
w.WakeupTime += delta.Ticks;
}
}
}

/// <inheritdoc />
public override long GetTimestamp()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<Category>Testing</Category>
<PackageTags>$(PackageTags);Testing;TimeProvider;FakeTimeProvider</PackageTags>
<InjectExperimentalAttributeOnLegacy>true</InjectExperimentalAttributeOnLegacy>
<InjectSharedDiagnosticIds>true</InjectSharedDiagnosticIds>
</PropertyGroup>

<PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,60 @@ public void TimeCannotGoBackwards()
Assert.Throws<ArgumentOutOfRangeException>(() => timeProvider.SetUtcNow(timeProvider.GetUtcNow() - TimeSpan.FromTicks(1)));
}

[Fact]
public void AdjustTimeForwardWorks()
{
var tp = new FakeTimeProvider();

var t1Tick = 0;
var t1 = tp.CreateTimer(_ =>
{
t1Tick++;
}, null, TimeSpan.FromSeconds(1), TimeSpan.Zero);

tp.AdjustTime(tp.GetUtcNow() + TimeSpan.FromMilliseconds(999));
Assert.Equal(0, t1Tick);

tp.AdjustTime(tp.GetUtcNow() + TimeSpan.FromMilliseconds(1));
Assert.Equal(0, t1Tick);

tp.AdjustTime(tp.GetUtcNow() + TimeSpan.FromSeconds(10));
Assert.Equal(0, t1Tick);

tp.Advance(TimeSpan.FromMilliseconds(999));
Assert.Equal(0, t1Tick);

tp.Advance(TimeSpan.FromMilliseconds(1));
Assert.Equal(1, t1Tick);
}

[Fact]
public void AdjustTimeBackwardWorks()
{
var tp = new FakeTimeProvider();

var t1Tick = 0;
var t1 = tp.CreateTimer(_ =>
{
t1Tick++;
}, null, TimeSpan.FromSeconds(1), TimeSpan.Zero);

tp.AdjustTime(tp.GetUtcNow() - TimeSpan.FromMilliseconds(999));
Assert.Equal(0, t1Tick);

tp.AdjustTime(tp.GetUtcNow() - TimeSpan.FromMilliseconds(1));
Assert.Equal(0, t1Tick);

tp.AdjustTime(tp.GetUtcNow() - TimeSpan.FromSeconds(10));
Assert.Equal(0, t1Tick);

tp.Advance(TimeSpan.FromMilliseconds(999));
Assert.Equal(0, t1Tick);

tp.Advance(TimeSpan.FromMilliseconds(1));
Assert.Equal(1, t1Tick);
}

[Fact]
public void ToStr()
{
Expand Down

0 comments on commit d3f4338

Please sign in to comment.