diff --git a/test/Polly.Core.Tests/CircuitBreaker/CircuitBreakerResilienceStrategyBuilderTests.cs b/test/Polly.Core.Tests/CircuitBreaker/CircuitBreakerResilienceStrategyBuilderTests.cs index bc7ddaeb098..cda5b215ee7 100644 --- a/test/Polly.Core.Tests/CircuitBreaker/CircuitBreakerResilienceStrategyBuilderTests.cs +++ b/test/Polly.Core.Tests/CircuitBreaker/CircuitBreakerResilienceStrategyBuilderTests.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using Microsoft.Extensions.Time.Testing; using Polly.CircuitBreaker; namespace Polly.Core.Tests.CircuitBreaker; @@ -100,10 +101,8 @@ public void AddCircuitBreaker_IntegrationTest() OnHalfOpened = (_) => { halfOpened++; return default; } }; - var timeProvider = new MockTimeProvider(); - var strategy = new ResilienceStrategyBuilder { TimeProvider = timeProvider.Object }.AddSimpleCircuitBreaker(options).Build(); - var time = DateTime.UtcNow; - timeProvider.Setup(v => v.GetUtcNow()).Returns(() => time); + var timeProvider = new FakeTimeProvider(); + var strategy = new ResilienceStrategyBuilder { TimeProvider = timeProvider }.AddSimpleCircuitBreaker(options).Build(); for (int i = 0; i < options.FailureThreshold; i++) { @@ -117,7 +116,7 @@ public void AddCircuitBreaker_IntegrationTest() Assert.Throws>(() => strategy.Execute(_ => 0)); // Circuit Half Opened - time += options.BreakDuration; + timeProvider.Advance(options.BreakDuration); strategy.Execute(_ => -1); Assert.Throws>(() => strategy.Execute(_ => 0)); opened.Should().Be(2); @@ -125,7 +124,7 @@ public void AddCircuitBreaker_IntegrationTest() closed.Should().Be(0); // Now close it - time += options.BreakDuration; + timeProvider.Advance(options.BreakDuration); strategy.Execute(_ => 0); opened.Should().Be(2); halfOpened.Should().Be(2); @@ -151,10 +150,8 @@ public void AddAdvancedCircuitBreaker_IntegrationTest() OnHalfOpened = (_) => { halfOpened++; return default; } }; - var timeProvider = new MockTimeProvider(); - var strategy = new ResilienceStrategyBuilder { TimeProvider = timeProvider.Object }.AddAdvancedCircuitBreaker(options).Build(); - var time = DateTime.UtcNow; - timeProvider.Setup(v => v.GetUtcNow()).Returns(() => time); + var timeProvider = new FakeTimeProvider(); + var strategy = new ResilienceStrategyBuilder { TimeProvider = timeProvider }.AddAdvancedCircuitBreaker(options).Build(); for (int i = 0; i < 10; i++) { @@ -168,7 +165,7 @@ public void AddAdvancedCircuitBreaker_IntegrationTest() Assert.Throws>(() => strategy.Execute(_ => 0)); // Circuit Half Opened - time += options.BreakDuration; + timeProvider.Advance(options.BreakDuration); strategy.Execute(_ => -1); Assert.Throws>(() => strategy.Execute(_ => 0)); opened.Should().Be(2); @@ -176,7 +173,7 @@ public void AddAdvancedCircuitBreaker_IntegrationTest() closed.Should().Be(0); // Now close it - time += options.BreakDuration; + timeProvider.Advance(options.BreakDuration); strategy.Execute(_ => 0); opened.Should().Be(2); halfOpened.Should().Be(2); diff --git a/test/Polly.Core.Tests/CircuitBreaker/CircuitBreakerResilienceStrategyTests.cs b/test/Polly.Core.Tests/CircuitBreaker/CircuitBreakerResilienceStrategyTests.cs index 1bf59f1fe55..da0e69080c8 100644 --- a/test/Polly.Core.Tests/CircuitBreaker/CircuitBreakerResilienceStrategyTests.cs +++ b/test/Polly.Core.Tests/CircuitBreaker/CircuitBreakerResilienceStrategyTests.cs @@ -1,3 +1,4 @@ +using Microsoft.Extensions.Time.Testing; using Moq; using Polly.CircuitBreaker; using Polly.Telemetry; @@ -6,7 +7,7 @@ namespace Polly.Core.Tests.CircuitBreaker; public class CircuitBreakerResilienceStrategyTests : IDisposable { - private readonly MockTimeProvider _timeProvider; + private readonly FakeTimeProvider _timeProvider; private readonly Mock _behavior; private readonly ResilienceStrategyTelemetry _telemetry; private readonly SimpleCircuitBreakerStrategyOptions _options; @@ -14,8 +15,7 @@ public class CircuitBreakerResilienceStrategyTests : IDisposable public CircuitBreakerResilienceStrategyTests() { - _timeProvider = new MockTimeProvider(); - _timeProvider.Setup(v => v.GetUtcNow()).Returns(DateTime.UtcNow); + _timeProvider = new FakeTimeProvider(); _behavior = new Mock(MockBehavior.Strict); _telemetry = TestUtilities.CreateResilienceTelemetry(Mock.Of()); _options = new SimpleCircuitBreakerStrategyOptions(); @@ -25,7 +25,7 @@ public CircuitBreakerResilienceStrategyTests() null, null, _behavior.Object, - _timeProvider.Object, + _timeProvider, _telemetry); } diff --git a/test/Polly.Core.Tests/CircuitBreaker/Controller/CircuitStateControllerTests.cs b/test/Polly.Core.Tests/CircuitBreaker/Controller/CircuitStateControllerTests.cs index ecd8be08423..b91f2fb830b 100644 --- a/test/Polly.Core.Tests/CircuitBreaker/Controller/CircuitStateControllerTests.cs +++ b/test/Polly.Core.Tests/CircuitBreaker/Controller/CircuitStateControllerTests.cs @@ -1,17 +1,17 @@ +using Microsoft.Extensions.Time.Testing; using Moq; using Polly.CircuitBreaker; using Polly.Telemetry; namespace Polly.Core.Tests.CircuitBreaker.Controller; + public class CircuitStateControllerTests { - private readonly MockTimeProvider _timeProvider = new(); + private readonly FakeTimeProvider _timeProvider = new(); + private readonly CircuitBreakerStrategyOptions _options = new SimpleCircuitBreakerStrategyOptions(); private readonly Mock _circuitBehavior = new(MockBehavior.Strict); private readonly Action _onTelemetry = _ => { }; - private DateTimeOffset _utcNow = DateTimeOffset.UtcNow; - - public CircuitStateControllerTests() => _timeProvider.Setup(v => v.GetUtcNow()).Returns(() => _utcNow); [Fact] public void Ctor_EnsureDefaults() @@ -41,7 +41,7 @@ public async Task IsolateAsync_Ok() return default; }; - _timeProvider.Setup(v => v.GetUtcNow()).Returns(DateTime.UtcNow); + _timeProvider.Advance(TimeSpan.FromSeconds(1)); using var controller = CreateController(); var context = ResilienceContext.Get(); @@ -79,7 +79,7 @@ public async Task BreakAsync_Ok() return default; }; - _timeProvider.Setup(v => v.GetUtcNow()).Returns(DateTime.UtcNow); + _timeProvider.Advance(TimeSpan.FromSeconds(1)); using var controller = CreateController(); await controller.IsolateCircuitAsync(ResilienceContext.Get()); _circuitBehavior.Setup(v => v.OnCircuitClosed()); @@ -123,7 +123,7 @@ public async Task OnActionPreExecute_CircuitOpenedByValue() error.Should().BeOfType>(); error.Result.Should().Be(99); - GetBlockedTill(controller).Should().Be(_utcNow + _options.BreakDuration); + GetBlockedTill(controller).Should().Be(_timeProvider.GetUtcNow() + _options.BreakDuration); } [Fact] @@ -132,7 +132,7 @@ public async Task HalfOpen_EnsureBreakDuration() using var controller = CreateController(); await TransitionToState(controller, CircuitState.HalfOpen); - GetBlockedTill(controller).Should().Be(_utcNow + _options.BreakDuration); + GetBlockedTill(controller).Should().Be(_timeProvider.GetUtcNow() + _options.BreakDuration); } [InlineData(true)] @@ -316,12 +316,14 @@ public async Task OnActionFailureAsync_EnsureBreakDurationNotOverflow(bool overf using var controller = CreateController(); var shouldBreak = true; await TransitionToState(controller, CircuitState.HalfOpen); - _utcNow = DateTime.MaxValue - _options.BreakDuration; + var utcNow = DateTime.MaxValue - _options.BreakDuration; if (overflow) { - _utcNow += TimeSpan.FromMilliseconds(10); + utcNow += TimeSpan.FromMilliseconds(10); } + _timeProvider.SetUtcNow(utcNow); + _circuitBehavior.Setup(v => v.OnActionFailure(CircuitState.HalfOpen, out shouldBreak)); // act @@ -336,7 +338,7 @@ public async Task OnActionFailureAsync_EnsureBreakDurationNotOverflow(bool overf } else { - blockedTill.Should().Be(_utcNow + _options.BreakDuration); + blockedTill.Should().Be(utcNow + _options.BreakDuration); } } @@ -433,7 +435,7 @@ private async Task OpenCircuit(CircuitStateController controller, Outcome(true)); } - private void AdvanceTime(TimeSpan timespan) => _utcNow += timespan; + private void AdvanceTime(TimeSpan timespan) => _timeProvider.Advance(timespan); private CircuitStateController CreateController() => new( _options.BreakDuration, @@ -441,6 +443,6 @@ private async Task OpenCircuit(CircuitStateController controller, Outcome _onTelemetry.Invoke(args))); + _timeProvider, + TestUtilities.CreateResilienceTelemetry(_onTelemetry.Invoke)); } diff --git a/test/Polly.Core.Tests/CircuitBreaker/Health/RollingHealthMetricsTests.cs b/test/Polly.Core.Tests/CircuitBreaker/Health/RollingHealthMetricsTests.cs index 863b4b4d187..2c4d5d8005d 100644 --- a/test/Polly.Core.Tests/CircuitBreaker/Health/RollingHealthMetricsTests.cs +++ b/test/Polly.Core.Tests/CircuitBreaker/Health/RollingHealthMetricsTests.cs @@ -1,13 +1,15 @@ +using Microsoft.Extensions.Time.Testing; using Polly.CircuitBreaker.Health; namespace Polly.Core.Tests.CircuitBreaker.Health; + public class RollingHealthMetricsTests { - private readonly MockTimeProvider _timeProvider; + private readonly FakeTimeProvider _timeProvider; private readonly TimeSpan _samplingDuration = TimeSpan.FromSeconds(10); private readonly short _windows = 10; - public RollingHealthMetricsTests() => _timeProvider = new MockTimeProvider().SetupUtcNow(); + public RollingHealthMetricsTests() => _timeProvider = new FakeTimeProvider(); [Fact] public void Ctor_EnsureDefaults() @@ -53,11 +55,11 @@ public void GetHealthInfo_EnsureWindowRespected() } metrics.IncrementSuccess(); - _timeProvider.AdvanceTime(TimeSpan.FromSeconds(2)); + _timeProvider.Advance(TimeSpan.FromSeconds(2)); health.Add(metrics.GetHealthInfo()); } - _timeProvider.AdvanceTime(TimeSpan.FromSeconds(2)); + _timeProvider.Advance(TimeSpan.FromSeconds(2)); health.Add(metrics.GetHealthInfo()); health[0].Should().Be(new HealthInfo(2, 0.5)); @@ -76,11 +78,11 @@ public void GetHealthInfo_EnsureWindowCapacityRespected() for (int i = 0; i < _windows; i++) { metrics.IncrementSuccess(); - _timeProvider.AdvanceTime(delay); + _timeProvider.Advance(delay); } metrics.GetHealthInfo().Throughput.Should().Be(9); - _timeProvider.AdvanceTime(delay); + _timeProvider.Advance(delay); metrics.GetHealthInfo().Throughput.Should().Be(8); } @@ -105,10 +107,10 @@ public void GetHealthInfo_SamplingDurationRespected(bool variance) metrics.IncrementSuccess(); metrics.IncrementSuccess(); - _timeProvider.AdvanceTime(_samplingDuration + (variance ? TimeSpan.FromMilliseconds(1) : TimeSpan.Zero)); + _timeProvider.Advance(_samplingDuration + (variance ? TimeSpan.FromMilliseconds(1) : TimeSpan.Zero)); metrics.GetHealthInfo().Should().Be(new HealthInfo(0, 0)); } - private RollingHealthMetrics Create() => new(_samplingDuration, _windows, _timeProvider.Object); + private RollingHealthMetrics Create() => new(_samplingDuration, _windows, _timeProvider); } diff --git a/test/Polly.Core.Tests/CircuitBreaker/Health/SingleHealthMetricsTests.cs b/test/Polly.Core.Tests/CircuitBreaker/Health/SingleHealthMetricsTests.cs index 0c8d916db25..6ded9ea4e2f 100644 --- a/test/Polly.Core.Tests/CircuitBreaker/Health/SingleHealthMetricsTests.cs +++ b/test/Polly.Core.Tests/CircuitBreaker/Health/SingleHealthMetricsTests.cs @@ -1,16 +1,18 @@ +using Microsoft.Extensions.Time.Testing; using Polly.CircuitBreaker.Health; namespace Polly.Core.Tests.CircuitBreaker.Health; + public class SingleHealthMetricsTests { - private readonly MockTimeProvider _timeProvider; + private readonly FakeTimeProvider _timeProvider; - public SingleHealthMetricsTests() => _timeProvider = new MockTimeProvider().SetupUtcNow(); + public SingleHealthMetricsTests() => _timeProvider = new FakeTimeProvider(); [Fact] public void Ctor_EnsureDefaults() { - var metrics = new SingleHealthMetrics(TimeSpan.FromMilliseconds(100), _timeProvider.Object); + var metrics = new SingleHealthMetrics(TimeSpan.FromMilliseconds(100), _timeProvider); var health = metrics.GetHealthInfo(); health.FailureRate.Should().Be(0); @@ -20,7 +22,7 @@ public void Ctor_EnsureDefaults() [Fact] public void Increment_Ok() { - var metrics = new SingleHealthMetrics(TimeSpan.FromMilliseconds(100), _timeProvider.Object); + var metrics = new SingleHealthMetrics(TimeSpan.FromMilliseconds(100), _timeProvider); metrics.IncrementFailure(); metrics.IncrementSuccess(); @@ -37,7 +39,7 @@ public void Increment_Ok() [Fact] public void Reset_Ok() { - var metrics = new SingleHealthMetrics(TimeSpan.FromMilliseconds(100), _timeProvider.Object); + var metrics = new SingleHealthMetrics(TimeSpan.FromMilliseconds(100), _timeProvider); metrics.IncrementSuccess(); metrics.Reset(); @@ -48,12 +50,12 @@ public void Reset_Ok() [Fact] public void SamplingDurationRespected_Ok() { - var metrics = new SingleHealthMetrics(TimeSpan.FromMilliseconds(100), _timeProvider.Object); + var metrics = new SingleHealthMetrics(TimeSpan.FromMilliseconds(100), _timeProvider); metrics.IncrementSuccess(); metrics.IncrementSuccess(); - _timeProvider.AdvanceTime(TimeSpan.FromMilliseconds(100)); + _timeProvider.Advance(TimeSpan.FromMilliseconds(100)); metrics.GetHealthInfo().Throughput.Should().Be(0); } diff --git a/test/Polly.Core.Tests/GenericResilienceStrategyBuilderTests.cs b/test/Polly.Core.Tests/GenericResilienceStrategyBuilderTests.cs index 08c379572d0..07462ca655d 100644 --- a/test/Polly.Core.Tests/GenericResilienceStrategyBuilderTests.cs +++ b/test/Polly.Core.Tests/GenericResilienceStrategyBuilderTests.cs @@ -1,3 +1,4 @@ +using Microsoft.Extensions.Time.Testing; using Polly.Utils; namespace Polly.Core.Tests; @@ -28,7 +29,7 @@ public void Properties_GetSet_Ok() _builder.BuilderName = "dummy"; _builder.BuilderName.Should().Be("dummy"); - var timeProvider = new MockTimeProvider().Object; + var timeProvider = new FakeTimeProvider(); _builder.TimeProvider = timeProvider; _builder.TimeProvider.Should().Be(timeProvider); diff --git a/test/Polly.Core.Tests/Helpers/FakeTimeProvider.cs b/test/Polly.Core.Tests/Helpers/FakeTimeProvider.cs new file mode 100644 index 00000000000..e436d536d0a --- /dev/null +++ b/test/Polly.Core.Tests/Helpers/FakeTimeProvider.cs @@ -0,0 +1,279 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#pragma warning disable + +// Replace with Microsoft.Extensions.TimeProvider.Testing when TimeProvider is used (see https://github.com/App-vNext/Polly/pull/1144) +// Based on https://github.com/dotnet/extensions/blob/14917b87e8fc81f10d44ceea52d9b24e50e26550/src/Libraries/Microsoft.Extensions.TimeProvider.Testing/FakeTimeProvider.cs + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Threading; +using Microsoft.Extensions.Time.Testing; + +namespace Microsoft.Extensions.Time.Testing; + +/// +/// A synthetic time provider used to enable deterministic behavior in tests. +/// +internal class FakeTimeProvider : TimeProvider +{ + internal readonly HashSet Waiters = new(); + private DateTimeOffset _now = new(2000, 1, 1, 0, 0, 0, 0, TimeSpan.Zero); + private TimeZoneInfo _localTimeZone = TimeZoneInfo.Utc; + private int _wakeWaitersGate; + private TimeSpan _autoAdvanceAmount; + + /// + /// Initializes a new instance of the class. + /// + /// + /// This creates a provider whose time is initially set to midnight January 1st 2000. + /// The provider is set to not automatically advance time each time it is read. + /// + public FakeTimeProvider() + { + Start = _now; + } + + /// + /// Initializes a new instance of the class. + /// + /// The initial time and date reported by the provider. + /// + /// The provider is set to not automatically advance time each time it is read. + /// + public FakeTimeProvider(DateTimeOffset startDateTime) + { + _now = startDateTime; + Start = _now; + } + + /// + /// Gets the starting date and time for this provider. + /// + public DateTimeOffset Start { get; } + + /// + /// Gets or sets the amount of time by which time advances whenever the clock is read. + /// + /// + /// This defaults to . + /// + public TimeSpan AutoAdvanceAmount + { + get => _autoAdvanceAmount; + set + { + _autoAdvanceAmount = value; + } + } + + /// + public override DateTimeOffset GetUtcNow() + { + DateTimeOffset result; + + lock (Waiters) + { + result = _now; + _now += _autoAdvanceAmount; + } + + WakeWaiters(); + return result; + } + + /// + /// Sets the date and time in the UTC time zone. + /// + /// The date and time in the UTC time zone. + public void SetUtcNow(DateTimeOffset value) + { + lock (Waiters) + { + if (value < _now) + { + throw new ArgumentOutOfRangeException(nameof(value), $"Cannot go back in time. Current time is {_now}."); + } + + _now = value; + } + + WakeWaiters(); + } + + /// + /// Advances time by a specific amount. + /// + /// The amount of time to advance the clock by. + /// + /// Advancing time affects the timers created from this provider, and all other operations that are directly or + /// indirectly using this provider as a time source. Whereas when using , time + /// marches forward automatically in hardware, for the fake time provider the application is responsible for + /// doing this explicitly by calling this method. + /// + public void Advance(TimeSpan delta) + { + lock (Waiters) + { + _now += delta; + } + + WakeWaiters(); + } + + /// + public override long GetTimestamp() + { + // Notionally we're multiplying by frequency and dividing by ticks per second, + // which are the same value for us. Don't actually do the math as the full + // precision of ticks (a long) cannot be represented in a double during division. + // For test stability we want a reproducible result. + // + // The same issue could occur converting back, in GetElapsedTime(). Unfortunately + // that isn't virtual so we can't do the same trick. However, if tests advance + // the clock in multiples of 1ms or so this loss of precision will not be visible. + Debug.Assert(TimestampFrequency == TimeSpan.TicksPerSecond, "Assuming frequency equals ticks per second"); + return _now.Ticks; + } + + /// + public override TimeZoneInfo LocalTimeZone => _localTimeZone; + + /// + /// Sets the local time zone. + /// + /// The local time zone. + public void SetLocalTimeZone(TimeZoneInfo localTimeZone) => _localTimeZone = localTimeZone; + + /// + /// Gets the amount by which the value from increments per second. + /// + /// + /// This is fixed to the value of . + /// + public override long TimestampFrequency => TimeSpan.TicksPerSecond; + + /// + /// Returns a string representation this provider's idea of current time. + /// + /// A string representing the provider's current time. + public override string ToString() => GetUtcNow().ToString("yyyy-MM-ddTHH:mm:ss.fff", CultureInfo.InvariantCulture); + + internal void RemoveWaiter(Waiter waiter) + { + lock (Waiters) + { + _ = Waiters.Remove(waiter); + } + } + + internal void AddWaiter(Waiter waiter, long dueTime) + { + lock (Waiters) + { + waiter.ScheduledOn = _now.Ticks; + waiter.WakeupTime = _now.Ticks + dueTime; + _ = Waiters.Add(waiter); + } + + WakeWaiters(); + } + + private void WakeWaiters() + { + if (Interlocked.CompareExchange(ref _wakeWaitersGate, 1, 0) == 1) + { + // some other thread is already in here, so let it take care of things + return; + } + + while (true) + { + Waiter? candidate = null; + lock (Waiters) + { + // find an expired waiter + foreach (var waiter in Waiters) + { + if (waiter.WakeupTime > _now.Ticks) + { + // not expired yet + } + else if (candidate is null) + { + // our first candidate + candidate = waiter; + } + else if (waiter.WakeupTime < candidate.WakeupTime) + { + // found a waiter with an earlier wake time, it's our new candidate + candidate = waiter; + } + else if (waiter.WakeupTime > candidate.WakeupTime) + { + // the waiter has a later wake time, so keep the current candidate + } + else if (waiter.ScheduledOn < candidate.ScheduledOn) + { + // the new waiter has the same wake time aa the candidate, pick whichever was scheduled earliest to maintain order + candidate = waiter; + } + } + } + + if (candidate == null) + { + // didn't find a candidate to wake, we're done + _wakeWaitersGate = 0; + return; + } + + // invoke the callback + candidate.InvokeCallback(); + + // see if we need to reschedule the waiter + if (candidate.Period > 0) + { + // update the waiter's state + candidate.ScheduledOn = _now.Ticks; + candidate.WakeupTime += candidate.Period; + } + else + { + // this waiter is never running again, so remove from the set. + RemoveWaiter(candidate); + } + } + } +} + +// We keep all timer state here in order to prevent Timer instances from being self-referential, +// which would block them being collected when someone forgets to call Dispose on the timer. With +// this arrangement, the Timer object will always be collectible, which will end up calling Dispose +// on this object due to the timer's finalizer. +internal sealed class Waiter +{ + private readonly TimerCallback _callback; + private readonly object? _state; + + public long ScheduledOn { get; set; } = -1; + public long WakeupTime { get; set; } = -1; + public long Period { get; } + + public Waiter(TimerCallback callback, object? state, long period) + { + _callback = callback; + _state = state; + Period = period; + } + + public void InvokeCallback() + { + _callback(_state); + } +} diff --git a/test/Polly.Core.Tests/Issues/IssuesTests.CircuitBreakerStateSharing_959.cs b/test/Polly.Core.Tests/Issues/IssuesTests.CircuitBreakerStateSharing_959.cs index 77e7a827bd4..712667c2416 100644 --- a/test/Polly.Core.Tests/Issues/IssuesTests.CircuitBreakerStateSharing_959.cs +++ b/test/Polly.Core.Tests/Issues/IssuesTests.CircuitBreakerStateSharing_959.cs @@ -23,7 +23,7 @@ public void CircuitBreakerStateSharing_959() }; // create the strategy - var strategy = new ResilienceStrategyBuilder { TimeProvider = TimeProvider.Object }.AddAdvancedCircuitBreaker(options).Build(); + var strategy = new ResilienceStrategyBuilder { TimeProvider = TimeProvider }.AddAdvancedCircuitBreaker(options).Build(); // now trigger the circuit breaker by evaluating multiple result types for (int i = 0; i < 5; i++) @@ -37,7 +37,7 @@ public void CircuitBreakerStateSharing_959() strategy.Invoking(s => s.Execute(_ => "valid-result")).Should().Throw(); // now wait for recovery - TimeProvider.AdvanceTime(options.BreakDuration); + TimeProvider.Advance(options.BreakDuration); // OK, circuit is closed now strategy.Execute(_ => 0); diff --git a/test/Polly.Core.Tests/Issues/IssuesTests.HandleMultipleResults_898.cs b/test/Polly.Core.Tests/Issues/IssuesTests.HandleMultipleResults_898.cs index 9e571250d75..23bebf5f910 100644 --- a/test/Polly.Core.Tests/Issues/IssuesTests.HandleMultipleResults_898.cs +++ b/test/Polly.Core.Tests/Issues/IssuesTests.HandleMultipleResults_898.cs @@ -31,7 +31,7 @@ public void HandleMultipleResults_898() }; // create the strategy - var strategy = new ResilienceStrategyBuilder { TimeProvider = TimeProvider.Object }.AddRetry(options).Build(); + var strategy = new ResilienceStrategyBuilder { TimeProvider = TimeProvider }.AddRetry(options).Build(); // check that int-based results is retried bool isRetry = false; diff --git a/test/Polly.Core.Tests/Issues/IssuesTests.cs b/test/Polly.Core.Tests/Issues/IssuesTests.cs index d42f8581d31..7c9074896bc 100644 --- a/test/Polly.Core.Tests/Issues/IssuesTests.cs +++ b/test/Polly.Core.Tests/Issues/IssuesTests.cs @@ -1,6 +1,8 @@ +using Microsoft.Extensions.Time.Testing; + namespace Polly.Core.Tests.Issues; public partial class IssuesTests { - private MockTimeProvider TimeProvider { get; } = new MockTimeProvider().SetupUtcNow().SetupAnyDelay().SetupGetTimestamp().SetupTimestampFrequency(); + private FakeTimeProvider TimeProvider { get; } = new FakeTimeProvider(); } diff --git a/test/Polly.Core.Tests/ResilienceStrategyBuilderContextTests.cs b/test/Polly.Core.Tests/ResilienceStrategyBuilderContextTests.cs index 1e5a44627d4..9320693bbfc 100644 --- a/test/Polly.Core.Tests/ResilienceStrategyBuilderContextTests.cs +++ b/test/Polly.Core.Tests/ResilienceStrategyBuilderContextTests.cs @@ -1,3 +1,4 @@ +using Microsoft.Extensions.Time.Testing; using Moq; namespace Polly.Core.Tests; @@ -8,15 +9,15 @@ public class ResilienceStrategyBuilderContextTests public void Ctor_EnsureDefaults() { var properties = new ResilienceProperties(); - var timeProvider = new MockTimeProvider(); - var context = new ResilienceStrategyBuilderContext("builder-name", properties, "strategy-name", "strategy-type", timeProvider.Object, true, Mock.Of(), () => 1.0); + var timeProvider = new FakeTimeProvider(); + var context = new ResilienceStrategyBuilderContext("builder-name", properties, "strategy-name", "strategy-type", timeProvider, true, Mock.Of(), () => 1.0); context.IsGenericBuilder.Should().BeTrue(); context.BuilderName.Should().Be("builder-name"); context.BuilderProperties.Should().BeSameAs(properties); context.StrategyName.Should().Be("strategy-name"); context.StrategyType.Should().Be("strategy-type"); - context.TimeProvider.Should().Be(timeProvider.Object); + context.TimeProvider.Should().Be(timeProvider); context.Telemetry.Should().NotBeNull(); context.Randomizer.Should().NotBeNull(); diff --git a/test/Polly.Core.Tests/ResilienceStrategyBuilderTests.cs b/test/Polly.Core.Tests/ResilienceStrategyBuilderTests.cs index dad6c319737..892d6f16a03 100644 --- a/test/Polly.Core.Tests/ResilienceStrategyBuilderTests.cs +++ b/test/Polly.Core.Tests/ResilienceStrategyBuilderTests.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using Microsoft.Extensions.Time.Testing; using Moq; using Polly.Utils; @@ -268,7 +269,7 @@ public void BuildStrategy_EnsureCorrectContext() var builder = new ResilienceStrategyBuilder { BuilderName = "builder-name", - TimeProvider = new MockTimeProvider().Object, + TimeProvider = new FakeTimeProvider(), }; builder.AddStrategy(