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 193bd6f
Show file tree
Hide file tree
Showing 4 changed files with 108 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
19 changes: 19 additions & 0 deletions test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Microsoft.Extensions.Compliance.Classification;
using Microsoft.Extensions.Logging;

public static partial class Bug5188
{
internal record User()
{
public int Id { get; set; }

[NoDataClassification]
public string? Email { get; set; }
}

internal static partial class Logging
{
[LoggerMessage(6001, LogLevel.Information, "Logging User: {User}")]
public static partial void LogUser(ILogger logger, [LogProperties] User user);

Check failure on line 17 in test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs

View check run for this annotation

Azure Pipelines / extensions-ci (Correctness WarningsCheck)

test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs#L17

test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs(17,81): error LOGGEN035: (NETCORE_ENGINEERING_TELEMETRY=Build) Parameter "user" of logging method "LogUser" has a sensitive field/property in its type (https://aka.ms/dotnet-extensions-warnings/LOGGEN035)

Check failure on line 17 in test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs

View check run for this annotation

Azure Pipelines / extensions-ci (Correctness WarningsCheck)

test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs#L17

test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs(17,36): error CS8795: (NETCORE_ENGINEERING_TELEMETRY=Build) Partial method 'Bug5188.Logging.LogUser(ILogger, Bug5188.User)' must have an implementation part because it has accessibility modifiers.

Check failure on line 17 in test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs

View check run for this annotation

Azure Pipelines / extensions-ci (Correctness WarningsCheck)

test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs#L17

test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs(17,81): error LOGGEN035: (NETCORE_ENGINEERING_TELEMETRY=Build) Parameter "user" of logging method "LogUser" has a sensitive field/property in its type (https://aka.ms/dotnet-extensions-warnings/LOGGEN035)

Check failure on line 17 in test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs

View check run for this annotation

Azure Pipelines / extensions-ci (Correctness WarningsCheck)

test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs#L17

test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs(17,36): error CS8795: (NETCORE_ENGINEERING_TELEMETRY=Build) Partial method 'Bug5188.Logging.LogUser(ILogger, Bug5188.User)' must have an implementation part because it has accessibility modifiers.

Check failure on line 17 in test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs

View check run for this annotation

Azure Pipelines / extensions-ci (Build Ubuntu)

test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs#L17

test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs(17,81): error LOGGEN035: (NETCORE_ENGINEERING_TELEMETRY=Build) Parameter "user" of logging method "LogUser" has a sensitive field/property in its type (https://aka.ms/dotnet-extensions-warnings/LOGGEN035)

Check failure on line 17 in test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs

View check run for this annotation

Azure Pipelines / extensions-ci (Build Ubuntu)

test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs#L17

test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs(17,36): error CS8795: (NETCORE_ENGINEERING_TELEMETRY=Build) Partial method 'Bug5188.Logging.LogUser(ILogger, Bug5188.User)' must have an implementation part because it has accessibility modifiers.

Check failure on line 17 in test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs

View check run for this annotation

Azure Pipelines / extensions-ci (Build Ubuntu)

test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs#L17

test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs(17,81): error LOGGEN035: (NETCORE_ENGINEERING_TELEMETRY=Build) Parameter "user" of logging method "LogUser" has a sensitive field/property in its type (https://aka.ms/dotnet-extensions-warnings/LOGGEN035)

Check failure on line 17 in test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs

View check run for this annotation

Azure Pipelines / extensions-ci (Build Ubuntu)

test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs#L17

test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs(17,36): error CS8795: (NETCORE_ENGINEERING_TELEMETRY=Build) Partial method 'Bug5188.Logging.LogUser(ILogger, Bug5188.User)' must have an implementation part because it has accessibility modifiers.

Check failure on line 17 in test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs

View check run for this annotation

Azure Pipelines / extensions-ci

test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs#L17

test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs(17,81): error LOGGEN035: (NETCORE_ENGINEERING_TELEMETRY=Build) Parameter "user" of logging method "LogUser" has a sensitive field/property in its type (https://aka.ms/dotnet-extensions-warnings/LOGGEN035)

Check failure on line 17 in test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs

View check run for this annotation

Azure Pipelines / extensions-ci

test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs#L17

test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs(17,36): error CS8795: (NETCORE_ENGINEERING_TELEMETRY=Build) Partial method 'Bug5188.Logging.LogUser(ILogger, Bug5188.User)' must have an implementation part because it has accessibility modifiers.

Check failure on line 17 in test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs

View check run for this annotation

Azure Pipelines / extensions-ci

test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs#L17

test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs(17,81): error LOGGEN035: (NETCORE_ENGINEERING_TELEMETRY=Build) Parameter "user" of logging method "LogUser" has a sensitive field/property in its type (https://aka.ms/dotnet-extensions-warnings/LOGGEN035)

Check failure on line 17 in test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs

View check run for this annotation

Azure Pipelines / extensions-ci

test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs#L17

test/Generators/Microsoft.Gen.Logging/TestClasses/Bug5188.cs(17,36): error CS8795: (NETCORE_ENGINEERING_TELEMETRY=Build) Partial method 'Bug5188.Logging.LogUser(ILogger, Bug5188.User)' must have an implementation part because it has accessibility modifiers.
}
}
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 193bd6f

Please sign in to comment.