diff --git a/src/Polly.Core/PublicAPI.Unshipped.txt b/src/Polly.Core/PublicAPI.Unshipped.txt index 25636288b2c..4045c01b593 100644 --- a/src/Polly.Core/PublicAPI.Unshipped.txt +++ b/src/Polly.Core/PublicAPI.Unshipped.txt @@ -7,6 +7,6 @@ Polly.CircuitBreaker.BreakDurationGeneratorArguments.FailureCount.get -> int Polly.CircuitBreaker.BreakDurationGeneratorArguments.FailureRate.get -> double Polly.CircuitBreaker.CircuitBreakerStrategyOptions.BreakDurationGenerator.get -> System.Func>? Polly.CircuitBreaker.CircuitBreakerStrategyOptions.BreakDurationGenerator.set -> void -Polly.ResiliencePipelineBuilderBase.TimeProvider.get -> System.TimeProvider! +Polly.ResiliencePipelineBuilderBase.TimeProvider.get -> System.TimeProvider? Polly.ResiliencePipelineBuilderBase.TimeProvider.set -> void Polly.StrategyBuilderContext.TimeProvider.get -> System.TimeProvider! diff --git a/src/Polly.Core/Registry/RegistryPipelineComponentBuilder.cs b/src/Polly.Core/Registry/RegistryPipelineComponentBuilder.cs index 6a3c0a00757..aecef2fdab1 100644 --- a/src/Polly.Core/Registry/RegistryPipelineComponentBuilder.cs +++ b/src/Polly.Core/Registry/RegistryPipelineComponentBuilder.cs @@ -59,7 +59,7 @@ private Builder CreateBuilder() builder.InstanceName = _instanceName; _configure(builder, context); - var timeProvider = builder.TimeProvider; + var timeProvider = builder.GetTimeProvider(); var telemetry = new ResilienceStrategyTelemetry( new ResilienceTelemetrySource(builder.Name, builder.InstanceName, null), builder.TelemetryListener); diff --git a/src/Polly.Core/ResiliencePipelineBuilderBase.cs b/src/Polly.Core/ResiliencePipelineBuilderBase.cs index 61fb7ca0a8c..f6d587f7fca 100644 --- a/src/Polly.Core/ResiliencePipelineBuilderBase.cs +++ b/src/Polly.Core/ResiliencePipelineBuilderBase.cs @@ -71,10 +71,9 @@ private protected ResiliencePipelineBuilderBase(ResiliencePipelineBuilderBase ot /// Gets or sets a that is used by strategies that work with time. /// /// - /// The default value is . + /// The default value is and unless set, the will be used. /// - [Required] - public TimeProvider TimeProvider { get; set; } = TimeProvider.System; + public TimeProvider? TimeProvider { get; set; } /// /// Gets or sets the that is used by Polly to report resilience events. @@ -129,13 +128,15 @@ internal PipelineComponent BuildPipelineComponent() var source = new ResilienceTelemetrySource(Name, InstanceName, null); - return PipelineComponentFactory.CreateComposite(components, new ResilienceStrategyTelemetry(source, TelemetryListener), TimeProvider); + return PipelineComponentFactory.CreateComposite(components, new ResilienceStrategyTelemetry(source, TelemetryListener), GetTimeProvider()); } + internal TimeProvider GetTimeProvider() => TimeProvider ?? TimeProvider.System; + private PipelineComponent CreateComponent(Entry entry) { var source = new ResilienceTelemetrySource(Name, InstanceName, entry.Options.Name); - var context = new StrategyBuilderContext(new ResilienceStrategyTelemetry(source, TelemetryListener), TimeProvider); + var context = new StrategyBuilderContext(new ResilienceStrategyTelemetry(source, TelemetryListener), GetTimeProvider()); var strategy = entry.Factory(context); strategy.Options = entry.Options; diff --git a/src/Polly.Extensions/DependencyInjection/PollyServiceCollectionExtensions.cs b/src/Polly.Extensions/DependencyInjection/PollyServiceCollectionExtensions.cs index b6fdf2aa2ef..d345ec7ce41 100644 --- a/src/Polly.Extensions/DependencyInjection/PollyServiceCollectionExtensions.cs +++ b/src/Polly.Extensions/DependencyInjection/PollyServiceCollectionExtensions.cs @@ -245,6 +245,12 @@ private static void AddResiliencePipelineBuilder(this IServiceCollection service { var builder = new ResiliencePipelineBuilder(); builder.ConfigureTelemetry(serviceProvider.GetRequiredService>().Value); + + if (serviceProvider.GetService() is { } timeProvider) + { + builder.TimeProvider = timeProvider; + } + return builder; }); } diff --git a/src/Polly.Testing/PublicAPI.Unshipped.txt b/src/Polly.Testing/PublicAPI.Unshipped.txt index 6acf8af9d2a..ab058de62d4 100644 --- a/src/Polly.Testing/PublicAPI.Unshipped.txt +++ b/src/Polly.Testing/PublicAPI.Unshipped.txt @@ -1,3 +1 @@ #nullable enable -Polly.TestingResiliencePipelineBuilderExtensions -static Polly.TestingResiliencePipelineBuilderExtensions.WithTimeProvider(this TBuilder! builder, System.TimeProvider! timeProvider) -> TBuilder! diff --git a/src/Polly.Testing/TestingResiliencePipelineBuilderExtensions.cs b/src/Polly.Testing/TestingResiliencePipelineBuilderExtensions.cs deleted file mode 100644 index dac9d5a1f4c..00000000000 --- a/src/Polly.Testing/TestingResiliencePipelineBuilderExtensions.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Polly.Utils; - -namespace Polly; - -/// -/// Testing related extensions for resilience pipeline builder. -/// -public static class TestingResiliencePipelineBuilderExtensions -{ - /// - /// Updates a that the builder uses. - /// - /// The resilience pipeline builder. - /// The builder instance. - /// The time provider instance. - /// The same builder instance. - public static TBuilder WithTimeProvider(this TBuilder builder, TimeProvider timeProvider) - where TBuilder : ResiliencePipelineBuilderBase - { - Guard.NotNull(builder); - Guard.NotNull(timeProvider); - - builder.TimeProvider = timeProvider; - return builder; - } -} diff --git a/test/Polly.Core.Tests/GenericResiliencePipelineBuilderTests.cs b/test/Polly.Core.Tests/GenericResiliencePipelineBuilderTests.cs index 64d19efdff4..bfe69b5f8cc 100644 --- a/test/Polly.Core.Tests/GenericResiliencePipelineBuilderTests.cs +++ b/test/Polly.Core.Tests/GenericResiliencePipelineBuilderTests.cs @@ -12,7 +12,7 @@ public class GenericResiliencePipelineBuilderTests public void Ctor_EnsureDefaults() { _builder.Name.Should().BeNull(); - _builder.TimeProvider.Should().Be(TimeProvider.System); + _builder.TimeProvider.Should().BeNull(); } [Fact] diff --git a/test/Polly.Core.Tests/ResiliencePipelineBuilderTests.cs b/test/Polly.Core.Tests/ResiliencePipelineBuilderTests.cs index db3ed78c984..f64543ca506 100644 --- a/test/Polly.Core.Tests/ResiliencePipelineBuilderTests.cs +++ b/test/Polly.Core.Tests/ResiliencePipelineBuilderTests.cs @@ -16,7 +16,19 @@ public void Ctor_EnsureDefaults() var builder = new ResiliencePipelineBuilder(); builder.Name.Should().BeNull(); - builder.TimeProvider.Should().Be(TimeProvider.System); + builder.TimeProvider.Should().BeNull(); + } + + [Fact] + public void GetTimeProvider_Ok() + { + var builder = new ResiliencePipelineBuilder(); + builder.GetTimeProvider().Should().Be(TimeProvider.System); + + var timeProvider = Substitute.For(); + builder.TimeProvider = timeProvider; + + builder.TimeProvider.Should().Be(timeProvider); } [Fact] diff --git a/test/Polly.Extensions.Tests/DependencyInjection/PollyServiceCollectionExtensionTests.cs b/test/Polly.Extensions.Tests/DependencyInjection/PollyServiceCollectionExtensionTests.cs index e149792fbf1..4179e5932d8 100644 --- a/test/Polly.Extensions.Tests/DependencyInjection/PollyServiceCollectionExtensionTests.cs +++ b/test/Polly.Extensions.Tests/DependencyInjection/PollyServiceCollectionExtensionTests.cs @@ -1,8 +1,10 @@ using System.Globalization; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; +using NSubstitute; using Polly.DependencyInjection; using Polly.Registry; using Polly.Telemetry; @@ -14,7 +16,7 @@ public class PollyServiceCollectionExtensionTests private const string Key = "my-pipeline"; private ServiceCollection _services; - public PollyServiceCollectionExtensionTests() => _services = new ServiceCollection(); + public PollyServiceCollectionExtensionTests() => _services = []; [Fact] public void AddResiliencePipeline_ArgValidation() @@ -23,7 +25,7 @@ public void AddResiliencePipeline_ArgValidation() Assert.Throws(() => AddResiliencePipeline(Key)); Assert.Throws(() => AddResiliencePipeline(Key)); - _services = new ServiceCollection(); + _services = []; Assert.Throws(() => _services.AddResiliencePipeline( Key, (Action>)null!)); @@ -250,6 +252,37 @@ public void AddResiliencePipeline_Multiple_Ok() .HaveCount(30); } + [InlineData(true)] + [InlineData(false)] + [Theory] + public void AddResiliencePipeline_EnsureTimeProvider(bool timeProviderRegistered) + { + var timeProvider = Substitute.For(); + var asserted = false; + + if (timeProviderRegistered) + { + _services.TryAddSingleton(timeProvider); + } + + _services.AddResiliencePipeline("dummy", builder => + { + if (timeProviderRegistered) + { + builder.TimeProvider.Should().Be(timeProvider); + } + else + { + builder.TimeProvider.Should().BeNull(); + } + + asserted = true; + }); + + CreateProvider().GetPipeline("dummy"); + asserted.Should().BeTrue(); + } + [Fact] public void AddResiliencePipelineRegistry_Ok() { diff --git a/test/Polly.Testing.Tests/TestingResiliencePipelineBuilderExtensionsTests.cs b/test/Polly.Testing.Tests/TestingResiliencePipelineBuilderExtensionsTests.cs deleted file mode 100644 index 9fd711e265e..00000000000 --- a/test/Polly.Testing.Tests/TestingResiliencePipelineBuilderExtensionsTests.cs +++ /dev/null @@ -1,15 +0,0 @@ -using NSubstitute; - -namespace Polly.Testing.Tests; - -public class TestingResiliencePipelineBuilderExtensionsTests -{ - [Fact] - public void WithTimeProvider_Ok() - { - var timeProvider = Substitute.For(); - var builder = new ResiliencePipelineBuilder().WithTimeProvider(timeProvider); - - builder.TimeProvider.Should().BeSameAs(timeProvider); - } -}