From b95754ab8e41df5bf31bc1cc5d977e0ce00fdca7 Mon Sep 17 00:00:00 2001 From: Martin Tomka Date: Tue, 29 Aug 2023 11:17:14 +0200 Subject: [PATCH 1/5] Cleanup Outcome internals and drop unused hedging and fallbacks APIs --- .../Controller/CircuitStateController.cs | 2 +- src/Polly.Core/Fallback/FallbackHandler.cs | 9 +-- ...backResiliencePipelineBuilderExtensions.cs | 21 ------- .../Fallback/FallbackResilienceStrategy.cs | 2 +- .../Fallback/FallbackStrategyOptions.cs | 9 --- .../Controller/HedgingExecutionContext.cs | 13 +++-- .../Hedging/Controller/HedgingHandler.cs | 41 +++----------- .../Hedging/Controller/TaskExecution.cs | 2 +- ...gingResiliencePipelineBuilderExtensions.cs | 33 +---------- .../Hedging/HedgingResilienceStrategy.cs | 2 +- .../Hedging/HedgingStrategyOptions.cs | 9 --- src/Polly.Core/Outcome.TResult.cs | 28 +--------- src/Polly.Core/Outcome.cs | 19 +++++++ .../Utils/Pipeline/BridgeComponent.TResult.cs | 49 +++++++++++++++-- src/Polly.Core/Utils/TaskHelper.cs | 16 ------ .../Fallback/FallbackHandlerTests.cs | 12 +--- ...esiliencePipelineBuilderExtensionsTests.cs | 29 ---------- .../HedgingExecutionContextTests.cs | 3 +- .../Hedging/HedgingHandlerTests.cs | 55 +------------------ .../Polly.Core.Tests/Hedging/HedgingHelper.cs | 5 +- ...esiliencePipelineBuilderExtensionsTests.cs | 42 +++----------- test/Polly.Core.Tests/OutcomeTests.cs | 31 ++++++----- test/Polly.TestUtils/TestUtilities.cs | 2 +- 23 files changed, 120 insertions(+), 314 deletions(-) delete mode 100644 src/Polly.Core/Fallback/FallbackStrategyOptions.cs delete mode 100644 src/Polly.Core/Hedging/HedgingStrategyOptions.cs diff --git a/src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs b/src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs index 34ade5d02fa..696233b1f6f 100644 --- a/src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs +++ b/src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs @@ -292,7 +292,7 @@ private bool PermitHalfOpenCircuitTest_NeedsLock() private void SetLastHandledOutcome_NeedsLock(Outcome outcome) { - _lastOutcome = outcome.AsOutcome(); + _lastOutcome = Outcome.ToObjectOutcome(outcome); if (outcome.Exception is Exception exception) { diff --git a/src/Polly.Core/Fallback/FallbackHandler.cs b/src/Polly.Core/Fallback/FallbackHandler.cs index 54a53606075..9d0cfde7ad3 100644 --- a/src/Polly.Core/Fallback/FallbackHandler.cs +++ b/src/Polly.Core/Fallback/FallbackHandler.cs @@ -4,13 +4,6 @@ internal sealed record class FallbackHandler( Func, ValueTask> ShouldHandle, Func, ValueTask>> ActionGenerator) { - public async ValueTask> GetFallbackOutcomeAsync(FallbackActionArguments args) - { - var copiedArgs = new FallbackActionArguments( - args.Context, - args.Outcome.AsOutcome()); - - return (await ActionGenerator(copiedArgs).ConfigureAwait(args.Context.ContinueOnCapturedContext)).AsOutcome(); - } + public ValueTask> GetFallbackOutcomeAsync(FallbackActionArguments args) => ActionGenerator(args); } diff --git a/src/Polly.Core/Fallback/FallbackResiliencePipelineBuilderExtensions.cs b/src/Polly.Core/Fallback/FallbackResiliencePipelineBuilderExtensions.cs index 52092a03400..b0dd748b469 100644 --- a/src/Polly.Core/Fallback/FallbackResiliencePipelineBuilderExtensions.cs +++ b/src/Polly.Core/Fallback/FallbackResiliencePipelineBuilderExtensions.cs @@ -32,27 +32,6 @@ public static class FallbackResiliencePipelineBuilderExtensions return builder.AddStrategy(context => CreateFallback(context, options), options); } - /// - /// Adds a fallback resilience strategy with the provided options to the builder. - /// - /// The resilience pipeline builder. - /// The options to configure the fallback resilience strategy. - /// The builder instance with the fallback strategy added. - /// Thrown when or is . - /// Thrown when are invalid. - [UnconditionalSuppressMessage( - "Trimming", - "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", - Justification = "All options members preserved.")] - [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(FallbackStrategyOptions))] - internal static ResiliencePipelineBuilder AddFallback(this ResiliencePipelineBuilder builder, FallbackStrategyOptions options) - { - Guard.NotNull(builder); - Guard.NotNull(options); - - return builder.AddStrategy(context => CreateFallback(context, options), options); - } - private static ResilienceStrategy CreateFallback( StrategyBuilderContext context, FallbackStrategyOptions options) diff --git a/src/Polly.Core/Fallback/FallbackResilienceStrategy.cs b/src/Polly.Core/Fallback/FallbackResilienceStrategy.cs index 790c5a5499e..5fb5d4ba019 100644 --- a/src/Polly.Core/Fallback/FallbackResilienceStrategy.cs +++ b/src/Polly.Core/Fallback/FallbackResilienceStrategy.cs @@ -37,7 +37,7 @@ protected internal override async ValueTask> ExecuteCore(Func try { - return await _handler.GetFallbackOutcomeAsync(new FallbackActionArguments(context, outcome)).ConfigureAwait(context.ContinueOnCapturedContext); + return await _handler.GetFallbackOutcomeAsync(new FallbackActionArguments(context, outcome)).ConfigureAwait(context.ContinueOnCapturedContext); } catch (Exception e) { diff --git a/src/Polly.Core/Fallback/FallbackStrategyOptions.cs b/src/Polly.Core/Fallback/FallbackStrategyOptions.cs deleted file mode 100644 index c7990fa6fbe..00000000000 --- a/src/Polly.Core/Fallback/FallbackStrategyOptions.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Polly.Fallback; - -/// -/// Represents the options for configuring a fallback resilience strategy. -/// -internal class FallbackStrategyOptions : FallbackStrategyOptions -{ -} - diff --git a/src/Polly.Core/Hedging/Controller/HedgingExecutionContext.cs b/src/Polly.Core/Hedging/Controller/HedgingExecutionContext.cs index b3ac6a9ab6f..30f93c010ba 100644 --- a/src/Polly.Core/Hedging/Controller/HedgingExecutionContext.cs +++ b/src/Polly.Core/Hedging/Controller/HedgingExecutionContext.cs @@ -55,7 +55,7 @@ public async ValueTask> LoadExecutionAsync( { if (LoadedTasks >= _maxAttempts) { - return CreateExecutionInfoWhenNoExecution(); + return CreateExecutionInfoWhenNoExecution(); } // determine what type of task we are creating @@ -77,7 +77,7 @@ public async ValueTask> LoadExecutionAsync( else { _executionPool.Return(execution); - return CreateExecutionInfoWhenNoExecution(); + return CreateExecutionInfoWhenNoExecution(); } } @@ -148,17 +148,20 @@ public async ValueTask DisposeAsync() return TryRemoveExecutedTask(); } - private ExecutionInfo CreateExecutionInfoWhenNoExecution() + private ExecutionInfo CreateExecutionInfoWhenNoExecution() { // if there are no more executing tasks we need to check finished ones if (_executingTasks.Count == 0) { var finishedExecution = _tasks.First(static t => t.ExecutionTaskSafe!.IsCompleted); finishedExecution.AcceptOutcome(); - return new ExecutionInfo(null, false, finishedExecution.Outcome.AsOutcome()); + + var outcome = Outcome.FromObjectOutcome(finishedExecution.Outcome); + + return new ExecutionInfo(null, false, outcome); } - return new ExecutionInfo(null, false, null); + return new ExecutionInfo(null, false, null); } private Task WaitForTaskCompetitionAsync() diff --git a/src/Polly.Core/Hedging/Controller/HedgingHandler.cs b/src/Polly.Core/Hedging/Controller/HedgingHandler.cs index 9c87b852994..4b61b14b909 100644 --- a/src/Polly.Core/Hedging/Controller/HedgingHandler.cs +++ b/src/Polly.Core/Hedging/Controller/HedgingHandler.cs @@ -2,44 +2,17 @@ namespace Polly.Hedging.Utils; internal sealed record class HedgingHandler( Func, ValueTask> ShouldHandle, - Func, Func>>?> ActionGenerator, - bool IsGeneric) + Func, Func>>?> ActionGenerator) { public Func>>? GenerateAction(HedgingActionGeneratorArguments args) { - if (IsGeneric) - { - var copiedArgs = new HedgingActionGeneratorArguments( - args.PrimaryContext, - args.ActionContext, - args.AttemptNumber, - args.Callback); + var copiedArgs = new HedgingActionGeneratorArguments( + args.PrimaryContext, + args.ActionContext, + args.AttemptNumber, + args.Callback); - return ActionGenerator(copiedArgs); - } - - return CreateNonGenericAction(args); - } - - private Func>>? CreateNonGenericAction(HedgingActionGeneratorArguments args) - { - var generator = (Func, Func>>?>)(object)ActionGenerator; - var action = generator(new HedgingActionGeneratorArguments(args.PrimaryContext, args.ActionContext, args.AttemptNumber, async context => - { - var outcome = await args.Callback(context).ConfigureAwait(context.ContinueOnCapturedContext); - return outcome.AsOutcome(); - })); - - if (action is null) - { - return null; - } - - return async () => - { - var outcome = await action().ConfigureAwait(args.ActionContext.ContinueOnCapturedContext); - return outcome.AsOutcome(); - }; + return ActionGenerator(copiedArgs); } } diff --git a/src/Polly.Core/Hedging/Controller/TaskExecution.cs b/src/Polly.Core/Hedging/Controller/TaskExecution.cs index 58bee637e37..cfe72736739 100644 --- a/src/Polly.Core/Hedging/Controller/TaskExecution.cs +++ b/src/Polly.Core/Hedging/Controller/TaskExecution.cs @@ -225,7 +225,7 @@ private async Task ExecutePrimaryActionAsync(Func outcome) { var args = new HedgingPredicateArguments(Context, outcome); - Outcome = outcome.AsOutcome(); + Outcome = Polly.Outcome.ToObjectOutcome(outcome); IsHandled = await _handler.ShouldHandle(args).ConfigureAwait(Context.ContinueOnCapturedContext); TelemetryUtil.ReportExecutionAttempt(_telemetry, Context, outcome, AttemptNumber, ExecutionTime, IsHandled); } diff --git a/src/Polly.Core/Hedging/HedgingResiliencePipelineBuilderExtensions.cs b/src/Polly.Core/Hedging/HedgingResiliencePipelineBuilderExtensions.cs index 81c732c3f90..1c46dd4463a 100644 --- a/src/Polly.Core/Hedging/HedgingResiliencePipelineBuilderExtensions.cs +++ b/src/Polly.Core/Hedging/HedgingResiliencePipelineBuilderExtensions.cs @@ -30,39 +30,12 @@ public static class HedgingResiliencePipelineBuilderExtensions Guard.NotNull(builder); Guard.NotNull(options); - return builder.AddStrategy(context => CreateHedgingStrategy(context, options, isGeneric: true), options); + return builder.AddStrategy(context => CreateHedgingStrategy(context, options), options); } - /// - /// Adds a hedging with the provided options to the builder. - /// - /// The resilience pipeline builder. - /// The options to configure the hedging. - /// The builder instance with the hedging added. - /// Thrown when or is . - /// Thrown when are invalid. - [UnconditionalSuppressMessage( - "Trimming", - "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", - Justification = "All options members preserved.")] - [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(HedgingStrategyOptions))] - internal static ResiliencePipelineBuilder AddHedging(this ResiliencePipelineBuilder builder, HedgingStrategyOptions options) - { - Guard.NotNull(builder); - Guard.NotNull(options); - - return builder.AddStrategy(context => CreateHedgingStrategy(context, options, isGeneric: false), options); - } - - private static HedgingResilienceStrategy CreateHedgingStrategy( - StrategyBuilderContext context, - HedgingStrategyOptions options, - bool isGeneric) + private static HedgingResilienceStrategy CreateHedgingStrategy(StrategyBuilderContext context, HedgingStrategyOptions options) { - var handler = new HedgingHandler( - options.ShouldHandle!, - options.ActionGenerator, - IsGeneric: isGeneric); + var handler = new HedgingHandler(options.ShouldHandle!, options.ActionGenerator); return new HedgingResilienceStrategy( options.Delay, diff --git a/src/Polly.Core/Hedging/HedgingResilienceStrategy.cs b/src/Polly.Core/Hedging/HedgingResilienceStrategy.cs index 311016ad7b7..6ca9c056861 100644 --- a/src/Polly.Core/Hedging/HedgingResilienceStrategy.cs +++ b/src/Polly.Core/Hedging/HedgingResilienceStrategy.cs @@ -99,7 +99,7 @@ await HandleOnHedgingAsync( continue; } - outcome = execution.Outcome.AsOutcome(); + outcome = Outcome.FromObjectOutcome(execution.Outcome); if (!execution.IsHandled) { diff --git a/src/Polly.Core/Hedging/HedgingStrategyOptions.cs b/src/Polly.Core/Hedging/HedgingStrategyOptions.cs deleted file mode 100644 index 0798b6c020e..00000000000 --- a/src/Polly.Core/Hedging/HedgingStrategyOptions.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Polly.Hedging; - -/// -/// Hedging strategy options. -/// -internal class HedgingStrategyOptions : HedgingStrategyOptions -{ -} - diff --git a/src/Polly.Core/Outcome.TResult.cs b/src/Polly.Core/Outcome.TResult.cs index abd8d9a64ba..2bb88010b36 100644 --- a/src/Polly.Core/Outcome.TResult.cs +++ b/src/Polly.Core/Outcome.TResult.cs @@ -1,6 +1,5 @@ #pragma warning disable CA1815 // Override equals and operator equals on value types -using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; namespace Polly; @@ -20,7 +19,7 @@ internal Outcome(Exception exception) internal Outcome(TResult? result) : this() => Result = result; - private Outcome(ExceptionDispatchInfo exceptionDispatchInfo) + internal Outcome(ExceptionDispatchInfo exceptionDispatchInfo) : this() => ExceptionDispatchInfo = Guard.NotNull(exceptionDispatchInfo); /// @@ -92,29 +91,4 @@ internal TResult GetResultOrRethrow() return Result!; } - internal Outcome AsOutcome() => AsOutcome(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal Outcome AsOutcome() - { - if (ExceptionDispatchInfo is not null) - { - return new Outcome(ExceptionDispatchInfo); - } - - if (Result is null) - { - return new Outcome(default(T)); - } - - if (typeof(T) == typeof(TResult)) - { - var result = Result; - - // We can use the unsafe cast here because we know for sure these two types are the same - return new Outcome(Unsafe.As(ref result)); - } - - return new Outcome((T)(object)Result); - } } diff --git a/src/Polly.Core/Outcome.cs b/src/Polly.Core/Outcome.cs index 275fe5089d1..f9f053cdab6 100644 --- a/src/Polly.Core/Outcome.cs +++ b/src/Polly.Core/Outcome.cs @@ -53,4 +53,23 @@ public static ValueTask> FromExceptionAsTask(Exception internal static Outcome FromException(Exception exception) => FromException(exception); + internal static Outcome ToObjectOutcome(Outcome outcome) + { + if (outcome.ExceptionDispatchInfo is null) + { + return FromResult((object?)outcome.Result); + } + + return new Outcome(outcome.ExceptionDispatchInfo); + } + + internal static Outcome FromObjectOutcome(Outcome outcome) + { + if (outcome.ExceptionDispatchInfo is null) + { + return FromResult((T)outcome.Result!); + } + + return new Outcome(outcome.ExceptionDispatchInfo); + } } diff --git a/src/Polly.Core/Utils/Pipeline/BridgeComponent.TResult.cs b/src/Polly.Core/Utils/Pipeline/BridgeComponent.TResult.cs index 0c4b7b3ce44..3125d7cd359 100644 --- a/src/Polly.Core/Utils/Pipeline/BridgeComponent.TResult.cs +++ b/src/Polly.Core/Utils/Pipeline/BridgeComponent.TResult.cs @@ -1,4 +1,6 @@ -namespace Polly.Utils.Pipeline; +using System.Runtime.CompilerServices; + +namespace Polly.Utils.Pipeline; [DebuggerDisplay("{Strategy}")] internal sealed class BridgeComponent : BridgeComponentBase @@ -16,7 +18,7 @@ internal override ValueTask> ExecuteCore( // Check if we can cast directly, thus saving some cycles and improving the performance if (callback is Func>> casted) { - return TaskHelper.ConvertValueTask( + return ConvertValueTask( Strategy.ExecuteCore(casted, context, state), context); } @@ -26,12 +28,51 @@ internal override ValueTask> ExecuteCore( static async (context, state) => { var outcome = await state.callback(context, state.state).ConfigureAwait(context.ContinueOnCapturedContext); - return outcome.AsOutcome(); + return AsOutcome(outcome); }, context, (callback, state)); - return TaskHelper.ConvertValueTask(valueTask, context); + return ConvertValueTask(valueTask, context); + } + } + + private static ValueTask> ConvertValueTask(ValueTask> valueTask, ResilienceContext resilienceContext) + { + if (valueTask.IsCompletedSuccessfully) + { + return new ValueTask>(AsOutcome(valueTask.Result)); + } + + return ConvertValueTaskAsync(valueTask, resilienceContext); + + static async ValueTask> ConvertValueTaskAsync(ValueTask> valueTask, ResilienceContext resilienceContext) + { + var outcome = await valueTask.ConfigureAwait(resilienceContext.ContinueOnCapturedContext); + return AsOutcome(outcome); } } + + internal static Outcome AsOutcome(Outcome outcome) + { + if (outcome.ExceptionDispatchInfo is not null) + { + return new Outcome(outcome.ExceptionDispatchInfo); + } + + if (outcome.Result is null) + { + return new Outcome(default(TTo)); + } + + if (typeof(TTo) == typeof(TFrom)) + { + var result = outcome.Result; + + // We can use the unsafe cast here because we know for sure these two types are the same + return new Outcome(Unsafe.As(ref result)); + } + + return new Outcome((TTo)(object)outcome.Result); + } } diff --git a/src/Polly.Core/Utils/TaskHelper.cs b/src/Polly.Core/Utils/TaskHelper.cs index e3b970ccc32..cd998dea2b7 100644 --- a/src/Polly.Core/Utils/TaskHelper.cs +++ b/src/Polly.Core/Utils/TaskHelper.cs @@ -34,20 +34,4 @@ public static TResult GetResult(this ValueTask task) return task.Preserve().GetAwaiter().GetResult(); } - - public static ValueTask> ConvertValueTask(ValueTask> valueTask, ResilienceContext resilienceContext) - { - if (valueTask.IsCompletedSuccessfully) - { - return new ValueTask>(valueTask.Result.AsOutcome()); - } - - return ConvertValueTaskAsync(valueTask, resilienceContext); - - static async ValueTask> ConvertValueTaskAsync(ValueTask> valueTask, ResilienceContext resilienceContext) - { - var outcome = await valueTask.ConfigureAwait(resilienceContext.ContinueOnCapturedContext); - return outcome.AsOutcome(); - } - } } diff --git a/test/Polly.Core.Tests/Fallback/FallbackHandlerTests.cs b/test/Polly.Core.Tests/Fallback/FallbackHandlerTests.cs index d95f21b906c..f0f3bad70c1 100644 --- a/test/Polly.Core.Tests/Fallback/FallbackHandlerTests.cs +++ b/test/Polly.Core.Tests/Fallback/FallbackHandlerTests.cs @@ -8,17 +8,7 @@ public async Task GenerateAction_Generic_Ok() { var handler = FallbackHelper.CreateHandler(_ => true, () => Outcome.FromResult("secondary")); var context = ResilienceContextPool.Shared.Get(); - var outcome = await handler.GetFallbackOutcomeAsync(new FallbackActionArguments(context, Outcome.FromResult("primary")))!; - - outcome.Result.Should().Be("secondary"); - } - - [Fact] - public async Task GenerateAction_NonGeneric_Ok() - { - var handler = FallbackHelper.CreateHandler(_ => true, () => Outcome.FromResult((object)"secondary")); - var context = ResilienceContextPool.Shared.Get(); - var outcome = await handler.GetFallbackOutcomeAsync(new FallbackActionArguments(context, Outcome.FromResult((object)"primary")))!; + var outcome = await handler.GetFallbackOutcomeAsync(new FallbackActionArguments(context, Outcome.FromResult("primary")))!; outcome.Result.Should().Be("secondary"); } diff --git a/test/Polly.Core.Tests/Fallback/FallbackResiliencePipelineBuilderExtensionsTests.cs b/test/Polly.Core.Tests/Fallback/FallbackResiliencePipelineBuilderExtensionsTests.cs index 1069fb4f4c5..5c601b34b91 100644 --- a/test/Polly.Core.Tests/Fallback/FallbackResiliencePipelineBuilderExtensionsTests.cs +++ b/test/Polly.Core.Tests/Fallback/FallbackResiliencePipelineBuilderExtensionsTests.cs @@ -28,35 +28,6 @@ public void AddFallback_Generic_Ok(Action> config builder.Build().GetPipelineDescriptor().FirstStrategy.StrategyInstance.Should().BeOfType(typeof(FallbackResilienceStrategy)); } - [Fact] - public void AddFallback_Ok() - { - var options = new FallbackStrategyOptions - { - ShouldHandle = args => args.Outcome switch - { - { Exception: InvalidOperationException } => PredicateResult.True, - { Result: -1 } => PredicateResult.True, - _ => PredicateResult.False - }, - FallbackAction = _ => Outcome.FromResultAsTask((object)1) - }; - - var strategy = new ResiliencePipelineBuilder().AddFallback(options).Build(); - - strategy.Execute(_ => -1).Should().Be(1); - strategy.Execute(_ => throw new InvalidOperationException()).Should().Be(1); - } - - [Fact] - public void AddFallback_InvalidOptions_Throws() - { - new ResiliencePipelineBuilder() - .Invoking(b => b.AddFallback(new FallbackStrategyOptions())) - .Should() - .Throw(); - } - [Fact] public void AddFallbackT_InvalidOptions_Throws() { diff --git a/test/Polly.Core.Tests/Hedging/Controller/HedgingExecutionContextTests.cs b/test/Polly.Core.Tests/Hedging/Controller/HedgingExecutionContextTests.cs index 2770c62d395..85621cd079c 100644 --- a/test/Polly.Core.Tests/Hedging/Controller/HedgingExecutionContextTests.cs +++ b/test/Polly.Core.Tests/Hedging/Controller/HedgingExecutionContextTests.cs @@ -103,7 +103,8 @@ public async Task TryWaitForCompletedExecutionAsync_FinishedTask_Ok() task.Should().NotBeNull(); task!.ExecutionTaskSafe!.IsCompleted.Should().BeTrue(); - task.Outcome.AsOutcome().Result!.Name.Should().Be("dummy"); + + Outcome.FromObjectOutcome(task.Outcome).Result!.Name.Should().Be("dummy"); task.AcceptOutcome(); context.LoadedTasks.Should().Be(1); } diff --git a/test/Polly.Core.Tests/Hedging/HedgingHandlerTests.cs b/test/Polly.Core.Tests/Hedging/HedgingHandlerTests.cs index 75de9741ce6..45baab8fdc4 100644 --- a/test/Polly.Core.Tests/Hedging/HedgingHandlerTests.cs +++ b/test/Polly.Core.Tests/Hedging/HedgingHandlerTests.cs @@ -10,8 +10,7 @@ public async Task GenerateAction_Generic_Ok() { var handler = new HedgingHandler( args => PredicateResult.True, - args => () => Outcome.FromResultAsTask("ok"), - true); + args => () => Outcome.FromResultAsTask("ok")); var action = handler.GenerateAction(new HedgingActionGeneratorArguments( ResilienceContextPool.Shared.Get(), @@ -22,56 +21,4 @@ public async Task GenerateAction_Generic_Ok() res.Result.Should().Be("ok"); } - - [InlineData(true)] - [InlineData(false)] - [Theory] - public async Task GenerateAction_NonGeneric_Ok(bool nullAction) - { - var handler = new HedgingHandler( - args => PredicateResult.True, - args => - { - if (nullAction) - { - return null; - } - - return () => Outcome.FromResultAsTask((object)"ok"); - }, - false); - - var action = handler.GenerateAction(new HedgingActionGeneratorArguments( - ResilienceContextPool.Shared.Get(), - ResilienceContextPool.Shared.Get(), - 0, - _ => Outcome.FromResultAsTask((object)"primary")))!; - if (nullAction) - { - action.Should().BeNull(); - } - else - { - var res = await action(); - res.Result.Should().Be("ok"); - } - } - - [Fact] - public async Task GenerateAction_NonGeneric_FromCallback() - { - var handler = new HedgingHandler( - args => PredicateResult.True, - args => () => args.Callback(args.ActionContext), - false); - - var action = handler.GenerateAction( - new HedgingActionGeneratorArguments( - ResilienceContextPool.Shared.Get(), - ResilienceContextPool.Shared.Get(), - 0, - _ => Outcome.FromResultAsTask((object)"callback")))!; - var res = await action(); - res.Result.Should().Be("callback"); - } } diff --git a/test/Polly.Core.Tests/Hedging/HedgingHelper.cs b/test/Polly.Core.Tests/Hedging/HedgingHelper.cs index 81eb49833de..995977544b5 100644 --- a/test/Polly.Core.Tests/Hedging/HedgingHelper.cs +++ b/test/Polly.Core.Tests/Hedging/HedgingHelper.cs @@ -9,10 +9,7 @@ public static HedgingHandler CreateHandler( Func, bool> shouldHandle, Func, Func>>?> generator) { - return new HedgingHandler( - args => new ValueTask(shouldHandle(args.Outcome!))!, - generator, - true); + return new HedgingHandler(args => new ValueTask(shouldHandle(args.Outcome!))!, generator); } } diff --git a/test/Polly.Core.Tests/Hedging/HedgingResiliencePipelineBuilderExtensionsTests.cs b/test/Polly.Core.Tests/Hedging/HedgingResiliencePipelineBuilderExtensionsTests.cs index 5a4a3d91af6..bfa2f2b02e5 100644 --- a/test/Polly.Core.Tests/Hedging/HedgingResiliencePipelineBuilderExtensionsTests.cs +++ b/test/Polly.Core.Tests/Hedging/HedgingResiliencePipelineBuilderExtensionsTests.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.Globalization; using Polly.Hedging; using Polly.Testing; @@ -6,51 +7,26 @@ namespace Polly.Core.Tests.Hedging; public class HedgingResiliencePipelineBuilderExtensionsTests { - private readonly ResiliencePipelineBuilder _builder = new(); - private readonly ResiliencePipelineBuilder _genericBuilder = new(); - - [Fact] - public void AddHedging_Ok() - { - _builder.AddHedging(new HedgingStrategyOptions { ShouldHandle = _ => PredicateResult.True }); - - _builder.Build().GetPipelineDescriptor().FirstStrategy.StrategyInstance - .Should().BeOfType>().Subject - .HedgingHandler.IsGeneric.Should().BeFalse(); - } + private readonly ResiliencePipelineBuilder _builder = new(); [Fact] public void AddHedging_Generic_Ok() { - _genericBuilder.AddHedging(new HedgingStrategyOptions + _builder.AddHedging(new HedgingStrategyOptions { ActionGenerator = args => () => Outcome.FromResultAsTask("dummy"), ShouldHandle = _ => PredicateResult.True }); - _genericBuilder.Build().GetPipelineDescriptor().FirstStrategy.StrategyInstance + _builder.Build().GetPipelineDescriptor().FirstStrategy.StrategyInstance .Should().BeOfType>().Subject - .HedgingHandler.IsGeneric.Should().BeTrue(); - } - - [Fact] - public void AddHedging_InvalidOptions_Throws() - { - _builder - .Invoking(b => b.AddHedging(new HedgingStrategyOptions { ActionGenerator = null! })) - .Should() - .Throw(); + .HedgingHandler.ActionGenerator.Should().NotBeNull(); } [Fact] public void AddHedgingT_InvalidOptions_Throws() { _builder - .Invoking(b => b.AddHedging(new HedgingStrategyOptions { MaxHedgedAttempts = 1000 })) - .Should() - .Throw(); - - _genericBuilder .Invoking(b => b.AddHedging(new HedgingStrategyOptions { ShouldHandle = null! })) .Should() .Throw(); @@ -63,7 +39,7 @@ public async Task AddHedging_IntegrationTest() ConcurrentQueue results = new(); var strategy = _builder - .AddHedging(new HedgingStrategyOptions + .AddHedging(new() { MaxHedgedAttempts = 4, Delay = TimeSpan.FromMilliseconds(20), @@ -80,17 +56,17 @@ public async Task AddHedging_IntegrationTest() if (args.AttemptNumber == 3) { - return Outcome.FromResult((object)"success"); + return Outcome.FromResult("success"); } - return Outcome.FromResult((object)"error"); + return Outcome.FromResult("error"); }; }, OnHedging = args => { if (args.Outcome is { } outcome) { - results.Enqueue(outcome.Result!.ToString()!); + results.Enqueue(outcome.Result!.ToString(CultureInfo.InvariantCulture)!); } else { diff --git a/test/Polly.Core.Tests/OutcomeTests.cs b/test/Polly.Core.Tests/OutcomeTests.cs index e0e3af20bb7..507f2067923 100644 --- a/test/Polly.Core.Tests/OutcomeTests.cs +++ b/test/Polly.Core.Tests/OutcomeTests.cs @@ -13,10 +13,11 @@ public void Ctor_Result_Ok() result.Should().Be(10); outcome.ToString().Should().Be("10"); - outcome.AsOutcome().HasResult.Should().BeTrue(); - outcome.AsOutcome().Exception.Should().BeNull(); - outcome.AsOutcome().IsVoidResult.Should().BeFalse(); - outcome.AsOutcome().TryGetResult(out var resultObj).Should().BeTrue(); + var objectOutcome = Outcome.ToObjectOutcome(outcome); + objectOutcome.HasResult.Should().BeTrue(); + objectOutcome.Exception.Should().BeNull(); + objectOutcome.IsVoidResult.Should().BeFalse(); + objectOutcome.TryGetResult(out var resultObj).Should().BeTrue(); resultObj.Should().Be(10); } @@ -31,11 +32,12 @@ public void Ctor_VoidResult_Ok() outcome.Result.Should().Be(VoidResult.Instance); outcome.ToString().Should().Be("void"); - outcome.AsOutcome().HasResult.Should().BeTrue(); - outcome.AsOutcome().Exception.Should().BeNull(); - outcome.AsOutcome().IsVoidResult.Should().BeTrue(); - outcome.AsOutcome().TryGetResult(out _).Should().BeFalse(); - outcome.AsOutcome().Result.Should().Be(VoidResult.Instance); + var objectOutcome = Outcome.ToObjectOutcome(outcome); + objectOutcome.HasResult.Should().BeTrue(); + objectOutcome.Exception.Should().BeNull(); + objectOutcome.IsVoidResult.Should().BeTrue(); + objectOutcome.TryGetResult(out _).Should().BeFalse(); + objectOutcome.Result.Should().Be(VoidResult.Instance); } [Fact] @@ -49,11 +51,12 @@ public void Ctor_Exception_Ok() outcome.TryGetResult(out var result).Should().BeFalse(); outcome.ToString().Should().Be("Dummy message."); - outcome.AsOutcome().HasResult.Should().BeFalse(); - outcome.AsOutcome().Exception.Should().NotBeNull(); - outcome.AsOutcome().IsVoidResult.Should().BeFalse(); - outcome.AsOutcome().TryGetResult(out _).Should().BeFalse(); - outcome.AsOutcome().ExceptionDispatchInfo.Should().Be(outcome.ExceptionDispatchInfo); + var objectOutcome = Outcome.ToObjectOutcome(outcome); + objectOutcome.HasResult.Should().BeFalse(); + objectOutcome.Exception.Should().NotBeNull(); + objectOutcome.IsVoidResult.Should().BeFalse(); + objectOutcome.TryGetResult(out _).Should().BeFalse(); + objectOutcome.ExceptionDispatchInfo.Should().Be(outcome.ExceptionDispatchInfo); } [Fact] diff --git a/test/Polly.TestUtils/TestUtilities.cs b/test/Polly.TestUtils/TestUtilities.cs index 47909c50876..0dd5080c8ed 100644 --- a/test/Polly.TestUtils/TestUtilities.cs +++ b/test/Polly.TestUtils/TestUtilities.cs @@ -104,6 +104,6 @@ public static TelemetryEventArguments AsObjectArguments() : null); + args.Outcome.HasValue ? Outcome.ToObjectOutcome(args.Outcome.Value) : null); } } From 4fec8ed5051fd7f21f79cb6bef864972efbf19a7 Mon Sep 17 00:00:00 2001 From: Martin Tomka Date: Tue, 29 Aug 2023 11:23:34 +0200 Subject: [PATCH 2/5] Cleanup --- src/Polly.Core/Utils/Pipeline/BridgeComponent.TResult.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Polly.Core/Utils/Pipeline/BridgeComponent.TResult.cs b/src/Polly.Core/Utils/Pipeline/BridgeComponent.TResult.cs index 3125d7cd359..222e5de7338 100644 --- a/src/Polly.Core/Utils/Pipeline/BridgeComponent.TResult.cs +++ b/src/Polly.Core/Utils/Pipeline/BridgeComponent.TResult.cs @@ -28,7 +28,7 @@ internal override ValueTask> ExecuteCore( static async (context, state) => { var outcome = await state.callback(context, state.state).ConfigureAwait(context.ContinueOnCapturedContext); - return AsOutcome(outcome); + return ConvertOutcome(outcome); }, context, (callback, state)); @@ -41,7 +41,7 @@ private static ValueTask> ConvertValueTask(ValueTask>(AsOutcome(valueTask.Result)); + return new ValueTask>(ConvertOutcome(valueTask.Result)); } return ConvertValueTaskAsync(valueTask, resilienceContext); @@ -49,11 +49,11 @@ private static ValueTask> ConvertValueTask(ValueTask> ConvertValueTaskAsync(ValueTask> valueTask, ResilienceContext resilienceContext) { var outcome = await valueTask.ConfigureAwait(resilienceContext.ContinueOnCapturedContext); - return AsOutcome(outcome); + return ConvertOutcome(outcome); } } - internal static Outcome AsOutcome(Outcome outcome) + private static Outcome ConvertOutcome(Outcome outcome) { if (outcome.ExceptionDispatchInfo is not null) { From 1cc23d71cd762a283157a3ff54caad630d6a3712 Mon Sep 17 00:00:00 2001 From: Martin Tomka Date: Tue, 29 Aug 2023 11:28:47 +0200 Subject: [PATCH 3/5] test fixes --- .../FallbackResilienceStrategyTests.cs | 38 ++++++++++++++----- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/test/Polly.Core.Tests/Fallback/FallbackResilienceStrategyTests.cs b/test/Polly.Core.Tests/Fallback/FallbackResilienceStrategyTests.cs index d75648978e8..8d7120a4f79 100644 --- a/test/Polly.Core.Tests/Fallback/FallbackResilienceStrategyTests.cs +++ b/test/Polly.Core.Tests/Fallback/FallbackResilienceStrategyTests.cs @@ -1,3 +1,4 @@ +using System.Runtime.CompilerServices; using Polly.Fallback; using Polly.Telemetry; @@ -31,25 +32,42 @@ public void Handle_Result_Ok() called.Should().BeTrue(); } - [Fact] - public void ShouldHandle_ArgumentsSetCorrectly() + [InlineData(true)] + [InlineData(false)] + [Theory] + public void ShouldHandle_ArgumentsSetCorrectly(bool handle) { - var called = false; + var called = 0; _handler = new FallbackHandler( args => { - called = true; args.Outcome.Result.Should().Be("ok"); args.Context.Should().NotBeNull(); - called = true; + called++; - return PredicateResult.False; + return new ValueTask(handle); }, - args => Outcome.FromResultAsTask("fallback")); - - Create().Execute(_ => "ok").Should().Be("ok"); - called.Should().BeTrue(); + args => + { + args.Outcome.Result.Should().Be("ok"); + args.Context.Should().NotBeNull(); + called++; + return Outcome.FromResultAsTask("fallback"); + }); + + var result = Create().Execute(_ => "ok"); + + if (handle) + { + result.Should().Be("fallback"); + called.Should().Be(2); + } + else + { + result.Should().Be("ok"); + called.Should().Be(1); + } } [Fact] From 7dfc25066c6348989de091f320ed67b842affcc5 Mon Sep 17 00:00:00 2001 From: Martin Tomka Date: Tue, 29 Aug 2023 11:35:15 +0200 Subject: [PATCH 4/5] fixes --- .../Polly.Core.Tests/Fallback/FallbackResilienceStrategyTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/test/Polly.Core.Tests/Fallback/FallbackResilienceStrategyTests.cs b/test/Polly.Core.Tests/Fallback/FallbackResilienceStrategyTests.cs index 8d7120a4f79..0e0cbad4ab9 100644 --- a/test/Polly.Core.Tests/Fallback/FallbackResilienceStrategyTests.cs +++ b/test/Polly.Core.Tests/Fallback/FallbackResilienceStrategyTests.cs @@ -1,4 +1,3 @@ -using System.Runtime.CompilerServices; using Polly.Fallback; using Polly.Telemetry; From 37318c41b501e77bca7abaeee7ef86ee112a53e0 Mon Sep 17 00:00:00 2001 From: Martin Tomka Date: Tue, 29 Aug 2023 11:48:21 +0200 Subject: [PATCH 5/5] Drop object-based outcome conversions --- ...akerResiliencePipelineBuilderExtensions.cs | 2 ++ .../CircuitBreakerResilienceStrategy.cs | 2 +- .../CircuitBreakerStateProvider.cs | 11 +--------- .../Controller/CircuitStateController.cs | 10 ++++----- .../Controller/HedgingExecutionContext.cs | 4 +--- .../Hedging/Controller/TaskExecution.cs | 4 ++-- .../Hedging/HedgingResilienceStrategy.cs | 2 +- src/Polly.Core/Outcome.cs | 20 ------------------ .../CircuitBreakerResilienceStrategyTests.cs | 1 - .../CircuitBreakerStateProviderTests.cs | 13 ++---------- .../HedgingExecutionContextTests.cs | 2 +- .../Hedging/Controller/TaskExecutionTests.cs | 4 ++-- test/Polly.Core.Tests/OutcomeTests.cs | 21 ------------------- test/Polly.TestUtils/TestUtilities.cs | 9 +++++++- 14 files changed, 26 insertions(+), 79 deletions(-) diff --git a/src/Polly.Core/CircuitBreaker/CircuitBreakerResiliencePipelineBuilderExtensions.cs b/src/Polly.Core/CircuitBreaker/CircuitBreakerResiliencePipelineBuilderExtensions.cs index 8a2c8f4ee90..189d3c5c886 100644 --- a/src/Polly.Core/CircuitBreaker/CircuitBreakerResiliencePipelineBuilderExtensions.cs +++ b/src/Polly.Core/CircuitBreaker/CircuitBreakerResiliencePipelineBuilderExtensions.cs @@ -77,6 +77,7 @@ internal static CircuitBreakerResilienceStrategy CreateStrategy( options.BreakDuration, options.OnOpened, @@ -85,6 +86,7 @@ internal static CircuitBreakerResilienceStrategy CreateStrategy( options.ShouldHandle!, diff --git a/src/Polly.Core/CircuitBreaker/CircuitBreakerResilienceStrategy.cs b/src/Polly.Core/CircuitBreaker/CircuitBreakerResilienceStrategy.cs index c53c9ae97ee..d509f99fd89 100644 --- a/src/Polly.Core/CircuitBreaker/CircuitBreakerResilienceStrategy.cs +++ b/src/Polly.Core/CircuitBreaker/CircuitBreakerResilienceStrategy.cs @@ -15,7 +15,7 @@ public CircuitBreakerResilienceStrategy( _handler = handler; _controller = controller; - stateProvider?.Initialize(() => _controller.CircuitState, () => _controller.LastHandledOutcome); + stateProvider?.Initialize(() => _controller.CircuitState); _manualControlRegistration = manualControl?.Initialize( async c => await _controller.IsolateCircuitAsync(c).ConfigureAwait(c.ContinueOnCapturedContext), async c => await _controller.CloseCircuitAsync(c).ConfigureAwait(c.ContinueOnCapturedContext)); diff --git a/src/Polly.Core/CircuitBreaker/CircuitBreakerStateProvider.cs b/src/Polly.Core/CircuitBreaker/CircuitBreakerStateProvider.cs index 90ff5567663..314d8fa9f23 100644 --- a/src/Polly.Core/CircuitBreaker/CircuitBreakerStateProvider.cs +++ b/src/Polly.Core/CircuitBreaker/CircuitBreakerStateProvider.cs @@ -6,9 +6,8 @@ namespace Polly.CircuitBreaker; public sealed class CircuitBreakerStateProvider { private Func? _circuitStateProvider; - private Func?>? _lastHandledOutcomeProvider; - internal void Initialize(Func circuitStateProvider, Func?> lastHandledOutcomeProvider) + internal void Initialize(Func circuitStateProvider) { if (_circuitStateProvider != null) { @@ -16,7 +15,6 @@ internal void Initialize(Func circuitStateProvider, Func @@ -32,11 +30,4 @@ internal void Initialize(Func circuitStateProvider, Func public CircuitState CircuitState => _circuitStateProvider?.Invoke() ?? CircuitState.Closed; - - /// - /// Gets the last outcome handled by the circuit-breaker. - /// - /// This will be null if no exceptions or results have been handled by the circuit-breaker since the circuit last closed. - /// - internal Outcome? LastHandledOutcome => _lastHandledOutcomeProvider?.Invoke(); } diff --git a/src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs b/src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs index 696233b1f6f..cc56c1b87aa 100644 --- a/src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs +++ b/src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs @@ -18,7 +18,7 @@ internal sealed class CircuitStateController : IDisposable private readonly TimeSpan _breakDuration; private DateTimeOffset _blockedUntil; private CircuitState _circuitState = CircuitState.Closed; - private Outcome? _lastOutcome; + private Outcome? _lastOutcome; private BrokenCircuitException _breakingException = new(); private bool _disposed; @@ -66,7 +66,7 @@ public Exception? LastException } } - public Outcome? LastHandledOutcome + public Outcome? LastHandledOutcome { get { @@ -290,9 +290,9 @@ private bool PermitHalfOpenCircuitTest_NeedsLock() return false; } - private void SetLastHandledOutcome_NeedsLock(Outcome outcome) + private void SetLastHandledOutcome_NeedsLock(Outcome outcome) { - _lastOutcome = Outcome.ToObjectOutcome(outcome); + _lastOutcome = outcome; if (outcome.Exception is Exception exception) { @@ -300,7 +300,7 @@ private void SetLastHandledOutcome_NeedsLock(Outcome outcome) } else if (outcome.TryGetResult(out var result)) { - _breakingException = new BrokenCircuitException(BrokenCircuitException.DefaultMessage, result!); + _breakingException = new BrokenCircuitException(BrokenCircuitException.DefaultMessage, result!); } } diff --git a/src/Polly.Core/Hedging/Controller/HedgingExecutionContext.cs b/src/Polly.Core/Hedging/Controller/HedgingExecutionContext.cs index 30f93c010ba..ff01546489f 100644 --- a/src/Polly.Core/Hedging/Controller/HedgingExecutionContext.cs +++ b/src/Polly.Core/Hedging/Controller/HedgingExecutionContext.cs @@ -156,9 +156,7 @@ private ExecutionInfo CreateExecutionInfoWhenNoExecution() var finishedExecution = _tasks.First(static t => t.ExecutionTaskSafe!.IsCompleted); finishedExecution.AcceptOutcome(); - var outcome = Outcome.FromObjectOutcome(finishedExecution.Outcome); - - return new ExecutionInfo(null, false, outcome); + return new ExecutionInfo(null, false, finishedExecution.Outcome); } return new ExecutionInfo(null, false, null); diff --git a/src/Polly.Core/Hedging/Controller/TaskExecution.cs b/src/Polly.Core/Hedging/Controller/TaskExecution.cs index cfe72736739..568173ea299 100644 --- a/src/Polly.Core/Hedging/Controller/TaskExecution.cs +++ b/src/Polly.Core/Hedging/Controller/TaskExecution.cs @@ -49,7 +49,7 @@ public TaskExecution(HedgingHandler handler, CancellationTokenSourcePool canc /// public Task? ExecutionTaskSafe { get; private set; } - public Outcome Outcome { get; private set; } + public Outcome Outcome { get; private set; } public bool IsHandled { get; private set; } @@ -225,7 +225,7 @@ private async Task ExecutePrimaryActionAsync(Func outcome) { var args = new HedgingPredicateArguments(Context, outcome); - Outcome = Polly.Outcome.ToObjectOutcome(outcome); + Outcome = outcome; IsHandled = await _handler.ShouldHandle(args).ConfigureAwait(Context.ContinueOnCapturedContext); TelemetryUtil.ReportExecutionAttempt(_telemetry, Context, outcome, AttemptNumber, ExecutionTime, IsHandled); } diff --git a/src/Polly.Core/Hedging/HedgingResilienceStrategy.cs b/src/Polly.Core/Hedging/HedgingResilienceStrategy.cs index 6ca9c056861..b8c8cf1ea25 100644 --- a/src/Polly.Core/Hedging/HedgingResilienceStrategy.cs +++ b/src/Polly.Core/Hedging/HedgingResilienceStrategy.cs @@ -99,7 +99,7 @@ await HandleOnHedgingAsync( continue; } - outcome = Outcome.FromObjectOutcome(execution.Outcome); + outcome = execution.Outcome; if (!execution.IsHandled) { diff --git a/src/Polly.Core/Outcome.cs b/src/Polly.Core/Outcome.cs index f9f053cdab6..adf0a210fe5 100644 --- a/src/Polly.Core/Outcome.cs +++ b/src/Polly.Core/Outcome.cs @@ -52,24 +52,4 @@ public static ValueTask> FromExceptionAsTask(Exception internal static Outcome Void => FromResult(VoidResult.Instance); internal static Outcome FromException(Exception exception) => FromException(exception); - - internal static Outcome ToObjectOutcome(Outcome outcome) - { - if (outcome.ExceptionDispatchInfo is null) - { - return FromResult((object?)outcome.Result); - } - - return new Outcome(outcome.ExceptionDispatchInfo); - } - - internal static Outcome FromObjectOutcome(Outcome outcome) - { - if (outcome.ExceptionDispatchInfo is null) - { - return FromResult((T)outcome.Result!); - } - - return new Outcome(outcome.ExceptionDispatchInfo); - } } diff --git a/test/Polly.Core.Tests/CircuitBreaker/CircuitBreakerResilienceStrategyTests.cs b/test/Polly.Core.Tests/CircuitBreaker/CircuitBreakerResilienceStrategyTests.cs index c747c56d5db..fe0d54ee9e9 100644 --- a/test/Polly.Core.Tests/CircuitBreaker/CircuitBreakerResilienceStrategyTests.cs +++ b/test/Polly.Core.Tests/CircuitBreaker/CircuitBreakerResilienceStrategyTests.cs @@ -44,7 +44,6 @@ public void Ctor_StateProvider_EnsureAttached() _options.StateProvider.IsInitialized.Should().BeTrue(); _options.StateProvider.CircuitState.Should().Be(CircuitState.Closed); - _options.StateProvider.LastHandledOutcome.Should().Be(null); } [Fact] diff --git a/test/Polly.Core.Tests/CircuitBreaker/CircuitBreakerStateProviderTests.cs b/test/Polly.Core.Tests/CircuitBreaker/CircuitBreakerStateProviderTests.cs index 73e596a148f..4707c5fa319 100644 --- a/test/Polly.Core.Tests/CircuitBreaker/CircuitBreakerStateProviderTests.cs +++ b/test/Polly.Core.Tests/CircuitBreaker/CircuitBreakerStateProviderTests.cs @@ -18,7 +18,6 @@ public void NotInitialized_EnsureDefaults() var provider = new CircuitBreakerStateProvider(); provider.CircuitState.Should().Be(CircuitState.Closed); - provider.LastHandledOutcome.Should().Be(null); } [Fact] @@ -36,10 +35,10 @@ await control public void Initialize_Twice_Throws() { var provider = new CircuitBreakerStateProvider(); - provider.Initialize(() => CircuitState.Closed, () => null); + provider.Initialize(() => CircuitState.Closed); provider - .Invoking(c => c.Initialize(() => CircuitState.Closed, () => null)) + .Invoking(c => c.Initialize(() => CircuitState.Closed)) .Should() .Throw(); } @@ -49,24 +48,16 @@ public void Initialize_Ok() { var provider = new CircuitBreakerStateProvider(); var stateCalled = false; - var exceptionCalled = false; provider.Initialize( () => { stateCalled = true; return CircuitState.HalfOpen; - }, - () => - { - exceptionCalled = true; - return Outcome.FromException(new InvalidOperationException()); }); provider.CircuitState.Should().Be(CircuitState.HalfOpen); - provider.LastHandledOutcome!.Value.Exception.Should().BeOfType(); stateCalled.Should().BeTrue(); - exceptionCalled.Should().BeTrue(); } } diff --git a/test/Polly.Core.Tests/Hedging/Controller/HedgingExecutionContextTests.cs b/test/Polly.Core.Tests/Hedging/Controller/HedgingExecutionContextTests.cs index 85621cd079c..bbd20da0eb9 100644 --- a/test/Polly.Core.Tests/Hedging/Controller/HedgingExecutionContextTests.cs +++ b/test/Polly.Core.Tests/Hedging/Controller/HedgingExecutionContextTests.cs @@ -104,7 +104,7 @@ public async Task TryWaitForCompletedExecutionAsync_FinishedTask_Ok() task.Should().NotBeNull(); task!.ExecutionTaskSafe!.IsCompleted.Should().BeTrue(); - Outcome.FromObjectOutcome(task.Outcome).Result!.Name.Should().Be("dummy"); + task.Outcome.Result!.Name.Should().Be("dummy"); task.AcceptOutcome(); context.LoadedTasks.Should().Be(1); } diff --git a/test/Polly.Core.Tests/Hedging/Controller/TaskExecutionTests.cs b/test/Polly.Core.Tests/Hedging/Controller/TaskExecutionTests.cs index 669bd4d18da..760831a6b66 100644 --- a/test/Polly.Core.Tests/Hedging/Controller/TaskExecutionTests.cs +++ b/test/Polly.Core.Tests/Hedging/Controller/TaskExecutionTests.cs @@ -58,7 +58,7 @@ await execution.InitializeAsync(HedgedTaskType.Primary, _snapshot, 99); await execution.ExecutionTaskSafe!; - ((DisposableResult)execution.Outcome.Result!).Name.Should().Be(value); + execution.Outcome.Result!.Name.Should().Be(value); execution.IsHandled.Should().Be(handled); AssertPrimaryContext(execution.Context, execution); @@ -98,7 +98,7 @@ public async Task Initialize_Secondary_Ok(string value, bool handled) await execution.ExecutionTaskSafe!; - ((DisposableResult)execution.Outcome.Result!).Name.Should().Be(value); + execution.Outcome.Result!.Name.Should().Be(value); execution.IsHandled.Should().Be(handled); AssertSecondaryContext(execution.Context, execution); } diff --git a/test/Polly.Core.Tests/OutcomeTests.cs b/test/Polly.Core.Tests/OutcomeTests.cs index 507f2067923..4474a652b0a 100644 --- a/test/Polly.Core.Tests/OutcomeTests.cs +++ b/test/Polly.Core.Tests/OutcomeTests.cs @@ -12,13 +12,6 @@ public void Ctor_Result_Ok() outcome.TryGetResult(out var result).Should().BeTrue(); result.Should().Be(10); outcome.ToString().Should().Be("10"); - - var objectOutcome = Outcome.ToObjectOutcome(outcome); - objectOutcome.HasResult.Should().BeTrue(); - objectOutcome.Exception.Should().BeNull(); - objectOutcome.IsVoidResult.Should().BeFalse(); - objectOutcome.TryGetResult(out var resultObj).Should().BeTrue(); - resultObj.Should().Be(10); } [Fact] @@ -31,13 +24,6 @@ public void Ctor_VoidResult_Ok() outcome.TryGetResult(out var result).Should().BeFalse(); outcome.Result.Should().Be(VoidResult.Instance); outcome.ToString().Should().Be("void"); - - var objectOutcome = Outcome.ToObjectOutcome(outcome); - objectOutcome.HasResult.Should().BeTrue(); - objectOutcome.Exception.Should().BeNull(); - objectOutcome.IsVoidResult.Should().BeTrue(); - objectOutcome.TryGetResult(out _).Should().BeFalse(); - objectOutcome.Result.Should().Be(VoidResult.Instance); } [Fact] @@ -50,13 +36,6 @@ public void Ctor_Exception_Ok() outcome.IsVoidResult.Should().BeFalse(); outcome.TryGetResult(out var result).Should().BeFalse(); outcome.ToString().Should().Be("Dummy message."); - - var objectOutcome = Outcome.ToObjectOutcome(outcome); - objectOutcome.HasResult.Should().BeFalse(); - objectOutcome.Exception.Should().NotBeNull(); - objectOutcome.IsVoidResult.Should().BeFalse(); - objectOutcome.TryGetResult(out _).Should().BeFalse(); - objectOutcome.ExceptionDispatchInfo.Should().Be(outcome.ExceptionDispatchInfo); } [Fact] diff --git a/test/Polly.TestUtils/TestUtilities.cs b/test/Polly.TestUtils/TestUtilities.cs index 0dd5080c8ed..53bb9ccb680 100644 --- a/test/Polly.TestUtils/TestUtilities.cs +++ b/test/Polly.TestUtils/TestUtilities.cs @@ -99,11 +99,18 @@ public static ResilienceContext WithVoidResultType(this ResilienceContext contex public static TelemetryEventArguments AsObjectArguments(this TelemetryEventArguments args) { + Outcome? outcome = args.Outcome switch + { + null => null, + { Exception: { } ex } => Outcome.FromException(ex), + _ => Outcome.FromResult(args.Outcome!.Value.Result), + }; + return new TelemetryEventArguments( args.Source, args.Event, args.Context, args.Arguments!, - args.Outcome.HasValue ? Outcome.ToObjectOutcome(args.Outcome.Value) : null); + outcome); } }